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