chiark / gitweb /
Make the selector type symbols an enumeration rather than a bunch of
[mLib] / base64.c
1 /* -*-c-*-
2  *
3  * $Id: base64.c,v 1.1 1999/05/17 20:35:00 mdw Exp $
4  *
5  * Base64 encoding and decoding.
6  *
7  * (c) 1997 Straylight/Edgeware
8  */
9
10 /*----- Licensing notice --------------------------------------------------* 
11  *
12  * This file is part of the mLib utilities library.
13  *
14  * mLib is free software; you can redistribute it and/or modify
15  * it under the terms of the GNU Library General Public License as
16  * published by the Free Software Foundation; either version 2 of the
17  * License, or (at your option) any later version.
18  * 
19  * mLib is distributed in the hope that it will be useful,
20  * but WITHOUT ANY WARRANTY; without even the implied warranty of
21  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
22  * GNU Library General Public License for more details.
23  * 
24  * You should have received a copy of the GNU Library General Public
25  * License along with mLib; if not, write to the Free
26  * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
27  * MA 02111-1307, USA.
28  */
29
30 /*----- Revision history --------------------------------------------------*
31  *
32  * $Log: base64.c,v $
33  * Revision 1.1  1999/05/17 20:35:00  mdw
34  * Base64 encoding and decoding support.
35  *
36  */
37
38 /*----- Header files ------------------------------------------------------*/
39
40 #include <stdio.h>
41 #include <stdlib.h>
42 #include <string.h>
43
44 #include "base64.h"
45 #include "dstr.h"
46
47 /*----- Important tables --------------------------------------------------*/
48
49 static const char base64_encodeMap[] = { "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
50                                          "abcdefghijklmnopqrstuvwxyz" 
51                                          "0123456789+/" };
52
53 static const signed char base64_decodeMap[] = {
54   -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,  /* 0x */
55   -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,  /* 1x */
56   -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 62, -1, -1, -1, 63,  /* 2x */
57   52, 53, 54, 55, 56, 57, 58, 59, 60, 61, -1, -1, -1, -1, -1, -1,  /* 3x */
58   -1,  0,  1,  2,  3,  4,  5,  6,  7,  8,  9, 10, 11, 12, 13, 14,  /* 4x */
59   15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, -1, -1, -1, -1, -1,  /* 5x */
60   -1, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36 ,37, 38, 39, 40,  /* 6x */
61   41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, -1, -1, -1, -1, -1   /* 7x */
62 };  
63
64 /*----- Main code ---------------------------------------------------------*/
65
66 /* --- @base64_encode@ --- *
67  *
68  * Arguments:   @base64_ctx *ctx@ = pointer to a context block
69  *              @const unsigned char *src@ = pointer to a source buffer
70  *              @size_t sz@ = size of the source buffer
71  *              @dstr *d@ = pointer to destination string
72  *
73  * Returns:     ---
74  *
75  * Use:         Encodes a binary string in base64.  To flush out the final
76  *              few characters (if necessary), pass a null source pointer.
77  */
78
79 void base64_encode(base64_ctx *ctx,
80                    const unsigned char *src, size_t sz,
81                    dstr *d)
82 {
83   if (src) {
84     unsigned long acc = ctx->acc;
85     unsigned qsz = ctx->qsz;
86
87     while (sz) {
88       acc = (acc << 8) | *src++;
89       qsz++;
90       sz--;
91       if (qsz == 3) {
92         dstr_putc(d, base64_encodeMap[(acc >> 18) & 0x3f]);
93         dstr_putc(d, base64_encodeMap[(acc >> 12) & 0x3f]);
94         dstr_putc(d, base64_encodeMap[(acc >>  6) & 0x3f]);
95         dstr_putc(d, base64_encodeMap[(acc >>  0) & 0x3f]);
96         ctx->lnlen += 4;
97         if (ctx->maxline && ctx->lnlen >= ctx->maxline) {
98           dstr_puts(d, ctx->indent);
99           ctx->lnlen = 0;
100         }
101         qsz = 0;
102         acc = 0;
103       }
104     }
105
106     ctx->acc = acc;
107     ctx->qsz = qsz;
108   } else {
109     unsigned long acc = ctx->acc;
110     unsigned qsz = ctx->qsz;
111
112     switch (qsz) {
113       case 0:
114         break;
115       case 1:
116         acc <<= 16;
117         dstr_putc(d, base64_encodeMap[(acc >> 18) & 0x3f]);
118         dstr_putc(d, base64_encodeMap[(acc >> 12) & 0x3f]);
119         dstr_putc(d, '=');
120         dstr_putc(d, '=');
121         ctx->lnlen += 4;
122         break;
123       case 2:
124         acc <<= 8;
125         dstr_putc(d, base64_encodeMap[(acc >> 18) & 0x3f]);
126         dstr_putc(d, base64_encodeMap[(acc >> 12) & 0x3f]);
127         dstr_putc(d, base64_encodeMap[(acc >>  6) & 0x3f]);
128         dstr_putc(d, '=');
129         ctx->lnlen += 4;
130         break;
131     }
132     ctx->qsz = 0;
133     ctx->acc = 0;
134   }
135 }
136
137 /* --- @base64_decode@ --- *
138  *
139  * Arguments:   @base64_ctx *ctx@ = pointer to a context block
140  *              @const unsigned char *src@ = pointer to a source buffer
141  *              @size_t sz@ = size of the source buffer
142  *              @dstr *d@ = pointer to destination string
143  *
144  * Returns:     ---
145  *
146  * Use:         Decodes a binary string in base64.  To flush out the final
147  *              few characters (if necessary), pass a null source pointer.
148  */
149
150 void base64_decode(base64_ctx *ctx,
151                    const unsigned char *src, size_t sz,
152                    dstr *d)
153 {
154   if (src) {
155     unsigned long acc = ctx->acc;
156     unsigned qsz = ctx->qsz;
157     int ch;
158
159     while (sz) {
160
161       /* --- Get the next character and convert it --- */
162
163       ch = *src++;
164       if (ch >= 128 || ch < 0)
165         ch = -1;
166       else
167         ch = base64_decodeMap[ch];
168       sz--;
169       if (ch == -1)
170         continue;
171
172       /* --- Bung it in the accumulator --- */
173
174       acc = (acc << 6) | ch;
175       qsz++;
176
177       /* --- Maybe write out a completed triplet --- */
178
179       if (qsz == 4) {
180         dstr_putc(d, (acc >> 16) & 0xff);
181         dstr_putc(d, (acc >>  8) & 0xff);
182         dstr_putc(d, (acc >>  0) & 0xff);
183         acc = 0;
184         qsz = 0;
185       }
186     }
187
188     ctx->acc = acc;
189     ctx->qsz = qsz;
190   } else {
191
192     /* --- Notes about the tail-end bits --- *
193      *
194      * Ending Base64 decoding is messy.  The reference I'm using to define
195      * the encoding, RFC1521 section 5.2, is a little hazy on exactly what to
196      * do at the end.  It explains that I'm meant to ignore spurious `='
197      * characters, and points out that I'm not guaranteed to see anything
198      * interesting at the end.  I'll play safe here, and ignore all `='
199      * characters, relying on my client to work out when to stop feeding me
200      * data.  I'll use the queue size to work out how many tail-end bytes
201      * I ought to write.
202      */
203
204     unsigned long acc = ctx->acc;
205     unsigned qsz = ctx->qsz;
206
207     /* --- Now fiddle with everything else --- */
208
209     acc <<= 6 * (4 - qsz);
210     qsz *= 6;
211     while (qsz > 8) {
212       dstr_putc(d, (acc >> 16) & 0xff);
213       acc <<= 8;
214       qsz -= 8;
215     }
216
217     /* --- That seems to be good enough --- */
218
219     ctx->qsz = 0;
220     ctx->acc = 0;
221   }
222 }
223
224 /* --- @base64_init@ --- *
225  *
226  * Arguments:   @base64_ctx *ctx@ = pointer to context block to initialize
227  *
228  * Returns:     ---
229  *
230  * Use:         Initializes a base64 context properly.
231  */
232
233 void base64_init(base64_ctx *ctx)
234 {
235   ctx->acc = 0;
236   ctx->qsz = 0;
237   ctx->lnlen = 0;
238   ctx->indent = "\n";
239   ctx->maxline = 72;
240 }
241
242 /*----- Test driver code --------------------------------------------------*/
243
244 #ifdef TEST_RIG
245
246 int main(int argc, char *argv[])
247 {
248   unsigned char buf[BUFSIZ];
249   dstr d;
250   base64_ctx ctx;
251   void (*proc)(base64_ctx *, const unsigned char *, size_t, dstr *);
252   size_t sz;
253
254   dstr_create(&d);
255   base64_init(&ctx);
256
257   if (argc > 1 && strcmp(argv[1], "-d") == 0)
258     proc = base64_decode;
259   else {
260     proc = base64_encode;
261     putchar('\t');
262     ctx.indent = "\n\t";
263     ctx.maxline = 64;
264   }
265
266   do {
267     sz = fread(buf, 1, sizeof(buf), stdin);
268     if (sz) {
269       proc(&ctx, buf, sz, &d);
270       dstr_write(&d, stdout);
271       dstr_destroy(&d);
272     }
273   } while (sz == sizeof(buf));
274
275   proc(&ctx, 0, 0, &d);
276   dstr_write(&d, stdout);
277
278   if (proc == base64_encode)
279     putchar('\n');
280
281   return (0);
282 }
283
284 #endif
285
286 /*----- That's all, folks -------------------------------------------------*/