]>
Commit | Line | Data |
---|---|---|
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 | } |