#include <ctype.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#define BLANK_LINES 1
#define ALL_LINES   2

static char **parse_options(char *argv[]);
static void read_file(FILE *in);
static void version(void);
static void help(void);

static int control;    /* Show control characters as ^ */
static int show_count; /* Show line count */
static int show_end;   /* End lines with $ */
static int show_tab;   /* Show tabs as ^I */
static int squeeze;    /* Change multiple blank lines into one */

static int line = 1;

int main(int argc, char *argv[])
{
  argv = parse_options(argv);

  if (*argv == NULL) {
    read_file(stdin);
  } else {
    FILE *in;

    for (; *argv != NULL; *++argv) {
      if (**argv == '-') {
        in = stdin;
      } else {
        if ((in = fopen(*argv, "r")) == NULL) {
          continue;
        }
      }

      read_file(in);
    }
  }

  return 0;
}

char **parse_options(char *argv[])
{
  while (*++argv != NULL && **argv == '-') {
    /* Terminating options */
    if (strcmp(*argv, "--version") == 0) {
      version();
    }
    if (strcmp(*argv, "--help") == 0) {
      help();
    }

    /* Behavioral switches */
    while (*++(*argv) != '\0') {
      switch (**argv) {
        case 'b': show_count = BLANK_LINES;          break;
        case 'e': control = show_end = 1;            break;
        case 'n': show_count = ALL_LINES;            break;
        case 's': squeeze = 1;                       break;
        case 't': control = show_tab = 1;            break;
        case 'u': /* No-op for Unix compatibility */ break;
        case 'v': control = 1;                       break;
        case 'A': control = show_end = show_tab = 1; break;
        case 'E': show_end = 1;                      break;
        case 'T': show_tab = 1;                      break;
        default: /* Ignore unrecognized switches */  break;
      }
    }
  }

  return argv;
}

void read_file(FILE *in)
{
  int ch, last;

  for (last = '\n'; (ch = getc(in)) != EOF; last = ch) {
    if (last == '\n') {
line_count:
      if (show_count == ALL_LINES) {
        printf("%-5d", line++);
      } else if (show_count == BLANK_LINES && ch != '\n') {
        printf("%-5d", line++);
      }

      if (squeeze && ch == '\n') {
        if (show_end) {
          putchar('$');
        }
        putchar('\n');

        /* Ignore extra empty lines */
        while ((ch = getc(in)) != EOF && ch == '\n') {
          /* Do nothing */
        }

        if (ch == EOF) {
          break;
        } else {
          goto line_count; /* Print line # first */
        }
      }
    }

    if (show_tab && ch == '\t') {
      printf("^I");
    } else if (control && iscntrl(ch)) {
      putchar('^');
    } else if (show_end && ch == '\n') {
      printf("$\n");
    } else {
      putchar(ch);
    }
  }
}

void version(void)
{
  printf("cat clone v1.0\n");
  exit(EXIT_SUCCESS);
}

void help(void)
{
  printf("cat [-benstuvAET] [--help] [--version] [file...]\n");
  exit(EXIT_SUCCESS);
}
3
Contributors
2
Replies
6
Views
12 Years
Discussion Span
Last Post by Ene Uran
0

Thank you for putting up this, it's nice, and certainly much more advanced than mine, though I just wanted to provide a minimal kit which enables to edit files. I have a few questions though, not necessarily about your code, but about such things in general, my code is not better either in that respect.

1. If it doesn't succeed to open a file, it should return abnormally (return 1).

2. Don't know whether using getch is even the best, if we execute the program inside shell script, we don't know whether it read all files, or only part of them. Maybe it's better to use fread, so that we can distinguish between end of file and read error, or then we must find out the file size and count characters.

3. It's a question whether using pipes and redirection, there is any need to check output errors. Good pipes should provide that, so that the system would not become too messed up when we have a faulty hard drive or other devise, but likely at least redirection doesn't provide any such possibility. Well, our programs may be perfect though, in spite that the system is not so perfect.

Also, who don't know, at least with some compilers, if you use wildcards in file name in this program, it supplies all files it finds in a command line, this is the only way to do it in strict ansi c, which has no glob function.

Have something to contribute to this discussion? Please be thoughtful, detailed and courteous, and be sure to adhere to our posting rules.