xref: /illumos-gate/usr/src/cmd/fold/fold.c (revision 8b80e8cb6855118d46f605e91b5ed4ce83417395)
1 /*
2  * CDDL HEADER START
3  *
4  * The contents of this file are subject to the terms of the
5  * Common Development and Distribution License, Version 1.0 only
6  * (the "License").  You may not use this file except in compliance
7  * with the License.
8  *
9  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10  * or http://www.opensolaris.org/os/licensing.
11  * See the License for the specific language governing permissions
12  * and limitations under the License.
13  *
14  * When distributing Covered Code, include this CDDL HEADER in each
15  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16  * If applicable, add the following below this CDDL HEADER, with the
17  * fields enclosed by brackets "[]" replaced with your own identifying
18  * information: Portions Copyright [yyyy] [name of copyright owner]
19  *
20  * CDDL HEADER END
21  */
22 /*
23  * Copyright 2004 Sun Microsystems, Inc.  All rights reserved.
24  * Use is subject to license terms.
25  */
26 
27 /*	Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T	*/
28 /*	  All Rights Reserved  	*/
29 
30 /*
31  * University Copyright- Copyright (c) 1982, 1986, 1988
32  * The Regents of the University of California
33  * All Rights Reserved
34  *
35  * University Acknowledgment- Portions of this document are derived from
36  * software developed by the University of California, Berkeley, and its
37  * contributors.
38  */
39 
40 #pragma ident	"%Z%%M%	%I%	%E% SMI"
41 
42 #include <stdio.h>
43 #include <locale.h>
44 #include <wchar.h>
45 #include <euc.h>
46 #include <stdlib.h>		/* XCU4 */
47 #include <limits.h>
48 #include <libintl.h>
49 #include <langinfo.h>
50 #include <utime.h>
51 #include <widec.h>
52 #include <wctype.h>
53 #include <errno.h>
54 
55 
56 /*
57  * fold - fold long lines for finite output devices
58  */
59 
60 static int fold =  80;
61 static int bflg = 0;
62 static int sflg = 0;
63 static int wflg = 0;
64 static int lastc = 0;
65 static int col = 0;
66 static int ncol = 0;
67 static int spcol = 0;
68 static wchar_t line[LINE_MAX];
69 static wchar_t *lastout = line;
70 static wchar_t *curc = line;
71 static wchar_t *lastsp = NULL;
72 #define	MAXARG _POSIX_ARG_MAX
73 
74 /*
75  * Fix lint errors
76  */
77 void exit();
78 static void Usage();
79 static void putch();
80 static void newline_init();
81 static int chr_width();
82 extern int errno;
83 static int get_foldw();
84 
85 
86 int
87 main(int argc, char *argv[])
88 {
89 	int c, narg;
90 	int ch;
91 	char *cmdline[MAXARG];
92 	int	new_argc;
93 	int w;
94 	extern int optind;
95 	extern char *optarg;
96 
97 	(void) setlocale(LC_ALL, "");
98 #if !defined(TEXT_DOMAIN)
99 #define	TEXT_DOMAIN "SYS_TEST"
100 #endif
101 	(void) textdomain(TEXT_DOMAIN);
102 
103 	/*
104 	 * Parse -width separately and build
105 	 * the new command line without -width.
106 	 * Next, use getopt() to parse this
107 	 * new command line.
108 	 */
109 
110 	for (narg = new_argc = 0; narg < argc; narg ++) {
111 		if (argv[narg][0] == '-' &&
112 			isdigit(argv[narg][1])) {
113 
114 			if (get_foldw((char *)&argv[narg][1], &w) < 0)
115 				exit(1);
116 
117 			fold = w;	/* Update with new width */
118 		} else {
119 			/* Build the new command line */
120 			cmdline[new_argc++] = argv[narg];
121 
122 			/*
123 			 * Check to make sure the option with
124 			 * required arg should have arg.
125 			 * This would check errors introduced in
126 			 * mixing non-getopt() options and that of
127 			 * getopt()'s due to stripping non-getopt
128 			 * options.
129 			 */
130 			if ((argv[narg][0] == '-') && (argv[narg][1] == 'w')) {
131 				if (((narg+1) < argc) &&
132 					(argv[narg+1][0] == '-')) {
133 					(void) fprintf(stderr, "fold");
134 					(void) fprintf(stderr, gettext(
135 				": option requires an argument -- w\n"));
136 					Usage();
137 					exit(1);
138 				}
139 			}
140 		}
141 	}
142 
143 	while ((ch = getopt(new_argc, cmdline, "w:bs")) != EOF) {
144 		switch (ch) {
145 			case 'b':
146 				bflg++;
147 				break;
148 			case 's':
149 				sflg++;
150 				break;
151 			case 'w':
152 				wflg++;
153 				/* No required arg ? */
154 				if ((optarg == (char *)NULL) ||
155 					((optarg != (char *)NULL) &&
156 					(*optarg == '-'))) {
157 						(void) fprintf(stderr, "fold");
158 						(void) fprintf(stderr, gettext(
159 				": option requires an argument -- w\n"));
160 						Usage();
161 						exit(1);
162 				}
163 				/* Bad number ? */
164 				if (get_foldw(optarg, &w) < 0)
165 					exit(1);
166 
167 				fold = w;
168 				break;
169 			default:
170 				/*
171 				 * Errors should be filtered in previous
172 				 * pass.
173 				 */
174 				Usage();
175 				exit(1);
176 		} /* switch */
177 	} /* while */
178 
179 	do {
180 		if (new_argc > optind) {
181 			if (freopen(cmdline[optind], "r", stdin) == NULL) {
182 				perror(cmdline[optind]);
183 				Usage();
184 				exit(1);
185 			}
186 			optind++;
187 		}
188 
189 		for (;;) {
190 			c = getwc(stdin);
191 			if (c == EOF)
192 				break;
193 			(void) putch(c);
194 			lastc = c;
195 		}
196 		if (col != 0) newline_init();
197 	} while (new_argc > optind);
198 
199 	return (0);
200 }
201 
202 static void
203 putch(int c)
204 {
205 	wchar_t tline[LINE_MAX];
206 
207 	switch (c) {
208 		case '\n':
209 			ncol = 0;
210 			break;
211 		case '\t':
212 			if (bflg)
213 				ncol = col + chr_width(c);
214 			else
215 				ncol = (col + 8) &~ 7;
216 			break;
217 		case '\b':
218 			if (bflg)
219 				ncol = col + chr_width(c);
220 			else
221 				ncol = col ? col - 1 : 0;
222 			break;
223 		case '\r':
224 			if (bflg)
225 				ncol = col + chr_width(c);
226 			else
227 				ncol = 0;
228 			break;
229 
230 		default:
231 			if (bflg)
232 				ncol = col + chr_width(c);
233 			else
234 				ncol = col + wcwidth(c);
235 
236 	}
237 
238 	/*
239 	 * Special processing when -b is not specified
240 	 * for backspace, and carriage return.
241 	 * No newline is inseted before or after the
242 	 * special character: backspace, or cr.
243 	 * See man page for the handling of backspace
244 	 * and CR when there is no -b.
245 	 */
246 	if ((ncol > fold) && (bflg ||
247 		(!bflg && (lastc != '\b') && (c != '\b') &&
248 		(lastc != '\n') && (c != '\n'))))  {
249 		/*
250 		 * Need to check the last position for blank
251 		 */
252 		if (sflg && lastsp) {
253 			/*
254 			 * Save the output buffer
255 			 * as NULL has to be insert into the last
256 			 * sp position.
257 			 */
258 			(void) wscpy(tline, line);
259 			*lastsp = (wchar_t)NULL;
260 			(void) fputws(lastout, stdout);
261 			(void) putwchar('\n');
262 			/*
263 			 * Restore the output buffer to stuff
264 			 * NULL into the last sp position
265 			 * for the new line.
266 			 */
267 			(void) wscpy(line, tline);
268 			lastout = lastsp;
269 			lastsp = NULL;	/* Reset the last sp */
270 			ncol -= spcol;	/* Reset column positions */
271 			col -= spcol;
272 		} else {
273 			(void) newline_init();
274 			(void) putwchar('\n');
275 			lastout = curc;
276 		}
277 	}
278 	/* Output buffer is full ? */
279 	if ((curc + 1) >= (line + LINE_MAX)) {
280 		/* Reach buffer limit */
281 		if (col > 0) {
282 			*curc = (wchar_t)NULL;
283 			(void) fputws(lastout, stdout);
284 			lastsp = NULL;
285 		}
286 		curc = lastout = line;
287 
288 	}
289 
290 	/* Store in output buffer */
291 	*curc++ = (wchar_t)c;
292 
293 	switch (c) {
294 		case '\n':
295 			(void)  newline_init();
296 			curc = lastout = line;
297 			break;
298 		case '\t':
299 			if (bflg)
300 				col = col + chr_width(c);
301 			else
302 				col = (col + 8) &~ 7;
303 			if (sflg && iswspace(c)) {
304 				lastsp = curc;
305 				spcol = ncol;
306 			}
307 
308 			break;
309 		case '\b':
310 			if (bflg)
311 				col = ncol;
312 			else {
313 				if (col)
314 					col--;
315 			}
316 			break;
317 		case '\r':
318 			col = 0;
319 			break;
320 		default:
321 			if (sflg && iswspace(c)) {
322 				lastsp = curc;
323 				spcol = ncol;
324 			}
325 
326 			if (bflg)
327 				col += chr_width(c);
328 			else
329 				col += wcwidth(c);
330 
331 			break;
332 	}
333 }
334 static
335 void
336 Usage()
337 {
338 	(void) fprintf(stderr, gettext(
339 	    "Usage: fold [-bs] [-w width | -width ] [file...]\n"));
340 }
341 
342 static
343 void
344 newline_init()
345 {
346 	*curc = (wchar_t)NULL;
347 	(void) fputws(lastout, stdout);
348 	ncol = col = spcol = 0;
349 	lastsp = NULL;
350 }
351 
352 static int
353 chr_width(c)
354 register int c;
355 {
356 	char chr[MB_LEN_MAX+1];
357 	register int	n;
358 
359 	n = wctomb(chr, (wchar_t)c);
360 
361 	return (n > 0 ? n : 0);
362 }
363 
364 static int
365 get_foldw(toptarg, width)
366 char	*toptarg;
367 int		*width;
368 {
369 	char	*p;
370 
371 	if (!toptarg)
372 		goto badno;
373 
374 	*width = 0;
375 	errno = 0;
376 	*width = strtoul(toptarg, &p, 10);
377 	if (*width == -1)
378 		goto badno;
379 
380 	if (*p)
381 		goto badno;
382 
383 	if (!*width)
384 		goto badno;
385 
386 	return (0);
387 
388 badno:
389 	/* fold error message */
390 	(void) fprintf(stderr, gettext(
391 		"Bad number for fold\n"));
392 	Usage();
393 	return (-1);
394 }
395