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