xref: /plan9/sys/src/cmd/gs/src/sa85d.c (revision 593dc095aefb2a85c828727bbfa9da139a49bdf4)
1 /* Copyright (C) 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: sa85d.c,v 1.9 2003/04/24 19:42:13 ray Exp $ */
18 /* ASCII85Decode filter */
19 #include "std.h"
20 #include "strimpl.h"
21 #include "sa85d.h"
22 #include "scanchar.h"
23 
24 /* ------ ASCII85Decode ------ */
25 
26 private_st_A85D_state();
27 
28 /* Initialize the state */
29 private int
s_A85D_init(stream_state * st)30 s_A85D_init(stream_state * st)
31 {
32     stream_A85D_state *const ss = (stream_A85D_state *) st;
33 
34     return s_A85D_init_inline(ss);
35 }
36 
37 /* Process a buffer */
38 private int a85d_finish(int, ulong, stream_cursor_write *);
39 private int
s_A85D_process(stream_state * st,stream_cursor_read * pr,stream_cursor_write * pw,bool last)40 s_A85D_process(stream_state * st, stream_cursor_read * pr,
41 	       stream_cursor_write * pw, bool last)
42 {
43     stream_A85D_state *const ss = (stream_A85D_state *) st;
44     register const byte *p = pr->ptr;
45     register byte *q = pw->ptr;
46     const byte *rlimit = pr->limit;
47     byte *wlimit = pw->limit;
48     int ccount = ss->odd;
49     ulong word = ss->word;
50     int status = 0;
51 
52     while (p < rlimit) {
53 	int ch = *++p;
54 	uint ccode = ch - '!';
55 
56 	if (ccode < 85) {	/* catches ch < '!' as well */
57 	    if (ccount == 4) {
58 		/*
59 		 * We've completed a 32-bit group.  Make sure we have
60 		 * room for it in the output.
61 		 */
62 		if (wlimit - q < 4) {
63 		    p--;
64 		    status = 1;
65 		    break;
66 		}
67 		/* Check for overflow condition, throw ioerror if so */
68 		if (word >= 0x03030303 && ccode > 0) {
69 		    status = ERRC;
70 	            break;
71 	        }
72 		word = word * 85 + ccode;
73 		q[1] = (byte) (word >> 24);
74 		q[2] = (byte) (word >> 16);
75 		q[3] = (byte) ((uint) word >> 8);
76 		q[4] = (byte) word;
77 		q += 4;
78 		word = 0;
79 		ccount = 0;
80 	    } else {
81 		word = word * 85 + ccode;
82 		++ccount;
83 	    }
84 	} else if (ch == 'z' && ccount == 0) {
85 	    if (wlimit - q < 4) {
86 		p--;
87 		status = 1;
88 		break;
89 	    }
90 	    q[1] = q[2] = q[3] = q[4] = 0,
91 		q += 4;
92 	} else if (scan_char_decoder[ch] == ctype_space)
93 	    DO_NOTHING;
94 	else if (ch == '~') {
95 	    int i = 1;
96 
97 	    /* Handle odd bytes. */
98 	    if (p == rlimit) {
99 		if (last)
100 		    status = ERRC;
101 		else
102 		    p--;
103 		break;
104 	    }
105 	    if ((int)(wlimit - q) < ccount - 1) {
106 		status = 1;
107 		p--;
108 		break;
109 	    }
110 
111 	    /* According to PLRM 3rd, if the A85 filter encounters '~',
112 	     * the next character must be '>'.
113 	     * And any other characters should raise an ioerror.
114 	     * But Adobe Acrobat allows CR/LF between ~ and >.
115 	     * So we allow CR/LF between them. */
116 	    while ((p[i] == 13 || p[i] == 10) && (p+i <= rlimit))
117 		i++;
118 	    if (p[i] != '>') {
119 		if (p+i == rlimit) {
120 		    if (last)
121 			status = ERRC;
122 		    else
123 			p--;	/* we'll see the '~' after filling the buffer */
124 		}
125 		break;
126 	    }
127 	    p += i;		/* advance to the '>' */
128 	    pw->ptr = q;
129 	    status = a85d_finish(ccount, word, pw);
130 	    q = pw->ptr;
131 	    break;
132 	} else {		/* syntax error or exception */
133 	    status = ERRC;
134 	    break;
135 	}
136     }
137     pw->ptr = q;
138     if (status == 0 && last) {
139 	if ((int)(wlimit - q) < ccount - 1)
140 	    status = 1;
141 	else
142 	    status = a85d_finish(ccount, word, pw);
143     }
144     pr->ptr = p;
145     ss->odd = ccount;
146     ss->word = word;
147     return status;
148 }
149 /* Handle the end of input data. */
150 private int
a85d_finish(int ccount,ulong word,stream_cursor_write * pw)151 a85d_finish(int ccount, ulong word, stream_cursor_write * pw)
152 {
153     /* Assume there is enough room in the output buffer! */
154     byte *q = pw->ptr;
155     int status = EOFC;
156 
157     switch (ccount) {
158 	case 0:
159 	    break;
160 	case 1:		/* syntax error */
161 	    status = ERRC;
162 	    break;
163 	case 2:		/* 1 odd byte */
164 	    word = word * (85L * 85 * 85) + 85L * 85 * 85 - 1L;
165 	    goto o1;
166 	case 3:		/* 2 odd bytes */
167 	    word = word * (85L * 85) + 85L * 85L - 1L;
168 	    goto o2;
169 	case 4:		/* 3 odd bytes */
170 	    word = word * 85L + 84L;
171 	    q[3] = (byte) (word >> 8);
172 o2:	    q[2] = (byte) (word >> 16);
173 o1:	    q[1] = (byte) (word >> 24);
174 	    q += ccount - 1;
175 	    pw->ptr = q;
176     }
177     return status;
178 }
179 
180 /* Stream template */
181 const stream_template s_A85D_template = {
182     &st_A85D_state, s_A85D_init, s_A85D_process, 2, 4
183 };
184