xref: /freebsd/usr.bin/unexpand/unexpand.c (revision a2641fe8d735f8fa86fb8fef95448fb1da8b2454)
19b50d902SRodney W. Grimes /*-
29b50d902SRodney W. Grimes  * Copyright (c) 1980, 1993
39b50d902SRodney W. Grimes  *	The Regents of the University of California.  All rights reserved.
49b50d902SRodney W. Grimes  *
59b50d902SRodney W. Grimes  * Redistribution and use in source and binary forms, with or without
69b50d902SRodney W. Grimes  * modification, are permitted provided that the following conditions
79b50d902SRodney W. Grimes  * are met:
89b50d902SRodney W. Grimes  * 1. Redistributions of source code must retain the above copyright
99b50d902SRodney W. Grimes  *    notice, this list of conditions and the following disclaimer.
109b50d902SRodney W. Grimes  * 2. Redistributions in binary form must reproduce the above copyright
119b50d902SRodney W. Grimes  *    notice, this list of conditions and the following disclaimer in the
129b50d902SRodney W. Grimes  *    documentation and/or other materials provided with the distribution.
139b50d902SRodney W. Grimes  * 3. All advertising materials mentioning features or use of this software
149b50d902SRodney W. Grimes  *    must display the following acknowledgement:
159b50d902SRodney W. Grimes  *	This product includes software developed by the University of
169b50d902SRodney W. Grimes  *	California, Berkeley and its contributors.
179b50d902SRodney W. Grimes  * 4. Neither the name of the University nor the names of its contributors
189b50d902SRodney W. Grimes  *    may be used to endorse or promote products derived from this software
199b50d902SRodney W. Grimes  *    without specific prior written permission.
209b50d902SRodney W. Grimes  *
219b50d902SRodney W. Grimes  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
229b50d902SRodney W. Grimes  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
239b50d902SRodney W. Grimes  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
249b50d902SRodney W. Grimes  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
259b50d902SRodney W. Grimes  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
269b50d902SRodney W. Grimes  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
279b50d902SRodney W. Grimes  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
289b50d902SRodney W. Grimes  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
299b50d902SRodney W. Grimes  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
309b50d902SRodney W. Grimes  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
319b50d902SRodney W. Grimes  * SUCH DAMAGE.
329b50d902SRodney W. Grimes  */
339b50d902SRodney W. Grimes 
34b6a5c4e0SMark Murray #include <sys/cdefs.h>
35b6a5c4e0SMark Murray 
36b6a5c4e0SMark Murray __FBSDID("$FreeBSD$");
37b6a5c4e0SMark Murray 
389b50d902SRodney W. Grimes #ifndef lint
3956d8f922SPhilippe Charnier static const char copyright[] =
409b50d902SRodney W. Grimes "@(#) Copyright (c) 1980, 1993\n\
419b50d902SRodney W. Grimes 	The Regents of the University of California.  All rights reserved.\n";
42b6a5c4e0SMark Murray #endif
439b50d902SRodney W. Grimes 
449b50d902SRodney W. Grimes #ifndef lint
45b6a5c4e0SMark Murray static const char sccsid[] = "@(#)unexpand.c	8.1 (Berkeley) 6/6/93";
4656d8f922SPhilippe Charnier #endif
479b50d902SRodney W. Grimes 
489b50d902SRodney W. Grimes /*
499b50d902SRodney W. Grimes  * unexpand - put tabs into a file replacing blanks
509b50d902SRodney W. Grimes  */
51ec85e6a0STim J. Robbins #include <ctype.h>
5256d8f922SPhilippe Charnier #include <err.h>
53aca29625STim J. Robbins #include <limits.h>
54ec85e6a0STim J. Robbins #include <locale.h>
559b50d902SRodney W. Grimes #include <stdio.h>
56948a3f54SDima Dorfman #include <stdlib.h>
575b116430SJohn Birrell #include <string.h>
58aca29625STim J. Robbins #include <unistd.h>
59a2641fe8STim J. Robbins #include <wchar.h>
60a2641fe8STim J. Robbins #include <wctype.h>
619b50d902SRodney W. Grimes 
629b50d902SRodney W. Grimes int	all;
63aca29625STim J. Robbins int	nstops;
64aca29625STim J. Robbins int	tabstops[100];
659b50d902SRodney W. Grimes 
66aca29625STim J. Robbins static void getstops(const char *);
673f330d7dSWarner Losh static void usage(void);
68a2641fe8STim J. Robbins static int tabify(const char *);
6956d8f922SPhilippe Charnier 
7001492790SPhilippe Charnier int
71f4ac32deSDavid Malone main(int argc, char *argv[])
729b50d902SRodney W. Grimes {
734596ce3dSTim J. Robbins 	int ch, failed;
744596ce3dSTim J. Robbins 	char *filename;
759b50d902SRodney W. Grimes 
76ec85e6a0STim J. Robbins 	setlocale(LC_CTYPE, "");
77ec85e6a0STim J. Robbins 
78aca29625STim J. Robbins 	nstops = 1;
79aca29625STim J. Robbins 	tabstops[0] = 8;
80aca29625STim J. Robbins 	while ((ch = getopt(argc, argv, "at:")) != -1) {
81aca29625STim J. Robbins 		switch (ch) {
82aca29625STim J. Robbins 		case 'a':	/* Un-expand all spaces, not just leading. */
83aca29625STim J. Robbins 			all = 1;
84aca29625STim J. Robbins 			break;
85aca29625STim J. Robbins 		case 't':	/* Specify tab list, implies -a. */
86aca29625STim J. Robbins 			getstops(optarg);
87aca29625STim J. Robbins 			all = 1;
88aca29625STim J. Robbins 			break;
89aca29625STim J. Robbins 		default:
9056d8f922SPhilippe Charnier 			usage();
91aca29625STim J. Robbins 			/*NOTREACHED*/
929b50d902SRodney W. Grimes 		}
93aca29625STim J. Robbins 	}
94aca29625STim J. Robbins 	argc -= optind;
95aca29625STim J. Robbins 	argv += optind;
96aca29625STim J. Robbins 
974596ce3dSTim J. Robbins 	failed = 0;
984596ce3dSTim J. Robbins 	if (argc == 0)
99a2641fe8STim J. Robbins 		failed |= tabify("stdin");
1004596ce3dSTim J. Robbins 	else {
1014596ce3dSTim J. Robbins 		while ((filename = *argv++) != NULL) {
1024596ce3dSTim J. Robbins 			if (freopen(filename, "r", stdin) == NULL) {
1034596ce3dSTim J. Robbins 				warn("%s", filename);
104a2641fe8STim J. Robbins 				failed = 1;
1054596ce3dSTim J. Robbins 			} else
106a2641fe8STim J. Robbins 				failed |= tabify(filename);
1074596ce3dSTim J. Robbins 		}
1084596ce3dSTim J. Robbins 	}
1094596ce3dSTim J. Robbins 	exit(failed != 0);
1109b50d902SRodney W. Grimes }
1119b50d902SRodney W. Grimes 
11256d8f922SPhilippe Charnier static void
113f4ac32deSDavid Malone usage(void)
11456d8f922SPhilippe Charnier {
115aca29625STim J. Robbins 	fprintf(stderr, "usage: unexpand [-a] [-t tablist] [file ...]\n");
11656d8f922SPhilippe Charnier 	exit(1);
11756d8f922SPhilippe Charnier }
11856d8f922SPhilippe Charnier 
119a2641fe8STim J. Robbins static int
120a2641fe8STim J. Robbins tabify(const char *curfile)
1219b50d902SRodney W. Grimes {
122a2641fe8STim J. Robbins 	int dcol, doneline, limit, n, ocol, width;
123a2641fe8STim J. Robbins 	wint_t ch;
1249b50d902SRodney W. Grimes 
125aca29625STim J. Robbins 	limit = nstops == 1 ? INT_MAX : tabstops[nstops - 1] - 1;
1269b50d902SRodney W. Grimes 
127aca29625STim J. Robbins 	doneline = ocol = dcol = 0;
128a2641fe8STim J. Robbins 	while ((ch = getwchar()) != WEOF) {
1294596ce3dSTim J. Robbins 		if (ch == ' ' && !doneline) {
130aca29625STim J. Robbins 			if (++dcol >= limit)
131aca29625STim J. Robbins 				doneline = 1;
132aca29625STim J. Robbins 			continue;
133aca29625STim J. Robbins 		} else if (ch == '\t') {
134aca29625STim J. Robbins 			if (nstops == 1) {
135aca29625STim J. Robbins 				dcol = (1 + dcol / tabstops[0]) *
136aca29625STim J. Robbins 				    tabstops[0];
137aca29625STim J. Robbins 				continue;
138aca29625STim J. Robbins 			} else {
139aca29625STim J. Robbins 				for (n = 0; tabstops[n] - 1 < dcol &&
140aca29625STim J. Robbins 				    n < nstops; n++)
141aca29625STim J. Robbins 					;
142aca29625STim J. Robbins 				if (n < nstops - 1 && tabstops[n] - 1 < limit) {
143aca29625STim J. Robbins 					dcol = tabstops[n];
144aca29625STim J. Robbins 					continue;
1459b50d902SRodney W. Grimes 				}
146aca29625STim J. Robbins 				doneline = 1;
147aca29625STim J. Robbins 			}
148aca29625STim J. Robbins 		}
149aca29625STim J. Robbins 
150aca29625STim J. Robbins 		/* Output maximal number of tabs. */
151aca29625STim J. Robbins 		if (nstops == 1) {
152aca29625STim J. Robbins 			while (((ocol + tabstops[0]) / tabstops[0])
153aca29625STim J. Robbins 			    <= (dcol / tabstops[0])) {
154aca29625STim J. Robbins 				if (dcol - ocol < 2)
155aca29625STim J. Robbins 					break;
156a2641fe8STim J. Robbins 				putwchar('\t');
157aca29625STim J. Robbins 				ocol = (1 + ocol / tabstops[0]) *
158aca29625STim J. Robbins 				    tabstops[0];
159aca29625STim J. Robbins 			}
160aca29625STim J. Robbins 		} else {
161aca29625STim J. Robbins 			for (n = 0; tabstops[n] - 1 < ocol && n < nstops; n++)
162aca29625STim J. Robbins 				;
163aca29625STim J. Robbins 			while (ocol < dcol && n < nstops && ocol < limit) {
164a2641fe8STim J. Robbins 				putwchar('\t');
165aca29625STim J. Robbins 				ocol = tabstops[n++];
166aca29625STim J. Robbins 			}
167aca29625STim J. Robbins 		}
168aca29625STim J. Robbins 
169aca29625STim J. Robbins 		/* Then spaces. */
170aca29625STim J. Robbins 		while (ocol < dcol && ocol < limit) {
171a2641fe8STim J. Robbins 			putwchar(' ');
1729b50d902SRodney W. Grimes 			ocol++;
1739b50d902SRodney W. Grimes 		}
174aca29625STim J. Robbins 
1754596ce3dSTim J. Robbins 		if (ch == '\b') {
176a2641fe8STim J. Robbins 			putwchar('\b');
1774596ce3dSTim J. Robbins 			if (ocol > 0)
1784596ce3dSTim J. Robbins 				ocol--, dcol--;
1794596ce3dSTim J. Robbins 		} else if (ch == '\n') {
180a2641fe8STim J. Robbins 			putwchar('\n');
1814596ce3dSTim J. Robbins 			doneline = ocol = dcol = 0;
18226cabd19STim J. Robbins 			continue;
1834596ce3dSTim J. Robbins 		} else if (ch != ' ' || dcol > limit) {
184a2641fe8STim J. Robbins 			putwchar(ch);
185a2641fe8STim J. Robbins 			if ((width = wcwidth(ch)) > 0)
186a2641fe8STim J. Robbins 				ocol += width, dcol += width;
1879b50d902SRodney W. Grimes 		}
188aca29625STim J. Robbins 
189aca29625STim J. Robbins 		/*
190aca29625STim J. Robbins 		 * Only processing leading blanks or we've gone past the
191aca29625STim J. Robbins 		 * last tab stop. Emit remainder of this line unchanged.
192aca29625STim J. Robbins 		 */
193aca29625STim J. Robbins 		if (!all || dcol >= limit) {
194a2641fe8STim J. Robbins 			while ((ch = getwchar()) != '\n' && ch != WEOF)
195a2641fe8STim J. Robbins 				putwchar(ch);
196aca29625STim J. Robbins 			if (ch == '\n')
197a2641fe8STim J. Robbins 				putwchar('\n');
1984596ce3dSTim J. Robbins 			doneline = ocol = dcol = 0;
199aca29625STim J. Robbins 		}
200aca29625STim J. Robbins 	}
201a2641fe8STim J. Robbins 	if (ferror(stdin)) {
202a2641fe8STim J. Robbins 		warn("%s", curfile);
203a2641fe8STim J. Robbins 		return (1);
204a2641fe8STim J. Robbins 	}
205a2641fe8STim J. Robbins 	return (0);
206aca29625STim J. Robbins }
207aca29625STim J. Robbins 
208aca29625STim J. Robbins static void
209f4ac32deSDavid Malone getstops(const char *cp)
210aca29625STim J. Robbins {
211aca29625STim J. Robbins 	int i;
212aca29625STim J. Robbins 
213aca29625STim J. Robbins 	nstops = 0;
214aca29625STim J. Robbins 	for (;;) {
215aca29625STim J. Robbins 		i = 0;
216aca29625STim J. Robbins 		while (*cp >= '0' && *cp <= '9')
217aca29625STim J. Robbins 			i = i * 10 + *cp++ - '0';
218aca29625STim J. Robbins 		if (i <= 0)
219aca29625STim J. Robbins 			errx(1, "bad tab stop spec");
220aca29625STim J. Robbins 		if (nstops > 0 && i <= tabstops[nstops-1])
221aca29625STim J. Robbins 			errx(1, "bad tab stop spec");
222aca29625STim J. Robbins 		if (nstops == sizeof(tabstops) / sizeof(*tabstops))
223aca29625STim J. Robbins 			errx(1, "too many tab stops");
224aca29625STim J. Robbins 		tabstops[nstops++] = i;
225aca29625STim J. Robbins 		if (*cp == 0)
226aca29625STim J. Robbins 			break;
227ec85e6a0STim J. Robbins 		if (*cp != ',' && !isblank((unsigned char)*cp))
228aca29625STim J. Robbins 			errx(1, "bad tab stop spec");
2299b50d902SRodney W. Grimes 		cp++;
2309b50d902SRodney W. Grimes 	}
2319b50d902SRodney W. Grimes }
232