/*********************************************************************** * * * This software is part of the ast package * * Copyright (c) 1992-2009 AT&T Intellectual Property * * and is licensed under the * * Common Public License, Version 1.0 * * by AT&T Intellectual Property * * * * A copy of the License is available at * * http://www.opensource.org/licenses/cpl1.0.txt * * (with md5 checksum 059e8cd6165cb4c31e351f2b69388fd9) * * * * Information and Software Systems Research * * AT&T Research * * Florham Park NJ * * * * Glenn Fowler * * David Korn * * * ***********************************************************************/ #pragma prototyped /* * David Korn * Glenn Fowler * AT&T Bell Laboratories * * cat */ #include #include static const char usage[] = "[-?\n@(#)$Id: cat (AT&T Research) 2009-03-31 $\n]" USAGE_LICENSE "[+NAME?cat - concatenate files]" "[+DESCRIPTION?\bcat\b copies each \afile\a in sequence to the standard" " output. If no \afile\a is given, or if the \afile\a is \b-\b," " \bcat\b copies from standard input starting at the current location.]" "[b:number-nonblank?Number lines as with \b-n\b but omit line numbers from" " blank lines.]" "[d:dos-input?Input files are opened in \atext\amode which removes carriage" " returns in front of new-lines on some systems.]" "[e?Equivalent to \b-vE\b.]" "[n:number?Causes a line number to be inserted at the beginning of each line.]" "[s?Equivalent to \b-S\b for \aatt\a universe and \b-B\b otherwise.]" "[t?Equivalent to \b-vT\b.]" "[u:unbuffer?The output is not delayed by buffering.]" "[v:show-nonprinting?Causes non-printing characters (whith the exception of" " tabs, new-lines, and form-feeds) to be output as printable charater" " sequences. ASCII control characters are printed as \b^\b\an\a," " where \an\a is the corresponding ASCII character in the range" " octal 100-137. The DEL character (octal 0177) is copied" " as \b^?\b. Other non-printable characters are copied as \bM-\b\ax\a" " where \ax\a is the ASCII character specified by the low-order seven" " bits. Multibyte characters in the current locale are treated as" " printable characters.]" "[A:show-all?Equivalent to \b-vET\b.]" "[B:squeeze-blank?Multiple adjacent new-line characters are replace by one" " new-line.]" "[D:dos-output?Output files are opened in \atext\amode which inserts carriage" " returns in front of new-lines on some systems.]" "[E:show-ends?Causes a \b$\b to be inserted before each new-line.]" "[R:regress?Regression test defaults: \b-v\b buffer size 4.]" "[S:silent?\bcat\b is silent about non-existent files.]" "[T:show-blank?Causes tabs to be copied as \b^I\b and formfeeds as \b^L\b.]" "\n" "\n[file ...]\n" "\n" "[+SEE ALSO?\bcp\b(1), \bgetconf\b(1), \bpr\b(1)]" ; #define RUBOUT 0177 /* control flags */ #define B_FLAG (1<<0) #define E_FLAG (1<<1) #define F_FLAG (1<<2) #define N_FLAG (1<<3) #define S_FLAG (1<<4) #define T_FLAG (1<<5) #define U_FLAG (1<<6) #define V_FLAG (1<<7) #define D_FLAG (1<<8) #define d_FLAG (1<<9) /* character types */ #define T_ERROR 1 #define T_EOF 2 #define T_ENDBUF 3 #define T_NEWLINE 4 #define T_CONTROL 5 #define T_EIGHTBIT 6 #define T_CNTL8BIT 7 #define printof(c) ((c)^0100) typedef void* (*Reserve_f)(Sfio_t*, ssize_t, int); #ifndef sfvalue #define sfvalue(f) ((f)->_val) #endif static void* regress(Sfio_t* sp, ssize_t n, int f) { void* r; if (!(r = sfreserve(sp, 4, f))) r = sfreserve(sp, n, f); else if (sfvalue(sp) > 4) sfvalue(sp) = 4; return r; } /* * called for any special output processing */ static int vcat(register char* states, Sfio_t* ip, Sfio_t* op, Reserve_f reserve, int flags) { register unsigned char* cp; register unsigned char* pp; unsigned char* cur; unsigned char* end; unsigned char* buf; unsigned char* nxt; register int n; register int line; register int raw; int last; int c; int m; int any; int header; unsigned char meta[4]; unsigned char tmp[32]; meta[0] = 'M'; meta[1] = '-'; last = -1; *(cp = buf = end = tmp) = 0; any = 0; header = flags & (B_FLAG|N_FLAG); line = 1; states[0] = T_ENDBUF; raw = !mbwide(); for (;;) { cur = cp; if (raw) while (!(n = states[*cp++])); else for (;;) { while (!(n = states[*cp++])); if (n < T_CONTROL) break; if ((m = mbsize(pp = cp - 1)) > 1) cp += m - 1; else { if (m <= 0) { if (cur == pp) { if (last > 0) { *end = last; last = -1; c = end - pp + 1; if ((m = mbsize(pp)) == c) { any = 1; if (header) { header = 0; sfprintf(op, "%6d\t", line); } sfwrite(op, cur, m); *(cp = cur = end) = 0; } else { memcpy(tmp, pp, c); if (!(nxt = (unsigned char*)(*reserve)(ip, SF_UNBOUND, 0))) { states[0] = sfvalue(ip) ? T_ERROR : T_EOF; *(cp = end = tmp + sizeof(tmp) - 1) = 0; last = -1; } else if ((n = sfvalue(ip)) <= 0) { states[0] = n ? T_ERROR : T_EOF; *(cp = end = tmp + sizeof(tmp) - 1) = 0; last = -1; } else { cp = buf = nxt; end = buf + n - 1; last = *end; *end = 0; } mb: if ((n = end - cp + 1) >= (sizeof(tmp) - c)) n = sizeof(tmp) - c - 1; memcpy(tmp + c, cp, n); if ((m = mbsize(tmp)) >= c) { any = 1; if (header) { header = 0; sfprintf(op, "%6d\t", line); } sfwrite(op, tmp, m); cur = cp += m - c; } } continue; } } else { cp = pp + 1; n = 0; } } break; } } c = *--cp; if ((m = cp - cur) || n >= T_CONTROL) { flush: any = 1; if (header) { header = 0; sfprintf(op, "%6d\t", line); } if (m) sfwrite(op, cur, m); } special: switch (n) { case T_ERROR: if (cp != end) { n = T_CONTROL; goto flush; } return -1; case T_EOF: if (cp != end) { n = T_CONTROL; goto flush; } return 0; case T_ENDBUF: if (cp != end) { n = T_CONTROL; goto flush; } c = last; if (!(nxt = (unsigned char*)(*reserve)(ip, SF_UNBOUND, 0))) { *(cp = end = tmp) = 0; states[0] = sfvalue(ip) ? T_ERROR : T_EOF; last = -1; } else if ((m = sfvalue(ip)) <= 0) { *(cp = end = tmp) = 0; states[0] = m ? T_ERROR : T_EOF; last = -1; } else { buf = nxt; end = buf + m - 1; last = *end; *end = 0; cp = buf; } if (c >= 0) { if (!(n = states[c])) { *(cur = tmp) = c; m = 1; goto flush; } if (raw || n < T_CONTROL) { cp--; goto special; } tmp[0] = c; c = 1; goto mb; } break; case T_CONTROL: do { sfputc(op, '^'); sfputc(op, printof(c)); } while (states[c = *++cp] == T_CONTROL); break; case T_CNTL8BIT: meta[2] = '^'; do { n = c & ~0200; meta[3] = printof(n); sfwrite(op, (char*)meta, 4); } while (states[c = *++cp] == T_CNTL8BIT && raw); break; case T_EIGHTBIT: do { meta[2] = c & ~0200; sfwrite(op, (char*)meta, 3); } while (states[c = *++cp] == T_EIGHTBIT && raw); break; case T_NEWLINE: if (header && !(flags & B_FLAG)) sfprintf(op, "%6d\t", line); if (flags & E_FLAG) sfputc(op, '$'); sfputc(op, '\n'); if (!header || !(flags & B_FLAG)) line++; header = !(flags & S_FLAG); for (;;) { if ((n = states[*++cp]) == T_ENDBUF) { if (cp != end || last != '\n') break; if (!(nxt = (unsigned char*)(*reserve)(ip, SF_UNBOUND, 0))) { states[0] = sfvalue(ip) ? T_ERROR : T_EOF; cp = end = tmp; *cp-- = 0; last = -1; } else if ((n = sfvalue(ip)) <= 0) { states[0] = n ? T_ERROR : T_EOF; cp = end = tmp; *cp-- = 0; last = -1; } else { buf = nxt; end = buf + n - 1; last = *end; *end = 0; cp = buf - 1; } } else if (n != T_NEWLINE) break; if (!(flags & S_FLAG) || any || header) { any = 0; header = 0; if ((flags & (B_FLAG|N_FLAG)) == N_FLAG) sfprintf(op, "%6d\t", line); if (flags & E_FLAG) sfputc(op, '$'); sfputc(op, '\n'); } if (!(flags & B_FLAG)) line++; } header = flags & (B_FLAG|N_FLAG); break; } } } int b_cat(int argc, char** argv, void* context) { register int n; register int flags = 0; register char* cp; register Sfio_t* fp; char* mode; Reserve_f reserve = sfreserve; int att; int dovcat = 0; char states[UCHAR_MAX+1]; cmdinit(argc, argv, context, ERROR_CATALOG, 0); setlocale(LC_ALL, ""); att = !strcmp(astconf("UNIVERSE", NiL, NiL), "att"); mode = "r"; for (;;) { n = 0; switch (optget(argv, usage)) { case 'A': n = T_FLAG|E_FLAG|V_FLAG; break; case 'B': n = S_FLAG; break; case 'b': n = B_FLAG; break; case 'd': mode = opt_info.num ? "rt" : "r"; continue; case 'D': n = d_FLAG; break; case 'E': n = E_FLAG; break; case 'e': n = E_FLAG|V_FLAG; break; case 'n': n = N_FLAG; break; case 'R': reserve = opt_info.num ? regress : sfreserve; continue; case 's': n = att ? F_FLAG : S_FLAG; break; case 'S': n = F_FLAG; break; case 'T': n = T_FLAG; break; case 't': n = T_FLAG|V_FLAG; break; case 'u': n = U_FLAG; break; case 'v': n = V_FLAG; break; case ':': error(2, "%s", opt_info.arg); break; case '?': error(ERROR_usage(2), "%s", opt_info.arg); break; } if (!n) break; if (opt_info.num) flags |= n; else flags &= ~n; } argv += opt_info.index; if (error_info.errors) error(ERROR_usage(2), "%s", optusage(NiL)); memset(states, 0, sizeof(states)); if (flags&V_FLAG) { memset(states, T_CONTROL, ' '); states[RUBOUT] = T_CONTROL; memset(states+0200, T_EIGHTBIT, 0200); memset(states+0200, T_CNTL8BIT, ' '); states[RUBOUT|0200] = T_CNTL8BIT; states['\n'] = 0; } if (flags&T_FLAG) states['\t'] = T_CONTROL; states[0] = T_ENDBUF; if (att) { if (flags&V_FLAG) { states['\n'|0200] = T_EIGHTBIT; if (!(flags&T_FLAG)) { states['\t'] = states['\f'] = 0; states['\t'|0200] = states['\f'|0200] = T_EIGHTBIT; } } } else if (flags) { if (!(flags&T_FLAG)) states['\t'] = 0; } if (flags&(V_FLAG|T_FLAG|N_FLAG|E_FLAG|B_FLAG|S_FLAG)) { states['\n'] = T_NEWLINE; dovcat = 1; } if (flags&d_FLAG) sfopen(sfstdout, NiL, "wt"); if (cp = *argv) argv++; do { if (!cp || streq(cp, "-")) { fp = sfstdin; if (flags&D_FLAG) sfopen(fp, NiL, mode); } else if (!(fp = sfopen(NiL, cp, mode))) { if (!(flags&F_FLAG)) error(ERROR_system(0), "%s: cannot open", cp); error_info.errors = 1; continue; } if (flags&U_FLAG) sfsetbuf(fp, (void*)fp, -1); if (dovcat) n = vcat(states, fp, sfstdout, reserve, flags); else if (sfmove(fp, sfstdout, SF_UNBOUND, -1) >= 0 && sfeof(fp)) n = 0; else n = -1; if (fp != sfstdin) sfclose(fp); if (n < 0 && errno != EPIPE) { if (cp) error(ERROR_system(0), "%s: read error", cp); else error(ERROR_system(0), "read error"); } if (sferror(sfstdout)) break; } while (cp = *argv++); if (sfsync(sfstdout)) error(ERROR_system(0), "write error"); if (flags&d_FLAG) sfopen(sfstdout, NiL, "w"); return error_info.errors; }