added return values to indicate failure
[base1.git] / main.c
1 /* base1 en- and decoder with usage similar to GNU's base64.
2 (C) 2016 Tobias Girstmair, http://isticktoit.net/
3 Released under the GNU GPL v3. See LICENSE for details. */
4
5 #define _XOPEN_SOURCE
6 #include <stdio.h>
7 #include <stdlib.h>
8 #include <string.h>
9 #include <unistd.h>
10 #include <limits.h>
11 #include <stdint.h>
12
13 #define VERSION "0.1"
14
15 #define DEFAULT_WRAP 72
16
17 #define COPYRIGHT "(C) 2016 Tobias Girstmair, GPLv3"
18 #define HELP_TEXT \
19 "Usage: %s [OPTION]... [FILE]\n"\
20 "Options: -d (decode), -i (ignore-garbage), -w COLS (wrap, default %d)"\
21 " -t (test) -v (version) -h (help)\n"
22
23 #define BASE ((1<<CHAR_BIT)) //2 ^ (bits of a byte)
24
25 struct ops {
26 FILE* f;
27 int d; /*decode*/
28 int i; /*ignore-garbage*/
29 int w; /*wrap*/
30 int t; /*test*/
31 };
32
33 size_t highestOneBitPosition(unsigned long long a);
34 int multiplication_is_safe(unsigned long long a, unsigned long long b);
35 int addition_is_safe(unsigned long long a, unsigned long long b);
36
37 int do_encode (FILE* in, FILE* out, int wrap_column, int test_only);
38 int do_decode (FILE* in, FILE* out, int ignore_garbage);
39
40
41 int main (int argc, char** argv) {
42 struct ops op = {stdin, 0, 0, DEFAULT_WRAP, 0};
43 int opt;
44
45 opterr=0; /* suppress default error messages */
46 while ( (opt = getopt(argc, argv, "diw:tvh")) != -1) {
47 switch (opt) {
48 case 'd': op.d = 1; break;
49 case 'i': op.i = 1; break;
50 case 'w': op.w = atoi (optarg); break;
51 case 't': op.t = 1; break;
52 case 'v':
53 fprintf (stderr, "base1 %s\n%s\n", VERSION, COPYRIGHT);
54 return 0;
55 case 'h':
56 fprintf (stderr, HELP_TEXT, argv[0], DEFAULT_WRAP);
57 return 0;
58 default:
59 fprintf (stderr, "unknown option '-%c'.\n", optopt);
60 return 1;
61 }
62 }
63 if (argc-optind > 1) {
64 fprintf (stderr, "%s: extra operand '%s'. \n", argv[0], argv[argc-1]);
65 return 1;
66 } else if (optind < argc) {
67 if (strcmp (argv [optind], "-") != 0)
68 op.f = fopen (argv[optind], "rb");
69 }
70
71 int retval;
72 if (op.d) {
73 retval = do_decode (op.f, stdout, op.i);
74 } else {
75 retval = do_encode (op.f, stdout, op.w, op.t);
76 }
77
78 if (op.f != stdin) retval = !(fclose (op.f) == 0);
79
80 return retval;
81 }
82
83
84
85
86 int do_encode (FILE* in, FILE* out, int wrap_column, int test_only) {
87 int in_len = 0;
88 unsigned long long out_len = 0;
89
90 for (int in_char; (in_char = getc(in)) != EOF; in_len++) {
91 if (!multiplication_is_safe (out_len, BASE)){
92 fputs ("overflowed.", stderr);
93 return 1;
94 }
95 out_len *= BASE;
96 if (!addition_is_safe (out_len, in_char)) {
97 fputs ("overflowed.", stderr);
98 return 1;
99 }
100 out_len += in_char;
101 }
102
103 if (test_only) {
104 printf ("Length of output:\t%llu\nMaximum value of ULL:\t%llu\n", out_len, ULLONG_MAX);
105 return 0;
106 }
107
108 unsigned long long block_size = 1;
109
110 for (int i = 0; i < in_len; i++) {
111 out_len += block_size;
112 block_size *= BASE;
113 }
114
115 for (unsigned long long i = 0; i < out_len; i++)
116 fprintf (out, "%s%c", wrap_column&&!(i%wrap_column)&&i?"\n":"", 'A');
117 //TODO: use faster function
118
119 return 0;
120 }
121
122 int do_decode (FILE* in, FILE* out, int ignore_garbage) {
123 unsigned long long in_len = 0;
124
125 for (int in_char; (in_char = getc (in)) != EOF;) {
126 if (in_char == ' ' || in_char == '\n' || in_char == '\t') {
127 continue;
128 } else if (in_char == 'A') {
129 in_len++;
130 } else if (!ignore_garbage) {
131 fprintf (stderr, "Unrecognized glyph %c\n", in_char);
132 return 1;
133 }
134 // TODO: check for overflow!
135 }
136
137 int bin_len = 0;
138 unsigned long long block_size = 1;
139 while (in_len >= block_size) {
140 in_len -= block_size;
141 bin_len++;
142 block_size *= BASE;
143 }
144
145 char b;
146 char out_buf[bin_len];
147 for (int byte_num = bin_len-1; byte_num >= 0; byte_num--) {
148 b = in_len & (BASE - 1);
149 out_buf[byte_num] = b;
150 in_len -= b;
151 in_len /= BASE;
152 }
153
154 for (int i = 0; i < bin_len; i++) putc (out_buf[i], out);
155
156 return 0;
157 }
158
159 /* http://stackoverflow.com/a/199455 */
160 int addition_is_safe(unsigned long long a, unsigned long long b) {
161 size_t a_bits=highestOneBitPosition(a), b_bits=highestOneBitPosition(b);
162 return (a_bits<64 && b_bits<64);
163 }
164
165 int multiplication_is_safe(unsigned long long a, unsigned long long b) {
166 size_t a_bits=highestOneBitPosition(a), b_bits=highestOneBitPosition(b);
167 return (a_bits+b_bits<=64);
168 }
169 size_t highestOneBitPosition(unsigned long long a) {
170 size_t bits=0;
171 while (a!=0) {
172 ++bits;
173 a>>=1;
174 };
175 return bits;
176 }
Imprint / Impressum