xref: /plan9/sys/src/cmd/gs/src/sstring.c (revision 593dc095aefb2a85c828727bbfa9da139a49bdf4)
1 /* Copyright (C) 1993, 1995, 1996, 1997, 1998, 1999 Aladdin Enterprises.  All rights reserved.
2 
3   This software is provided AS-IS with no warranty, either express or
4   implied.
5 
6   This software is distributed under license and may not be copied,
7   modified or distributed except as expressly authorized under the terms
8   of the license contained in the file LICENSE in this distribution.
9 
10   For more information about licensing, please refer to
11   http://www.ghostscript.com/licensing/. For information on
12   commercial licensing, go to http://www.artifex.com/licensing/ or
13   contact Artifex Software, Inc., 101 Lucas Valley Road #110,
14   San Rafael, CA  94903, U.S.A., +1(415)492-9861.
15 */
16 
17 /* $Id: sstring.c,v 1.5 2005/04/25 12:28:49 igor Exp $ */
18 /* String and hexstring streams (filters) */
19 #include "stdio_.h"		/* includes std.h */
20 #include "memory_.h"
21 #include "string_.h"
22 #include "strimpl.h"
23 #include "sstring.h"
24 #include "scanchar.h"
25 
26 /* ------ ASCIIHexEncode ------ */
27 
28 private_st_AXE_state();
29 
30 /* Initialize the state */
31 private int
s_AXE_init(stream_state * st)32 s_AXE_init(stream_state * st)
33 {
34     stream_AXE_state *const ss = (stream_AXE_state *) st;
35 
36     return s_AXE_init_inline(ss);
37 }
38 
39 /* Process a buffer */
40 private int
s_AXE_process(stream_state * st,stream_cursor_read * pr,stream_cursor_write * pw,bool last)41 s_AXE_process(stream_state * st, stream_cursor_read * pr,
42 	      stream_cursor_write * pw, bool last)
43 {
44     stream_AXE_state *const ss = (stream_AXE_state *) st;
45     const byte *p = pr->ptr;
46     byte *q = pw->ptr;
47     int rcount = pr->limit - p;
48     int wcount = pw->limit - q;
49     int count;
50     int pos = ss->count;
51     const char *hex_digits = "0123456789ABCDEF";
52     int status = 0;
53 
54     if (last && ss->EndOfData)
55 	wcount--;		/* leave room for '>' */
56     wcount -= (wcount + 64) / 65;	/* leave room for \n */
57     wcount >>= 1;		/* 2 chars per input byte */
58     count = (wcount < rcount ? (status = 1, wcount) : rcount);
59     while (--count >= 0) {
60 	*++q = hex_digits[*++p >> 4];
61 	*++q = hex_digits[*p & 0xf];
62 	if (!(++pos & 31) && (count != 0 || !last))
63 	    *++q = '\n';
64     }
65     if (last && status == 0 && ss->EndOfData)
66 	*++q = '>';
67     pr->ptr = p;
68     pw->ptr = q;
69     ss->count = pos & 31;
70     return status;
71 }
72 
73 /* Stream template */
74 const stream_template s_AXE_template =
75 {&st_AXE_state, s_AXE_init, s_AXE_process, 1, 3
76 };
77 
78 /* ------ ASCIIHexDecode ------ */
79 
80 private_st_AXD_state();
81 
82 /* Initialize the state */
83 private int
s_AXD_init(stream_state * st)84 s_AXD_init(stream_state * st)
85 {
86     stream_AXD_state *const ss = (stream_AXD_state *) st;
87 
88     return s_AXD_init_inline(ss);
89 }
90 
91 /* Process a buffer */
92 private int
s_AXD_process(stream_state * st,stream_cursor_read * pr,stream_cursor_write * pw,bool last)93 s_AXD_process(stream_state * st, stream_cursor_read * pr,
94 	      stream_cursor_write * pw, bool last)
95 {
96     stream_AXD_state *const ss = (stream_AXD_state *) st;
97     int code = s_hex_process(pr, pw, &ss->odd, hex_ignore_whitespace);
98 
99     switch (code) {
100 	case 0:
101 	    if (ss->odd >= 0 && last) {
102 		if (pw->ptr == pw->limit)
103 		    return 1;
104 		*++(pw->ptr) = ss->odd << 4;
105 	    }
106 	    /* falls through */
107 	case 1:
108 	    /* We still need to read ahead and check for EOD. */
109 	    for (; pr->ptr < pr->limit; pr->ptr++)
110 		if (scan_char_decoder[pr->ptr[1]] != ctype_space) {
111 		    if (pr->ptr[1] == '>') {
112 			pr->ptr++;
113 			goto eod;
114 		    }
115 		    return 1;
116 		}
117 	    return 0;		/* still need to scan ahead */
118 	default:
119 	    return code;
120 	case ERRC:
121 	    ;
122     }
123     /*
124      * Check for EOD.  ERRC implies at least one more character
125      * was read; we must unread it, since the caller might have
126      * invoked the filter with exactly the right count to read all
127      * the available data, and we might be reading past the end.
128      */
129     if (*pr->ptr != '>') {	/* EOD */
130 	--(pr->ptr);
131 	return ERRC;
132     }
133   eod:if (ss->odd >= 0) {
134 	if (pw->ptr == pw->limit)
135 	    return 1;
136 	*++(pw->ptr) = ss->odd << 4;
137     }
138     return EOFC;
139 }
140 
141 /* Stream template */
142 const stream_template s_AXD_template =
143 {&st_AXD_state, s_AXD_init, s_AXD_process, 2, 1
144 };
145 
146 /* ------ PSStringEncode ------ */
147 
148 /* Process a buffer */
149 private int
s_PSSE_process(stream_state * st,stream_cursor_read * pr,stream_cursor_write * pw,bool last)150 s_PSSE_process(stream_state * st, stream_cursor_read * pr,
151 	       stream_cursor_write * pw, bool last)
152 {
153     const byte *p = pr->ptr;
154     const byte *rlimit = pr->limit;
155     byte *q = pw->ptr;
156     byte *wlimit = pw->limit;
157     int status = 0;
158 
159     /* This doesn't have to be very efficient. */
160     while (p < rlimit) {
161 	int c = *++p;
162 
163 	if (c < 32 || c >= 127) {
164 	    const char *pesc;
165 	    const char *const esc = "\n\r\t\b\f";
166 
167 	    if (c < 32 && c != 0 && (pesc = strchr(esc, c)) != 0) {
168 		if (wlimit - q < 2) {
169 		    --p;
170 		    status = 1;
171 		    break;
172 		}
173 		*++q = '\\';
174 		*++q = "nrtbf"[pesc - esc];
175 		continue;
176 	    }
177 	    if (wlimit - q < 4) {
178 		--p;
179 		status = 1;
180 		break;
181 	    }
182 	    q[1] = '\\';
183 	    q[2] = (c >> 6) + '0';
184 	    q[3] = ((c >> 3) & 7) + '0';
185 	    q[4] = (c & 7) + '0';
186 	    q += 4;
187 	    continue;
188 	} else if (c == '(' || c == ')' || c == '\\') {
189 	    if (wlimit - q < 2) {
190 		--p;
191 		status = 1;
192 		break;
193 	    }
194 	    *++q = '\\';
195 	} else {
196 	    if (q == wlimit) {
197 		--p;
198 		status = 1;
199 		break;
200 	    }
201 	}
202 	*++q = c;
203     }
204     if (last && status == 0) {
205 	if (q == wlimit)
206 	    status = 1;
207 	else
208 	    *++q = ')';
209     }
210     pr->ptr = p;
211     pw->ptr = q;
212     return status;
213 }
214 
215 /* Stream template */
216 const stream_template s_PSSE_template =
217 {&st_stream_state, NULL, s_PSSE_process, 1, 4
218 };
219 
220 /* ------ PSStringDecode ------ */
221 
222 private_st_PSSD_state();
223 
224 /* Initialize the state */
225 int
s_PSSD_init(stream_state * st)226 s_PSSD_init(stream_state * st)
227 {
228     stream_PSSD_state *const ss = (stream_PSSD_state *) st;
229 
230     ss->from_string = false;
231     return s_PSSD_partially_init_inline(ss);
232 }
233 
234 /* Process a buffer */
235 private int
s_PSSD_process(stream_state * st,stream_cursor_read * pr,stream_cursor_write * pw,bool last)236 s_PSSD_process(stream_state * st, stream_cursor_read * pr,
237 	       stream_cursor_write * pw, bool last)
238 {
239     stream_PSSD_state *const ss = (stream_PSSD_state *) st;
240     const byte *p = pr->ptr;
241     const byte *rlimit = pr->limit;
242     byte *q = pw->ptr;
243     byte *wlimit = pw->limit;
244     int status = 0;
245     int c;
246 
247 #define check_p(n)\
248   if ( p == rlimit ) { p -= n; goto out; }
249 #define check_q(n)\
250   if ( q == wlimit ) { p -= n; status = 1; goto out; }
251     while (p < rlimit) {
252 	c = *++p;
253 	if (c == '\\' && !ss->from_string) {
254 	    check_p(1);
255 	    switch ((c = *++p)) {
256 		case 'n':
257 		    c = '\n';
258 		    goto put;
259 		case 'r':
260 		    c = '\r';
261 		    goto put;
262 		case 't':
263 		    c = '\t';
264 		    goto put;
265 		case 'b':
266 		    c = '\b';
267 		    goto put;
268 		case 'f':
269 		    c = '\f';
270 		    goto put;
271 		default:	/* ignore the \ */
272 		  put:check_q(2);
273 		    *++q = c;
274 		    continue;
275 		case char_CR:	/* ignore, check for following \n */
276 		    check_p(2);
277 		    if (p[1] == char_EOL)
278 			p++;
279 		    continue;
280 		case char_EOL:	/* ignore */
281 		    continue;
282 		case '0':
283 		case '1':
284 		case '2':
285 		case '3':
286 		case '4':
287 		case '5':
288 		case '6':
289 		case '7':
290 		    {
291 			int d;
292 
293 			check_p(2);
294 			d = p[1];
295 			c -= '0';
296 			if (d >= '0' && d <= '7') {
297 			    if (p + 1 == rlimit) {
298 				p -= 2;
299 				goto out;
300 			    }
301 			    check_q(2);
302 			    c = (c << 3) + d - '0';
303 			    d = p[2];
304 			    if (d >= '0' && d <= '7') {
305 				c = (c << 3) + d - '0';
306 				p += 2;
307 			    } else
308 				p++;
309 			} else
310 			    check_q(2);
311 			*++q = c;
312 			continue;
313 		    }
314 	    }
315 	} else
316 	    switch (c) {
317 		case '(':
318 		    check_q(1);
319 		    ss->depth++;
320 		    break;
321 		case ')':
322 		    if (ss->depth == 0) {
323 			status = EOFC;
324 			goto out;
325 		    }
326 		    check_q(1);
327 		    ss->depth--;
328 		    break;
329 		case char_CR:	/* convert to \n */
330 		    check_p(1);
331 		    check_q(1);
332 		    if (p[1] == char_EOL)
333 			p++;
334 		    *++q = '\n';
335 		    continue;
336 		case char_EOL:
337 		    c = '\n';
338 		default:
339 		    check_q(1);
340 		    break;
341 	    }
342 	*++q = c;
343     }
344 #undef check_p
345 #undef check_q
346   out:pr->ptr = p;
347     pw->ptr = q;
348     if (last && status == 0 && p != rlimit)
349 	status = ERRC;
350     return status;
351 }
352 
353 /* Stream template */
354 const stream_template s_PSSD_template =
355 {&st_PSSD_state, s_PSSD_init, s_PSSD_process, 4, 1
356 };
357 
358 /* ------ Utilities ------ */
359 
360 /*
361  * Convert hex data to binary.  Return 1 if we filled the string, 0 if
362  * we ran out of input data before filling the string, or ERRC on error.
363  * The caller must set *odd_digit to -1 before the first call;
364  * after each call, if an odd number of hex digits has been read (total),
365  * *odd_digit is the odd digit value, otherwise *odd_digit = -1.
366  * See strimpl.h for the definition of syntax.
367  */
368 int
s_hex_process(stream_cursor_read * pr,stream_cursor_write * pw,int * odd_digit,hex_syntax syntax)369 s_hex_process(stream_cursor_read * pr, stream_cursor_write * pw,
370 	      int *odd_digit, hex_syntax syntax)
371 {
372     const byte *p = pr->ptr;
373     const byte *rlimit = pr->limit;
374     byte *q = pw->ptr;
375     byte *wlimit = pw->limit;
376     byte *q0 = q;
377     byte val1 = (byte) * odd_digit;
378     byte val2;
379     uint rcount;
380     byte *flimit;
381     const byte *const decoder = scan_char_decoder;
382     int code = 0;
383 
384     if (q >= wlimit)
385 	return 1;
386     if (val1 <= 0xf)
387 	goto d2;
388   d1:if ((rcount = (rlimit - p) >> 1) == 0)
389 	goto x1;
390     /* Set up a fast end-of-loop check, so we don't have to test */
391     /* both p and q against their respective limits. */
392     flimit = (rcount < wlimit - q ? q + rcount : wlimit);
393   f1:if ((val1 = decoder[p[1]]) <= 0xf &&
394 	(val2 = decoder[p[2]]) <= 0xf
395 	) {
396 	p += 2;
397 	*++q = (val1 << 4) + val2;
398 	if (q < flimit)
399 	    goto f1;
400 	if (q >= wlimit)
401 	    goto px;
402     }
403   x1:if (p >= rlimit)
404 	goto end1;
405     if ((val1 = decoder[*++p]) > 0xf) {
406 	if (val1 == ctype_space) {
407 	    switch (syntax) {
408 		case hex_ignore_whitespace:
409 		    goto x1;
410 		case hex_ignore_leading_whitespace:
411 		    if (q == q0 && *odd_digit < 0)
412 			goto x1;
413 		    --p;
414 		    code = 1;
415 		    goto end1;
416 		case hex_ignore_garbage:
417 		    goto x1;
418 	    }
419 	} else if (syntax == hex_ignore_garbage)
420 	    goto x1;
421 	code = ERRC;
422 	goto end1;
423     }
424   d2:if (p >= rlimit) {
425 	*odd_digit = val1;
426 	goto ended;
427     }
428     if ((val2 = decoder[*++p]) > 0xf) {
429 	if (val2 == ctype_space)
430 	    switch (syntax) {
431 		case hex_ignore_whitespace:
432 		    goto d2;
433 		case hex_ignore_leading_whitespace:
434 		    if (q == q0)
435 			goto d2;
436 		    --p;
437 		    *odd_digit = val1;
438 		    code = 1;
439 		    goto ended;
440 		case hex_ignore_garbage:	/* pacify compilers */
441 		    ;
442 	    }
443 	if (syntax == hex_ignore_garbage)
444 	    goto d2;
445 	*odd_digit = val1;
446 	code = ERRC;
447 	goto ended;
448     }
449     *++q = (val1 << 4) + val2;
450     if (q < wlimit)
451 	goto d1;
452   px:code = 1;
453   end1:*odd_digit = -1;
454   ended:pr->ptr = p;
455     pw->ptr = q;
456     return code;
457 }
458