xref: /original-bsd/lib/libc/gen/vis.c (revision 2e5c0888)
1 /*
2  * Copyright (c) 1989 The Regents of the University of California.
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms are permitted
6  * provided that the above copyright notice and this paragraph are
7  * duplicated in all such forms and that any documentation,
8  * advertising materials, and other materials related to such
9  * distribution and use acknowledge that the software was developed
10  * by the University of California, Berkeley.  The name of the
11  * University may not be used to endorse or promote products derived
12  * from this software without specific prior written permission.
13  * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
14  * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
15  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
16  */
17 
18 #if defined(LIBC_SCCS) && !defined(lint)
19 static char sccsid[] = "@(#)vis.c	5.2 (Berkeley) 05/11/90";
20 #endif /* LIBC_SCCS and not lint */
21 
22 #include <sys/types.h>
23 #include <ctype.h>
24 #include <cencode.h>
25 
26 #define	isoctal(c)	(((u_char)(c)) >= '0' && ((u_char)(c)) <= '7')
27 
28 /*
29  * vis - visually encode characters
30  */
31 char *
32 vis(dst, c, flag, nextc)
33 	register char *dst, c;
34 	char nextc;
35 	register int flag;
36 {
37 	if (isascii(c) && isgraph(c) ||
38 	   ((flag & VIS_WHITE) == 0 &&
39 		(c == ' ' || c == '\n' || (flag & VIS_TAB) == 0 && c == '\t')) ||
40 	   ((flag & VIS_SAFE) && (c == '\b' || c == '\007' || c == '\r'))) {
41 		*dst++ = c;
42 		if (c == '\\' && (flag & VIS_NOSLASH) == 0)
43 			*dst++ = '\\';
44 		*dst = '\0';
45 		return (dst);
46 	}
47 
48 	if ((flag & VIS_NOSLASH) == 0)
49 		*dst++ = '\\';
50 	if (flag & VIS_CSTYLE) {
51 		switch(c) {
52 		case '\n':
53 			*dst++ = 'n';
54 			goto done;
55 		case '\r':
56 			*dst++ = 'r';
57 			goto done;
58 		case '\b':
59 			*dst++ = 'b';
60 			goto done;
61 		case '\007':	/* waiting for ansi compiler */
62 			*dst++ = 'a';
63 			goto done;
64 		case '\v':
65 			*dst++ = 'v';
66 			goto done;
67 		case '\t':
68 			*dst++ = 't';
69 			goto done;
70 		case '\f':
71 			*dst++ = 'f';
72 			goto done;
73 		case ' ':
74 			*dst++ = 's';
75 			goto done;
76 		case '\0':
77 			*dst++ = '0';
78 			if ((flag & VIS_NEXTC) == 0 || isoctal(nextc)) {
79 				*dst++ = '0';
80 				*dst++ = '0';
81 			}
82 			goto done;
83 		}
84 	}
85 	if (((c & 0177) == ' ') || (flag & VIS_OCTAL)) {
86 		*dst++ = ((u_char)c >> 6 & 07) + '0';
87 		*dst++ = ((u_char)c >> 3 & 07) + '0';
88 		*dst++ = ((u_char)c & 07) + '0';
89 		goto done;
90 	}
91 	if (c & 0200) {
92 		c &= 0177;
93 		*dst++ = 'M';
94 	}
95 	if (iscntrl(c)) {
96 		*dst++ = '^';
97 		if (c == 0177)
98 			*dst++ = '?';
99 		else
100 			*dst++ = c + '@';
101 	} else {
102 		*dst++ = '-';
103 		*dst++ = c;
104 	}
105 done:
106 	*dst = '\0';
107 	return (dst);
108 }
109 
110 
111 /*
112  * decode driven by state machine
113  */
114 #define	S_NORMAL	1	/* haven't seen escape char */
115 #define	S_START		2	/* start decoding special sequence */
116 #define	S_META		3	/* metachar started (M) */
117 #define	S_META1		4	/* metachar more, regular char (-) */
118 #define	S_CTRL		5	/* control char started (^) */
119 #define	S_OCTAL		6	/* octal number */
120 
121 /*
122  *
123  */
124 cunvis(c, cp, flags)
125 	char c;
126 	char *cp;
127 {
128 	static int state = S_NORMAL;
129 	static u_char buildchar;
130 	static int octal;
131 
132 	if (flags&UNVIS_END) {
133 		int ostate = state;
134 		state = S_NORMAL;
135 		if (ostate == S_OCTAL) {
136 			*cp = buildchar;
137 			return(UNVIS_OK);
138 		} else if (ostate == S_META1) {
139 			/* slightly forgiving, if not wrong */
140 			*cp = ' ' | 0200;
141 			return(UNVIS_OK);
142 		} else
143 			return(ostate == S_NORMAL ? UNVIS_NOCHAR : CDEC_SYNBAD);
144 	}
145 
146 	switch (state) {
147 	case S_NORMAL:
148 		buildchar = 0;
149 		if (c == '\\') {
150 			state = S_START;
151 			return(UNVIS_NEEDMORE);
152 		} else if (flags&UNVIS_HAT && c == '^') {
153 			state = S_CTRL;
154 			return(UNVIS_NEEDMORE);
155 		} else {
156 			*cp = c;
157 			return(UNVIS_OK);
158 		}
159 		break;
160 	case S_START:
161 		state = S_NORMAL;
162 		if (c == '\\') {
163 			*cp = c;
164 			return(UNVIS_OK);
165 		}
166 		if (isoctal(c)) {
167 			buildchar = (c-'0');
168 			octal = 1;
169 			state = S_OCTAL;
170 			return(UNVIS_NEEDMORE);
171 		}
172 		switch(c) {
173 		case 'M':
174 			buildchar |= 0200;
175 			state = S_META;
176 			return(UNVIS_NEEDMORE);
177 		case '^':
178 			state = S_CTRL;
179 			return(UNVIS_NEEDMORE);
180 		case 'n':
181 			*cp = '\n';
182 			return(UNVIS_OK);
183 		case 'r':
184 			*cp = '\r';
185 			return(UNVIS_OK);
186 		case 'b':
187 			*cp = '\b';
188 			return(UNVIS_OK);
189 		case 'a':
190 			*cp = '\007';
191 			return(UNVIS_OK);
192 		case 'v':
193 			*cp = '\v';
194 			return(UNVIS_OK);
195 		case 't':
196 			*cp = '\t';
197 			return(UNVIS_OK);
198 		case 'f':
199 			*cp = '\f';
200 			return(UNVIS_OK);
201 		case 's':			/* does anyone use this ? */
202 			*cp = ' ';
203 			return(UNVIS_OK);
204 		case 'E':
205 			*cp = '\033';
206 			return(UNVIS_OK);
207 		case '\n':
208 			return(UNVIS_NOCHAR);	/* hidden newline */
209 		}
210 		state = S_NORMAL;
211 		return(UNVIS_SYNBAD);
212 	case S_META:
213 		if (c == '-')
214 			state = S_META1;
215 		else if (c == '^')
216 			state = S_CTRL;
217 		else {
218 			state = S_NORMAL;
219 			return(UNVIS_SYNBAD);
220 		}
221 		return(UNVIS_NEEDMORE);
222 	case S_META1:
223 		state = S_NORMAL;
224 		*cp = c | buildchar;
225 		return(UNVIS_OK);
226 	case S_CTRL:
227 		if (c == '?')
228 			buildchar |= 0177;
229 		else
230 			buildchar |= c&037;
231 		state = S_NORMAL;
232 		*cp = buildchar;
233 		return(UNVIS_OK);
234 	case S_OCTAL:
235 		if (isoctal(c)) {
236 			buildchar = (buildchar<<3) + (c-'0');
237 			if (++octal == 3) {
238 				state = S_NORMAL;
239 				*cp = buildchar;
240 				return(UNVIS_OK);
241 			} else
242 				return(UNVIS_NEEDMORE);
243 		} else {
244 			state = S_NORMAL;
245 			*cp = buildchar;
246 			return(UNVIS_OKPUSH);
247 		}
248 	}
249 }
250 
251 /*
252  * strvis - visually encode characters from src into dst
253  *
254  *	If len >= 0, encodes exactly len chars from src (including NULL's).
255  *	Otherwise, stops before first NULL in src.  In all cases, dst is
256  *	NULL terminated.
257  *
258  *	Dst must be 4 times the size of src to account for possible
259  *	expansion.  The length of dst, not including the trailing NULL,
260  *	is returned.
261  */
262 strvis(dst, src, len, flag)
263 	register char *dst, *src;
264 	register int len;
265 {
266 	char *start = dst;
267 
268 	for (;;) {
269 		if (len > 0) {
270 			if (len-- == 0)
271 				break;
272 		} else if (!*src)
273 			break;
274 		dst = vis(dst, *src, flag | VIS_NEXTC, *(src+1));
275 		src++;
276 	}
277 
278 	return (dst - start);
279 }
280