xref: /illumos-gate/usr/src/cmd/expand/unexpand.c (revision a61ed2ce7a86a4d6428f2a83eb4739fae945447e)
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 /*
43  * unexpand - put tabs into a file replacing blanks
44  */
45 #include <stdio.h>
46 #include <limits.h>
47 #include <stdlib.h>
48 #include <locale.h>
49 #include <libintl.h>
50 #include <wchar.h>
51 
52 #define	INPUT_SIZ	LINE_MAX	/* POSIX.2 */
53 #define	MAX_TABS	100		/* maximum number of tabstops */
54 
55 static int	nstops = 0;		/* total number of tabstops */
56 static int	tabstops[MAX_TABS];	/* the tabstops themselves */
57 
58 static void tabify(wchar_t *, int);
59 static void getstops(const char *);
60 static void usage(void);
61 
62 int
63 main(argc, argv)
64 int argc;
65 char *argv[];
66 {
67 	int		flag;		/* option flag read by getopt() */
68 	int		all = 0;	/* -a flag */
69 	int		status = 0;
70 	wchar_t		input_buf[INPUT_SIZ+1];
71 
72 	(void) setlocale(LC_ALL, "");
73 #if !defined(TEXT_DOMAIN)		/* Should be defined by cc -D */
74 #define	TEXT_DOMAIN	"SYS_TEST"	/* Use this only if it weren't */
75 #endif
76 	(void) textdomain(TEXT_DOMAIN);
77 
78 	while ((flag = getopt(argc, argv, "at:")) != EOF) {
79 		switch (flag) {
80 		case 'a':
81 			all++;
82 			break;
83 
84 		case 't':		/* POSIX.2 */
85 			all++;		/* -t turns on -a */
86 			getstops(optarg);
87 			break;
88 
89 		default:
90 			usage();
91 			break;
92 		}
93 	}
94 
95 	argc -= optind;
96 	argv = &argv[optind];
97 
98 	do {
99 		if (argc > 0) {
100 			if (freopen(argv[0], "r", stdin) == NULL) {
101 				(void) fprintf(stderr, "unexpand: ");
102 				perror(argv[0]);
103 				status++;
104 			}
105 			argc--, argv++;
106 		}
107 
108 		while (fgetws(input_buf, INPUT_SIZ, stdin) != NULL) {
109 			input_buf[INPUT_SIZ] = 0;
110 			tabify(input_buf, all);
111 		}
112 	} while (argc > 0);
113 
114 	return (status);
115 	/* NOTREACHED */
116 }
117 
118 void
119 tabify(wchar_t *ibuf, int all)
120 {
121 	wchar_t *cp;		/* current position in ibuf */
122 	int ocol = 0;		/* current output column */
123 	int cstop = 0;		/* current tabstop */
124 	int spaces = 0;		/* spaces to convert to tab */
125 	int	p_col;
126 
127 	cp = ibuf;
128 
129 	for (;;) {
130 		switch (*cp) {
131 		case ' ':
132 			cp++;
133 
134 			spaces++;
135 			ocol++;
136 
137 			if (nstops == 0) {		/* default tab = 8 */
138 				if ((ocol & 7) != 0)
139 					break;
140 			} else if (nstops == 1) {	/* tab width */
141 				if ((ocol % tabstops[0]) != 0)
142 					break;
143 			} else {			/* explicit tabstops */
144 				while (cstop < nstops &&
145 				    ocol > tabstops[cstop])
146 					cstop++;
147 
148 				if (cstop >= nstops) {
149 					(void) putchar(' ');
150 					spaces = 0;
151 					break;
152 				}
153 
154 				if (ocol != tabstops[cstop])
155 					break;
156 				cstop++;
157 			}
158 
159 			/*
160 			 * if we get to this point, we must be at a
161 			 * tab stop.  if spaces, then write out a tab.
162 			 */
163 			if (spaces > 0) {
164 				(void) putchar(((spaces > 1) ? '\t' : ' '));
165 				spaces = 0;
166 			}
167 
168 			break;
169 
170 		case '\b':		/* POSIX.2 */
171 			while (spaces-- > 0)
172 				(void) putchar(' ');
173 			spaces = 0;
174 
175 			cp++;
176 			(void) putchar('\b');
177 
178 			if (--ocol < 0)
179 				ocol = 0;
180 
181 			/* just in case */
182 			cstop = 0;
183 			break;
184 
185 		case '\t':
186 			cp++;
187 			(void) putchar('\t');
188 
189 			/* adjust ocol to current tabstop */
190 			if (nstops == 0) {
191 				ocol = (ocol + 8) & ~07;
192 			} else if (nstops == 1) {
193 				ocol += ocol % tabstops[0];
194 			} else {
195 				if (cstop < nstops &&
196 				    ocol < tabstops[cstop])
197 					ocol = tabstops[cstop++];
198 				else
199 					ocol++;
200 			}
201 
202 			spaces = 0;
203 			break;
204 
205 		default:
206 			while (spaces-- > 0)
207 				(void) putchar(' ');
208 			spaces = 0;
209 
210 			if (*cp == 0 || *cp == '\n' || all == 0) {
211 				/*
212 				 * either end of input line or -a not set
213 				 */
214 				while (*cp != 0)
215 					(void) putwchar(*cp++);
216 				return;
217 			}
218 
219 			(void) putwchar(*cp++);
220 			if ((p_col = wcwidth(*cp)) < 0)
221 				p_col = 0;
222 			ocol += p_col;
223 			break;
224 		}
225 	}
226 }
227 
228 static void
229 getstops(const char *cp)
230 {
231 	register int i;
232 
233 	for (;;) {
234 		i = 0;
235 		while (*cp >= '0' && *cp <= '9')
236 			i = i * 10 + *cp++ - '0';
237 
238 		if (i <= 0 || i > INT_MAX) {
239 			(void) fprintf(stderr, gettext(
240 			    "unexpand: invalid tablist item\n"));
241 			usage();
242 		}
243 
244 		if (nstops > 0 && i <= tabstops[nstops-1]) {
245 			(void) fprintf(stderr, gettext(
246 			    "unexpand: tablist must be increasing\n"));
247 			usage();
248 		}
249 
250 		if (nstops == MAX_TABS) {
251 			(void) fprintf(stderr, gettext(
252 			    "unexpand: number of tabstops limited to %d\n"),
253 				MAX_TABS);
254 			usage();
255 		}
256 
257 		tabstops[nstops++] = i;
258 		if (*cp == 0)
259 			break;
260 		if (*cp != ',' && *cp != ' ') {
261 			(void) fprintf(stderr, gettext(
262 			    "unexpand: invalid tablist separator\n"));
263 			usage();
264 		}
265 
266 		cp++;
267 	}
268 }
269 
270 static void
271 usage(void)
272 {
273 	(void) fprintf(stderr, gettext(
274 	    "usage: unexpand [-a ] [-t tablist] [file ...]\n"));
275 	exit(2);
276 	/* NOTREACHED */
277 }
278