xref: /illumos-gate/usr/src/cmd/expand/expand.c (revision 7a6d80f1660abd4755c68cbd094d4a914681d26e)
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 1989 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 <libintl.h>
42 #include <locale.h>
43 #include <stdlib.h>
44 #include <string.h>
45 #include <ctype.h>
46 #include <limits.h>
47 #include <wchar.h>
48 
49 /*
50  * expand - expand tabs to equivalent spaces
51  */
52 static int		nstops = 0;
53 static int		tabstops[100];
54 static int		isClocale;
55 
56 static void getstops(const char *);
57 static void usage(void);
58 
59 int
60 main(argc, argv)
61 int argc;
62 char *argv[];
63 {
64 	static char	ibuf[BUFSIZ];
65 	register int	c, column;
66 	register int	n;
67 	register int	i, j;
68 	char		*locale;
69 	int		flag, tflag = 0;
70 	int		len;
71 	int		p_col;
72 	wchar_t		wc;
73 	char		*p1, *p2;
74 
75 	(void) setlocale(LC_ALL, "");
76 	locale = setlocale(LC_CTYPE, NULL);
77 	isClocale = (strcmp(locale, "C") == 0);
78 #if !defined(TEXT_DOMAIN)		/* Should be defined by cc -D */
79 #define	TEXT_DOMAIN	"SYS_TEST"	/* Use this only if it weren't */
80 #endif
81 	(void) textdomain(TEXT_DOMAIN);
82 
83 	/*
84 	 * First, look for and extract any "-<number>" args then pass
85 	 * them to getstops().
86 	 */
87 	for (i = 1; i < argc; i++) {
88 		if (strcmp(argv[i], "--") == 0)
89 			break;
90 
91 		if (*argv[i] != '-')
92 			continue;
93 		if (!isdigit(*(argv[i]+1)))
94 			continue;
95 
96 		getstops(argv[i]+1);
97 		tflag++;
98 
99 		/* Pull this arg from list */
100 		for (j = i; j < (argc-1); j++)
101 			argv[j] = argv[j+1];
102 		argc--;
103 	}
104 
105 	while ((flag = getopt(argc, argv, "t:")) != EOF) {
106 		switch (flag) {
107 		case 't':
108 			if (tflag)
109 				usage();
110 
111 			getstops(optarg);
112 			break;
113 
114 		default:
115 			usage();
116 			break;
117 		}
118 	}
119 
120 	argc -= optind;
121 	argv = &argv[optind];
122 
123 	do {
124 		if (argc > 0) {
125 			if (freopen(argv[0], "r", stdin) == NULL) {
126 				perror(argv[0]);
127 				exit(1);
128 				/* NOTREACHED */
129 			}
130 			argc--;
131 			argv++;
132 		}
133 
134 		column = 0;
135 		p1 = p2 = ibuf;
136 		for (;;) {
137 			if (p1 >= p2) {
138 				p1 = ibuf;
139 				if ((len = fread(p1, 1, BUFSIZ, stdin)) <= 0)
140 					break;
141 				p2 = p1 + len;
142 			}
143 
144 			c = *p1++;
145 			switch (c) {
146 			case '\t':
147 				if (nstops == 0) {
148 					do {
149 						(void) putchar(' ');
150 						column++;
151 					} while (column & 07);
152 					continue;
153 				}
154 				if (nstops == 1) {
155 					do {
156 						(void) putchar(' ');
157 						column++;
158 					} while (
159 					    ((column - 1) % tabstops[0]) !=
160 						(tabstops[0] - 1));
161 					continue;
162 				}
163 				for (n = 0; n < nstops; n++)
164 					if (tabstops[n] > column)
165 						break;
166 				if (n == nstops) {
167 					(void) putchar(' ');
168 					column++;
169 					continue;
170 				}
171 				while (column < tabstops[n]) {
172 					(void) putchar(' ');
173 					column++;
174 				}
175 				continue;
176 
177 			case '\b':
178 				if (column)
179 					column--;
180 				(void) putchar('\b');
181 				continue;
182 
183 			default:
184 				if (isClocale) {
185 					(void) putchar(c);
186 					column++;
187 					continue;
188 				}
189 
190 				if (isascii(c)) {
191 					(void) putchar(c);
192 					column++;
193 					continue;
194 				}
195 
196 				p1--;
197 				if ((len = (p2 - p1)) <
198 					(unsigned int)MB_CUR_MAX) {
199 					for (n = 0; n < len; n++)
200 						ibuf[n] = *p1++;
201 					p1 = ibuf;
202 					p2 = p1 + n;
203 					if ((len = fread(p2, 1, BUFSIZ - n,
204 							stdin)) > 0)
205 						p2 += len;
206 				}
207 				if ((len = (p2 - p1)) >
208 					(unsigned int)MB_CUR_MAX)
209 					len = (unsigned int)MB_CUR_MAX;
210 
211 				if ((len = mbtowc(&wc, p1, len)) <= 0) {
212 					(void) putchar(c);
213 					column++;
214 					p1++;
215 					continue;
216 				}
217 
218 				if ((p_col = wcwidth(wc)) < 0)
219 					p_col = len;
220 				p1 += len;
221 				(void) putwchar(wc);
222 				column += p_col;
223 				continue;
224 
225 			case '\n':
226 				(void) putchar(c);
227 				column = 0;
228 				continue;
229 			}
230 		}
231 	} while (argc > 0);
232 
233 	return (0);
234 	/* NOTREACHED */
235 }
236 
237 static void
238 getstops(const char *cp)
239 {
240 	register int i;
241 
242 	for (;;) {
243 		i = 0;
244 		while (*cp >= '0' && *cp <= '9')
245 			i = i * 10 + *cp++ - '0';
246 
247 		if (i <= 0 || i > INT_MAX) {
248 			(void) fprintf(stderr, gettext(
249 				"expand: invalid tablist\n"));
250 			usage();
251 		}
252 
253 		if (nstops > 0 && i <= tabstops[nstops-1]) {
254 			(void) fprintf(stderr, gettext(
255 				"expand: tablist must be increasing\n"));
256 			usage();
257 		}
258 
259 		tabstops[nstops++] = i;
260 		if (*cp == 0)
261 			break;
262 
263 		if (*cp != ',' && *cp != ' ') {
264 			(void) fprintf(stderr, gettext(
265 				"expand: invalid tablist\n"));
266 			usage();
267 		}
268 		cp++;
269 	}
270 }
271 
272 static void
273 usage(void)
274 {
275 	(void) fprintf(stderr, gettext(
276 		"usage: expand [-t tablist] [file ...]\n"
277 		"       expand [-tabstop] [-tab1,tab2,...,tabn] [file ...]\n"));
278 	exit(2);
279 	/* NOTREACHED */
280 }
281