]>
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 | void do_encode (FILE* in, FILE* out, int wrap_column, int test_only); | |
38 | void 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 | if (op.d) { | |
72 | do_decode (op.f, stdout, op.i); | |
73 | } else { | |
74 | do_encode (op.f, stdout, op.w, op.t); | |
75 | } | |
76 | ||
77 | if (op.f != stdin) fclose (op.f); | |
78 | ||
79 | return 0; | |
80 | } | |
81 | ||
82 | ||
83 | ||
84 | ||
85 | void do_encode (FILE* in, FILE* out, int wrap_column, int test_only) { | |
86 | int in_len = 0; | |
87 | unsigned long long out_len = 0; | |
88 | ||
89 | for (int in_char; (in_char = getc(in)) != EOF; in_len++) { | |
90 | if (!multiplication_is_safe (out_len, BASE)){ | |
91 | puts ("overflowed."); | |
92 | return; | |
93 | } | |
94 | out_len *= BASE; | |
95 | if (!addition_is_safe (out_len, in_char)) { | |
96 | puts ("overflowed."); | |
97 | return; | |
98 | } | |
99 | out_len += in_char; | |
100 | } | |
101 | ||
102 | if (test_only) { | |
103 | printf ("Length of output:\t%llu\nMaximum value of ULL:\t%llu\n", out_len, ULLONG_MAX); | |
104 | return; | |
105 | } | |
106 | ||
107 | unsigned long long block_size = 1; | |
108 | ||
109 | for (int i = 0; i < in_len; i++) { | |
110 | out_len += block_size; | |
111 | block_size *= BASE; | |
112 | } | |
113 | ||
114 | for (unsigned long long i = 0; i < out_len; i++) | |
115 | fprintf (out, "%s%c", wrap_column&&!(i%wrap_column)&&i?"\n":"", 'A'); | |
116 | //TODO: use faster function | |
117 | } | |
118 | ||
119 | void do_decode (FILE* in, FILE* out, int ignore_garbage) { | |
120 | unsigned long long in_len = 0; | |
121 | ||
122 | for (int in_char; (in_char = getc (in)) != EOF;) { | |
123 | if (in_char == ' ' || in_char == '\n' || in_char == '\t') { | |
124 | continue; | |
125 | } else if (in_char == 'A') { | |
126 | in_len++; | |
127 | } else if (!ignore_garbage) { | |
128 | fprintf (stderr, "Unrecognized glyph %c\n", in_char); | |
129 | return; | |
130 | } | |
131 | // TODO: check for overflow! | |
132 | } | |
133 | ||
134 | int bin_len = 0; | |
135 | unsigned long long block_size = 1; | |
136 | while (in_len >= block_size) { | |
137 | in_len -= block_size; | |
138 | bin_len++; | |
139 | block_size *= BASE; | |
140 | } | |
141 | ||
142 | char b; | |
143 | char out_buf[bin_len]; | |
144 | for (int byte_num = bin_len-1; byte_num >= 0; byte_num--) { | |
145 | b = in_len & (BASE - 1); | |
146 | out_buf[byte_num] = b; | |
147 | in_len -= b; | |
148 | in_len /= BASE; | |
149 | } | |
150 | ||
151 | for (int i = 0; i < bin_len; i++) putc (out_buf[i], out); | |
152 | } | |
153 | ||
154 | /* http://stackoverflow.com/a/199455 */ | |
155 | int addition_is_safe(unsigned long long a, unsigned long long b) { | |
156 | size_t a_bits=highestOneBitPosition(a), b_bits=highestOneBitPosition(b); | |
157 | return (a_bits<64 && b_bits<64); | |
158 | } | |
159 | ||
160 | int multiplication_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+b_bits<=64); | |
163 | } | |
164 | size_t highestOneBitPosition(unsigned long long a) { | |
165 | size_t bits=0; | |
166 | while (a!=0) { | |
167 | ++bits; | |
168 | a>>=1; | |
169 | }; | |
170 | return bits; | |
171 | } |