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