"Fossies" - the Fresh Open Source Software Archive 
As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) C and C++ source code syntax highlighting (style:
standard) with prefixed line numbers and
code folding option.
Alternatively you can here
view or
download the uninterpreted source code file.
For more information about "b64.c" see the
Fossies "Dox" file reference documentation.
1 /*********************************************************************\
2
3 MODULE NAME: b64.c
4
5 AUTHOR: Bob Trower 08/04/01
6
7 PROJECT: Crypt Data Packaging
8
9 COPYRIGHT: Copyright (c) Trantor Standard Systems Inc., 2001
10
11 NOTE: This source code may be used as you wish, subject to
12 the MIT license. See the LICENCE section below.
13
14 DESCRIPTION:
15 This little utility implements the Base64
16 Content-Transfer-Encoding standard described in
17 RFC1113 (http://www.faqs.org/rfcs/rfc1113.html).
18
19 This is the coding scheme used by MIME to allow
20 binary data to be transferred by SMTP mail.
21
22 Groups of 3 bytes from a binary stream are coded as
23 groups of 4 bytes in a text stream.
24
25 The input stream is 'padded' with zeros to create
26 an input that is an even multiple of 3.
27
28 A special character ('=') is used to denote padding so
29 that the stream can be decoded back to its exact size.
30
31 Encoded output is formatted in lines which should
32 be a maximum of 72 characters to conform to the
33 specification. This program defaults to 72 characters,
34 but will allow more or less through the use of a
35 switch. The program enforces a minimum line size
36 of 4 characters.
37
38 Example encoding:
39
40 The stream 'ABCD' is 32 bits long. It is mapped as
41 follows:
42
43 ABCD
44
45 A (65) B (66) C (67) D (68) (None) (None)
46 01000001 01000010 01000011 01000100
47
48 16 (Q) 20 (U) 9 (J) 3 (D) 17 (R) 0 (A) NA (=) NA (=)
49 010000 010100 001001 000011 010001 000000 000000 000000
50
51
52 QUJDRA==
53
54 Decoding is the process in reverse. A 'decode' lookup
55 table has been created to avoid string scans.
56
57 DESIGN GOALS: Specifically:
58 Code is a stand-alone utility to perform base64
59 encoding/decoding. It should be genuinely useful
60 when the need arises and it meets a need that is
61 likely to occur for some users.
62 Code acts as sample code to show the author's
63 design and coding style.
64
65 Generally:
66 This program is designed to survive:
67 Everything you need is in a single source file.
68 It compiles cleanly using a vanilla ANSI C compiler.
69 It does its job correctly with a minimum of fuss.
70 The code is not overly clever, not overly simplistic
71 and not overly verbose.
72 Access is 'cut and paste' from a web page.
73 Terms of use are reasonable.
74
75 VALIDATION: Non-trivial code is never without errors. This
76 file likely has some problems, since it has only
77 been tested by the author. It is expected with most
78 source code that there is a period of 'burn-in' when
79 problems are identified and corrected. That being
80 said, it is possible to have 'reasonably correct'
81 code by following a regime of unit test that covers
82 the most likely cases and regression testing prior
83 to release. This has been done with this code and
84 it has a good probability of performing as expected.
85
86 Unit Test Cases:
87
88 case 0:empty file:
89 CASE0.DAT -> ->
90 (Zero length target file created
91 on both encode and decode.)
92
93 case 1:One input character:
94 CASE1.DAT A -> QQ== -> A
95
96 case 2:Two input characters:
97 CASE2.DAT AB -> QUJD -> AB
98
99 case 3:Three input characters:
100 CASE3.DAT ABC -> QUJD -> ABC
101
102 case 4:Four input characters:
103 case4.dat ABCD -> QUJDRA== -> ABCD
104
105 case 5:All chars from 0 to ff, linesize set to 50:
106
107 AAECAwQFBgcICQoLDA0ODxAREhMUFRYXGBkaGxwdHh8gISIj
108 JCUmJygpKissLS4vMDEyMzQ1Njc4OTo7PD0+P0BBQkNERUZH
109 SElKS0xNTk9QUVJTVFVWV1hZWltcXV5fYGFiY2RlZmdoaWpr
110 bG1ub3BxcnN0dXZ3eHl6e3x9fn+AgYKDhIWGh4iJiouMjY6P
111 kJGSk5SVlpeYmZqbnJ2en6ChoqOkpaanqKmqq6ytrq+wsbKz
112 tLW2t7i5uru8vb6/wMHCw8TFxsfIycrLzM3Oz9DR0tPU1dbX
113 2Nna29zd3t/g4eLj5OXm5+jp6uvs7e7v8PHy8/T19vf4+fr7
114 /P3+/w==
115
116 case 6:Mime Block from e-mail:
117 (Data same as test case 5)
118
119 case 7: Large files:
120 Tested 28 MB file in/out.
121
122 case 8: Random Binary Integrity:
123 This binary program (b64.exe) was encoded to base64,
124 back to binary and then executed.
125
126 case 9 Stress:
127 All files in a working directory encoded/decoded
128 and compared with file comparison utility to
129 ensure that multiple runs do not cause problems
130 such as exhausting file handles, tmp storage, etc.
131
132 -------------
133
134 Syntax, operation and failure:
135 All options/switches tested. Performs as
136 expected.
137
138 case 10:
139 No Args -- Shows Usage Screen
140 Return Code 1 (Invalid Syntax)
141 case 11:
142 One Arg (invalid) -- Shows Usage Screen
143 Return Code 1 (Invalid Syntax)
144 case 12:
145 One Arg Help (-?) -- Shows detailed Usage Screen.
146 Return Code 0 (Success -- help request is valid).
147 case 13:
148 One Arg Help (-h) -- Shows detailed Usage Screen.
149 Return Code 0 (Success -- help request is valid).
150 case 14:
151 One Arg (valid) -- Uses stdin/stdout (filter)
152 Return Code 0 (Sucess)
153 case 15:
154 Two Args (invalid file) -- shows system error.
155 Return Code 2 (File Error)
156 case 16:
157 Encode non-existent file -- shows system error.
158 Return Code 2 (File Error)
159 case 17:
160 Out of disk space -- shows system error.
161 Return Code 3 (File I/O Error)
162
163 -------------
164
165 Compile/Regression test:
166 gcc compiled binary under Cygwin
167 Microsoft Visual Studio under Windows 2000
168 Microsoft Version 6.0 C under Windows 2000
169
170 DEPENDENCIES: None
171
172 LICENCE: Copyright (c) 2001 Bob Trower, Trantor Standard Systems Inc.
173
174 Permission is hereby granted, free of charge, to any person
175 obtaining a copy of this software and associated
176 documentation files (the "Software"), to deal in the
177 Software without restriction, including without limitation
178 the rights to use, copy, modify, merge, publish, distribute,
179 sublicense, and/or sell copies of the Software, and to
180 permit persons to whom the Software is furnished to do so,
181 subject to the following conditions:
182
183 The above copyright notice and this permission notice shall
184 be included in all copies or substantial portions of the
185 Software.
186
187 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
188 KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
189 WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
190 PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS
191 OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
192 OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
193 OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
194 SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
195
196 VERSION HISTORY:
197 Bob Trower 08/04/01 -- Create Version 0.00.00B
198
199 \******************************************************************* */
200
201 #include <stdio.h>
202 #include <stdlib.h>
203
204 /*
205 ** Translation Table as described in RFC1113
206 */
207 static const char cb64[]="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
208
209 /*
210 ** Translation Table to decode (created by author)
211 */
212 static const char cd64[]="|$$$}rstuvwxyz{$$$$$$$>?@ABCDEFGHIJKLMNOPQRSTUVW$$$$$$XYZ[\\]^_`abcdefghijklmnopq";
213
214 /*
215 ** encodeblock
216 **
217 ** encode 3 8-bit binary bytes as 4 '6-bit' characters
218 */
219 void encodeblock( unsigned char in[3], unsigned char out[4], int len )
220 {
221 out[0] = cb64[ in[0] >> 2 ];
222 out[1] = cb64[ ((in[0] & 0x03) << 4) | ((in[1] & 0xf0) >> 4) ];
223 out[2] = (unsigned char) (len > 1 ? cb64[ ((in[1] & 0x0f) << 2) | ((in[2] & 0xc0) >> 6) ] : '=');
224 out[3] = (unsigned char) (len > 2 ? cb64[ in[2] & 0x3f ] : '=');
225 }
226
227 /*
228 ** encode
229 **
230 ** base64 encode a stream adding padding and line breaks as per spec.
231 */
232 void encode( FILE *infile, FILE *outfile, int linesize )
233 {
234 unsigned char in[3], out[4];
235 int i, len, blocksout = 0;
236
237 while( !feof( infile ) ) {
238 len = 0;
239 for( i = 0; i < 3; i++ ) {
240 in[i] = (unsigned char) getc( infile );
241 if( !feof( infile ) ) {
242 len++;
243 }
244 else {
245 in[i] = 0;
246 }
247 }
248 if( len ) {
249 encodeblock( in, out, len );
250 for( i = 0; i < 4; i++ ) {
251 putc( out[i], outfile );
252 }
253 blocksout++;
254 }
255 if( blocksout >= (linesize/4) || feof( infile ) ) {
256 if( blocksout ) {
257 fprintf( outfile, "\r\n" );
258 }
259 blocksout = 0;
260 }
261 }
262 }
263
264 /*
265 ** decodeblock
266 **
267 ** decode 4 '6-bit' characters into 3 8-bit binary bytes
268 */
269 void decodeblock( unsigned char in[4], unsigned char out[3] )
270 {
271 out[ 0 ] = (unsigned char ) (in[0] << 2 | in[1] >> 4);
272 out[ 1 ] = (unsigned char ) (in[1] << 4 | in[2] >> 2);
273 out[ 2 ] = (unsigned char ) (((in[2] << 6) & 0xc0) | in[3]);
274 }
275
276 /*
277 ** decode
278 **
279 ** decode a base64 encoded stream discarding padding, line breaks and noise
280 */
281 void decode( FILE *infile, FILE *outfile )
282 {
283 unsigned char in[4], out[3], v;
284 int i, len;
285
286 while( !feof( infile ) ) {
287 for( len = 0, i = 0; i < 4 && !feof( infile ); i++ ) {
288 v = 0;
289 while( !feof( infile ) && v == 0 ) {
290 v = (unsigned char) getc( infile );
291 v = (unsigned char) ((v < 43 || v > 122) ? 0 : cd64[ v - 43 ]);
292 if( v ) {
293 v = (unsigned char) ((v == '$') ? 0 : v - 61);
294 }
295 }
296 if( !feof( infile ) ) {
297 len++;
298 if( v ) {
299 in[ i ] = (unsigned char) (v - 1);
300 }
301 }
302 else {
303 in[i] = 0;
304 }
305 }
306 if( len ) {
307 decodeblock( in, out );
308 for( i = 0; i < len - 1; i++ ) {
309 putc( out[i], outfile );
310 }
311 }
312 }
313 }
314
315 /*
316 ** returnable errors
317 **
318 ** Error codes returned to the operating system.
319 **
320 */
321 #define B64_SYNTAX_ERROR 1
322 #define B64_FILE_ERROR 2
323 #define B64_FILE_IO_ERROR 3
324 #define B64_ERROR_OUT_CLOSE 4
325 #define B64_LINE_SIZE_TO_MIN 5
326
327 /*
328 ** b64_message
329 **
330 ** Gather text messages in one place.
331 **
332 */
333 char *b64_message( int errcode )
334 {
335 #define B64_MAX_MESSAGES 6
336 char *msgs[ B64_MAX_MESSAGES ] = {
337 "b64:000:Invalid Message Code.",
338 "b64:001:Syntax Error -- check help for usage.",
339 "b64:002:File Error Opening/Creating Files.",
340 "b64:003:File I/O Error -- Note: output file not removed.",
341 "b64:004:Error on output file close.",
342 "b64:004:linesize set to minimum."
343 };
344 char *msg = msgs[ 0 ];
345
346 if( errcode > 0 && errcode < B64_MAX_MESSAGES ) {
347 msg = msgs[ errcode ];
348 }
349
350 return( msg );
351 }
352
353 /*
354 ** b64
355 **
356 ** 'engine' that opens streams and calls encode/decode
357 */
358
359 int b64( int opt, char *infilename, char *outfilename, int linesize )
360 {
361 FILE *infile;
362 int retcode = B64_FILE_ERROR;
363
364 if( !infilename ) {
365 infile = stdin;
366 }
367 else {
368 infile = fopen( infilename, "rb" );
369 }
370 if( !infile ) {
371 perror( infilename );
372 }
373 else {
374 FILE *outfile;
375 if( !outfilename ) {
376 outfile = stdout;
377 }
378 else {
379 outfile = fopen( outfilename, "wb" );
380 }
381 if( !outfile ) {
382 perror( outfilename );
383 }
384 else {
385 if( opt == 'e' ) {
386 encode( infile, outfile, linesize );
387 }
388 else {
389 decode( infile, outfile );
390 }
391 if (ferror( infile ) || ferror( outfile )) {
392 retcode = B64_FILE_IO_ERROR;
393 }
394 else {
395 retcode = 0;
396 }
397 if( outfile != stdout ) {
398 if( fclose( outfile ) != 0 ) {
399 perror( b64_message( B64_ERROR_OUT_CLOSE ) );
400 retcode = B64_FILE_IO_ERROR;
401 }
402 }
403 }
404 if( infile != stdin ) {
405 fclose( infile );
406 }
407 }
408
409 return( retcode );
410 }
411
412 /*
413 ** showuse
414 **
415 ** display usage information, help, version info
416 */
417 void showuse( int morehelp )
418 {
419 {
420 printf( "\n" );
421 printf( " b64 (Base64 Encode/Decode) Bob Trower 08/03/01 \n" );
422 printf( " (C) Copr Bob Trower 1986-01. Version 0.00B \n" );
423 printf( " Usage: b64 -option [ -l num ] [<FileIn> [<FileOut>]] \n" );
424 printf( " Purpose: This program is a simple utility that implements\n" );
425 printf( " Base64 Content-Transfer-Encoding (RFC1113). \n" );
426 }
427 if( !morehelp ) {
428 printf( " Use -h option for additional help. \n" );
429 }
430 else {
431 printf( " Options: -e encode to Base64 -h This help text. \n" );
432 printf( " -d decode from Base64 -? This help text. \n" );
433 printf( " Note: -l use to change line size (from 72 characters)\n" );
434 printf( " Returns: 0 = success. Non-zero is an error code. \n" );
435 printf( " ErrCode: 1 = Bad Syntax, 2 = File Open, 3 = File I/O \n" );
436 printf( " Example: b64 -e binfile b64file <- Encode to b64 \n" );
437 printf( " b64 -d b64file binfile <- Decode from b64 \n" );
438 printf( " b64 -e -l40 infile outfile <- Line Length of 40 \n" );
439 printf( " Note: Will act as a filter, but this should only be \n" );
440 printf( " used on text files due to translations made by \n" );
441 printf( " operating systems. \n" );
442 printf( " Release: 0.00.00, Tue Aug 7 2:00:00 2001, ANSI-SOURCE C\n" );
443 }
444 }
445
446 #define B64_DEF_LINE_SIZE 72
447 #define B64_MIN_LINE_SIZE 4
448
449 #define THIS_OPT(ac, av) (ac > 1 ? av[1][0] == '-' ? av[1][1] : 0 : 0)
450
451 /*
452 ** main
453 **
454 ** parse and validate arguments and call b64 engine or help
455 */
456 int main( int argc, char **argv )
457 {
458 int opt = 0;
459 int retcode = 0;
460 int linesize = B64_DEF_LINE_SIZE;
461 char *infilename = NULL, *outfilename = NULL;
462
463 while( THIS_OPT( argc, argv ) ) {
464 switch( THIS_OPT(argc, argv) ) {
465 case 'l':
466 linesize = atoi( &(argv[1][2]) );
467 if( linesize < B64_MIN_LINE_SIZE ) {
468 linesize = B64_MIN_LINE_SIZE;
469 printf( "%s\n", b64_message( B64_LINE_SIZE_TO_MIN ) );
470 }
471 break;
472 case '?':
473 case 'h':
474 opt = 'h';
475 break;
476 case 'e':
477 case 'd':
478 opt = THIS_OPT(argc, argv);
479 break;
480 default:
481 opt = 0;
482 break;
483 }
484 argv++;
485 argc--;
486 }
487 switch( opt ) {
488 case 'e':
489 case 'd':
490 infilename = argc > 1 ? argv[1] : NULL;
491 outfilename = argc > 2 ? argv[2] : NULL;
492 retcode = b64( opt, infilename, outfilename, linesize );
493 break;
494 case 0:
495 retcode = B64_SYNTAX_ERROR;
496 case 'h':
497 showuse( opt );
498 break;
499
500 }
501 if( retcode ) {
502 printf( "%s\n", b64_message( retcode ) );
503 }
504
505 return( retcode );
506 }