comparison modules/base64/base64.c @ 265:8efd647ea9ca r20-5b31

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