xref: /dragonfly/usr.bin/vis/vis.c (revision abf903a5)
1 /*-
2  * Copyright (c) 1989, 1993
3  *	The Regents of the University of California.  All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  * 1. Redistributions of source code must retain the above copyright
9  *    notice, this list of conditions and the following disclaimer.
10  * 2. Redistributions in binary form must reproduce the above copyright
11  *    notice, this list of conditions and the following disclaimer in the
12  *    documentation and/or other materials provided with the distribution.
13  * 3. Neither the name of the University nor the names of its contributors
14  *    may be used to endorse or promote products derived from this software
15  *    without specific prior written permission.
16  *
17  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
18  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
21  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
23  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27  * SUCH DAMAGE.
28  *
29  * @(#) Copyright (c) 1989, 1993 The Regents of the University of California.  All rights reserved.
30  * @(#)vis.c	8.1 (Berkeley) 6/6/93
31  * $NetBSD: vis.c,v 1.25 2015/05/24 19:42:39 christos Exp $
32  */
33 
34 #include <stdio.h>
35 #include <string.h>
36 #include <stdlib.h>
37 #include <errno.h>
38 #include <wchar.h>
39 #include <limits.h>
40 #include <unistd.h>
41 #include <err.h>
42 #include <vis.h>
43 
44 #include "extern.h"
45 
46 static int eflags, fold, foldwidth = 80, none, markeol;
47 #ifdef DEBUG
48 int debug;
49 #endif
50 static const char *extra = "";
51 
52 static void process(FILE *);
53 
54 int
55 main(int argc, char *argv[])
56 {
57 	FILE *fp;
58 	int ch;
59 	int rval;
60 
61 	while ((ch = getopt(argc, argv, "abcde:F:fhlMmNnoSstw")) != -1)
62 		switch((char)ch) {
63 		case 'a':
64 			eflags |= VIS_ALL;
65 			break;
66 		case 'b':
67 			eflags |= VIS_NOSLASH;
68 			break;
69 		case 'c':
70 			eflags |= VIS_CSTYLE;
71 			break;
72 #ifdef DEBUG
73 		case 'd':
74 			debug++;
75 			break;
76 #endif
77 		case 'e':
78 			extra = optarg;
79 			break;
80 		case 'F':
81 			if ((foldwidth = atoi(optarg)) < 5) {
82 				errx(1, "can't fold lines to less than 5 cols");
83 			}
84 			markeol++;
85 			break;
86 		case 'f':
87 			fold++;		/* fold output lines to 80 cols */
88 			break;		/* using hidden newline */
89 		case 'h':
90 			eflags |= VIS_HTTPSTYLE;
91 			break;
92 		case 'l':
93 			markeol++;	/* mark end of line with \$ */
94 			break;
95 		case 'M':
96 			eflags |= VIS_META;
97 			break;
98 		case 'm':
99 			eflags |= VIS_MIMESTYLE;
100 			if (foldwidth == 80)
101 				foldwidth = 76;
102 			break;
103 		case 'N':
104 			eflags |= VIS_NOLOCALE;
105 			break;
106 		case 'n':
107 			none++;
108 			break;
109 		case 'o':
110 			eflags |= VIS_OCTAL;
111 			break;
112 		case 'S':
113 			eflags |= VIS_SHELL;
114 			break;
115 		case 's':
116 			eflags |= VIS_SAFE;
117 			break;
118 		case 't':
119 			eflags |= VIS_TAB;
120 			break;
121 		case 'w':
122 			eflags |= VIS_WHITE;
123 			break;
124 		case '?':
125 		default:
126 			fprintf(stderr,
127 			    "usage: %s [-abcfhlMmNnoSstw] [-e extra]"
128 			    " [-F foldwidth] [file ...]\n", getprogname());
129 			return 1;
130 		}
131 
132 	if ((eflags & (VIS_HTTPSTYLE|VIS_MIMESTYLE)) ==
133 	    (VIS_HTTPSTYLE|VIS_MIMESTYLE))
134 		errx(1, "Can't specify -m and -h at the same time");
135 
136 	argc -= optind;
137 	argv += optind;
138 
139 	rval = 0;
140 
141 	if (*argv)
142 		while (*argv) {
143 			if ((fp = fopen(*argv, "r")) != NULL) {
144 				process(fp);
145 				fclose(fp);
146 			} else {
147 				warn("%s", *argv);
148 				rval = 1;
149 			}
150 			argv++;
151 		}
152 	else
153 		process(stdin);
154 	return rval;
155 }
156 
157 static void
158 process(FILE *fp)
159 {
160 	static int col = 0;
161 	static char nul[] = "\0";
162 	char *cp = nul + 1;	/* so *(cp-1) starts out != '\n' */
163 	wint_t c, c1, rachar;
164 	char mbibuff[2 * MB_LEN_MAX + 1]; /* max space for 2 wchars */
165 	char buff[4 * MB_LEN_MAX + 1]; /* max encoding length for one char */
166 	int mbilen, cerr = 0, raerr = 0;
167 
168 	/*
169 	 * The input stream is considered to be multibyte characters.
170 	 * The input loop will read this data inputing one character,
171 	 * possibly multiple bytes, at a time and converting each to
172 	 * a wide character wchar_t.
173 	 *
174 	 * The vis(3) functions, however, require single either bytes
175 	 * or a multibyte string as their arguments.  So we convert
176 	 * our input wchar_t and the following look-ahead wchar_t to
177 	 * a multibyte string for processing by vis(3).
178 	 */
179 
180 	/* Read one multibyte character, store as wchar_t */
181 	c = getwc(fp);
182 	if (c == WEOF && errno == EILSEQ) {
183 		/* Error in multibyte data.  Read one byte. */
184 		c = (wint_t)getc(fp);
185 		cerr = 1;
186 	}
187 	while (c != WEOF) {
188 		/* Clear multibyte input buffer. */
189 		memset(mbibuff, 0, sizeof(mbibuff));
190 		/* Read-ahead next multibyte character. */
191 		if (!cerr)
192 			rachar = getwc(fp);
193 		if (cerr || (rachar == WEOF && errno == EILSEQ)) {
194 			/* Error in multibyte data.  Read one byte. */
195 			rachar = (wint_t)getc(fp);
196 			raerr = 1;
197 		}
198 		if (none) {
199 			/* Handle -n flag. */
200 			cp = buff;
201 			*cp++ = c;
202 			if (c == '\\')
203 				*cp++ = '\\';
204 			*cp = '\0';
205 		} else if (markeol && c == '\n') {
206 			/* Handle -l flag. */
207 			cp = buff;
208 			if ((eflags & VIS_NOSLASH) == 0)
209 				*cp++ = '\\';
210 			*cp++ = '$';
211 			*cp++ = '\n';
212 			*cp = '\0';
213 		} else {
214 			/*
215 			 * Convert character using vis(3) library.
216 			 * At this point we will process one character.
217 			 * But we must pass the vis(3) library this
218 			 * character plus the next one because the next
219 			 * one is used as a look-ahead to decide how to
220 			 * encode this one under certain circumstances.
221 			 *
222 			 * Since our characters may be multibyte, e.g.,
223 			 * in the UTF-8 locale, we cannot use vis() and
224 			 * svis() which require byte input, so we must
225 			 * create a multibyte string and use strvisx().
226 			 */
227 			/* Treat EOF as a NUL char. */
228 			c1 = rachar;
229 			if (c1 == WEOF)
230 				c1 = L'\0';
231 			/*
232 			 * If we hit a multibyte conversion error above,
233 			 * insert byte directly into string buff because
234 			 * wctomb() will fail.  Else convert wchar_t to
235 			 * multibyte using wctomb().
236 			 */
237 			if (cerr) {
238 				*mbibuff = (char)c;
239 				mbilen = 1;
240 			} else
241 				mbilen = wctomb(mbibuff, c);
242 			/* Same for look-ahead character. */
243 			if (raerr)
244 				mbibuff[mbilen] = (char)c1;
245 			else
246 				wctomb(mbibuff + mbilen, c1);
247 			/* Perform encoding on just first character. */
248 			strsenvisx(buff, 4 * MB_LEN_MAX, mbibuff,
249 			    1, eflags, extra, &cerr);
250 		}
251 
252 		cp = buff;
253 		if (fold) {
254 #ifdef DEBUG
255 			if (debug)
256 				printf("<%02d,", col);
257 #endif
258 			col = foldit(cp, col, foldwidth, eflags);
259 #ifdef DEBUG
260 			if (debug)
261 				printf("%02d>", col);
262 #endif
263 		}
264 		do {
265 			putchar(*cp);
266 		} while (*++cp);
267 		c = rachar;
268 		cerr = raerr;
269 	}
270 	/*
271 	 * terminate partial line with a hidden newline
272 	 */
273 	if (fold && *(cp - 1) != '\n')
274 		printf(eflags & VIS_MIMESTYLE ? "=\n" : "\\\n");
275 }
276