First version
[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 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 }
Imprint / Impressum