b93c820d |
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 | } |