xref: /titanic_51/usr/src/lib/libcmd/common/cut.c (revision da2e3ebdc1edfbc5028edf1354e7dd2fa69a7968)
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