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