1/*-
2 * SPDX-License-Identifier: BSD-3-Clause
3 *
4 * Copyright (c) 1980, 1993
5 *	The Regents of the University of California.  All rights reserved.
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
9 * are met:
10 * 1. Redistributions of source code must retain the above copyright
11 *    notice, this list of conditions and the following disclaimer.
12 * 2. Redistributions in binary form must reproduce the above copyright
13 *    notice, this list of conditions and the following disclaimer in the
14 *    documentation and/or other materials provided with the distribution.
15 * 3. Neither the name of the University nor the names of its contributors
16 *    may be used to endorse or promote products derived from this software
17 *    without specific prior written permission.
18 *
19 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
20 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
22 * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
23 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
24 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
25 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
26 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
27 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
28 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
29 * SUCH DAMAGE.
30 */
31
32#include <ctype.h>
33#include <err.h>
34#include <locale.h>
35#include <stdio.h>
36#include <stdlib.h>
37#include <unistd.h>
38#include <wchar.h>
39#include <wctype.h>
40
41/*
42 * expand - expand tabs to equivalent spaces
43 */
44static int	nstops;
45static int	tabstops[100];
46
47static void getstops(char *);
48static void usage(void) __dead2;
49
50int
51main(int argc, char *argv[])
52{
53	const char *curfile;
54	wint_t wc;
55	int c, column;
56	int n;
57	int rval;
58	int width;
59
60	setlocale(LC_CTYPE, "");
61
62	/* handle obsolete syntax */
63	while (argc > 1 && argv[1][0] == '-' &&
64	    isdigit((unsigned char)argv[1][1])) {
65		getstops(&argv[1][1]);
66		argc--; argv++;
67	}
68
69	while ((c = getopt (argc, argv, "t:")) != -1) {
70		switch (c) {
71		case 't':
72			getstops(optarg);
73			break;
74		case '?':
75		default:
76			usage();
77			/* NOTREACHED */
78		}
79	}
80	argc -= optind;
81	argv += optind;
82
83	rval = 0;
84	do {
85		if (argc > 0) {
86			if (freopen(argv[0], "r", stdin) == NULL) {
87				warn("%s", argv[0]);
88				rval = 1;
89				argc--, argv++;
90				continue;
91			}
92			curfile = argv[0];
93			argc--, argv++;
94		} else
95			curfile = "stdin";
96		column = 0;
97		while ((wc = getwchar()) != WEOF) {
98			switch (wc) {
99			case '\t':
100				if (nstops == 0) {
101					do {
102						putwchar(' ');
103						column++;
104					} while (column & 07);
105					continue;
106				}
107				if (nstops == 1) {
108					do {
109						putwchar(' ');
110						column++;
111					} while (((column - 1) % tabstops[0]) != (tabstops[0] - 1));
112					continue;
113				}
114				for (n = 0; n < nstops; n++)
115					if (tabstops[n] > column)
116						break;
117				if (n == nstops) {
118					putwchar(' ');
119					column++;
120					continue;
121				}
122				while (column < tabstops[n]) {
123					putwchar(' ');
124					column++;
125				}
126				continue;
127
128			case '\b':
129				if (column)
130					column--;
131				putwchar('\b');
132				continue;
133
134			default:
135				putwchar(wc);
136				if ((width = wcwidth(wc)) > 0)
137					column += width;
138				continue;
139
140			case '\n':
141				putwchar(wc);
142				column = 0;
143				continue;
144			}
145		}
146		if (ferror(stdin)) {
147			warn("%s", curfile);
148			rval = 1;
149		}
150	} while (argc > 0);
151	exit(rval);
152}
153
154static void
155getstops(char *cp)
156{
157	int i;
158
159	nstops = 0;
160	for (;;) {
161		i = 0;
162		while (*cp >= '0' && *cp <= '9')
163			i = i * 10 + *cp++ - '0';
164		if (i <= 0)
165			errx(1, "bad tab stop spec");
166		if (nstops > 0 && i <= tabstops[nstops-1])
167			errx(1, "bad tab stop spec");
168		if (nstops == sizeof(tabstops) / sizeof(*tabstops))
169			errx(1, "too many tab stops");
170		tabstops[nstops++] = i;
171		if (*cp == 0)
172			break;
173		if (*cp != ',' && !isblank((unsigned char)*cp))
174			errx(1, "bad tab stop spec");
175		cp++;
176	}
177}
178
179static void
180usage(void)
181{
182	(void)fprintf (stderr, "usage: expand [-t tablist] [file ...]\n");
183	exit(1);
184}
185