1*4887Schin /*********************************************************************** 2*4887Schin * * 3*4887Schin * This software is part of the ast package * 4*4887Schin * Copyright (c) 1992-2007 AT&T Knowledge Ventures * 5*4887Schin * and is licensed under the * 6*4887Schin * Common Public License, Version 1.0 * 7*4887Schin * by AT&T Knowledge Ventures * 8*4887Schin * * 9*4887Schin * A copy of the License is available at * 10*4887Schin * http://www.opensource.org/licenses/cpl1.0.txt * 11*4887Schin * (with md5 checksum 059e8cd6165cb4c31e351f2b69388fd9) * 12*4887Schin * * 13*4887Schin * Information and Software Systems Research * 14*4887Schin * AT&T Research * 15*4887Schin * Florham Park NJ * 16*4887Schin * * 17*4887Schin * Glenn Fowler <gsf@research.att.com> * 18*4887Schin * David Korn <dgk@research.att.com> * 19*4887Schin * * 20*4887Schin ***********************************************************************/ 21*4887Schin #pragma prototyped 22*4887Schin /* 23*4887Schin * David Korn 24*4887Schin * Glenn Fowler 25*4887Schin * AT&T Bell Laboratories 26*4887Schin * 27*4887Schin * cat 28*4887Schin */ 29*4887Schin 30*4887Schin #include <cmd.h> 31*4887Schin #include <fcntl.h> 32*4887Schin 33*4887Schin static const char usage[] = 34*4887Schin "[-?\n@(#)$Id: cat (AT&T Research) 2006-05-17 $\n]" 35*4887Schin USAGE_LICENSE 36*4887Schin "[+NAME?cat - concatenate files]" 37*4887Schin "[+DESCRIPTION?\bcat\b copies each \afile\a in sequence to the standard" 38*4887Schin " output. If no \afile\a is given, or if the \afile\a is \b-\b," 39*4887Schin " \bcat\b copies from standard input starting at the current location.]" 40*4887Schin 41*4887Schin "[b:number-nonblank?Number lines as with \b-n\b but omit line numbers from" 42*4887Schin " blank lines.]" 43*4887Schin "[d:dos-input?Input files are opened in \atext\amode which removes carriage" 44*4887Schin " returns in front of new-lines on some systems.]" 45*4887Schin "[e?Equivalent to \b-vE\b.]" 46*4887Schin "[n:number?Causes a line number to be inserted at the beginning of each line.]" 47*4887Schin "[s?Equivalent to \b-S\b for \aatt\a universe and \b-B\b otherwise.]" 48*4887Schin "[t?Equivalent to \b-vT\b.]" 49*4887Schin "[u:unbuffer?The output is not delayed by buffering.]" 50*4887Schin "[v:show-nonprinting?Causes non-printing characters (whith the exception of" 51*4887Schin " tabs, new-lines, and form-feeds) to be output as printable charater" 52*4887Schin " sequences. ASCII control characters are printed as \b^\b\an\a," 53*4887Schin " where \an\a is the corresponding ASCII character in the range" 54*4887Schin " octal 100-137. The DEL character (octal 0177) is copied" 55*4887Schin " as \b^?\b. Other non-printable characters are copied as \bM-\b\ax\a" 56*4887Schin " where \ax\a is the ASCII character specified by the low-order seven" 57*4887Schin " bits. Multibyte characters in the current locale are treated as" 58*4887Schin " printable characters.]" 59*4887Schin "[A:show-all?Equivalent to \b-vET\b.]" 60*4887Schin "[B:squeeze-blank?Multiple adjacent new-line characters are replace by one" 61*4887Schin " new-line.]" 62*4887Schin "[D:dos-output?Output files are opened in \atext\amode which inserts carriage" 63*4887Schin " returns in front of new-lines on some systems.]" 64*4887Schin "[E:show-ends?Causes a \b$\b to be inserted before each new-line.]" 65*4887Schin "[S:silent?\bcat\b is silent about non-existent files.]" 66*4887Schin "[T:show-blank?Causes tabs to be copied as \b^I\b and formfeeds as \b^L\b.]" 67*4887Schin 68*4887Schin "\n" 69*4887Schin "\n[file ...]\n" 70*4887Schin "\n" 71*4887Schin 72*4887Schin "[+SEE ALSO?\bcp\b(1), \bgetconf\b(1), \bpr\b(1)]" 73*4887Schin ; 74*4887Schin 75*4887Schin #define RUBOUT 0177 76*4887Schin 77*4887Schin /* control flags */ 78*4887Schin #define B_FLAG (1<<0) 79*4887Schin #define E_FLAG (1<<1) 80*4887Schin #define F_FLAG (1<<2) 81*4887Schin #define N_FLAG (1<<3) 82*4887Schin #define S_FLAG (1<<4) 83*4887Schin #define T_FLAG (1<<5) 84*4887Schin #define U_FLAG (1<<6) 85*4887Schin #define V_FLAG (1<<7) 86*4887Schin #define D_FLAG (1<<8) 87*4887Schin #define d_FLAG (1<<9) 88*4887Schin 89*4887Schin /* character types */ 90*4887Schin #define T_ENDBUF 1 91*4887Schin #define T_CONTROL 2 92*4887Schin #define T_NEWLINE 3 93*4887Schin #define T_EIGHTBIT 4 94*4887Schin #define T_CNTL8BIT 5 95*4887Schin 96*4887Schin #define printof(c) ((c)^0100) 97*4887Schin 98*4887Schin /* 99*4887Schin * called for any special output processing 100*4887Schin */ 101*4887Schin 102*4887Schin static int 103*4887Schin vcat(register char* states, Sfio_t *fdin, Sfio_t *fdout, int flags) 104*4887Schin { 105*4887Schin register unsigned char* cp; 106*4887Schin register unsigned char* cpold; 107*4887Schin register int n; 108*4887Schin register int m; 109*4887Schin register int line = 1; 110*4887Schin register unsigned char* endbuff; 111*4887Schin unsigned char* inbuff; 112*4887Schin int printdefer = (flags&(B_FLAG|N_FLAG)); 113*4887Schin int lastchar; 114*4887Schin 115*4887Schin unsigned char meta[4]; 116*4887Schin 117*4887Schin meta[0] = 'M'; 118*4887Schin meta[1] = '-'; 119*4887Schin for (;;) 120*4887Schin { 121*4887Schin /* read in a buffer full */ 122*4887Schin if (!(inbuff = (unsigned char*)sfreserve(fdin, SF_UNBOUND, 0))) 123*4887Schin return sfvalue(fdin) ? -1 : 0; 124*4887Schin if ((n = sfvalue(fdin)) <= 0) 125*4887Schin return n; 126*4887Schin cp = inbuff; 127*4887Schin lastchar = *(endbuff = cp + --n); 128*4887Schin *endbuff = 0; 129*4887Schin if (printdefer) 130*4887Schin { 131*4887Schin if (states[*cp]!=T_NEWLINE || !(flags&B_FLAG)) 132*4887Schin sfprintf(fdout,"%6d\t",line); 133*4887Schin printdefer = 0; 134*4887Schin } 135*4887Schin while (endbuff) 136*4887Schin { 137*4887Schin cpold = cp; 138*4887Schin /* skip over printable characters */ 139*4887Schin if (mbwide()) 140*4887Schin while ((n = (m = mbsize(cp)) < 2 ? states[*cp++] : (cp += m, states['a'])) == 0); 141*4887Schin else 142*4887Schin while ((n = states[*cp++]) == 0); 143*4887Schin if (n==T_ENDBUF) 144*4887Schin { 145*4887Schin if (cp>endbuff) 146*4887Schin { 147*4887Schin if (!(n = states[lastchar])) 148*4887Schin { 149*4887Schin *endbuff = lastchar; 150*4887Schin cp++; 151*4887Schin } 152*4887Schin else 153*4887Schin { 154*4887Schin if (--cp > cpold) 155*4887Schin sfwrite(fdout,(char*)cpold,cp-cpold); 156*4887Schin if (endbuff==inbuff) 157*4887Schin *++endbuff = 0; 158*4887Schin cp = cpold = endbuff; 159*4887Schin cp[-1] = lastchar; 160*4887Schin if (n==T_ENDBUF) 161*4887Schin n = T_CONTROL; 162*4887Schin 163*4887Schin } 164*4887Schin endbuff = 0; 165*4887Schin } 166*4887Schin else n = T_CONTROL; 167*4887Schin } 168*4887Schin if (--cp>cpold) 169*4887Schin sfwrite(fdout,(char*)cpold,cp-cpold); 170*4887Schin switch(n) 171*4887Schin { 172*4887Schin case T_CNTL8BIT: 173*4887Schin meta[2] = '^'; 174*4887Schin do 175*4887Schin { 176*4887Schin n = (*cp++)&~0200; 177*4887Schin meta[3] = printof(n); 178*4887Schin sfwrite(fdout,(char*)meta,4); 179*4887Schin } 180*4887Schin while ((n=states[*cp])==T_CNTL8BIT); 181*4887Schin break; 182*4887Schin case T_EIGHTBIT: 183*4887Schin do 184*4887Schin { 185*4887Schin meta[2] = (*cp++)&~0200; 186*4887Schin sfwrite(fdout,(char*)meta,3); 187*4887Schin } 188*4887Schin while ((n=states[*cp])==T_EIGHTBIT); 189*4887Schin break; 190*4887Schin case T_CONTROL: 191*4887Schin do 192*4887Schin { 193*4887Schin n = *cp++; 194*4887Schin sfputc(fdout,'^'); 195*4887Schin sfputc(fdout,printof(n)); 196*4887Schin } 197*4887Schin while ((n=states[*cp])==T_CONTROL); 198*4887Schin break; 199*4887Schin case T_NEWLINE: 200*4887Schin if (flags&S_FLAG) 201*4887Schin { 202*4887Schin while (states[*++cp]==T_NEWLINE) 203*4887Schin line++; 204*4887Schin cp--; 205*4887Schin } 206*4887Schin do 207*4887Schin { 208*4887Schin cp++; 209*4887Schin if (flags&E_FLAG) 210*4887Schin sfputc(fdout,'$'); 211*4887Schin sfputc(fdout,'\n'); 212*4887Schin if (!(flags&(N_FLAG|B_FLAG))) 213*4887Schin continue; 214*4887Schin line++; 215*4887Schin if (cp < endbuff) 216*4887Schin sfprintf(fdout,"%6d\t",line); 217*4887Schin else printdefer = 1; 218*4887Schin } 219*4887Schin while (states[*cp]==T_NEWLINE); 220*4887Schin break; 221*4887Schin } 222*4887Schin } 223*4887Schin } 224*4887Schin } 225*4887Schin 226*4887Schin int 227*4887Schin b_cat(int argc, char** argv, void* context) 228*4887Schin { 229*4887Schin register int n; 230*4887Schin register int flags = 0; 231*4887Schin register char* cp; 232*4887Schin register Sfio_t* fp; 233*4887Schin char* mode; 234*4887Schin int att; 235*4887Schin int dovcat=0; 236*4887Schin char states[UCHAR_MAX+1]; 237*4887Schin 238*4887Schin NoP(argc); 239*4887Schin cmdinit(argc, argv, context, ERROR_CATALOG, 0); 240*4887Schin att = !strcmp(astconf("UNIVERSE", NiL, NiL), "att"); 241*4887Schin mode = "r"; 242*4887Schin for (;;) 243*4887Schin { 244*4887Schin switch (optget(argv, usage)) 245*4887Schin { 246*4887Schin case 'A': 247*4887Schin flags |= T_FLAG|E_FLAG|V_FLAG; 248*4887Schin continue; 249*4887Schin case 'B': 250*4887Schin flags |= S_FLAG; 251*4887Schin continue; 252*4887Schin case 'b': 253*4887Schin flags |= B_FLAG; 254*4887Schin continue; 255*4887Schin case 'E': 256*4887Schin flags |= E_FLAG; 257*4887Schin continue; 258*4887Schin case 'e': 259*4887Schin flags |= E_FLAG|V_FLAG; 260*4887Schin continue; 261*4887Schin case 'n': 262*4887Schin flags |= N_FLAG; 263*4887Schin continue; 264*4887Schin case 's': 265*4887Schin flags |= att ? F_FLAG : S_FLAG; 266*4887Schin continue; 267*4887Schin case 'S': 268*4887Schin flags |= F_FLAG; 269*4887Schin continue; 270*4887Schin case 'T': 271*4887Schin flags |= T_FLAG; 272*4887Schin continue; 273*4887Schin case 't': 274*4887Schin flags |= T_FLAG|V_FLAG; 275*4887Schin continue; 276*4887Schin case 'u': 277*4887Schin flags |= U_FLAG; 278*4887Schin continue; 279*4887Schin case 'v': 280*4887Schin flags |= V_FLAG; 281*4887Schin continue; 282*4887Schin case 'd': 283*4887Schin mode = "rt"; 284*4887Schin continue; 285*4887Schin case 'D': 286*4887Schin flags |= d_FLAG; 287*4887Schin continue; 288*4887Schin case ':': 289*4887Schin error(2, "%s", opt_info.arg); 290*4887Schin break; 291*4887Schin case '?': 292*4887Schin error(ERROR_usage(2), "%s", opt_info.arg); 293*4887Schin break; 294*4887Schin } 295*4887Schin break; 296*4887Schin } 297*4887Schin argv += opt_info.index; 298*4887Schin if (error_info.errors) 299*4887Schin error(ERROR_usage(2), "%s", optusage(NiL)); 300*4887Schin memset(states, 0, sizeof(states)); 301*4887Schin if (flags&V_FLAG) 302*4887Schin { 303*4887Schin memset(states, T_CONTROL, ' '); 304*4887Schin states[RUBOUT] = T_CONTROL; 305*4887Schin memset(states+0200, T_EIGHTBIT, 0200); 306*4887Schin memset(states+0200, T_CNTL8BIT, ' '); 307*4887Schin states[RUBOUT|0200] = T_CNTL8BIT; 308*4887Schin states['\n'] = 0; 309*4887Schin } 310*4887Schin if (flags&T_FLAG) 311*4887Schin states['\t'] = T_CONTROL; 312*4887Schin states[0] = T_ENDBUF; 313*4887Schin if (att) 314*4887Schin { 315*4887Schin if (flags&V_FLAG) 316*4887Schin { 317*4887Schin states['\n'|0200] = T_EIGHTBIT; 318*4887Schin if (!(flags&T_FLAG)) 319*4887Schin { 320*4887Schin states['\t'] = states['\f'] = 0; 321*4887Schin states['\t'|0200] = states['\f'|0200] = T_EIGHTBIT; 322*4887Schin } 323*4887Schin } 324*4887Schin } 325*4887Schin else if (flags) 326*4887Schin { 327*4887Schin if (!(flags&T_FLAG)) 328*4887Schin states['\t'] = 0; 329*4887Schin } 330*4887Schin if (flags&(V_FLAG|T_FLAG|N_FLAG|E_FLAG|B_FLAG)) 331*4887Schin { 332*4887Schin states['\n'] = T_NEWLINE; 333*4887Schin dovcat = 1; 334*4887Schin } 335*4887Schin if (flags&B_FLAG) 336*4887Schin flags |= S_FLAG; 337*4887Schin if (flags&d_FLAG) 338*4887Schin sfopen(sfstdout, NiL, "wt"); 339*4887Schin if (cp = *argv) 340*4887Schin argv++; 341*4887Schin do 342*4887Schin { 343*4887Schin if (!cp || streq(cp,"-")) 344*4887Schin { 345*4887Schin fp = sfstdin; 346*4887Schin if (flags&D_FLAG) 347*4887Schin sfopen(fp, NiL, mode); 348*4887Schin } 349*4887Schin else if (!(fp = sfopen(NiL, cp, mode))) 350*4887Schin { 351*4887Schin if (!(flags&F_FLAG)) 352*4887Schin error(ERROR_system(0), "%s: cannot open", cp); 353*4887Schin error_info.errors = 1; 354*4887Schin continue; 355*4887Schin } 356*4887Schin if (flags&U_FLAG) 357*4887Schin sfsetbuf(fp, (void*)fp, -1); 358*4887Schin if (dovcat) 359*4887Schin n = vcat(states, fp, sfstdout, flags); 360*4887Schin else if (sfmove(fp, sfstdout, SF_UNBOUND, -1) >= 0 && sfeof(fp)) 361*4887Schin n = 0; 362*4887Schin else 363*4887Schin n = -1; 364*4887Schin if (fp != sfstdin) 365*4887Schin sfclose(fp); 366*4887Schin if (n < 0 && errno != EPIPE) 367*4887Schin { 368*4887Schin if (cp) 369*4887Schin error(ERROR_system(0), "%s: read error", cp); 370*4887Schin else 371*4887Schin error(ERROR_system(0), "read error"); 372*4887Schin } 373*4887Schin if (sferror(sfstdout)) 374*4887Schin break; 375*4887Schin } while (cp = *argv++); 376*4887Schin if (sfsync(sfstdout)) 377*4887Schin error(ERROR_system(0), "write error"); 378*4887Schin if (flags&d_FLAG) 379*4887Schin sfopen(sfstdout, NiL, "w"); 380*4887Schin return error_info.errors; 381*4887Schin } 382