added return values to indicate failure
[base1.git] / main.c
CommitLineData
b93c820d 1/* base1 en- and decoder with usage similar to GNU's base64.
2(C) 2016 Tobias Girstmair, http://isticktoit.net/
3Released 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
25struct ops {
26 FILE* f;
27 int d; /*decode*/
28 int i; /*ignore-garbage*/
29 int w; /*wrap*/
30 int t; /*test*/
31};
32
33size_t highestOneBitPosition(unsigned long long a);
34int multiplication_is_safe(unsigned long long a, unsigned long long b);
35int addition_is_safe(unsigned long long a, unsigned long long b);
36
7f5bcb97 37int do_encode (FILE* in, FILE* out, int wrap_column, int test_only);
38int do_decode (FILE* in, FILE* out, int ignore_garbage);
b93c820d 39
40
41int 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
7f5bcb97 71 int retval;
b93c820d 72 if (op.d) {
7f5bcb97 73 retval = do_decode (op.f, stdout, op.i);
b93c820d 74 } else {
7f5bcb97 75 retval = do_encode (op.f, stdout, op.w, op.t);
b93c820d 76 }
77
7f5bcb97 78 if (op.f != stdin) retval = !(fclose (op.f) == 0);
b93c820d 79
7f5bcb97 80 return retval;
b93c820d 81}
82
83
84
85
7f5bcb97 86int do_encode (FILE* in, FILE* out, int wrap_column, int test_only) {
b93c820d 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)){
7f5bcb97 92 fputs ("overflowed.", stderr);
93 return 1;
b93c820d 94 }
95 out_len *= BASE;
96 if (!addition_is_safe (out_len, in_char)) {
7f5bcb97 97 fputs ("overflowed.", stderr);
98 return 1;
b93c820d 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);
7f5bcb97 105 return 0;
b93c820d 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
7f5bcb97 118
119 return 0;
b93c820d 120}
121
7f5bcb97 122int do_decode (FILE* in, FILE* out, int ignore_garbage) {
b93c820d 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);
7f5bcb97 132 return 1;
b93c820d 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);
7f5bcb97 155
156 return 0;
b93c820d 157}
158
159/* http://stackoverflow.com/a/199455 */
160int 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
165int 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}
169size_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