comparison modules/base64/base64.c @ 428:3ecd8885ac67 r21-2-22

Import from CVS: tag r21-2-22
author cvs
date Mon, 13 Aug 2007 11:28:15 +0200
parents
children 576fb035e263
comparison
equal deleted inserted replaced
427:0a0253eac470 428:3ecd8885ac67
1 /* base64 interface for XEmacs.
2 Copyright (C) 1998, 1999 Free Software Foundation, Inc.
3
4 This file is part of XEmacs.
5
6 XEmacs is free software; you can redistribute it and/or modify it
7 under the terms of the GNU General Public License as published by the
8 Free Software Foundation; either version 2, or (at your option) any
9 later version.
10
11 XEmacs is distributed in the hope that it will be useful, but WITHOUT
12 ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13 FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
14 for more details.
15
16 You should have received a copy of the GNU General Public License
17 along with XEmacs; see the file COPYING. If not, write to
18 the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
19 Boston, MA 02111-1307, USA. */
20
21 /* Synched up with: Not in FSF. */
22
23 /* Author: William Perry <wmperry@aventail.com> */
24
25 #include <emodules.h>
26
27 unsigned char alphabet[64] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
28
29 DEFUN ("base64-encode", Fbase64_encode, 1, 5, 0, /*
30 Return the base64 encoding of an object.
31 OBJECT is either a string or a buffer.
32 Optional arguments START and END denote buffer positions for computing the
33 hash of a portion of OBJECT. The optional CODING argument specifies the coding
34 system the text is to be represented in while computing the digest. This only
35 has meaning with MULE, and defaults to the current format of the data.
36 If ERROR-ME-NOT is nil, report an error if the coding system can't be
37 determined. Else assume binary coding if all else fails.
38 */
39 (object, start, end, coding, error_me_not))
40 {
41 int cols,bits,char_count;
42 Lisp_Object instream, outstream,deststream;
43 Lstream *istr, *ostr, *dstr;
44 static Extbyte_dynarr *conversion_out_dynarr;
45 static Extbyte_dynarr *out_dynarr;
46 char tempbuf[1024]; /* some random amount */
47 struct gcpro gcpro1, gcpro2;
48 #ifdef FILE_CODING
49 Lisp_Object conv_out_stream, coding_system;
50 Lstream *costr;
51 struct gcpro gcpro3;
52 #endif
53
54 if (!conversion_out_dynarr)
55 conversion_out_dynarr = Dynarr_new (Extbyte);
56 else
57 Dynarr_reset (conversion_out_dynarr);
58
59 if (!out_dynarr)
60 out_dynarr = Dynarr_new(Extbyte);
61 else
62 Dynarr_reset (out_dynarr);
63
64 char_count = bits = cols = 0;
65
66 /* set up the in stream */
67 if (BUFFERP (object))
68 {
69 struct buffer *b = decode_buffer (object, 1);
70 Bufpos begv, endv;
71 /* Figure out where we need to get info from */
72 get_buffer_range_char (b, start, end, &begv, &endv, GB_ALLOW_NIL);
73
74 instream = make_lisp_buffer_input_stream (b, begv, endv, 0);
75 }
76 else
77 {
78 Bytecount bstart, bend;
79 CHECK_STRING (object);
80 get_string_range_byte (object, start, end, &bstart, &bend,
81 GB_HISTORICAL_STRING_BEHAVIOR);
82 instream = make_lisp_string_input_stream (object, bstart, bend);
83 }
84 istr = XLSTREAM (instream);
85
86 #ifdef FILE_CODING
87 /* Find out what format the buffer will be saved in, so we can make
88 the digest based on what it will look like on disk */
89 if (NILP(coding))
90 {
91 if (BUFFERP(object))
92 {
93 /* Use the file coding for this buffer by default */
94 coding_system = XBUFFER(object)->buffer_file_coding_system;
95 }
96 else
97 {
98 /* attempt to autodetect the coding of the string. Note: this VERY hit-and-miss */
99 enum eol_type eol = EOL_AUTODETECT;
100 coding_system = Fget_coding_system(Qundecided);
101 determine_real_coding_system(istr, &coding_system, &eol);
102 }
103 if (NILP(coding_system))
104 coding_system = Fget_coding_system(Qbinary);
105 else
106 {
107 coding_system = Ffind_coding_system (coding_system);
108 if (NILP(coding_system))
109 coding_system = Fget_coding_system(Qbinary);
110 }
111 }
112 else
113 {
114 coding_system = Ffind_coding_system (coding);
115 if (NILP(coding_system))
116 {
117 if (NILP(error_me_not))
118 signal_simple_error("No such coding system", coding);
119 else
120 coding_system = Fget_coding_system(Qbinary); /* default to binary */
121 }
122 }
123 #endif
124
125 /* setup the out stream */
126 outstream = make_dynarr_output_stream((unsigned_char_dynarr *)conversion_out_dynarr);
127 ostr = XLSTREAM (outstream);
128 deststream = make_dynarr_output_stream((unsigned_char_dynarr *)out_dynarr);
129 dstr = XLSTREAM (deststream);
130 #ifdef FILE_CODING
131 /* setup the conversion stream */
132 conv_out_stream = make_encoding_output_stream (ostr, coding_system);
133 costr = XLSTREAM (conv_out_stream);
134 GCPRO3 (instream, outstream, conv_out_stream);
135 #else
136 GCPRO2 (instream, outstream);
137 #endif
138
139 /* Get the data while doing the conversion */
140 while (1) {
141 int size_in_bytes = Lstream_read (istr, tempbuf, sizeof (tempbuf));
142 int l;
143 if (!size_in_bytes)
144 break;
145 /* It does seem the flushes are necessary... */
146 #ifdef FILE_CODING
147 Lstream_write (costr, tempbuf, size_in_bytes);
148 Lstream_flush (costr);
149 #else
150 Lstream_write (ostr, tempbuf, size_in_bytes);
151 #endif
152 Lstream_flush (ostr);
153
154 /* Update the base64 output buffer */
155 for (l = 0; l < size_in_bytes; l++) {
156 bits += Dynarr_at(conversion_out_dynarr,l);
157 char_count++;
158 if (char_count == 3) {
159 static char obuf[4];
160 obuf[0] = alphabet[(bits >> 18)];
161 obuf[1] = alphabet[(bits >> 12) & 0x3f];
162 obuf[2] = alphabet[(bits >> 6) & 0x3f];
163 obuf[3] = alphabet[bits & 0x3f];
164
165 Lstream_write(dstr,obuf,sizeof(obuf));
166 cols += 4;
167 if (cols == 72) {
168 Lstream_write(dstr,"\n",sizeof(unsigned char));
169 cols = 0;
170 }
171 bits = char_count = 0;
172 } else {
173 bits <<= 8;
174 }
175 }
176 /* reset the dynarr */
177 Lstream_rewind(ostr);
178 }
179 Lstream_close (istr);
180 #ifdef FILE_CODING
181 Lstream_close (costr);
182 #endif
183 Lstream_close (ostr);
184
185 if (char_count != 0) {
186 bits <<= 16 - (8 * char_count);
187 Lstream_write(dstr,&alphabet[bits >> 18],sizeof(unsigned char));
188 Lstream_write(dstr,&alphabet[(bits >> 12) & 0x3f],sizeof(unsigned char));
189 if (char_count == 1) {
190 Lstream_write(dstr,"==",2 * sizeof(unsigned char));
191 } else {
192 Lstream_write(dstr,&alphabet[(bits >> 6) & 0x3f],sizeof(unsigned char));
193 Lstream_write(dstr,"=",sizeof(unsigned char));
194 }
195 }
196 #if 0
197 if (cols > 0) {
198 Lstream_write(dstr,"\n",sizeof(unsigned char));
199 }
200 #endif
201 UNGCPRO;
202 Lstream_delete (istr);
203 Lstream_delete (ostr);
204 #ifdef FILE_CODING
205 Lstream_delete (costr);
206 #endif
207 Lstream_flush(dstr);
208 Lstream_delete(dstr);
209
210 return(make_string(Dynarr_atp(out_dynarr,0),Dynarr_length(out_dynarr)));
211 }
212
213 DEFUN ("base64-decode", Fbase64_decode, 1, 5, 0, /*
214 Undo the base64 encoding of an object.
215 OBJECT is either a string or a buffer.
216 Optional arguments START and END denote buffer positions for computing the
217 hash of a portion of OBJECT. The optional CODING argument specifies the coding
218 system the text is to be represented in while computing the digest. This only
219 has meaning with MULE, and defaults to the current format of the data.
220 If ERROR-ME-NOT is nil, report an error if the coding system can't be
221 determined. Else assume binary coding if all else fails.
222 */
223 (object, start, end, coding, error_me_not))
224 {
225 static char inalphabet[256], decoder[256];
226 int i,cols,bits,char_count,hit_eof;
227 Lisp_Object instream, outstream,deststream;
228 Lstream *istr, *ostr, *dstr;
229 static Extbyte_dynarr *conversion_out_dynarr;
230 static Extbyte_dynarr *out_dynarr;
231 char tempbuf[1024]; /* some random amount */
232 struct gcpro gcpro1, gcpro2;
233 #ifdef FILE_CODING
234 Lisp_Object conv_out_stream, coding_system;
235 Lstream *costr;
236 struct gcpro gcpro3;
237 #endif
238
239 for (i = (sizeof alphabet) - 1; i >= 0 ; i--) {
240 inalphabet[alphabet[i]] = 1;
241 decoder[alphabet[i]] = i;
242 }
243
244 if (!conversion_out_dynarr)
245 conversion_out_dynarr = Dynarr_new (Extbyte);
246 else
247 Dynarr_reset (conversion_out_dynarr);
248
249 if (!out_dynarr)
250 out_dynarr = Dynarr_new(Extbyte);
251 else
252 Dynarr_reset (out_dynarr);
253
254 char_count = bits = cols = hit_eof = 0;
255
256 /* set up the in stream */
257 if (BUFFERP (object))
258 {
259 struct buffer *b = decode_buffer (object, 1);
260 Bufpos begv, endv;
261 /* Figure out where we need to get info from */
262 get_buffer_range_char (b, start, end, &begv, &endv, GB_ALLOW_NIL);
263
264 instream = make_lisp_buffer_input_stream (b, begv, endv, 0);
265 }
266 else
267 {
268 Bytecount bstart, bend;
269 CHECK_STRING (object);
270 get_string_range_byte (object, start, end, &bstart, &bend,
271 GB_HISTORICAL_STRING_BEHAVIOR);
272 instream = make_lisp_string_input_stream (object, bstart, bend);
273 }
274 istr = XLSTREAM (instream);
275
276 #ifdef FILE_CODING
277 /* Find out what format the buffer will be saved in, so we can make
278 the digest based on what it will look like on disk */
279 if (NILP(coding))
280 {
281 if (BUFFERP(object))
282 {
283 /* Use the file coding for this buffer by default */
284 coding_system = XBUFFER(object)->buffer_file_coding_system;
285 }
286 else
287 {
288 /* attempt to autodetect the coding of the string. Note: this VERY hit-and-miss */
289 enum eol_type eol = EOL_AUTODETECT;
290 coding_system = Fget_coding_system(Qundecided);
291 determine_real_coding_system(istr, &coding_system, &eol);
292 }
293 if (NILP(coding_system))
294 coding_system = Fget_coding_system(Qbinary);
295 else
296 {
297 coding_system = Ffind_coding_system (coding_system);
298 if (NILP(coding_system))
299 coding_system = Fget_coding_system(Qbinary);
300 }
301 }
302 else
303 {
304 coding_system = Ffind_coding_system (coding);
305 if (NILP(coding_system))
306 {
307 if (NILP(error_me_not))
308 signal_simple_error("No such coding system", coding);
309 else
310 coding_system = Fget_coding_system(Qbinary); /* default to binary */
311 }
312 }
313 #endif
314
315 /* setup the out stream */
316 outstream = make_dynarr_output_stream((unsigned_char_dynarr *)conversion_out_dynarr);
317 ostr = XLSTREAM (outstream);
318 deststream = make_dynarr_output_stream((unsigned_char_dynarr *)out_dynarr);
319 dstr = XLSTREAM (deststream);
320 #ifdef FILE_CODING
321 /* setup the conversion stream */
322 conv_out_stream = make_encoding_output_stream (ostr, coding_system);
323 costr = XLSTREAM (conv_out_stream);
324 GCPRO3 (instream, outstream, conv_out_stream);
325 #else
326 GCPRO2 (instream, outstream);
327 #endif
328
329 /* Get the data while doing the conversion */
330 while (1) {
331 int size_in_bytes = Lstream_read (istr, tempbuf, sizeof (tempbuf));
332 int l;
333 if (!size_in_bytes) {
334 hit_eof = 1;
335 break;
336 }
337 /* It does seem the flushes are necessary... */
338 #ifdef FILE_CODING
339 Lstream_write (costr, tempbuf, size_in_bytes);
340 Lstream_flush (costr);
341 #else
342 Lstream_write (ostr, tempbuf, size_in_bytes);
343 #endif
344 Lstream_flush (ostr);
345
346 /* Update the base64 output buffer */
347 for (l = 0; l < size_in_bytes; l++) {
348 if (Dynarr_at(conversion_out_dynarr,l) == '=')
349 goto decoder_out;
350 bits += decoder[Dynarr_at(conversion_out_dynarr,l)];
351 fprintf(stderr,"%d\n",bits);
352 char_count++;
353 if (char_count == 4) {
354 static unsigned char obuf[3];
355 obuf[0] = (bits >> 16);
356 obuf[1] = (bits >> 8) & 0xff;
357 obuf[2] = (bits & 0xff);
358
359 Lstream_write(dstr,obuf,sizeof(obuf));
360 bits = char_count = 0;
361 } else {
362 bits <<= 6;
363 }
364 }
365 /* reset the dynarr */
366 Lstream_rewind(ostr);
367 }
368 decoder_out:
369 Lstream_close (istr);
370 #ifdef FILE_CODING
371 Lstream_close (costr);
372 #endif
373 Lstream_close (ostr);
374
375 if (hit_eof) {
376 if (char_count) {
377 error_with_frob(object,"base64-decode failed: at least %d bits truncated",((4 - char_count) * 6));
378 }
379 }
380 switch(char_count) {
381 case 1:
382 error_with_frob(object, "base64 encoding incomplete: at least 2 bits missing");
383 break;
384 case 2:
385 char_count = bits >> 10;
386 Lstream_write(dstr,&char_count,sizeof(char_count));
387 break;
388 case 3:
389 {
390 unsigned char buf[2];
391 buf[0] = (bits >> 16);
392 buf[1] = (bits >> 8) & 0xff;
393 Lstream_write(dstr,buf,sizeof(buf));
394 break;
395 }
396 }
397
398 UNGCPRO;
399 Lstream_delete (istr);
400 Lstream_delete (ostr);
401 #ifdef FILE_CODING
402 Lstream_delete (costr);
403 #endif
404 Lstream_flush(dstr);
405 Lstream_delete(dstr);
406
407 return(make_string(Dynarr_atp(out_dynarr,0),Dynarr_length(out_dynarr)));
408 }
409
410 void
411 syms_of_base64 (void)
412 {
413 DEFSUBR(Fbase64_encode);
414 DEFSUBR(Fbase64_decode);
415 }
416
417 void
418 vars_of_base64 (void)
419 {
420 Fprovide (intern ("base64"));
421 }