/* c-cat -- template c progs like cat (use their args as files for read) */ /* $Id$ */ /* Carlos Duarte, 980420/990301 */ /* * To use this: * . search for "USER CODE", near the end of this file * . start editing below that point... * . fill in four functions: * usage() * print error messages on bad arg passing * do_args() * scan passed cmd line arguments * do_file() * process each input file * do_exit() * runs before program termination * * Any setup can be made, but the example (a simple cat(1)), uses the * following: * . make use of pre-defined functions * error() to flags an error message, and (optionally) exits * xmalloc/xrealloc, mem allocation, with error exiting * get_delim(), fetch lines, or records from input * * . use do_args(), to initialize defaults, then fill them, and * do some checkings * * . use do_exit(), to cleanup globals (closing files, freeing memory) * * . use a structure to keep all options data * */ #include #include #include #include #include #include #include #include #ifndef NULL #define NULL 0 #endif #ifndef EOF #define EOF (-1) #endif char *program_name; /* * if `status' != 0, exit; else, just warn; * if `error_no' != 0, print associated errno message */ static void error(int status, int error_no, char *fmt, ... ) { va_list ap; int did_write=0; if (program_name) { fprintf(stderr, "%s: ", program_name); did_write++; } if (fmt) { va_start(ap, fmt); vfprintf(stderr, fmt, ap); va_end(ap); did_write++; } if (error_no) { fprintf(stderr, "%s", sys_errlist[error_no]); did_write++; } if (did_write) { fprintf(stderr, "\n"); fflush(stderr); } if (status) exit(status); } static char * mk_progname(char *s) { char *t; if (s == NULL) return "????"; if ((t = strrchr(s, '/')) != NULL) return t+1; return s; } /* forw decls, of functions needed by main */ static void usage(void); static void do_args(int,char *[]); static void do_file(FILE *, char *); static void do_exit(void); int main(int argc, char *argv[]) { program_name = mk_progname(argv[0]); do_args(argc, argv); if (optind >= argc) do_file(stdin, "stdin"); else for (; optind < argc; optind++) { char *fn = argv[optind]; FILE *f; struct stat statbuf; if (strcmp(fn, "-") == 0) { do_file(stdin, "-"); } else if ((f = fopen(fn, "r")) == NULL) { error(0, errno, "%s: ", fn); } else if (fstat(fileno(f), &statbuf) < 0) { error(0, errno, "%s: ", fn); fclose(f); } else if ((statbuf.st_mode & S_IFMT) == S_IFDIR) { error(0, 0, "`%s' is a directory!", fn); fclose(f); } else { do_file(f, fn); fclose(f); } } do_exit(); exit(0); } /* get line facility */ static void * xmalloc(size_t n) { void *x = malloc(n); if (x==0) error(1, errno, "malloc"); return x; } static void * xrealloc(void *x, size_t n) { void *y = realloc(x,n); if (y==0) { free(x); error(1, errno, "realloc"); } return y; } /* if `delim'<0, eat -delim out; else, include it on buffer */ static int get_delim(FILE *f, char **bufp, size_t *dimp, int delim) { const int chunk = 512; char *top, *s; int c; int inc_delim; if (ferror(f) || feof(f)) return -1; if (*bufp == 0 || *dimp == 0) { *dimp = chunk; *bufp = xmalloc(*dimp); } inc_delim = delim>=0; if (delim < 0) delim = -delim; else if (delim == 0) delim = '\n'; s = *bufp; top = s + (*dimp - 2); /* leave two extra, for delim and \0 */ do { if ((c = getc(f)) == -1) { if (feof(f) && s > *bufp) goto skip_delim; return -1; } if (s >= top) { char *t = xrealloc(*bufp, *dimp+chunk); s = t + (s - *bufp); *bufp = t; *dimp += chunk; top = *bufp + (*dimp - 2); } *s++ = c; } while (c != delim); if (!inc_delim) s--; skip_delim: *s = '\0'; /* returns length of the useful part in buffer */ return s - *bufp; } /****************************************************************/ /* USER CODE */ /****************************************************************/ /* message to print on invalid argument passing */ static void usage(void) { fprintf(stderr, "\ usage: %s [-n] [-o outfile] [files...]\n\ ", program_name); exit(2); } struct options { int do_numbers; FILE *out_file; } opts; /* process cmd line args */ static void do_args(int argc, char *argv[]) { /* defaults here */ opts.out_file = NULL; opts.do_numbers = 0; for (;;) switch (getopt(argc, argv, "no:")) { case 'n': /* process option n, no args */ opts.do_numbers=1; break; case 'o': /* process option o, 1 arg on optarg */ if ((opts.out_file = fopen(optarg, "w")) == NULL) error(2,errno, "can not open %s for write", optarg); break; case EOF: goto out; case '?': default: usage(); } out: ; } char *bline = 0; size_t bline_size = 0; /* run just before program ends */ static void do_exit(void) { if (bline) free(bline); if (opts.out_file) fclose(opts.out_file); } int rec_cnt = 0; /* process file F, named FN */ static void do_file(FILE *f, char *fn) { int len; FILE *fout = stdout; if (opts.out_file) fout = opts.out_file; while ((len = get_delim(f, &bline, &bline_size, 0)) != -1) { rec_cnt++; if (opts.do_numbers) fprintf(fout, "%6d\t", rec_cnt); fwrite(bline, 1, len, fout); } }