Missed out a highlight.
[sgt/utils] / base64 / base64.c
CommitLineData
9acadc2b 1#include <stdio.h>
2#include <errno.h>
3#include <string.h>
4
5#define isbase64(c) ( ((c) >= 'A' && (c) <= 'Z') || \
6 ((c) >= 'a' && (c) <= 'z') || \
7 ((c) >= '0' && (c) <= '9') || \
8 (c) == '+' || (c) == '/' || (c) == '=' \
9 )
10
11int base64_decode_atom(char *atom, unsigned char *out) {
12 int vals[4];
13 int i, v, len;
14 unsigned word;
15 char c;
16
17 for (i = 0; i < 4; i++) {
18 c = atom[i];
19 if (c >= 'A' && c <= 'Z')
20 v = c - 'A';
21 else if (c >= 'a' && c <= 'z')
22 v = c - 'a' + 26;
23 else if (c >= '0' && c <= '9')
24 v = c - '0' + 52;
25 else if (c == '+')
26 v = 62;
27 else if (c == '/')
28 v = 63;
29 else if (c == '=')
30 v = -1;
31 else
32 return 0; /* invalid atom */
33 vals[i] = v;
34 }
35
36 if (vals[0] == -1 || vals[1] == -1)
37 return 0;
38 if (vals[2] == -1 && vals[3] != -1)
39 return 0;
40
41 if (vals[3] != -1)
42 len = 3;
43 else if (vals[2] != -1)
44 len = 2;
45 else
46 len = 1;
47
48 word = ((vals[0] << 18) |
49 (vals[1] << 12) |
50 ((vals[2] & 0x3F) << 6) |
51 (vals[3] & 0x3F));
52 out[0] = (word >> 16) & 0xFF;
53 if (len > 1)
54 out[1] = (word >> 8) & 0xFF;
55 if (len > 2)
56 out[2] = word & 0xFF;
57 return len;
58}
59
60void base64_encode_atom(unsigned char *data, int n, char *out) {
61 static const char base64_chars[] =
62 "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
63
64 unsigned word;
65
66 word = data[0] << 16;
67 if (n > 1)
68 word |= data[1] << 8;
69 if (n > 2)
70 word |= data[2];
71 out[0] = base64_chars[(word >> 18) & 0x3F];
72 out[1] = base64_chars[(word >> 12) & 0x3F];
73 if (n > 1)
74 out[2] = base64_chars[(word >> 6) & 0x3F];
75 else
76 out[2] = '=';
77 if (n > 2)
78 out[3] = base64_chars[word & 0x3F];
79 else
80 out[3] = '=';
81}
82
83const char usagemsg[] =
84 "usage: base64 [-d] [filename] decode from a file or from stdin\n"
85 " or: base64 -e [-cNNN] [filename] encode from a file or from stdin\n"
86 " also: base64 --version report version number\n"
87 " and: base64 --help display this help text\n"
88 "where: -d decode mode (default)\n"
89 " -e encode mode\n"
90 " -cNNN set number of chars per line for encoded output\n"
91 ;
92
93void usage(void) {
94 fputs(usagemsg, stdout);
95}
96
97void version(void) {
98#define SVN_REV "$Revision$"
99 char rev[sizeof(SVN_REV)];
100 char *p, *q;
101
102 strcpy(rev, SVN_REV);
103
104 for (p = rev; *p && *p != ':'; p++);
105 if (*p) {
106 p++;
107 while (*p && isspace(*p)) p++;
108 for (q = p; *q && *q != '$'; q++);
109 if (*q) *q = '\0';
110 printf("base64 revision %s\n", p);
111 } else {
112 printf("base64: unknown version\n");
113 }
114}
115
116int main(int ac, char **av) {
117 int encoding = 0;
118 int cpl = 64;
119 FILE *fp;
120 char *fname;
121 char *eptr;
122
123 fname = NULL;
124
125 while (--ac) {
126 char *v, *p = *++av;
127 if (*p == '-') {
128 while (*p) {
129 char c = *++p;
130 switch (c) {
131 case '-':
132 if (!strcmp(p, "version")) {
133 version();
134 exit(0);
135 }
136 if (!strcmp(p, "help")) {
137 usage();
138 exit(0);
139 }
140 break;
141 case 'v':
142 case 'V':
143 version();
144 exit(0);
145 break;
146 case 'h':
147 case 'H':
148 usage();
149 exit(0);
150 break;
151 case 'd':
152 encoding = 0;
153 break;
154 case 'e':
155 encoding = 1;
156 break;
157 case 'c':
158 /*
159 * Options requiring values.
160 */
161 v = p+1;
162 if (!*v && ac > 1) {
163 --ac;
164 v = *++av;
165 }
166 if (!*v) {
167 fprintf(stderr, "base64: option '-%c' expects"
168 " an argument\n", c);
169 exit(1);
170 }
171 switch (c) {
172 case 'c':
173 cpl = strtol(v, &eptr, 10);
174 if (eptr && *eptr) {
175 fprintf(stderr, "base64: option -c expects"
176 " a numeric argument\n");
177 exit(1);
178 }
179 if (cpl % 4) {
180 fprintf(stderr, "base64: chars per line should be"
181 " divisible by 4\n");
182 exit(1);
183 }
184 break;
185 }
186 p = "";
187 break;
188 }
189 }
190 } else {
191 if (!fname)
192 fname = p;
193 else {
194 fprintf(stderr, "base64: expected only one filename\n");
195 exit(0);
196 }
197 }
198 }
199
200 if (fname) {
201 fp = fopen(fname, encoding ? "rb" : "r");
202 if (!fp) {
203 fprintf(stderr, "base64: unable to open '%s': %s\n", fname,
204 strerror(errno));
205 exit(1);
206 }
207 } else
208 fp = stdin;
209
210 if (encoding) {
211 unsigned char in[3];
212 char out[4];
213 int column;
214 int n;
215
216 column = 0;
217 while (1) {
218 if (cpl && column >= cpl) {
219 putchar('\n');
220 column = 0;
221 }
222 n = fread(in, 1, 3, fp);
223 if (n == 0) break;
224 base64_encode_atom(in, n, out);
225 fwrite(out, 1, 4, stdout);
226 column += 4;
227 }
228
229 putchar('\n');
230 } else {
231 char in[4];
232 unsigned char out[3];
233 int c, i, n, eof;
234
235 eof = 0;
236 do {
237 for (i = 0; i < 4; i++) {
238 do {
239 c = fgetc(fp);
240 } while (c != EOF && !isbase64(c));
241 if (c == EOF) {
242 eof = 1;
243 break;
244 }
245 in[i] = c;
246 }
247 if (i > 0) {
248 if (i < 4) {
249 fprintf(stderr, "base64: warning: number of base64"
250 " characters was not a multiple of 4\n");
251 while (i < 4) in[i++] = '=';
252 }
253 n = base64_decode_atom(in, out);
254 fwrite(out, 1, n, stdout);
255 }
256 } while (!eof);
257 }
258
259 if (fname)
260 fclose(fp);
261
262 return 0;
263}