xref: /dragonfly/usr.bin/vis/vis.c (revision 70344474)
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 <string.h>
38 #include <errno.h>
39 #include <wchar.h>
40 #include <limits.h>
41 #include <unistd.h>
42 #include <err.h>
43 #include <vis.h>
44 
45 #include "extern.h"
46 
47 static int eflags, fold, foldwidth = 80, none, markeol;
48 #ifdef DEBUG
49 int debug;
50 #endif
51 static const char *extra = "";
52 
53 static void process(FILE *);
54 
55 int
56 main(int argc, char *argv[])
57 {
58 	FILE *fp;
59 	int ch;
60 	int rval;
61 
62 	while ((ch = getopt(argc, argv, "bcde:F:fhlMmNnoSstw")) != -1)
63 		switch((char)ch) {
64 		case 'b':
65 			eflags |= VIS_NOSLASH;
66 			break;
67 		case 'c':
68 			eflags |= VIS_CSTYLE;
69 			break;
70 #ifdef DEBUG
71 		case 'd':
72 			debug++;
73 			break;
74 #endif
75 		case 'e':
76 			extra = optarg;
77 			break;
78 		case 'F':
79 			if ((foldwidth = atoi(optarg)) < 5) {
80 				errx(1, "can't fold lines to less than 5 cols");
81 			}
82 			markeol++;
83 			break;
84 		case 'f':
85 			fold++;		/* fold output lines to 80 cols */
86 			break;		/* using hidden newline */
87 		case 'h':
88 			eflags |= VIS_HTTPSTYLE;
89 			break;
90 		case 'l':
91 			markeol++;	/* mark end of line with \$ */
92 			break;
93 		case 'M':
94 			eflags |= VIS_META;
95 			break;
96 		case 'm':
97 			eflags |= VIS_MIMESTYLE;
98 			if (foldwidth == 80)
99 				foldwidth = 76;
100 			break;
101 		case 'N':
102 			eflags |= VIS_NOLOCALE;
103 			break;
104 		case 'n':
105 			none++;
106 			break;
107 		case 'o':
108 			eflags |= VIS_OCTAL;
109 			break;
110 		case 'S':
111 			eflags |= VIS_SHELL;
112 			break;
113 		case 's':
114 			eflags |= VIS_SAFE;
115 			break;
116 		case 't':
117 			eflags |= VIS_TAB;
118 			break;
119 		case 'w':
120 			eflags |= VIS_WHITE;
121 			break;
122 		case '?':
123 		default:
124 			fprintf(stderr,
125 			    "usage: %s [-bcfhlMmNnoSstw] [-e extra]"
126 			    " [-F foldwidth] [file ...]\n", getprogname());
127 			return 1;
128 		}
129 
130 	if ((eflags & (VIS_HTTPSTYLE|VIS_MIMESTYLE)) ==
131 	    (VIS_HTTPSTYLE|VIS_MIMESTYLE))
132 		errx(1, "Can't specify -m and -h at the same time");
133 
134 	argc -= optind;
135 	argv += optind;
136 
137 	rval = 0;
138 
139 	if (*argv)
140 		while (*argv) {
141 			if ((fp = fopen(*argv, "r")) != NULL) {
142 				process(fp);
143 				fclose(fp);
144 			} else {
145 				warn("%s", *argv);
146 				rval = 1;
147 			}
148 			argv++;
149 		}
150 	else
151 		process(stdin);
152 	return rval;
153 }
154 
155 static void
156 process(FILE *fp)
157 {
158 	static int col = 0;
159 	static char nul[] = "\0";
160 	char *cp = nul + 1;	/* so *(cp-1) starts out != '\n' */
161 	wint_t c, c1, rachar;
162 	char mbibuff[2 * MB_LEN_MAX + 1]; /* max space for 2 wchars */
163 	char buff[4 * MB_LEN_MAX + 1]; /* max encoding length for one char */
164 	int mbilen, cerr = 0, raerr = 0;
165 
166         /*
167          * The input stream is considered to be multibyte characters.
168          * The input loop will read this data inputing one character,
169 	 * possibly multiple bytes, at a time and converting each to
170 	 * a wide character wchar_t.
171          *
172 	 * The vis(3) functions, however, require single either bytes
173 	 * or a multibyte string as their arguments.  So we convert
174 	 * our input wchar_t and the following look-ahead wchar_t to
175 	 * a multibyte string for processing by vis(3).
176          */
177 
178 	/* Read one multibyte character, store as wchar_t */
179 	c = getwc(fp);
180 	if (c == WEOF && errno == EILSEQ) {
181 		/* Error in multibyte data.  Read one byte. */
182 		c = (wint_t)getc(fp);
183 		cerr = 1;
184 	}
185 	while (c != WEOF) {
186 		/* Clear multibyte input buffer. */
187 		memset(mbibuff, 0, sizeof(mbibuff));
188 		/* Read-ahead next multibyte character. */
189 		if (!cerr)
190 			rachar = getwc(fp);
191 		if (cerr || (rachar == WEOF && errno == EILSEQ)) {
192 			/* Error in multibyte data.  Read one byte. */
193 			rachar = (wint_t)getc(fp);
194 			raerr = 1;
195 		}
196 		if (none) {
197 			/* Handle -n flag. */
198 			cp = buff;
199 			*cp++ = c;
200 			if (c == '\\')
201 				*cp++ = '\\';
202 			*cp = '\0';
203 		} else if (markeol && c == '\n') {
204 			/* Handle -l flag. */
205 			cp = buff;
206 			if ((eflags & VIS_NOSLASH) == 0)
207 				*cp++ = '\\';
208 			*cp++ = '$';
209 			*cp++ = '\n';
210 			*cp = '\0';
211 		} else {
212 			/*
213 			 * Convert character using vis(3) library.
214 			 * At this point we will process one character.
215 			 * But we must pass the vis(3) library this
216 			 * character plus the next one because the next
217 			 * one is used as a look-ahead to decide how to
218 			 * encode this one under certain circumstances.
219 			 *
220 			 * Since our characters may be multibyte, e.g.,
221 			 * in the UTF-8 locale, we cannot use vis() and
222 			 * svis() which require byte input, so we must
223 			 * create a multibyte string and use strvisx().
224 			 */
225 			/* Treat EOF as a NUL char. */
226 			c1 = rachar;
227 			if (c1 == WEOF)
228 				c1 = L'\0';
229 			/*
230 			 * If we hit a multibyte conversion error above,
231 			 * insert byte directly into string buff because
232 			 * wctomb() will fail.  Else convert wchar_t to
233 			 * multibyte using wctomb().
234 			 */
235 			if (cerr) {
236 				*mbibuff = (char)c;
237 				mbilen = 1;
238 			} else
239 				mbilen = wctomb(mbibuff, c);
240 			/* Same for look-ahead character. */
241 			if (raerr)
242 				mbibuff[mbilen] = (char)c1;
243 			else
244 				wctomb(mbibuff + mbilen, c1);
245 			/* Perform encoding on just first character. */
246 			strsenvisx(buff, 4 * MB_LEN_MAX, mbibuff,
247 			    1, eflags, extra, &cerr);
248 		}
249 
250 		cp = buff;
251 		if (fold) {
252 #ifdef DEBUG
253 			if (debug)
254 				printf("<%02d,", col);
255 #endif
256 			col = foldit(cp, col, foldwidth, eflags);
257 #ifdef DEBUG
258 			if (debug)
259 				printf("%02d>", col);
260 #endif
261 		}
262 		do {
263 			putchar(*cp);
264 		} while (*++cp);
265 		c = rachar;
266 		cerr = raerr;
267 	}
268 	/*
269 	 * terminate partial line with a hidden newline
270 	 */
271 	if (fold && *(cp - 1) != '\n')
272 		printf(eflags & VIS_MIMESTYLE ? "=\n" : "\\\n");
273 }
274