This is a live mirror of the Perl 5 development currently hosted at https://github.com/perl/perl5
Add a test for o PrintRet.
[perl5.git] / cpan / MIME-Base64 / Base64.xs
1 /*
2
3 Copyright 1997-2004 Gisle Aas
4
5 This library is free software; you can redistribute it and/or
6 modify it under the same terms as Perl itself.
7
8
9 The tables and some of the code that used to be here was borrowed from
10 metamail, which comes with this message:
11
12   Copyright (c) 1991 Bell Communications Research, Inc. (Bellcore)
13
14   Permission to use, copy, modify, and distribute this material
15   for any purpose and without fee is hereby granted, provided
16   that the above copyright notice and this permission notice
17   appear in all copies, and that the name of Bellcore not be
18   used in advertising or publicity pertaining to this
19   material without the specific, prior written permission
20   of an authorized representative of Bellcore.  BELLCORE
21   MAKES NO REPRESENTATIONS ABOUT THE ACCURACY OR SUITABILITY
22   OF THIS MATERIAL FOR ANY PURPOSE.  IT IS PROVIDED "AS IS",
23   WITHOUT ANY EXPRESS OR IMPLIED WARRANTIES.
24
25 */
26
27
28 #ifdef __cplusplus
29 extern "C" {
30 #endif
31 #define PERL_NO_GET_CONTEXT     /* we want efficiency */
32 #include "EXTERN.h"
33 #include "perl.h"
34 #include "XSUB.h"
35 #ifdef __cplusplus
36 }
37 #endif
38
39 #define MAX_LINE  76 /* size of encoded lines */
40
41 static const char basis_64[] =
42    "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
43
44 #define XX      255     /* illegal base64 char */
45 #define EQ      254     /* padding */
46 #define INVALID XX
47
48 static const unsigned char index_64[256] = {
49     XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX,
50     XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX,
51     XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,62, XX,XX,XX,63,
52     52,53,54,55, 56,57,58,59, 60,61,XX,XX, XX,EQ,XX,XX,
53     XX, 0, 1, 2,  3, 4, 5, 6,  7, 8, 9,10, 11,12,13,14,
54     15,16,17,18, 19,20,21,22, 23,24,25,XX, XX,XX,XX,XX,
55     XX,26,27,28, 29,30,31,32, 33,34,35,36, 37,38,39,40,
56     41,42,43,44, 45,46,47,48, 49,50,51,XX, XX,XX,XX,XX,
57
58     XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX,
59     XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX,
60     XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX,
61     XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX,
62     XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX,
63     XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX,
64     XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX,
65     XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX,
66 };
67
68 #ifdef SvPVbyte
69 #   if PERL_REVISION == 5 && PERL_VERSION < 7
70        /* SvPVbyte does not work in perl-5.6.1, borrowed version for 5.7.3 */
71 #       undef SvPVbyte
72 #       define SvPVbyte(sv, lp) \
73           ((SvFLAGS(sv) & (SVf_POK|SVf_UTF8)) == (SVf_POK) \
74            ? ((lp = SvCUR(sv)), SvPVX(sv)) : my_sv_2pvbyte(aTHX_ sv, &lp))
75        static char *
76        my_sv_2pvbyte(pTHX_ register SV *sv, STRLEN *lp)
77        {   
78            sv_utf8_downgrade(sv,0);
79            return SvPV(sv,*lp);
80        }
81 #   endif
82 #else
83 #   define SvPVbyte SvPV
84 #endif
85
86 #ifndef isXDIGIT
87 #   define isXDIGIT isxdigit
88 #endif
89
90 #ifndef NATIVE_TO_ASCII
91 #   define NATIVE_TO_ASCII(ch) (ch)
92 #endif
93
94 MODULE = MIME::Base64           PACKAGE = MIME::Base64
95
96 SV*
97 encode_base64(sv,...)
98         SV* sv
99         PROTOTYPE: $;$
100
101         PREINIT:
102         char *str;     /* string to encode */
103         SSize_t len;   /* length of the string */
104         const char*eol;/* the end-of-line sequence to use */
105         STRLEN eollen; /* length of the EOL sequence */
106         char *r;       /* result string */
107         STRLEN rlen;   /* length of result string */
108         unsigned char c1, c2, c3;
109         int chunk;
110         U32 had_utf8;
111
112         CODE:
113 #if PERL_REVISION == 5 && PERL_VERSION >= 6
114         had_utf8 = SvUTF8(sv);
115         sv_utf8_downgrade(sv, FALSE);
116 #endif
117         str = SvPV(sv, rlen); /* SvPV(sv, len) gives warning for signed len */
118         len = (SSize_t)rlen;
119
120         /* set up EOL from the second argument if present, default to "\n" */
121         if (items > 1 && SvOK(ST(1))) {
122             eol = SvPV(ST(1), eollen);
123         } else {
124             eol = "\n";
125             eollen = 1;
126         }
127
128         /* calculate the length of the result */
129         rlen = (len+2) / 3 * 4;  /* encoded bytes */
130         if (rlen) {
131             /* add space for EOL */
132             rlen += ((rlen-1) / MAX_LINE + 1) * eollen;
133         }
134
135         /* allocate a result buffer */
136         RETVAL = newSV(rlen ? rlen : 1);
137         SvPOK_on(RETVAL);       
138         SvCUR_set(RETVAL, rlen);
139         r = SvPVX(RETVAL);
140
141         /* encode */
142         for (chunk=0; len > 0; len -= 3, chunk++) {
143             if (chunk == (MAX_LINE/4)) {
144                 const char *c = eol;
145                 const char *e = eol + eollen;
146                 while (c < e)
147                     *r++ = *c++;
148                 chunk = 0;
149             }
150             c1 = *str++;
151             c2 = len > 1 ? *str++ : '\0';
152             *r++ = basis_64[c1>>2];
153             *r++ = basis_64[((c1 & 0x3)<< 4) | ((c2 & 0xF0) >> 4)];
154             if (len > 2) {
155                 c3 = *str++;
156                 *r++ = basis_64[((c2 & 0xF) << 2) | ((c3 & 0xC0) >>6)];
157                 *r++ = basis_64[c3 & 0x3F];
158             } else if (len == 2) {
159                 *r++ = basis_64[(c2 & 0xF) << 2];
160                 *r++ = '=';
161             } else { /* len == 1 */
162                 *r++ = '=';
163                 *r++ = '=';
164             }
165         }
166         if (rlen) {
167             /* append eol to the result string */
168             const char *c = eol;
169             const char *e = eol + eollen;
170             while (c < e)
171                 *r++ = *c++;
172         }
173         *r = '\0';  /* every SV in perl should be NUL-terminated */
174 #if PERL_REVISION == 5 && PERL_VERSION >= 6
175         if (had_utf8)
176             sv_utf8_upgrade(sv);
177 #endif
178
179         OUTPUT:
180         RETVAL
181
182 SV*
183 decode_base64(sv)
184         SV* sv
185         PROTOTYPE: $
186
187         PREINIT:
188         STRLEN len;
189         register unsigned char *str = (unsigned char*)SvPV(sv, len);
190         unsigned char const* end = str + len;
191         char *r;
192         unsigned char c[4];
193
194         CODE:
195         {
196             /* always enough, but might be too much */
197             STRLEN rlen = len * 3 / 4;
198             RETVAL = newSV(rlen ? rlen : 1);
199         }
200         SvPOK_on(RETVAL);
201         r = SvPVX(RETVAL);
202
203         while (str < end) {
204             int i = 0;
205             do {
206                 unsigned char uc = index_64[NATIVE_TO_ASCII(*str++)];
207                 if (uc != INVALID)
208                     c[i++] = uc;
209
210                 if (str == end) {
211                     if (i < 4) {
212                         if (i < 2) goto thats_it;
213                         if (i == 2) c[2] = EQ;
214                         c[3] = EQ;
215                     }
216                     break;
217                 }
218             } while (i < 4);
219         
220             if (c[0] == EQ || c[1] == EQ) {
221                 break;
222             }
223             /* printf("c0=%d,c1=%d,c2=%d,c3=%d\n", c[0],c[1],c[2],c[3]);*/
224
225             *r++ = (c[0] << 2) | ((c[1] & 0x30) >> 4);
226
227             if (c[2] == EQ)
228                 break;
229             *r++ = ((c[1] & 0x0F) << 4) | ((c[2] & 0x3C) >> 2);
230
231             if (c[3] == EQ)
232                 break;
233             *r++ = ((c[2] & 0x03) << 6) | c[3];
234         }
235
236       thats_it:
237         SvCUR_set(RETVAL, r - SvPVX(RETVAL));
238         *r = '\0';
239
240         OUTPUT:
241         RETVAL
242
243 int
244 encoded_base64_length(sv,...)
245         SV* sv
246         PROTOTYPE: $;$
247
248         PREINIT:
249         SSize_t len;   /* length of the string */
250         STRLEN eollen; /* length of the EOL sequence */
251         U32 had_utf8;
252
253         CODE:
254 #if PERL_REVISION == 5 && PERL_VERSION >= 6
255         had_utf8 = SvUTF8(sv);
256         sv_utf8_downgrade(sv, FALSE);
257 #endif
258         len = SvCUR(sv);
259 #if PERL_REVISION == 5 && PERL_VERSION >= 6
260         if (had_utf8)
261             sv_utf8_upgrade(sv);
262 #endif
263
264         if (items > 1 && SvOK(ST(1))) {
265             eollen = SvCUR(ST(1));
266         } else {
267             eollen = 1;
268         }
269
270         RETVAL = (len+2) / 3 * 4;        /* encoded bytes */
271         if (RETVAL) {
272             RETVAL += ((RETVAL-1) / MAX_LINE + 1) * eollen;
273         }
274
275         OUTPUT:
276         RETVAL
277
278 int
279 decoded_base64_length(sv)
280         SV* sv
281         PROTOTYPE: $
282
283         PREINIT:
284         STRLEN len;
285         register unsigned char *str = (unsigned char*)SvPV(sv, len);
286         unsigned char const* end = str + len;
287         int i = 0;
288
289         CODE:
290         RETVAL = 0;
291         while (str < end) {
292             unsigned char uc = index_64[NATIVE_TO_ASCII(*str++)];
293             if (uc == INVALID)
294                 continue;
295             if (uc == EQ)
296                 break;
297             if (i++) {
298                 RETVAL++;
299                 if (i == 4)
300                     i = 0;
301             }
302         }
303
304         OUTPUT:
305         RETVAL
306
307
308 MODULE = MIME::Base64           PACKAGE = MIME::QuotedPrint
309
310 #ifdef EBCDIC
311 #define qp_isplain(c) ((c) == '\t' || ((!isprint(c) && (c) != '=')))
312 #else
313 #define qp_isplain(c) ((c) == '\t' || (((c) >= ' ' && (c) <= '~') && (c) != '='))
314 #endif
315
316 SV*
317 encode_qp(sv,...)
318         SV* sv
319         PROTOTYPE: $;$$
320
321         PREINIT:
322         const char *eol;
323         STRLEN eol_len;
324         int binary;
325         STRLEN sv_len;
326         STRLEN linelen;
327         char *beg;
328         char *end;
329         char *p;
330         char *p_beg;
331         STRLEN p_len;
332         U32 had_utf8;
333
334         CODE:
335 #if PERL_REVISION == 5 && PERL_VERSION >= 6
336         had_utf8 = SvUTF8(sv);
337         sv_utf8_downgrade(sv, FALSE);
338 #endif
339         /* set up EOL from the second argument if present, default to "\n" */
340         if (items > 1 && SvOK(ST(1))) {
341             eol = SvPV(ST(1), eol_len);
342         } else {
343             eol = "\n";
344             eol_len = 1;
345         }
346
347         binary = (items > 2 && SvTRUE(ST(2)));
348
349         beg = SvPV(sv, sv_len);
350         end = beg + sv_len;
351
352         RETVAL = newSV(sv_len + 1);
353         sv_setpv(RETVAL, "");
354         linelen = 0;
355
356         p = beg;
357         while (1) {
358             p_beg = p;
359
360             /* skip past as much plain text as possible */
361             while (p < end && qp_isplain(*p)) {
362                 p++;
363             }
364             if (p == end || *p == '\n') {
365                 /* whitespace at end of line must be encoded */
366                 while (p > p_beg && (*(p - 1) == '\t' || *(p - 1) == ' '))
367                     p--;
368             }
369
370             p_len = p - p_beg;
371             if (p_len) {
372                 /* output plain text (with line breaks) */
373                 if (eol_len) {
374                     while (p_len > MAX_LINE - 1 - linelen) {
375                         STRLEN len = MAX_LINE - 1 - linelen;
376                         sv_catpvn(RETVAL, p_beg, len);
377                         p_beg += len;
378                         p_len -= len;
379                         sv_catpvn(RETVAL, "=", 1);
380                         sv_catpvn(RETVAL, eol, eol_len);
381                         linelen = 0;
382                     }
383                 }
384                 if (p_len) {
385                     sv_catpvn(RETVAL, p_beg, p_len);
386                     linelen += p_len;
387                 }
388             }
389
390             if (p == end) {
391                 break;
392             }
393             else if (*p == '\n' && eol_len && !binary) {
394                 if (linelen == 1 && SvCUR(RETVAL) > eol_len + 1 && SvEND(RETVAL)[-eol_len - 2] == '=') {
395                     /* fixup useless soft linebreak */
396                     SvEND(RETVAL)[-eol_len - 2] = SvEND(RETVAL)[-1];
397                     SvCUR_set(RETVAL, SvCUR(RETVAL) - 1);
398                 }
399                 else {
400                     sv_catpvn(RETVAL, eol, eol_len);
401                 }
402                 p++;
403                 linelen = 0;
404             }
405             else {
406                 /* output escaped char (with line breaks) */
407                 assert(p < end);
408                 if (eol_len && linelen > MAX_LINE - 4 && !(linelen == MAX_LINE - 3 && p + 1 < end && p[1] == '\n' && !binary)) {
409                     sv_catpvn(RETVAL, "=", 1);
410                     sv_catpvn(RETVAL, eol, eol_len);
411                     linelen = 0;
412                 }
413                 sv_catpvf(RETVAL, "=%02X", (unsigned char)*p);
414                 p++;
415                 linelen += 3;
416             }
417
418             /* optimize reallocs a bit */
419             if (SvLEN(RETVAL) > 80 && SvLEN(RETVAL) - SvCUR(RETVAL) < 3) {
420                 STRLEN expected_len = (SvCUR(RETVAL) * sv_len) / (p - beg);
421                 SvGROW(RETVAL, expected_len);
422             }
423         }
424
425         if (SvCUR(RETVAL) && eol_len && linelen) {
426             sv_catpvn(RETVAL, "=", 1);
427             sv_catpvn(RETVAL, eol, eol_len);
428         }
429 #if PERL_REVISION == 5 && PERL_VERSION >= 6
430         if (had_utf8)
431             sv_utf8_upgrade(sv);
432 #endif
433
434         OUTPUT:
435         RETVAL
436
437 SV*
438 decode_qp(sv)
439         SV* sv
440         PROTOTYPE: $
441
442         PREINIT:
443         STRLEN len;
444         char *str = SvPVbyte(sv, len);
445         char const* end = str + len;
446         char *r;
447         char *whitespace = 0;
448
449         CODE:
450         RETVAL = newSV(len ? len : 1);
451         SvPOK_on(RETVAL);
452         r = SvPVX(RETVAL);
453         while (str < end) {
454             if (*str == ' ' || *str == '\t') {
455                 if (!whitespace)
456                     whitespace = str;
457                 str++;
458             }
459             else if (*str == '\r' && (str + 1) < end && str[1] == '\n') {
460                 str++;
461             }
462             else if (*str == '\n') {
463                 whitespace = 0;
464                 *r++ = *str++;
465             }
466             else {
467                 if (whitespace) {
468                     while (whitespace < str) {
469                         *r++ = *whitespace++;
470                     }
471                     whitespace = 0;
472                 }
473                 if (*str == '=') {
474                     if ((str + 2) < end && isXDIGIT(str[1]) && isXDIGIT(str[2])) {
475                         char buf[3];
476                         str++;
477                         buf[0] = *str++;
478                         buf[1] = *str++;
479                         buf[2] = '\0';
480                         *r++ = (char)strtol(buf, 0, 16);
481                     }
482                     else {
483                         /* look for soft line break */
484                         char *p = str + 1;
485                         while (p < end && (*p == ' ' || *p == '\t'))
486                             p++;
487                         if (p < end && *p == '\n')
488                             str = p + 1;
489                         else if ((p + 1) < end && *p == '\r' && *(p + 1) == '\n')
490                             str = p + 2;
491                         else
492                             *r++ = *str++; /* give up */
493                     }
494                 }
495                 else {
496                     *r++ = *str++;
497                 }
498             }
499         }
500         if (whitespace) {
501             while (whitespace < str) {
502                 *r++ = *whitespace++;
503             }
504         }
505         *r = '\0';
506         SvCUR_set(RETVAL, r - SvPVX(RETVAL));
507
508         OUTPUT:
509         RETVAL
510
511
512 MODULE = MIME::Base64           PACKAGE = MIME::Base64