Silent a signed-comparison warning in the _isds_b64decode()
[libisds.git] / src / utils.c
blob85d463082c38125f81e143c0d14cbf17789caea9
1 #include "isds_priv.h"
2 #include <string.h>
3 #include <stdlib.h>
4 #include <stdio.h>
5 #include <iconv.h>
6 #ifndef _WIN32
7 #include <langinfo.h>
8 #endif
9 #include <time.h>
10 #include <errno.h>
11 #include "utils.h"
12 #include "cencode.h"
13 #include "cdecode.h"
14 #include "system.h"
16 /* Concatenate two strings into newly allocated buffer.
17 * You must free() them, when you don't need it anymore.
18 * Any of the arguments can be NULL meaning empty string.
19 * In case of error returns NULL.
20 * Empty string is always returned as allocated empty string. */
21 _hidden char *_isds_astrcat(const char *first, const char *second) {
22 size_t first_len, second_len;
23 char *buf;
25 first_len = (first) ? strlen(first) : 0;
26 second_len = (second) ? strlen(second) : 0;
27 buf = malloc(1 + first_len + second_len);
28 if (buf) {
29 buf[0] = '\0';
30 if (first) strcpy(buf, first);
31 if (second) strcpy(buf + first_len, second);
33 return buf;
37 /* Concatenate three strings into newly allocated buffer.
38 * You must free() them, when you don't need it anymore.
39 * Any of the arguments can be NULL meaning empty string.
40 * In case of error returns NULL.
41 * Empty string is always returned as allocated empty string. */
42 _hidden char *_isds_astrcat3(const char *first, const char *second,
43 const char *third) {
44 size_t first_len, second_len, third_len;
45 char *buf, *next;
47 first_len = (first) ? strlen(first) : 0;
48 second_len = (second) ? strlen(second) : 0;
49 third_len = (third) ? strlen(third) : 0;
50 buf = malloc(1 + first_len + second_len + third_len);
51 if (buf) {
52 buf[0] = '\0';
53 next = buf;
54 if (first) {
55 strcpy(next, first);
56 next += first_len;
58 if (second) {
59 strcpy(next, second);
60 next += second_len;
62 if (third) {
63 strcpy(next, third);
66 return buf;
70 /* Print formatted string into automatically reallocated @buffer.
71 * @buffer automatically reallocated buffer. Must be &NULL or preallocated
72 * memory.
73 * @format format string as for printf(3)
74 * @ap list of variadic arguments, after call will be in undefined state
75 * @Returns number of bytes printed. In case of error, -1 and NULL @buffer*/
76 _hidden int isds_vasprintf(char **buffer, const char *format, va_list ap) {
77 va_list aq;
78 int length, new_length;
79 char *new_buffer;
81 if (!buffer || !format) {
82 if (buffer) {
83 free(*buffer);
84 *buffer = NULL;
86 return -1;
89 va_copy(aq, ap);
90 length = vsnprintf(NULL, 0, format, aq) + 1;
91 va_end(aq);
92 if (length <= 0) {
93 free(*buffer);
94 *buffer = NULL;
95 return -1;
98 new_buffer = realloc(*buffer, length);
99 if (!new_buffer) {
100 free(*buffer);
101 *buffer = NULL;
102 return -1;
104 *buffer = new_buffer;
106 new_length = vsnprintf(*buffer, length, format, ap);
107 if (new_length >= length) {
108 free(*buffer);
109 *buffer = NULL;
110 return -1;
113 return new_length;
117 /* Print formatted string into automatically reallocated @buffer.
118 * @buffer automatically reallocated buffer. Must be &NULL or preallocated
119 * memory.
120 * @format format string as for printf(3)
121 * @... variadic arguments
122 * @Returns number of bytes printed. In case of error, -1 and NULL @buffer */
123 _hidden int isds_asprintf(char **buffer, const char *format, ...) {
124 int ret;
125 va_list ap;
126 va_start(ap, format);
127 ret = isds_vasprintf(buffer, format, ap);
128 va_end(ap);
129 return ret;
132 /* Converts a block from charset to charset.
133 * @from is input charset of @input block as known to iconv
134 * @to is output charset @input will be converted to @output
135 * @input is block in @from charset/encoding of length @input_length
136 * @input_length is size of @input block in bytes
137 * @output is automatically allocated block of data converted from @input. No
138 * NUL is apended. Can be NULL, if resulting size is 0. You must free it.
139 * @return size of @output in bytes. In case of error returns (size_t) -1 and
140 * deallocates @output if this function allocated it in this call. */
141 _hidden size_t _isds_any2any(const char *from, const char *to,
142 const void *input, size_t input_length, void **output) {
143 iconv_t state;
144 char *buffer = NULL, *new_buffer;
145 size_t buffer_length = 0, buffer_used = 0;
146 char *inbuf, *outbuf;
147 size_t inleft, outleft;
149 if (output != NULL) *output = NULL;
150 if (from == NULL || to == NULL || input == NULL || output == NULL)
151 return (size_t) -1;
153 state = iconv_open(to, from);
154 if (state == (iconv_t) -1) return (size_t) -1;
156 /* Get the initial output buffer length */
157 buffer_length = input_length;
159 inbuf = (char *) input;
160 inleft = input_length;
162 while (inleft > 0) {
163 /* Extend buffer */
164 new_buffer = realloc(buffer, buffer_length);
165 if (!new_buffer) {
166 zfree(buffer);
167 buffer_used = (size_t) -1;
168 goto leave;
170 buffer = new_buffer;
172 /* FIXME */
173 outbuf = buffer + buffer_used;
174 outleft = buffer_length - buffer_used;
176 /* Convert chunk of data */
177 if ((size_t) -1 == iconv(state, &inbuf, &inleft, &outbuf, &outleft) &&
178 errno != E2BIG) {
179 zfree(buffer);
180 buffer_used = (size_t) -1;
181 goto leave;
184 /* Update positions */
185 buffer_length += 1024;
186 buffer_used = outbuf - buffer;
189 leave:
190 iconv_close(state);
191 if (buffer_used == 0) zfree(buffer);
192 *output = buffer;
193 return buffer_used;
196 /* Converts UTF8 string into locale encoded string.
197 * @utf string int UTF-8 terminated by zero byte
198 * @return allocated string encoded in locale specific encoding. You must free
199 * it. In case of error or NULL @utf returns NULL. */
200 _hidden char *_isds_utf82locale(const char *utf) {
201 char *output, *bigger_output;
202 size_t length;
204 if (utf == NULL) return NULL;
206 length = _isds_any2any("UTF-8", nl_langinfo(CODESET), utf, strlen(utf),
207 (void **) &output);
208 if (length == (size_t) -1) return NULL;
210 bigger_output = realloc(output, length + 1);
211 if (bigger_output == NULL) {
212 zfree(output);
213 } else {
214 output = bigger_output;
215 output[length] = '\0';
218 return output;
222 /* Encode given data into MIME Base64 encoded zero terminated string.
223 * @plain are input data (binary stream)
224 * @length is length of @plain data in bytes
225 * @return allocated string of base64 encoded plain data or NULL in case of
226 * error. You must free it. */
227 _hidden char *_isds_b64encode(const void *plain, const size_t length) {
229 base64_encodestate state;
230 size_t code_length;
231 char *buffer, *new_buffer;
233 if (!plain) {
234 if (length) return NULL;
235 /* Empty input is valid input */
236 plain = "";
239 _isds_base64_init_encodestate(&state);
241 /* Allocate buffer
242 * (4 is padding, 1 is final new line, and 1 is string terminator) */
243 buffer = malloc(length * 2 + 4 + 1 + 1);
244 if (!buffer) return NULL;
246 /* Encode plain data */
247 code_length = _isds_base64_encode_block(plain, length, (int8_t *)buffer,
248 &state);
249 code_length += _isds_base64_encode_blockend(((int8_t*)buffer) + code_length,
250 &state);
252 /* Terminate string */
253 buffer[code_length++] = '\0';
255 /* Shrink the buffer */
256 new_buffer = realloc(buffer, code_length);
257 if (new_buffer) buffer = new_buffer;
259 return buffer;
263 /* Decode given data from MIME Base64 encoded zero terminated string to binary
264 * stream. Invalid Base64 symbols are skipped.
265 * @encoded are input data (Base64 zero terminated string)
266 * @plain are automatically reallocated output data (binary stream). You must
267 * free it. Will be freed in case of error.
268 * @return length of @plain data in bytes or (size_t) -1 in case of memory
269 * allocation failure. */
270 _hidden size_t _isds_b64decode(const char *encoded, void **plain) {
272 base64_decodestate state;
273 size_t encoded_length;
274 size_t plain_length;
275 char *buffer;
277 if (NULL == encoded || NULL == plain) {
278 if (NULL != plain) zfree(*plain);
279 return ((size_t) -1);
282 encoded_length = strlen(encoded);
283 _isds_base64_init_decodestate(&state);
285 /* Divert empty input */
286 if (encoded_length == 0) {
287 zfree(*plain);
288 return 0;
291 /* Allocate buffer */
292 buffer = realloc(*plain, encoded_length);
293 if (NULL == buffer) {
294 zfree(*plain);
295 return ((size_t) -1);
297 *plain = buffer;
299 /* Decode encoded data */
300 plain_length = _isds_base64_decode_block((const int8_t *)encoded,
301 encoded_length, *plain, &state);
302 if (plain_length >= (size_t) -1) {
303 zfree(*plain);
304 return((size_t) -1);
307 /* Shrink the buffer */
308 if (0 == plain_length) {
309 zfree(*plain);
310 } else {
311 buffer = realloc(*plain, plain_length);
312 /* realloc() can move pointer even when shrinking */
313 if (NULL != buffer) *plain = buffer;
316 return plain_length;
320 /* Convert hexadecimal digit to integer. Return negative value if character is
321 * not valid hexadecimal digit. */
322 _hidden int _isds_hex2i(char digit) {
323 if (digit >= '0' && digit <= '9')
324 return digit - '0';
325 if (digit >= 'a' && digit <= 'f')
326 return digit - 'a' + 10;
327 if (digit >= 'A' && digit <= 'F')
328 return digit - 'A' + 10;
329 return -1;