allow MIME::QuotePrint to handle ASCII code numbers on EBCDIC machines
[p5sagit/p5-mst-13.2.git] / ext / MIME / Base64 / Base64.xs
1 /*
2
3 Copyright 1997-1999,2001 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 #include "EXTERN.h"
32 #include "perl.h"
33 #include "XSUB.h"
34 #ifdef __cplusplus
35 }
36 #endif
37
38
39 #define MAX_LINE  76 /* size of encoded lines */
40
41 static 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 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
69
70 MODULE = MIME::Base64           PACKAGE = MIME::Base64
71
72 SV*
73 encode_base64(sv,...)
74         SV* sv
75         PROTOTYPE: $;$
76
77         PREINIT:
78         char *str;     /* string to encode */
79         SSize_t len;   /* length of the string */
80         char *eol;     /* the end-of-line sequence to use */
81         STRLEN eollen; /* length of the EOL sequence */
82         char *r;       /* result string */
83         STRLEN rlen;   /* length of result string */
84         unsigned char c1, c2, c3;
85         int chunk;
86
87         CODE:
88         sv_utf8_downgrade(sv, FALSE);
89         str = SvPV(sv, rlen); /* SvPV(sv, len) gives warning for signed len */
90         len = (SSize_t)rlen;
91
92         /* set up EOL from the second argument if present, default to "\n" */
93         if (items > 1 && SvOK(ST(1))) {
94             eol = SvPV(ST(1), eollen);
95         } else {
96             eol = "\n";
97             eollen = 1;
98         }
99
100         /* calculate the length of the result */
101         rlen = (len+2) / 3 * 4;  /* encoded bytes */
102         if (rlen) {
103             /* add space for EOL */
104             rlen += ((rlen-1) / MAX_LINE + 1) * eollen;
105         }
106
107         /* allocate a result buffer */
108         RETVAL = newSV(rlen ? rlen : 1);
109         SvPOK_on(RETVAL);       
110         SvCUR_set(RETVAL, rlen);
111         r = SvPVX(RETVAL);
112
113         /* encode */
114         for (chunk=0; len > 0; len -= 3, chunk++) {
115             if (chunk == (MAX_LINE/4)) {
116                 char *c = eol;
117                 char *e = eol + eollen;
118                 while (c < e)
119                     *r++ = *c++;
120                 chunk = 0;
121             }
122             c1 = *str++;
123             c2 = *str++;
124             *r++ = basis_64[c1>>2];
125             *r++ = basis_64[((c1 & 0x3)<< 4) | ((c2 & 0xF0) >> 4)];
126             if (len > 2) {
127                 c3 = *str++;
128                 *r++ = basis_64[((c2 & 0xF) << 2) | ((c3 & 0xC0) >>6)];
129                 *r++ = basis_64[c3 & 0x3F];
130             } else if (len == 2) {
131                 *r++ = basis_64[(c2 & 0xF) << 2];
132                 *r++ = '=';
133             } else { /* len == 1 */
134                 *r++ = '=';
135                 *r++ = '=';
136             }
137         }
138         if (rlen) {
139             /* append eol to the result string */
140             char *c = eol;
141             char *e = eol + eollen;
142             while (c < e)
143                 *r++ = *c++;
144         }
145         *r = '\0';  /* every SV in perl should be NUL-terminated */
146
147         OUTPUT:
148         RETVAL
149
150 SV*
151 decode_base64(sv)
152         SV* sv
153         PROTOTYPE: $
154
155         PREINIT:
156         STRLEN len;
157         register unsigned char *str = (unsigned char*)SvPVbyte(sv, len);
158         unsigned char const* end = str + len;
159         char *r;
160         unsigned char c[4];
161
162         CODE:
163         {
164             /* always enough, but might be too much */
165             STRLEN rlen = len * 3 / 4;
166             RETVAL = newSV(rlen ? rlen : 1);
167         }
168         SvPOK_on(RETVAL);
169         r = SvPVX(RETVAL);
170
171         while (str < end) {
172             int i = 0;
173             do {
174                 unsigned char uc = index_64[NATIVE_TO_ASCII(*str++)];
175                 if (uc != INVALID)
176                     c[i++] = uc;
177
178                 if (str == end) {
179                     if (i < 4) {
180                         if (i && PL_dowarn)
181                             warn("Premature end of base64 data");
182                         if (i < 2) goto thats_it;
183                         if (i == 2) c[2] = EQ;
184                         c[3] = EQ;
185                     }
186                     break;
187                 }
188             } while (i < 4);
189         
190             if (c[0] == EQ || c[1] == EQ) {
191                 if (PL_dowarn) warn("Premature padding of base64 data");
192                 break;
193             }
194             /* printf("c0=%d,c1=%d,c2=%d,c3=%d\n", c[0],c[1],c[2],c[3]);/**/
195
196             *r++ = (c[0] << 2) | ((c[1] & 0x30) >> 4);
197
198             if (c[2] == EQ)
199                 break;
200             *r++ = ((c[1] & 0x0F) << 4) | ((c[2] & 0x3C) >> 2);
201
202             if (c[3] == EQ)
203                 break;
204             *r++ = ((c[2] & 0x03) << 6) | c[3];
205         }
206
207       thats_it:
208         SvCUR_set(RETVAL, r - SvPVX(RETVAL));
209         *r = '\0';
210
211         OUTPUT:
212         RETVAL