xref: /freebsd/usr.bin/expand/expand.c (revision 0b8224d1cc9dc6c9778ba04a75b2c8d47e5d7481)
18a16b7a1SPedro F. Giffuni /*-
28a16b7a1SPedro F. Giffuni  * SPDX-License-Identifier: BSD-3-Clause
38a16b7a1SPedro F. Giffuni  *
49b50d902SRodney W. Grimes  * Copyright (c) 1980, 1993
59b50d902SRodney W. Grimes  *	The Regents of the University of California.  All rights reserved.
69b50d902SRodney W. Grimes  *
79b50d902SRodney W. Grimes  * Redistribution and use in source and binary forms, with or without
89b50d902SRodney W. Grimes  * modification, are permitted provided that the following conditions
99b50d902SRodney W. Grimes  * are met:
109b50d902SRodney W. Grimes  * 1. Redistributions of source code must retain the above copyright
119b50d902SRodney W. Grimes  *    notice, this list of conditions and the following disclaimer.
129b50d902SRodney W. Grimes  * 2. Redistributions in binary form must reproduce the above copyright
139b50d902SRodney W. Grimes  *    notice, this list of conditions and the following disclaimer in the
149b50d902SRodney W. Grimes  *    documentation and/or other materials provided with the distribution.
15fbbd9655SWarner Losh  * 3. Neither the name of the University nor the names of its contributors
169b50d902SRodney W. Grimes  *    may be used to endorse or promote products derived from this software
179b50d902SRodney W. Grimes  *    without specific prior written permission.
189b50d902SRodney W. Grimes  *
199b50d902SRodney W. Grimes  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
209b50d902SRodney W. Grimes  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
219b50d902SRodney W. Grimes  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
229b50d902SRodney W. Grimes  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
239b50d902SRodney W. Grimes  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
249b50d902SRodney W. Grimes  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
259b50d902SRodney W. Grimes  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
269b50d902SRodney W. Grimes  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
279b50d902SRodney W. Grimes  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
289b50d902SRodney W. Grimes  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
299b50d902SRodney W. Grimes  * SUCH DAMAGE.
309b50d902SRodney W. Grimes  */
319b50d902SRodney W. Grimes 
32ca156092SPhilippe Charnier #include <ctype.h>
33a2995dd0SPhilippe Charnier #include <err.h>
34ec85e6a0STim J. Robbins #include <locale.h>
359b50d902SRodney W. Grimes #include <stdio.h>
367a19d1bbSDima Dorfman #include <stdlib.h>
37ca156092SPhilippe Charnier #include <unistd.h>
38e545e3c5STim J. Robbins #include <wchar.h>
39821df508SXin LI #include <wctype.h>
40ca156092SPhilippe Charnier 
419b50d902SRodney W. Grimes /*
429b50d902SRodney W. Grimes  * expand - expand tabs to equivalent spaces
439b50d902SRodney W. Grimes  */
44cb230716SEd Schouten static int	nstops;
45cb230716SEd Schouten static int	tabstops[100];
469b50d902SRodney W. Grimes 
47f1bb2cd2SWarner Losh static void getstops(char *);
48*fe8c16ceSEitan Adler static void usage(void) __dead2;
49ca156092SPhilippe Charnier 
50ca156092SPhilippe Charnier int
main(int argc,char * argv[])51f4ac32deSDavid Malone main(int argc, char *argv[])
529b50d902SRodney W. Grimes {
53e545e3c5STim J. Robbins 	const char *curfile;
54e545e3c5STim J. Robbins 	wint_t wc;
55f4ac32deSDavid Malone 	int c, column;
56f4ac32deSDavid Malone 	int n;
575069e271STim J. Robbins 	int rval;
58e545e3c5STim J. Robbins 	int width;
599b50d902SRodney W. Grimes 
60ec85e6a0STim J. Robbins 	setlocale(LC_CTYPE, "");
61ec85e6a0STim J. Robbins 
62ca156092SPhilippe Charnier 	/* handle obsolete syntax */
63ec85e6a0STim J. Robbins 	while (argc > 1 && argv[1][0] == '-' &&
64ec85e6a0STim J. Robbins 	    isdigit((unsigned char)argv[1][1])) {
65ca156092SPhilippe Charnier 		getstops(&argv[1][1]);
66ca156092SPhilippe Charnier 		argc--; argv++;
679b50d902SRodney W. Grimes 	}
68ca156092SPhilippe Charnier 
69ca156092SPhilippe Charnier 	while ((c = getopt (argc, argv, "t:")) != -1) {
70ca156092SPhilippe Charnier 		switch (c) {
71ca156092SPhilippe Charnier 		case 't':
72ca156092SPhilippe Charnier 			getstops(optarg);
73ca156092SPhilippe Charnier 			break;
74ca156092SPhilippe Charnier 		case '?':
75ca156092SPhilippe Charnier 		default:
76ca156092SPhilippe Charnier 			usage();
77ca156092SPhilippe Charnier 			/* NOTREACHED */
78ca156092SPhilippe Charnier 		}
79ca156092SPhilippe Charnier 	}
80ca156092SPhilippe Charnier 	argc -= optind;
81ca156092SPhilippe Charnier 	argv += optind;
82ca156092SPhilippe Charnier 
835069e271STim J. Robbins 	rval = 0;
84ca156092SPhilippe Charnier 	do {
859b50d902SRodney W. Grimes 		if (argc > 0) {
865069e271STim J. Robbins 			if (freopen(argv[0], "r", stdin) == NULL) {
875069e271STim J. Robbins 				warn("%s", argv[0]);
885069e271STim J. Robbins 				rval = 1;
895069e271STim J. Robbins 				argc--, argv++;
905069e271STim J. Robbins 				continue;
915069e271STim J. Robbins 			}
92e545e3c5STim J. Robbins 			curfile = argv[0];
939b50d902SRodney W. Grimes 			argc--, argv++;
94e545e3c5STim J. Robbins 		} else
95e545e3c5STim J. Robbins 			curfile = "stdin";
969b50d902SRodney W. Grimes 		column = 0;
97e545e3c5STim J. Robbins 		while ((wc = getwchar()) != WEOF) {
98e545e3c5STim J. Robbins 			switch (wc) {
999b50d902SRodney W. Grimes 			case '\t':
1009b50d902SRodney W. Grimes 				if (nstops == 0) {
1019b50d902SRodney W. Grimes 					do {
102e545e3c5STim J. Robbins 						putwchar(' ');
1039b50d902SRodney W. Grimes 						column++;
1049b50d902SRodney W. Grimes 					} while (column & 07);
1059b50d902SRodney W. Grimes 					continue;
1069b50d902SRodney W. Grimes 				}
1079b50d902SRodney W. Grimes 				if (nstops == 1) {
1089b50d902SRodney W. Grimes 					do {
109e545e3c5STim J. Robbins 						putwchar(' ');
1109b50d902SRodney W. Grimes 						column++;
1119b50d902SRodney W. Grimes 					} while (((column - 1) % tabstops[0]) != (tabstops[0] - 1));
1129b50d902SRodney W. Grimes 					continue;
1139b50d902SRodney W. Grimes 				}
1149b50d902SRodney W. Grimes 				for (n = 0; n < nstops; n++)
1159b50d902SRodney W. Grimes 					if (tabstops[n] > column)
1169b50d902SRodney W. Grimes 						break;
1179b50d902SRodney W. Grimes 				if (n == nstops) {
118e545e3c5STim J. Robbins 					putwchar(' ');
1199b50d902SRodney W. Grimes 					column++;
1209b50d902SRodney W. Grimes 					continue;
1219b50d902SRodney W. Grimes 				}
1229b50d902SRodney W. Grimes 				while (column < tabstops[n]) {
123e545e3c5STim J. Robbins 					putwchar(' ');
1249b50d902SRodney W. Grimes 					column++;
1259b50d902SRodney W. Grimes 				}
1269b50d902SRodney W. Grimes 				continue;
1279b50d902SRodney W. Grimes 
1289b50d902SRodney W. Grimes 			case '\b':
1299b50d902SRodney W. Grimes 				if (column)
1309b50d902SRodney W. Grimes 					column--;
131e545e3c5STim J. Robbins 				putwchar('\b');
1329b50d902SRodney W. Grimes 				continue;
1339b50d902SRodney W. Grimes 
1349b50d902SRodney W. Grimes 			default:
135e545e3c5STim J. Robbins 				putwchar(wc);
136e545e3c5STim J. Robbins 				if ((width = wcwidth(wc)) > 0)
137e545e3c5STim J. Robbins 					column += width;
1389b50d902SRodney W. Grimes 				continue;
1399b50d902SRodney W. Grimes 
1409b50d902SRodney W. Grimes 			case '\n':
141e545e3c5STim J. Robbins 				putwchar(wc);
1429b50d902SRodney W. Grimes 				column = 0;
1439b50d902SRodney W. Grimes 				continue;
1449b50d902SRodney W. Grimes 			}
1459b50d902SRodney W. Grimes 		}
146e545e3c5STim J. Robbins 		if (ferror(stdin)) {
147e545e3c5STim J. Robbins 			warn("%s", curfile);
148e545e3c5STim J. Robbins 			rval = 1;
149e545e3c5STim J. Robbins 		}
1509b50d902SRodney W. Grimes 	} while (argc > 0);
1515069e271STim J. Robbins 	exit(rval);
1529b50d902SRodney W. Grimes }
1539b50d902SRodney W. Grimes 
154ca156092SPhilippe Charnier static void
getstops(char * cp)155f4ac32deSDavid Malone getstops(char *cp)
1569b50d902SRodney W. Grimes {
157f4ac32deSDavid Malone 	int i;
1589b50d902SRodney W. Grimes 
1599b50d902SRodney W. Grimes 	nstops = 0;
1609b50d902SRodney W. Grimes 	for (;;) {
1619b50d902SRodney W. Grimes 		i = 0;
1629b50d902SRodney W. Grimes 		while (*cp >= '0' && *cp <= '9')
1639b50d902SRodney W. Grimes 			i = i * 10 + *cp++ - '0';
16491dd86ffSDima Dorfman 		if (i <= 0)
165a2995dd0SPhilippe Charnier 			errx(1, "bad tab stop spec");
1669b50d902SRodney W. Grimes 		if (nstops > 0 && i <= tabstops[nstops-1])
167a2995dd0SPhilippe Charnier 			errx(1, "bad tab stop spec");
168a6150e80SRuslan Ermilov 		if (nstops == sizeof(tabstops) / sizeof(*tabstops))
169a6150e80SRuslan Ermilov 			errx(1, "too many tab stops");
1709b50d902SRodney W. Grimes 		tabstops[nstops++] = i;
1719b50d902SRodney W. Grimes 		if (*cp == 0)
1729b50d902SRodney W. Grimes 			break;
173ec85e6a0STim J. Robbins 		if (*cp != ',' && !isblank((unsigned char)*cp))
174a2995dd0SPhilippe Charnier 			errx(1, "bad tab stop spec");
175ca156092SPhilippe Charnier 		cp++;
1769b50d902SRodney W. Grimes 	}
1779b50d902SRodney W. Grimes }
178ca156092SPhilippe Charnier 
179ca156092SPhilippe Charnier static void
usage(void)180f4ac32deSDavid Malone usage(void)
181ca156092SPhilippe Charnier {
182ca156092SPhilippe Charnier 	(void)fprintf (stderr, "usage: expand [-t tablist] [file ...]\n");
183ca156092SPhilippe Charnier 	exit(1);
184ca156092SPhilippe Charnier }
185