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