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