/****************************************************************************** * cat.c * Catenates one or more files to the standard output. * * Notes * This code makes allowances for PC-DOS's treatment of newline * characters ('\n') by setting all file modes to BINARY. * * Notice * Copyright (c)1991-96, David R. Tribble. * * History * 1.00 05-23-1991 drt. First cut. * * 2.00 09-27-1991 drt. Added options, ala Unix cat(1). * 2.01 09-12-1994 drt. Added `-?' option. * 2.02 04-28-1995 drt. Made ANSI compliant. * Fixed `-t' and `-v' conflict. * 2.03 05-26-1995 drt. Reformatted. * Added `-o' option. * 2.04 05-15-1996 drt. Fixed `-nllf' option. */ /* Identification */ #define ID_VERS "2.04" #define ID_DATE "05-15-1996" #define ID_TITLE "CAT" #define ID_AUTH "David R. Tribble" #define ID_COMP __DATE__ " " __TIME__ static const char copyright[] = "Copyright (c)" "1995-96" ", " ID_AUTH "."; static const char id[] = "@(#)" ID_VERS " " ID_DATE " " __FILE__; static const char compile[] = "[" ID_TITLE ", " ID_VERS " " ID_DATE "] <" ID_COMP ">"; static const char title[] = ID_TITLE "\0#########"; static const char vers[] = ID_VERS "\0####"; static const char date[] = ID_DATE; static const char auth[] = ID_AUTH; static const char * prog = title; /* System includes */ #ifdef MSDOS #define PCDOS 1 #endif #ifdef __MSDOS__ #define PCDOS 1 #endif #include #include #include #include #include #include #ifdef PCDOS #include #endif #if 0 /* For declarations that are missing from */ extern int errno; extern char * sys_errlist[]; extern int sys_nerr; #endif /* Local includes */ #ifndef NFSEARCH #include "fsearch.h" #endif #ifdef PATH #include "path.h" #endif /* Debugging macros */ #if DEBUG #define DL(e) e #else #define DL(e) (0) #endif /* Return codes */ #define RC_OKAY 0 /* Successful */ #define RC_READ 1 /* Read error */ #define RC_WRITE 2 /* Write error */ #define RC_USAGE 255 /* Invalid usage */ /* Manifest constants */ #ifdef PCDOS #define READMODE "rb" #else #define READMODE "r" #endif #define TRUE 1 #define FALSE 0 #define CRLF "\r\n" #ifndef BUFSZ #define BUFSZ (24 * 1024) /* Buffer len */ #endif #define HT '\t' /* Tab */ #define NL '\n' /* Newline (linefeed) */ #define LF NL /* Linefeed (newline) */ #define FF '\f' /* Formfeed */ #define CR '\r' /* Return */ #define DEL 0x7F /* Delete (rubout) */ #define CTRL_Z ('Z'-'@') /* Control-Z (EOF) */ /* Private variables */ static short check_opts = FALSE; static short opt_ctrlz = FALSE; static short opt_vis = FALSE; static short opt_tab = FALSE; static short opt_ff = FALSE; static short opt_eoln = FALSE; static short opt_unbuf = FALSE; static short opt_repnl = FALSE; static short opt_crnl = FALSE; static short opt_lfnl = FALSE; static short opt_nlcr = FALSE; static short opt_nllf = FALSE; static short opt_nlnl = TRUE; static short opt_num = FALSE; static short opt_heading = FALSE; static const char * opt_outfname = NULL; /****************************************************************************** * setbinary -- set file mode to BINARY */ void setbinary(FILE *stream, const char *fname) { #ifdef PCDOS int i; #ifdef O_BINARY i = setmode(fileno(stream), O_BINARY); if (i == -1) fprintf(stderr, "%s: Can't set binary mode for %s\n", prog, fname); #endif #endif } /****************************************************************************** * cat_chars -- catenate by characters, one by one */ static unsigned long cat_chars(FILE *in, FILE *out) { size_t rlen; size_t wlen; int ch, lastch; int eoln; unsigned long count; unsigned long linec; /* copy in stream to out stream */ count = 0; lastch = EOF; eoln = TRUE; linec = 0; for (;;) { /* read next char from in */ ch = getc(in); /* handle line number prefixes */ if (ch != EOF && eoln) { linec++; if (opt_num) fprintf(out, "%-7lu ", linec); eoln = FALSE; } /* interpret double char codes */ if (lastch == CR && ch == LF) { if (!eoln || !opt_repnl) { if (opt_eoln) { putc('$', out); count++; } if (opt_nllf) { putc(LF, out); count++; } else if (opt_nlcr) { putc(CR, out); count++; } else if (opt_nlnl) { putc(CR, out); putc(LF, out); count += 2; } ch = EOF; eoln = TRUE; } } else { /* print lastch char */ if (lastch == EOF) ; /* do nothing */ else if (lastch == CR && opt_crnl) { if (!eoln || !opt_repnl) { putc(CR, out); putc(LF, out); count += 2; } eoln = TRUE; } else if (lastch == LF && opt_lfnl) { if (!eoln || !opt_repnl) { putc(CR, out); putc(LF, out); count += 2; } eoln = TRUE; } else if (lastch == NL && opt_eoln) { if (!eoln || !opt_repnl) { putc('^', out); putc('$', out); putc('\n', out); count += 3; } eoln = TRUE; } else if (lastch == HT) { if (opt_tab) { putc('^', out); putc((ch & 0x3F) + '@', out); count += 2; } else goto normal; } else if (lastch == FF) { if (opt_ff) { putc('^', out); putc((ch & 0x3F) + '@', out); count += 2; } else goto normal; } else if (opt_vis && !isprint(lastch)) { non_print: if (lastch < 0x80) { putc('^', out); putc((lastch == DEL ? '?' : lastch + '@'), out); count += 2; } else { putc('M', out); putc('-', out); count += 2; lastch &= 0x7F; if (!isprint(lastch)) goto non_print; else { putc(lastch, out); count++; } } } else { normal: /* print normal printable char */ putc(lastch, out); count++; } /* handle end of file */ if (ch == CTRL_Z && opt_ctrlz) { putc(CTRL_Z, out); count++; break; } else if (ch == EOF) { break; } } lastch = ch; } return count; } /****************************************************************************** * catenate -- catenate one stream onto another */ unsigned long catenate(FILE *in, FILE *out) { size_t rlen; size_t wlen; char buf[BUFSZ]; /* copy buffer */ unsigned long count; /* copy in stream to out stream */ count = 0; if (check_opts) { /* copy in to out char by char using options */ count = cat_chars(in, out); } else { /* fast copy of in to out by blocks */ while (rlen = fread(buf, 1, sizeof(buf), in), rlen > 0) { errno = 0; wlen = fwrite(buf, 1, rlen, out); count += wlen; if (wlen != rlen) { /* fwrite() failed */ fprintf(stderr, "%s: Write error: %s\n", prog, sys_errlist[errno]); break; } } } return count; } /****************************************************************************** * about * Print `about' message, then punt. */ static const char * about_m[] = { compile, "", copyright, "", "This software may be freely distributed. However, duplication", "and/or distribution of this software for financial gain without", "prior written consent of the author is prohibited.", "", "If you find this program useful, you can register it by sending the", "version and date shown above along with your name, address, and a", "donation of $10.00(US) to:", "", " David R. Tribble", " 6004 Cave River Dr.", " Plano, TX 75093", " USA", "", " dribble@flash.net", " drt@wcwcen.wf.com", NULL }; static void about(void) { int i; /* Print program info and abort */ for (i = 0; about_m[i] != NULL; i++) fprintf(stderr, "%s\n", about_m[i]); exit(RC_OKAY); } /****************************************************************************** * usage -- print usage message and die */ static const char * usage_m[] = { "Filenames may contain wildcards. Filename `-' indicates standard input.", "", "Options:", " -? About this program.", " -crnl Convert CR to CR/LF.", " -lfnl Convert LF to CR/LF.", " -nlcr Convert CR/LF to CR.", " -nllf Convert CR/LF to LF.", " -e Print newlines as `$'.", " -h Print file name headings.", " -o file Output is `file' (not standard output).", " -r Merge multiple newlines.", " -t Print tab characters as `^I'.", " -u Output is unbuffered.", " -v Make unprintable characters visible.", " -z Stop at first Ctrl-Z character.", NULL }; static void usage(void) { int i; /* Print usage message and abort */ fprintf(stderr, "[%s, %s %s]\n\n", title, vers, date); fprintf(stderr, "Catenate (write) files to standard output.\n\n"); fprintf(stderr, "usage: %s file...\n", prog); fprintf(stderr, " %s 0 ? CRLF : ""), fname, CRLF); setbinary(in, fname); catenate(in, outfp); fclose(in); catc++; } else fprintf(stderr, "%s: Can't read %s\n", prog, fname); } if (cnt == 0) fprintf(stderr, "%s: Can't find %s\n", prog, argv[i]); #else #ifdef PATH cnt = 0; p = path_start(argv[i], PATH_ALLF); while (fpnext(p) != NULL) { cnt++; in = fopen(p->p_name, READMODE); if (in != NULL) { if (opt_heading) printf("%s%s:%s", (catc > 0 ? CRLF : ""), p->p_name, CRLF); setbinary(in, p->p_name); catenate(in, outfp); fclose(in); catc++; } else fprintf(stderr, "%s: Can't read %s\n", prog, p->p_name); } if (cnt == 0) fprintf(stderr, "%s: Can't find %s\n", prog, argv[i]); #else in = fopen(argv[i], READMODE); if (in != NULL) { if (opt_heading) printf("%s%s:%s", (catc > 0 ? CRLF : ""), argv[i], CRLF); setbinary(in, argv[i]); catenate(in, outfp); fclose(in); catc++; } else fprintf(stderr, "%s: Can't read %s\n", prog, argv[i]); #endif #endif } } /* Clean up */ if (outfp != stdout) { /* Close output */ fclose(outfp); outfp = NULL; } return RC_OKAY; } /* End cat.c */