Integrate perlio:
[p5sagit/p5-mst-13.2.git] / ext / MIME / Base64 / Base64.xs
1 /* $Id: Base64.xs,v 1.18 2001/02/24 06:27:01 gisle Exp $
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 #include "patchlevel.h"
39 #if PATCHLEVEL <= 4 && !defined(PL_dowarn)
40    #define PL_dowarn dowarn
41 #endif
42
43 #define MAX_LINE  76 /* size of encoded lines */
44
45 static char basis_64[] =
46    "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
47
48 #define XX      255     /* illegal base64 char */
49 #define EQ      254     /* padding */
50 #define INVALID XX
51
52 static unsigned char index_64[256] = {
53     XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX,
54     XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX,
55     XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,62, XX,XX,XX,63,
56     52,53,54,55, 56,57,58,59, 60,61,XX,XX, XX,EQ,XX,XX,
57     XX, 0, 1, 2,  3, 4, 5, 6,  7, 8, 9,10, 11,12,13,14,
58     15,16,17,18, 19,20,21,22, 23,24,25,XX, XX,XX,XX,XX,
59     XX,26,27,28, 29,30,31,32, 33,34,35,36, 37,38,39,40,
60     41,42,43,44, 45,46,47,48, 49,50,51,XX, XX,XX,XX,XX,
61
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     XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX,
67     XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX,
68     XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX,
69     XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX,
70 };
71
72
73
74 MODULE = MIME::Base64           PACKAGE = MIME::Base64
75
76 SV*
77 encode_base64(sv,...)
78         SV* sv
79         PROTOTYPE: $;$
80
81         PREINIT:
82         char *str;     /* string to encode */
83         SSize_t len;   /* length of the string */
84         char *eol;     /* the end-of-line sequence to use */
85         STRLEN eollen; /* length of the EOL sequence */
86         char *r;       /* result string */
87         STRLEN rlen;   /* length of result string */
88         unsigned char c1, c2, c3;
89         int chunk;
90
91         CODE:
92 #ifdef sv_utf8_downgrade
93         sv_utf8_downgrade(sv, FALSE);
94 #endif
95         str = SvPV(sv, rlen); /* SvPV(sv, len) gives warning for signed len */
96         len = (SSize_t)rlen;
97
98         /* set up EOL from the second argument if present, default to "\n" */
99         if (items > 1 && SvOK(ST(1))) {
100             eol = SvPV(ST(1), eollen);
101         } else {
102             eol = "\n";
103             eollen = 1;
104         }
105
106         /* calculate the length of the result */
107         rlen = (len+2) / 3 * 4;  /* encoded bytes */
108         if (rlen) {
109             /* add space for EOL */
110             rlen += ((rlen-1) / MAX_LINE + 1) * eollen;
111         }
112
113         /* allocate a result buffer */
114         RETVAL = newSV(rlen ? rlen : 1);
115         SvPOK_on(RETVAL);       
116         SvCUR_set(RETVAL, rlen);
117         r = SvPVX(RETVAL);
118
119         /* encode */
120         for (chunk=0; len > 0; len -= 3, chunk++) {
121             if (chunk == (MAX_LINE/4)) {
122                 char *c = eol;
123                 char *e = eol + eollen;
124                 while (c < e)
125                     *r++ = *c++;
126                 chunk = 0;
127             }
128             c1 = *str++;
129             c2 = *str++;
130             *r++ = basis_64[c1>>2];
131             *r++ = basis_64[((c1 & 0x3)<< 4) | ((c2 & 0xF0) >> 4)];
132             if (len > 2) {
133                 c3 = *str++;
134                 *r++ = basis_64[((c2 & 0xF) << 2) | ((c3 & 0xC0) >>6)];
135                 *r++ = basis_64[c3 & 0x3F];
136             } else if (len == 2) {
137                 *r++ = basis_64[(c2 & 0xF) << 2];
138                 *r++ = '=';
139             } else { /* len == 1 */
140                 *r++ = '=';
141                 *r++ = '=';
142             }
143         }
144         if (rlen) {
145             /* append eol to the result string */
146             char *c = eol;
147             char *e = eol + eollen;
148             while (c < e)
149                 *r++ = *c++;
150         }
151         *r = '\0';  /* every SV in perl should be NUL-terminated */
152
153         OUTPUT:
154         RETVAL
155
156 SV*
157 decode_base64(sv)
158         SV* sv
159         PROTOTYPE: $
160
161         PREINIT:
162         STRLEN len;
163         register unsigned char *str = (unsigned char*)SvPVbyte(sv, len);
164         unsigned char const* end = str + len;
165         char *r;
166         unsigned char c[4];
167
168         CODE:
169         {
170             /* always enough, but might be too much */
171             STRLEN rlen = len * 3 / 4;
172             RETVAL = newSV(rlen ? rlen : 1);
173         }
174         SvPOK_on(RETVAL);
175         r = SvPVX(RETVAL);
176
177         while (str < end) {
178             int i = 0;
179             do {
180                 unsigned char uc = index_64[NATIVE_TO_ASCII(*str++)];
181                 if (uc != INVALID)
182                     c[i++] = uc;
183
184                 if (str == end) {
185                     if (i < 4) {
186                         if (i && PL_dowarn)
187                             warn("Premature end of base64 data");
188                         if (i < 2) goto thats_it;
189                         if (i == 2) c[2] = EQ;
190                         c[3] = EQ;
191                     }
192                     break;
193                 }
194             } while (i < 4);
195         
196             if (c[0] == EQ || c[1] == EQ) {
197                 if (PL_dowarn) warn("Premature padding of base64 data");
198                 break;
199             }
200             /* printf("c0=%d,c1=%d,c2=%d,c3=%d\n", c[0],c[1],c[2],c[3]);/**/
201
202             *r++ = (c[0] << 2) | ((c[1] & 0x30) >> 4);
203
204             if (c[2] == EQ)
205                 break;
206             *r++ = ((c[1] & 0x0F) << 4) | ((c[2] & 0x3C) >> 2);
207
208             if (c[3] == EQ)
209                 break;
210             *r++ = ((c[2] & 0x03) << 6) | c[3];
211         }
212
213       thats_it:
214         SvCUR_set(RETVAL, r - SvPVX(RETVAL));
215         *r = '\0';
216
217         OUTPUT:
218         RETVAL