1*da2e3ebdSchin /*********************************************************************** 2*da2e3ebdSchin * * 3*da2e3ebdSchin * This software is part of the ast package * 4*da2e3ebdSchin * Copyright (c) 1992-2007 AT&T Knowledge Ventures * 5*da2e3ebdSchin * and is licensed under the * 6*da2e3ebdSchin * Common Public License, Version 1.0 * 7*da2e3ebdSchin * by AT&T Knowledge Ventures * 8*da2e3ebdSchin * * 9*da2e3ebdSchin * A copy of the License is available at * 10*da2e3ebdSchin * http://www.opensource.org/licenses/cpl1.0.txt * 11*da2e3ebdSchin * (with md5 checksum 059e8cd6165cb4c31e351f2b69388fd9) * 12*da2e3ebdSchin * * 13*da2e3ebdSchin * Information and Software Systems Research * 14*da2e3ebdSchin * AT&T Research * 15*da2e3ebdSchin * Florham Park NJ * 16*da2e3ebdSchin * * 17*da2e3ebdSchin * Glenn Fowler <gsf@research.att.com> * 18*da2e3ebdSchin * David Korn <dgk@research.att.com> * 19*da2e3ebdSchin * * 20*da2e3ebdSchin ***********************************************************************/ 21*da2e3ebdSchin #pragma prototyped 22*da2e3ebdSchin /* 23*da2e3ebdSchin * David Korn 24*da2e3ebdSchin * AT&T Bell Laboratories 25*da2e3ebdSchin * 26*da2e3ebdSchin * cut [-sN] [-f flist] [-c clist] [-d delim] [-D delim] [-r reclen] [file] ... 27*da2e3ebdSchin * 28*da2e3ebdSchin * cut fields or columns from fields from a file 29*da2e3ebdSchin */ 30*da2e3ebdSchin 31*da2e3ebdSchin static const char usage[] = 32*da2e3ebdSchin "[-?\n@(#)$Id: cut (AT&T Research) 2007-01-23 $\n]" 33*da2e3ebdSchin USAGE_LICENSE 34*da2e3ebdSchin "[+NAME?cut - cut out selected columns or fields of each line of a file]" 35*da2e3ebdSchin "[+DESCRIPTION?\bcut\b bytes, characters, or character-delimited fields " 36*da2e3ebdSchin "from one or more files, contatenating them on standard output.]" 37*da2e3ebdSchin "[+?The option argument \alist\a is a comma-separated or blank-separated " 38*da2e3ebdSchin "list of positive numbers and ranges. Ranges can be of three " 39*da2e3ebdSchin "forms. The first is two positive integers separated by a hyphen " 40*da2e3ebdSchin "(\alow\a\b-\b\ahigh\a), which represents all fields from \alow\a to " 41*da2e3ebdSchin "\ahigh\a. The second is a positive number preceded by a hyphen " 42*da2e3ebdSchin "(\b-\b\ahigh\a), which represents all fields from field \b1\b to " 43*da2e3ebdSchin "\ahigh\a. The last is a positive number followed by a hyphen " 44*da2e3ebdSchin "(\alow\a\b-\b), which represents all fields from \alow\a to the " 45*da2e3ebdSchin "last field, inclusive. Elements in the \alist\a can be repeated, " 46*da2e3ebdSchin "can overlap, and can appear in any order. The order of the " 47*da2e3ebdSchin "output is that of the input.]" 48*da2e3ebdSchin "[+?One and only one of \b-b\b, \b-c\b, or \b-f\b must be specified.]" 49*da2e3ebdSchin "[+?If no \afile\a is given, or if the \afile\a is \b-\b, \bcut\b " 50*da2e3ebdSchin "cuts from standard input. The start of the file is defined " 51*da2e3ebdSchin "as the current offset.]" 52*da2e3ebdSchin "[b:bytes]:[list?\bcut\b based on a list of bytes.]" 53*da2e3ebdSchin "[c:characters]:[list?\bcut\b based on a list of characters.]" 54*da2e3ebdSchin "[d:delimiter]:[delim?The field character for the \b-f\b option is set " 55*da2e3ebdSchin "to \adelim\a. The default is the \btab\b character.]" 56*da2e3ebdSchin "[f:fields]:[list?\bcut\b based on fields separated by the delimiter " 57*da2e3ebdSchin "character specified with the \b-d\b optiion.]" 58*da2e3ebdSchin "[n:nosplit?Do not split characters. Currently ignored.]" 59*da2e3ebdSchin "[R|r:reclen]#[reclen?If \areclen\a > 0, the input will be read as fixed length " 60*da2e3ebdSchin "records of length \areclen\a when used with the \b-b\b or \b-c\b " 61*da2e3ebdSchin "option.]" 62*da2e3ebdSchin "[s:suppress|only-delimited?Suppress lines with no delimiter characters, " 63*da2e3ebdSchin "when used with the \b-f\b option. By default, lines with no " 64*da2e3ebdSchin "delimiters will be passsed in untouched.]" 65*da2e3ebdSchin "[D:line-delimeter|output-delimiter]:[ldelim?The line delimiter character for " 66*da2e3ebdSchin "the \b-f\b option is set to \aldelim\a. The default is the " 67*da2e3ebdSchin "\bnewline\b character.]" 68*da2e3ebdSchin "[N:nonewline?Do not output new-lines at end of each record when used " 69*da2e3ebdSchin "with the \b-b\b or \b-c\b option.]" 70*da2e3ebdSchin "\n" 71*da2e3ebdSchin "\n[file ...]\n" 72*da2e3ebdSchin "\n" 73*da2e3ebdSchin "[+EXIT STATUS?]{" 74*da2e3ebdSchin "[+0?All files processed successfully.]" 75*da2e3ebdSchin "[+>0?One or more files failed to open or could not be read.]" 76*da2e3ebdSchin "}" 77*da2e3ebdSchin "[+SEE ALSO?\bpaste\b(1), \bgrep\b(1)]" 78*da2e3ebdSchin ; 79*da2e3ebdSchin 80*da2e3ebdSchin #include <cmd.h> 81*da2e3ebdSchin #include <ctype.h> 82*da2e3ebdSchin 83*da2e3ebdSchin typedef struct Last_s 84*da2e3ebdSchin { 85*da2e3ebdSchin int seqno; 86*da2e3ebdSchin int seq; 87*da2e3ebdSchin int wdelim; 88*da2e3ebdSchin int ldelim; 89*da2e3ebdSchin } Last_t; 90*da2e3ebdSchin 91*da2e3ebdSchin typedef struct Cut_s 92*da2e3ebdSchin { 93*da2e3ebdSchin int cflag; 94*da2e3ebdSchin int sflag; 95*da2e3ebdSchin int nlflag; 96*da2e3ebdSchin int wdelim; 97*da2e3ebdSchin int ldelim; 98*da2e3ebdSchin int seqno; 99*da2e3ebdSchin int reclen; 100*da2e3ebdSchin signed char space[UCHAR_MAX]; 101*da2e3ebdSchin Last_t last; 102*da2e3ebdSchin int list[2]; /* NOTE: must be last member */ 103*da2e3ebdSchin } Cut_t; 104*da2e3ebdSchin 105*da2e3ebdSchin #define HUGE (1<<14) 106*da2e3ebdSchin #define BLOCK 8*1024 107*da2e3ebdSchin #define C_BYTES 1 108*da2e3ebdSchin #define C_CHARS 2 109*da2e3ebdSchin #define C_FIELDS 4 110*da2e3ebdSchin #define C_SUPRESS 8 111*da2e3ebdSchin #define C_NOCHOP 16 112*da2e3ebdSchin #define C_NONEWLINE 32 113*da2e3ebdSchin 114*da2e3ebdSchin /* 115*da2e3ebdSchin * compare the first of an array of integers 116*da2e3ebdSchin */ 117*da2e3ebdSchin 118*da2e3ebdSchin static int mycomp(register const void *a,register const void *b) 119*da2e3ebdSchin { 120*da2e3ebdSchin return(*((int*)a) - *((int*)b)); 121*da2e3ebdSchin } 122*da2e3ebdSchin 123*da2e3ebdSchin static Cut_t *cutinit(int mode,char *str,int wdelim,int ldelim,size_t reclen) 124*da2e3ebdSchin { 125*da2e3ebdSchin register int *lp, c, n=0; 126*da2e3ebdSchin register int range = 0; 127*da2e3ebdSchin register char *cp = str; 128*da2e3ebdSchin Cut_t *cuthdr; 129*da2e3ebdSchin if (!(cuthdr = (Cut_t*)stakalloc(sizeof(Cut_t)+strlen(cp)*sizeof(int)))) 130*da2e3ebdSchin error(ERROR_exit(1), "out of space"); 131*da2e3ebdSchin memset(cuthdr->space, 0, sizeof(cuthdr->space)); 132*da2e3ebdSchin cuthdr->last.seqno = 0; 133*da2e3ebdSchin cuthdr->last.seq = 0; 134*da2e3ebdSchin cuthdr->last.wdelim = 0; 135*da2e3ebdSchin cuthdr->last.ldelim = '\n'; 136*da2e3ebdSchin cuthdr->cflag = ((mode&C_CHARS)!=0 && mbwide()); 137*da2e3ebdSchin cuthdr->sflag = ((mode&C_SUPRESS)!=0); 138*da2e3ebdSchin cuthdr->nlflag = ((mode&C_NONEWLINE)!=0); 139*da2e3ebdSchin cuthdr->wdelim = wdelim; 140*da2e3ebdSchin cuthdr->ldelim = ldelim; 141*da2e3ebdSchin cuthdr->reclen = reclen; 142*da2e3ebdSchin cuthdr->seqno = ++cuthdr->last.seqno; 143*da2e3ebdSchin lp = cuthdr->list; 144*da2e3ebdSchin while(1) switch(c= *cp++) 145*da2e3ebdSchin { 146*da2e3ebdSchin case ' ': 147*da2e3ebdSchin case '\t': 148*da2e3ebdSchin while(*cp==' ' || *cp=='\t') 149*da2e3ebdSchin cp++; 150*da2e3ebdSchin case 0: 151*da2e3ebdSchin case ',': 152*da2e3ebdSchin if(range) 153*da2e3ebdSchin { 154*da2e3ebdSchin --range; 155*da2e3ebdSchin if((n = (n==0?HUGE:n-range)) < 0) 156*da2e3ebdSchin error(ERROR_exit(1),"invalid range for c/f option"); 157*da2e3ebdSchin *lp++ = range; 158*da2e3ebdSchin *lp++ = n; 159*da2e3ebdSchin } 160*da2e3ebdSchin else 161*da2e3ebdSchin { 162*da2e3ebdSchin *lp++ = --n; 163*da2e3ebdSchin *lp++ = 1; 164*da2e3ebdSchin } 165*da2e3ebdSchin if(c==0) 166*da2e3ebdSchin { 167*da2e3ebdSchin register int *dp; 168*da2e3ebdSchin *lp = HUGE; 169*da2e3ebdSchin n = 1 + (lp-cuthdr->list)/2; 170*da2e3ebdSchin qsort(lp=cuthdr->list,n,2*sizeof(*lp),mycomp); 171*da2e3ebdSchin /* eliminate overlapping regions */ 172*da2e3ebdSchin for(n=0,range= -2,dp=lp; *lp!=HUGE; lp+=2) 173*da2e3ebdSchin { 174*da2e3ebdSchin if(lp[0] <= range) 175*da2e3ebdSchin { 176*da2e3ebdSchin if(lp[1]==HUGE) 177*da2e3ebdSchin { 178*da2e3ebdSchin dp[-1] = HUGE; 179*da2e3ebdSchin break; 180*da2e3ebdSchin } 181*da2e3ebdSchin if((c = lp[0]+lp[1]-range)>0) 182*da2e3ebdSchin { 183*da2e3ebdSchin range += c; 184*da2e3ebdSchin dp[-1] += c; 185*da2e3ebdSchin } 186*da2e3ebdSchin } 187*da2e3ebdSchin else 188*da2e3ebdSchin { 189*da2e3ebdSchin range = *dp++ = lp[0]; 190*da2e3ebdSchin if(lp[1]==HUGE) 191*da2e3ebdSchin { 192*da2e3ebdSchin *dp++ = HUGE; 193*da2e3ebdSchin break; 194*da2e3ebdSchin } 195*da2e3ebdSchin range += (*dp++ = lp[1]); 196*da2e3ebdSchin } 197*da2e3ebdSchin } 198*da2e3ebdSchin *dp = HUGE; 199*da2e3ebdSchin lp = cuthdr->list; 200*da2e3ebdSchin /* convert ranges into gaps */ 201*da2e3ebdSchin for(n=0; *lp!=HUGE; lp+=2) 202*da2e3ebdSchin { 203*da2e3ebdSchin c = *lp; 204*da2e3ebdSchin *lp -= n; 205*da2e3ebdSchin n = c+lp[1]; 206*da2e3ebdSchin } 207*da2e3ebdSchin return(cuthdr); 208*da2e3ebdSchin } 209*da2e3ebdSchin n = range = 0; 210*da2e3ebdSchin break; 211*da2e3ebdSchin 212*da2e3ebdSchin case '-': 213*da2e3ebdSchin if(range) 214*da2e3ebdSchin error(ERROR_exit(1),"bad list for c/f option"); 215*da2e3ebdSchin range = n?n:1; 216*da2e3ebdSchin n = 0; 217*da2e3ebdSchin break; 218*da2e3ebdSchin 219*da2e3ebdSchin default: 220*da2e3ebdSchin if(!isdigit(c)) 221*da2e3ebdSchin error(ERROR_exit(1),"bad list for c/f option"); 222*da2e3ebdSchin n = 10*n + (c-'0'); 223*da2e3ebdSchin } 224*da2e3ebdSchin /* NOTREACHED */ 225*da2e3ebdSchin } 226*da2e3ebdSchin 227*da2e3ebdSchin /* 228*da2e3ebdSchin * advance <cp> by <n> multi-byte characters 229*da2e3ebdSchin */ 230*da2e3ebdSchin static int advance(const char *str, register int n, register int inlen) 231*da2e3ebdSchin { 232*da2e3ebdSchin register int size, len=inlen; 233*da2e3ebdSchin register const char *cp=str; 234*da2e3ebdSchin while(len>0 && n-->0) 235*da2e3ebdSchin { 236*da2e3ebdSchin size = mblen(cp, len); 237*da2e3ebdSchin if(size<0) 238*da2e3ebdSchin size = 1; 239*da2e3ebdSchin cp += size; 240*da2e3ebdSchin len -= size; 241*da2e3ebdSchin 242*da2e3ebdSchin } 243*da2e3ebdSchin if(n>0) 244*da2e3ebdSchin return(inlen+1); 245*da2e3ebdSchin return(cp-str); 246*da2e3ebdSchin } 247*da2e3ebdSchin 248*da2e3ebdSchin /* 249*da2e3ebdSchin * cut each line of file <fdin> and put results to <fdout> using list <list> 250*da2e3ebdSchin */ 251*da2e3ebdSchin 252*da2e3ebdSchin static int cutcols(Cut_t *cuthdr,Sfio_t *fdin,Sfio_t *fdout) 253*da2e3ebdSchin { 254*da2e3ebdSchin register int c, ncol=0,len; 255*da2e3ebdSchin register const int *lp = cuthdr->list; 256*da2e3ebdSchin register char *inp; 257*da2e3ebdSchin register int skip; /* non-zero for don't copy */ 258*da2e3ebdSchin while(1) 259*da2e3ebdSchin { 260*da2e3ebdSchin if(len = cuthdr->reclen) 261*da2e3ebdSchin inp = sfreserve(fdin, len, -1); 262*da2e3ebdSchin else 263*da2e3ebdSchin inp = sfgetr(fdin, '\n', 0); 264*da2e3ebdSchin if(!inp && !(inp = sfgetr(fdin, 0, SF_LASTR))) 265*da2e3ebdSchin break; 266*da2e3ebdSchin len = sfvalue(fdin); 267*da2e3ebdSchin if((ncol = skip = *(lp = cuthdr->list)) == 0) 268*da2e3ebdSchin ncol = *++lp; 269*da2e3ebdSchin while(1) 270*da2e3ebdSchin { 271*da2e3ebdSchin if((c=(cuthdr->cflag?advance(inp,ncol,len):ncol)) > len) 272*da2e3ebdSchin c = len; 273*da2e3ebdSchin else if(c==len && !skip) 274*da2e3ebdSchin ncol++; 275*da2e3ebdSchin ncol -= c; 276*da2e3ebdSchin if(!skip && sfwrite(fdout,(char*)inp,c)<0) 277*da2e3ebdSchin return(-1); 278*da2e3ebdSchin inp += c; 279*da2e3ebdSchin if(ncol) 280*da2e3ebdSchin break; 281*da2e3ebdSchin len -= c; 282*da2e3ebdSchin ncol = *++lp; 283*da2e3ebdSchin skip = !skip; 284*da2e3ebdSchin } 285*da2e3ebdSchin if(!cuthdr->nlflag && (skip || cuthdr->reclen)) 286*da2e3ebdSchin sfputc(fdout,cuthdr->ldelim); 287*da2e3ebdSchin } 288*da2e3ebdSchin return(c); 289*da2e3ebdSchin } 290*da2e3ebdSchin 291*da2e3ebdSchin /* 292*da2e3ebdSchin * cut each line of file <fdin> and put results to <fdout> using list <list> 293*da2e3ebdSchin * stream <fdin> must be line buffered 294*da2e3ebdSchin */ 295*da2e3ebdSchin 296*da2e3ebdSchin #define endline(c) (((signed char)-1)<0?(c)<0:(c)==((char)-1)) 297*da2e3ebdSchin 298*da2e3ebdSchin static int cutfields(Cut_t *cuthdr,Sfio_t *fdin,Sfio_t *fdout) 299*da2e3ebdSchin { 300*da2e3ebdSchin register unsigned char *cp; 301*da2e3ebdSchin register int c, nfields; 302*da2e3ebdSchin register const int *lp = cuthdr->list; 303*da2e3ebdSchin register unsigned char *copy; 304*da2e3ebdSchin register int nodelim, empty, inword=0; 305*da2e3ebdSchin register unsigned char *endbuff; 306*da2e3ebdSchin unsigned char *inbuff, *first; 307*da2e3ebdSchin int lastchar; 308*da2e3ebdSchin Sfio_t *fdtmp = 0; 309*da2e3ebdSchin long offset = 0; 310*da2e3ebdSchin if(cuthdr->seqno != cuthdr->last.seq) 311*da2e3ebdSchin { 312*da2e3ebdSchin cuthdr->space[cuthdr->last.ldelim] = 0; 313*da2e3ebdSchin cuthdr->space[cuthdr->last.wdelim] = 0; 314*da2e3ebdSchin cuthdr->space[cuthdr->last.wdelim=cuthdr->wdelim] = 1; 315*da2e3ebdSchin cuthdr->space[cuthdr->last.ldelim=cuthdr->ldelim] = -1; 316*da2e3ebdSchin cuthdr->last.seq = cuthdr->seqno; 317*da2e3ebdSchin } 318*da2e3ebdSchin /* process each buffer */ 319*da2e3ebdSchin while ((inbuff = (unsigned char*)sfreserve(fdin, SF_UNBOUND, 0)) && (c = sfvalue(fdin)) > 0) 320*da2e3ebdSchin { 321*da2e3ebdSchin cp = inbuff; 322*da2e3ebdSchin endbuff = cp + --c; 323*da2e3ebdSchin if((lastchar = cp[c]) != cuthdr->ldelim) 324*da2e3ebdSchin *endbuff = cuthdr->ldelim; 325*da2e3ebdSchin /* process each line in the buffer */ 326*da2e3ebdSchin while(cp <= endbuff) 327*da2e3ebdSchin { 328*da2e3ebdSchin first = cp; 329*da2e3ebdSchin if(!inword) 330*da2e3ebdSchin { 331*da2e3ebdSchin nodelim = empty = 1; 332*da2e3ebdSchin copy = cp; 333*da2e3ebdSchin if(nfields = *(lp = cuthdr->list)) 334*da2e3ebdSchin copy = 0; 335*da2e3ebdSchin else 336*da2e3ebdSchin nfields = *++lp; 337*da2e3ebdSchin } 338*da2e3ebdSchin else if(copy) 339*da2e3ebdSchin copy = cp; 340*da2e3ebdSchin inword = 0; 341*da2e3ebdSchin while(!inword) 342*da2e3ebdSchin { 343*da2e3ebdSchin /* skip over non-delimiter characters */ 344*da2e3ebdSchin while(!(c=cuthdr->space[*cp++])); 345*da2e3ebdSchin /* check for end-of-line */ 346*da2e3ebdSchin if(endline(c)) 347*da2e3ebdSchin { 348*da2e3ebdSchin if(cp<=endbuff) 349*da2e3ebdSchin break; 350*da2e3ebdSchin if((c=cuthdr->space[lastchar]),endline(c)) 351*da2e3ebdSchin break; 352*da2e3ebdSchin /* restore cuthdr->last. character */ 353*da2e3ebdSchin if(lastchar != cuthdr->ldelim) 354*da2e3ebdSchin *endbuff = lastchar; 355*da2e3ebdSchin inword++; 356*da2e3ebdSchin if(!c) 357*da2e3ebdSchin break; 358*da2e3ebdSchin } 359*da2e3ebdSchin nodelim = 0; 360*da2e3ebdSchin if(--nfields >0) 361*da2e3ebdSchin continue; 362*da2e3ebdSchin nfields = *++lp; 363*da2e3ebdSchin if(copy) 364*da2e3ebdSchin { 365*da2e3ebdSchin empty = 0; 366*da2e3ebdSchin if((c=(cp-1)-copy)>0 && sfwrite(fdout,(char*)copy,c)< 0) 367*da2e3ebdSchin goto failed; 368*da2e3ebdSchin copy = 0; 369*da2e3ebdSchin } 370*da2e3ebdSchin else 371*da2e3ebdSchin /* set to delimiter unless the first field */ 372*da2e3ebdSchin copy = cp -!empty; 373*da2e3ebdSchin } 374*da2e3ebdSchin if(!inword) 375*da2e3ebdSchin { 376*da2e3ebdSchin if(!copy) 377*da2e3ebdSchin { 378*da2e3ebdSchin if(nodelim) 379*da2e3ebdSchin { 380*da2e3ebdSchin if(!cuthdr->sflag) 381*da2e3ebdSchin { 382*da2e3ebdSchin if(offset) 383*da2e3ebdSchin { 384*da2e3ebdSchin sfseek(fdtmp,(Sfoff_t)0,SEEK_SET); 385*da2e3ebdSchin sfmove(fdtmp,fdout,offset,-1); 386*da2e3ebdSchin } 387*da2e3ebdSchin copy = first; 388*da2e3ebdSchin } 389*da2e3ebdSchin } 390*da2e3ebdSchin else 391*da2e3ebdSchin sfputc(fdout,'\n'); 392*da2e3ebdSchin } 393*da2e3ebdSchin if(offset) 394*da2e3ebdSchin sfseek(fdtmp,offset=0,SEEK_SET); 395*da2e3ebdSchin } 396*da2e3ebdSchin if(copy && (c=cp-copy)>0 && (!nodelim || !cuthdr->sflag) && sfwrite(fdout,(char*)copy,c)< 0) 397*da2e3ebdSchin goto failed; 398*da2e3ebdSchin } 399*da2e3ebdSchin /* see whether to save in tmp file */ 400*da2e3ebdSchin if(nodelim && inword && !cuthdr->sflag && (c=cp-first)>0) 401*da2e3ebdSchin { 402*da2e3ebdSchin /* copy line to tmpfile in case no fields */ 403*da2e3ebdSchin if(!fdtmp) 404*da2e3ebdSchin fdtmp = sftmp(BLOCK); 405*da2e3ebdSchin sfwrite(fdtmp,(char*)first,c); 406*da2e3ebdSchin offset +=c; 407*da2e3ebdSchin } 408*da2e3ebdSchin } 409*da2e3ebdSchin failed: 410*da2e3ebdSchin if(fdtmp) 411*da2e3ebdSchin sfclose(fdtmp); 412*da2e3ebdSchin return(0); 413*da2e3ebdSchin } 414*da2e3ebdSchin 415*da2e3ebdSchin int 416*da2e3ebdSchin b_cut(int argc,char *argv[], void* context) 417*da2e3ebdSchin { 418*da2e3ebdSchin register char *cp = 0; 419*da2e3ebdSchin register Sfio_t *fp; 420*da2e3ebdSchin int n; 421*da2e3ebdSchin Cut_t *cuthdr; 422*da2e3ebdSchin int mode = 0; 423*da2e3ebdSchin int wdelim = '\t'; 424*da2e3ebdSchin int ldelim = '\n'; 425*da2e3ebdSchin size_t reclen = 0; 426*da2e3ebdSchin 427*da2e3ebdSchin cmdinit(argc, argv, context, ERROR_CATALOG, 0); 428*da2e3ebdSchin while (n = optget(argv, usage)) switch (n) 429*da2e3ebdSchin { 430*da2e3ebdSchin case 'b': 431*da2e3ebdSchin case 'c': 432*da2e3ebdSchin if(mode&C_FIELDS) 433*da2e3ebdSchin { 434*da2e3ebdSchin error(2, "f option already specified"); 435*da2e3ebdSchin break; 436*da2e3ebdSchin } 437*da2e3ebdSchin cp = opt_info.arg; 438*da2e3ebdSchin if(n=='b') 439*da2e3ebdSchin mode |= C_BYTES; 440*da2e3ebdSchin else 441*da2e3ebdSchin mode |= C_CHARS; 442*da2e3ebdSchin break; 443*da2e3ebdSchin case 'D': 444*da2e3ebdSchin ldelim = *(unsigned char*)opt_info.arg; 445*da2e3ebdSchin break; 446*da2e3ebdSchin case 'd': 447*da2e3ebdSchin wdelim = *(unsigned char*)opt_info.arg; 448*da2e3ebdSchin break; 449*da2e3ebdSchin case 'f': 450*da2e3ebdSchin if(mode&(C_CHARS|C_BYTES)) 451*da2e3ebdSchin { 452*da2e3ebdSchin error(2, "c option already specified"); 453*da2e3ebdSchin break; 454*da2e3ebdSchin } 455*da2e3ebdSchin cp = opt_info.arg; 456*da2e3ebdSchin mode |= C_FIELDS; 457*da2e3ebdSchin break; 458*da2e3ebdSchin case 'n': 459*da2e3ebdSchin mode |= C_NOCHOP; 460*da2e3ebdSchin break; 461*da2e3ebdSchin case 'N': 462*da2e3ebdSchin mode |= C_NONEWLINE; 463*da2e3ebdSchin break; 464*da2e3ebdSchin case 'R': 465*da2e3ebdSchin case 'r': 466*da2e3ebdSchin if(opt_info.num>0) 467*da2e3ebdSchin reclen = opt_info.num; 468*da2e3ebdSchin break; 469*da2e3ebdSchin case 's': 470*da2e3ebdSchin mode |= C_SUPRESS; 471*da2e3ebdSchin break; 472*da2e3ebdSchin case ':': 473*da2e3ebdSchin error(2, "%s", opt_info.arg); 474*da2e3ebdSchin break; 475*da2e3ebdSchin case '?': 476*da2e3ebdSchin error(ERROR_usage(2), "%s", opt_info.arg); 477*da2e3ebdSchin break; 478*da2e3ebdSchin } 479*da2e3ebdSchin argv += opt_info.index; 480*da2e3ebdSchin if (error_info.errors) 481*da2e3ebdSchin error(ERROR_usage(2), "%s",optusage(NiL)); 482*da2e3ebdSchin if(!cp) 483*da2e3ebdSchin { 484*da2e3ebdSchin error(2, "b, c or f option must be specified"); 485*da2e3ebdSchin error(ERROR_usage(2), "%s", optusage(NiL)); 486*da2e3ebdSchin } 487*da2e3ebdSchin if(!*cp) 488*da2e3ebdSchin error(3, "non-empty b, c or f option must be specified"); 489*da2e3ebdSchin if((mode & (C_FIELDS|C_SUPRESS)) == C_SUPRESS) 490*da2e3ebdSchin error(3, "s option requires f option"); 491*da2e3ebdSchin cuthdr = cutinit(mode,cp,wdelim,ldelim,reclen); 492*da2e3ebdSchin if(cp = *argv) 493*da2e3ebdSchin argv++; 494*da2e3ebdSchin do 495*da2e3ebdSchin { 496*da2e3ebdSchin if(!cp || streq(cp,"-")) 497*da2e3ebdSchin fp = sfstdin; 498*da2e3ebdSchin else if(!(fp = sfopen(NiL,cp,"r"))) 499*da2e3ebdSchin { 500*da2e3ebdSchin error(ERROR_system(0),"%s: cannot open",cp); 501*da2e3ebdSchin continue; 502*da2e3ebdSchin } 503*da2e3ebdSchin if(mode&C_FIELDS) 504*da2e3ebdSchin cutfields(cuthdr,fp,sfstdout); 505*da2e3ebdSchin else 506*da2e3ebdSchin cutcols(cuthdr,fp,sfstdout); 507*da2e3ebdSchin if(fp!=sfstdin) 508*da2e3ebdSchin sfclose(fp); 509*da2e3ebdSchin } 510*da2e3ebdSchin while(cp= *argv++); 511*da2e3ebdSchin return(error_info.errors?1:0); 512*da2e3ebdSchin } 513