cat clone

Narue 0 Tallied Votes 144 Views Share

A fairly complete implementation to provide another perspective on a seemingly simple program.

#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);
}
TkTkorrovi 69 Junior Poster

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.

Ene Uran 638 Posting Virtuoso

What is "cat" and where does it come from?

Be a part of the DaniWeb community

We're a friendly, industry-focused community of developers, IT pros, digital marketers, and technology enthusiasts meeting, networking, learning, and sharing knowledge.