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