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