1 /* Copyright (C) 2001-2006 Artifex Software, Inc.
2    All Rights Reserved.
3 
4    This software is provided AS-IS with no warranty, either express or
5    implied.
6 
7    This software is distributed under license and may not be copied, modified
8    or distributed except as expressly authorized under the terms of that
9    license.  Refer to licensing information at http://www.artifex.com/
10    or contact Artifex Software, Inc.,  7 Mt. Lassen Drive - Suite A-134,
11    San Rafael, CA  94903, U.S.A., +1(415)492-9861, for further information.
12 */
13 
14 /* $Id: sa85d.c 10498 2009-12-13 01:31:59Z alexcher $ */
15 /* ASCII85Decode filter */
16 #include "std.h"
17 #include "strimpl.h"
18 #include "sa85d.h"
19 #include "scanchar.h"
20 
21 /* ------ ASCII85Decode ------ */
22 
23 private_st_A85D_state();
24 
25 /* Initialize the state */
26 static int
s_A85D_init(stream_state * st)27 s_A85D_init(stream_state * st)
28 {
29     stream_A85D_state *const ss = (stream_A85D_state *) st;
30 
31     return s_A85D_init_inline(ss);
32 }
33 
34 /* Process a buffer */
35 static int a85d_finish(int, ulong, stream_cursor_write *);
36 static int
s_A85D_process(stream_state * st,stream_cursor_read * pr,stream_cursor_write * pw,bool last)37 s_A85D_process(stream_state * st, stream_cursor_read * pr,
38 	       stream_cursor_write * pw, bool last)
39 {
40     stream_A85D_state *const ss = (stream_A85D_state *) st;
41     register const byte *p = pr->ptr;
42     register byte *q = pw->ptr;
43     /* stop processing early unless the target is empty (last == true)  */
44     /* to make sure we consume the EOD marker correctly. The EOD marker */
45     /* might be as many as 6 characters after the last valid data char  */
46     /* D <cr> <lf> '~' <cr> <lf> '>' where 'D' is a data character.     */
47     const byte *rlimit = pr->limit - (last ? 0 : 7); /* max EOD len + 1 */
48     const byte *r = max(p, rlimit);
49     byte *wlimit = pw->limit;
50     int ccount = ss->odd;
51     ulong word = ss->word;
52     int status = 0;
53 
54     /* scan to make sure that an EOD isn't fully contained in the */
55     /* last part of the buffer (between rlimit and pr->limit).    */
56     while (r < pr->limit) {
57 	if (*++r == '~')
58 	    while (r < pr->limit)
59 		if (*++r == '>') {
60 		    /* we have both characters of a complete EOD. */
61 		    rlimit = pr->limit; /* go ahead and process everything */
62 		    r = rlimit;		/* break out of the while loops */
63 		    break;
64 		}
65     }
66     while (p < rlimit) {
67 	int ch = *++p;
68 	uint ccode = ch - '!';
69 
70 	if (ccode < 85) {	/* catches ch < '!' as well */
71 	    if (ccount == 4) {
72 		/*
73 		 * We've completed a 32-bit group.  Make sure we have
74 		 * room for it in the output.
75 		 */
76 		if (wlimit - q < 4) {
77 		    p--;
78 		    status = 1;
79 		    break;
80 		}
81 		/* Check for overflow condition, throw ioerror if so */
82 		if (word >= 0x03030303 && ccode > 0) {
83 		    status = ERRC;
84 	            break;
85 	        }
86 		word = word * 85 + ccode;
87 		q[1] = (byte) (word >> 24);
88 		q[2] = (byte) (word >> 16);
89 		q[3] = (byte) ((uint) word >> 8);
90 		q[4] = (byte) word;
91 		q += 4;
92 		word = 0;
93 		ccount = 0;
94 	    } else {
95 		word = word * 85 + ccode;
96 		++ccount;
97 	    }
98 	} else if (ch == 'z' && ccount == 0) {
99 	    if (wlimit - q < 4) {
100 		p--;
101 		status = 1;
102 		break;
103 	    }
104 	    q[1] = q[2] = q[3] = q[4] = 0,
105 		q += 4;
106 	} else if (scan_char_decoder[ch] == ctype_space)
107 	    DO_NOTHING;
108 	else if (ch == '~') {
109 	    int i = 1;
110 
111 	    rlimit = pr->limit;		/* Here we use the real "limit" */
112 	    /* Handle odd bytes. */
113 	    if (p == rlimit) {
114 		if (last)
115 		    status = ERRC;
116 		else
117 		    p--;
118 		break;
119 	    }
120 	    if ((int)(wlimit - q) < ccount - 1) {
121 		status = 1;
122 		p--;
123 		break;
124 	    }
125 
126 	    /* According to PLRM 3rd, if the A85 filter encounters '~',
127 	     * the next character must be '>'.
128 	     * And any other characters should raise an ioerror.
129 	     * But Adobe Acrobat allows CR/LF between ~ and >.
130 	     * So we allow CR/LF between them. */
131 	    /* PDF further relaxes the requirements and accepts bare '~'.
132 	     */
133 	    while ((p[i] == 13 || p[i] == 10) && (p+i <= rlimit))
134 		i++;
135 	    if (p[i] != '>') {
136 		if (ss->pdf_rules) {
137 		    if (p[i] == 13 || p[i] == 10) {
138 		        if (!last)
139 		            break;
140 		    } else {
141 		        p--;
142 		    }
143 		} else {
144 		    if (p+i == rlimit) {
145 		        if (last)
146 			    status = ERRC;
147 		        else
148 			    p--;	/* we'll see the '~' after filling the buffer */
149 		    }
150 		    break;
151 		}
152 	    }
153 	    p += i;		/* advance to the '>' */
154 	    pw->ptr = q;
155 	    status = a85d_finish(ccount, word, pw);
156 	    q = pw->ptr;
157 	    break;
158 	} else {		/* syntax error or exception */
159 	    status = ERRC;
160 	    break;
161 	}
162     }
163     pw->ptr = q;
164     if (status == 0 && last) {
165 	if ((int)(wlimit - q) < ccount - 1)
166 	    status = 1;
167 	else
168 	    status = a85d_finish(ccount, word, pw);
169     }
170     pr->ptr = p;
171     ss->odd = ccount;
172     ss->word = word;
173     return status;
174 }
175 /* Handle the end of input data. */
176 static int
a85d_finish(int ccount,ulong word,stream_cursor_write * pw)177 a85d_finish(int ccount, ulong word, stream_cursor_write * pw)
178 {
179     /* Assume there is enough room in the output buffer! */
180     byte *q = pw->ptr;
181     int status = EOFC;
182 
183     switch (ccount) {
184 	case 0:
185 	    break;
186 	case 1:		/* syntax error */
187 	    status = ERRC;
188 	    break;
189 	case 2:		/* 1 odd byte */
190 	    word = word * (85L * 85 * 85) + 85L * 85 * 85 - 1L;
191 	    goto o1;
192 	case 3:		/* 2 odd bytes */
193 	    word = word * (85L * 85) + 85L * 85L - 1L;
194 	    goto o2;
195 	case 4:		/* 3 odd bytes */
196 	    word = word * 85L + 84L;
197 	    q[3] = (byte) (word >> 8);
198 o2:	    q[2] = (byte) (word >> 16);
199 o1:	    q[1] = (byte) (word >> 24);
200 	    q += ccount - 1;
201 	    pw->ptr = q;
202     }
203     return status;
204 }
205 
206 /* Stream template */
207 const stream_template s_A85D_template = {
208     &st_A85D_state, s_A85D_init, s_A85D_process, 2, 4
209 };
210