xref: /plan9/sys/src/cmd/gs/src/sfilter2.c (revision 593dc095aefb2a85c828727bbfa9da139a49bdf4)
1 /* Copyright (C) 1991, 2000 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: sfilter2.c,v 1.5 2002/02/21 22:24:54 giles Exp $ */
18 /* Simple Level 2 filters */
19 #include "stdio_.h"		/* includes std.h */
20 #include "memory_.h"
21 #include "gdebug.h"
22 #include "strimpl.h"
23 #include "sa85x.h"
24 #include "sbtx.h"
25 #include "scanchar.h"
26 
27 /* ------ ASCII85Encode ------ */
28 
29 private_st_A85E_state();
30 
31 /* Initialize the state */
32 private int
s_A85E_init(stream_state * st)33 s_A85E_init(stream_state * st)
34 {
35     stream_A85E_state *const ss = (stream_A85E_state *) st;
36 
37     return s_A85E_init_inline(ss);
38 }
39 
40 /* Process a buffer */
41 #define LINE_LIMIT 79		/* not 80, to satisfy Genoa FTS */
42 private int
s_A85E_process(stream_state * st,stream_cursor_read * pr,stream_cursor_write * pw,bool last)43 s_A85E_process(stream_state * st, stream_cursor_read * pr,
44 	       stream_cursor_write * pw, bool last)
45 {
46     stream_A85E_state *const ss = (stream_A85E_state *) st;
47     register const byte *p = pr->ptr;
48     register byte *q = pw->ptr;
49     byte *qn = q + (LINE_LIMIT - ss->count); /* value of q before next EOL */
50     const byte *rlimit = pr->limit;
51     byte *wlimit = pw->limit;
52     int status = 0;
53     int prev = ss->last_char;
54     int count;
55 
56     if_debug3('w', "[w85]initial ss->count = %d, rcount = %d, wcount = %d\n",
57 	      ss->count, (int)(rlimit - p), (int)(wlimit - q));
58     for (; (count = rlimit - p) >= 4; p += 4) {
59 	ulong word =
60 	    ((ulong) (((uint) p[1] << 8) + p[2]) << 16) +
61 	    (((uint) p[3] << 8) + p[4]);
62 
63 	if (word == 0) {
64 	    if (q >= qn) {
65 		if (wlimit - q < 2) {
66 		    status = 1;
67 		    break;
68 		}
69 		*++q = prev = '\n';
70 		qn = q + LINE_LIMIT;
71 		if_debug1('w', "[w85]EOL at %d bytes written\n",
72 			  (int)(q - pw->ptr));
73 	    } else {
74 		if (q >= wlimit) {
75 		    status = 1;
76 		    break;
77 		}
78 	    }
79 	    *++q = prev = 'z';
80 	} else {
81 	    ulong v4 = word / 85;	/* max 85^4 */
82 	    ulong v3 = v4 / 85;	/* max 85^3 */
83 	    uint v2 = v3 / 85;	/* max 85^2 */
84 	    uint v1 = v2 / 85;	/* max 85 */
85 
86 put:	    if (q + 5 > qn) {
87 		if (q >= wlimit) {
88 		    status = 1;
89 		    break;
90 		}
91 		*++q = prev = '\n';
92 		qn = q + LINE_LIMIT;
93 		if_debug1('w', "[w85]EOL at %d bytes written\n",
94 			  (int)(q - pw->ptr));
95 		goto put;
96 	    }
97 	    if (wlimit - q < 5) {
98 		status = 1;
99 		break;
100 	    }
101 	    q[1] = (byte) v1 + '!';
102 	    q[2] = (byte) (v2 - v1 * 85) + '!';
103 	    q[3] = (byte) ((uint) v3 - v2 * 85) + '!';
104 	    q[4] = (byte) ((uint) v4 - (uint) v3 * 85) + '!';
105 	    q[5] = (byte) ((uint) word - (uint) v4 * 85) + '!';
106 	    /*
107 	     * '%%' or '%!' at the beginning of the line will confuse some
108 	     * document managers: insert (an) EOL(s) if necessary to prevent
109 	     * this.
110 	     */
111 	    if (q[1] == '%') {
112 		if (prev == '%') {
113 		    if (qn - q == LINE_LIMIT - 1) {
114 			/* A line would begin with %%. */
115 			*++q = prev = '\n';
116 			qn = q + LINE_LIMIT;
117 			if_debug1('w',
118 				  "[w85]EOL for %%%% at %d bytes written\n",
119 				  (int)(q - pw->ptr));
120 			goto put;
121 		    }
122 		} else if (prev == '\n' && (q[2] == '%' || q[2] == '!')) {
123 		    /*
124 		     * We may have to insert more than one EOL if
125 		     * there are more than two %s in a row.
126 		     */
127 		    int extra =
128 			(q[2] == '!' ? 1 : /* else q[2] == '%' */
129 			 q[3] == '!' ? 2 :
130 			 q[3] != '%' ? 1 :
131 			 q[4] == '!' ? 3 :
132 			 q[4] != '%' ? 2 :
133 			 q[5] == '!' ? 4 :
134 			 q[5] != '%' ? 3 : 4);
135 
136 		    if (wlimit - q < 5 + extra) {
137 			status = 1;
138 			break;
139 		    }
140 		    if_debug6('w', "[w]%c%c%c%c%c extra = %d\n",
141 			      q[1], q[2], q[3], q[4], q[5], extra);
142 		    switch (extra) {
143 			case 4:
144 			    q[9] = q[5], q[8] = '\n';
145 			    goto e3;
146 			case 3:
147 			    q[8] = q[5];
148 			  e3:q[7] = q[4], q[6] = '\n';
149 			    goto e2;
150 			case 2:
151 			    q[7] = q[5], q[6] = q[4];
152 			  e2:q[5] = q[3], q[4] = '\n';
153 			    goto e1;
154 			case 1:
155 			    q[6] = q[5], q[5] = q[4], q[4] = q[3];
156 			  e1:q[3] = q[2], q[2] = '\n';
157 		    }
158 		    if_debug1('w', "[w85]EOL at %d bytes written\n",
159 			      (int)(q + 2 * extra - pw->ptr));
160 		    qn = q + 2 * extra + LINE_LIMIT;
161 		    q += extra;
162 		}
163 	    } else if (q[1] == '!' && prev == '%' &&
164 		       qn - q == LINE_LIMIT - 1
165 		       ) {
166 		/* A line would begin with %!. */
167 		*++q = prev = '\n';
168 		qn = q + LINE_LIMIT;
169 		if_debug1('w', "[w85]EOL for %%! at %d bytes written\n",
170 			  (int)(q - pw->ptr));
171 		goto put;
172 	    }
173 	    prev = *(q += 5);
174 	}
175     }
176  end:
177     ss->count = LINE_LIMIT - (qn - q);
178     /* Check for final partial word. */
179     if (last && status == 0 && count < 4) {
180 	int nchars = (count == 0 ? 2 : count + 3);
181 
182 	if (wlimit - q < nchars)
183 	    status = 1;
184 	else if (q + nchars > qn) {
185 	    *++q = '\n';
186 	    qn = q + LINE_LIMIT;
187 	    goto end;
188 	}
189 	else {
190 	    ulong word = 0;
191 	    ulong divisor = 85L * 85 * 85 * 85;
192 
193 	    switch (count) {
194 		case 3:
195 		    word += (uint) p[3] << 8;
196 		case 2:
197 		    word += (ulong) p[2] << 16;
198 		case 1:
199 		    word += (ulong) p[1] << 24;
200 		    p += count;
201 		    while (count-- >= 0) {
202 			ulong v = word / divisor;  /* actually only a byte */
203 
204 			*++q = (byte) v + '!';
205 			word -= v * divisor;
206 			divisor /= 85;
207 		    }
208 		    /*case 0: */
209 	    }
210 	    *++q = '~';
211 	    *++q = '>';
212 	}
213     }
214     if_debug3('w', "[w85]final ss->count = %d, %d bytes read, %d written\n",
215 	      ss->count, (int)(p - pr->ptr), (int)(q - pw->ptr));
216     pr->ptr = p;
217     if (q > pw->ptr)
218 	ss->last_char = *q;
219     pw->ptr = q;
220     return status;
221 }
222 #undef LINE_LIMIT
223 
224 /* Stream template */
225 const stream_template s_A85E_template = {
226     &st_A85E_state, s_A85E_init, s_A85E_process, 4, 6
227 };
228 
229 /* ------ ByteTranslateEncode/Decode ------ */
230 
231 private_st_BT_state();
232 
233 /* Process a buffer.  Note that the same code serves for both streams. */
234 private int
s_BT_process(stream_state * st,stream_cursor_read * pr,stream_cursor_write * pw,bool last)235 s_BT_process(stream_state * st, stream_cursor_read * pr,
236 	     stream_cursor_write * pw, bool last)
237 {
238     stream_BT_state *const ss = (stream_BT_state *) st;
239     const byte *p = pr->ptr;
240     byte *q = pw->ptr;
241     uint rcount = pr->limit - p;
242     uint wcount = pw->limit - q;
243     uint count;
244     int status;
245 
246     if (rcount <= wcount)
247 	count = rcount, status = 0;
248     else
249 	count = wcount, status = 1;
250     while (count--)
251 	*++q = ss->table[*++p];
252     pr->ptr = p;
253     pw->ptr = q;
254     return status;
255 }
256 
257 /* Stream template */
258 const stream_template s_BTE_template = {
259     &st_BT_state, NULL, s_BT_process, 1, 1
260 };
261 const stream_template s_BTD_template = {
262     &st_BT_state, NULL, s_BT_process, 1, 1
263 };
264