Do not use dolar in formatting strings
[libisds.git] / src / utils.c
blobd4a4c83b259d085f9732c74a4743c770c75b38b6
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 <limits.h>
10 #include <time.h>
11 #include <errno.h>
12 #include "utils.h"
13 #include "cencode.h"
14 #include "cdecode.h"
15 #include "system.h"
17 /* Concatenate two strings into newly allocated buffer.
18 * You must free() them, when you don't need it anymore.
19 * Any of the arguments can be NULL meaning empty string.
20 * In case of error returns NULL.
21 * Empty string is always returned as allocated empty string. */
22 _hidden char *_isds_astrcat(const char *first, const char *second) {
23 size_t first_len, second_len;
24 char *buf;
26 first_len = (first) ? strlen(first) : 0;
27 second_len = (second) ? strlen(second) : 0;
28 buf = malloc(1 + first_len + second_len);
29 if (buf) {
30 buf[0] = '\0';
31 if (first) strcpy(buf, first);
32 if (second) strcpy(buf + first_len, second);
34 return buf;
38 /* Concatenate three strings into newly allocated buffer.
39 * You must free() them, when you don't need it anymore.
40 * Any of the arguments can be NULL meaning empty string.
41 * In case of error returns NULL.
42 * Empty string is always returned as allocated empty string. */
43 _hidden char *_isds_astrcat3(const char *first, const char *second,
44 const char *third) {
45 size_t first_len, second_len, third_len;
46 char *buf, *next;
48 first_len = (first) ? strlen(first) : 0;
49 second_len = (second) ? strlen(second) : 0;
50 third_len = (third) ? strlen(third) : 0;
51 buf = malloc(1 + first_len + second_len + third_len);
52 if (buf) {
53 buf[0] = '\0';
54 next = buf;
55 if (first) {
56 strcpy(next, first);
57 next += first_len;
59 if (second) {
60 strcpy(next, second);
61 next += second_len;
63 if (third) {
64 strcpy(next, third);
67 return buf;
71 /* Print formatted string into automatically reallocated @buffer.
72 * @buffer automatically reallocated buffer. Must be &NULL or preallocated
73 * memory.
74 * @format format string as for printf(3)
75 * @ap list of variadic arguments, after call will be in undefined state
76 * @Returns number of bytes printed. In case of error, -1 and NULL @buffer*/
77 _hidden int isds_vasprintf(char **buffer, const char *format, va_list ap) {
78 va_list aq;
79 int length, new_length;
80 char *new_buffer;
82 if (!buffer || !format) {
83 if (buffer) {
84 free(*buffer);
85 *buffer = NULL;
87 return -1;
90 va_copy(aq, ap);
91 length = vsnprintf(NULL, 0, format, aq) + 1;
92 va_end(aq);
93 if (length <= 0) {
94 free(*buffer);
95 *buffer = NULL;
96 return -1;
99 new_buffer = realloc(*buffer, length);
100 if (!new_buffer) {
101 free(*buffer);
102 *buffer = NULL;
103 return -1;
105 *buffer = new_buffer;
107 new_length = vsnprintf(*buffer, length, format, ap);
108 if (new_length >= length) {
109 free(*buffer);
110 *buffer = NULL;
111 return -1;
114 return new_length;
118 /* Print formatted string into automatically reallocated @buffer.
119 * @buffer automatically reallocated buffer. Must be &NULL or preallocated
120 * memory.
121 * @format format string as for printf(3)
122 * @... variadic arguments
123 * @Returns number of bytes printed. In case of error, -1 and NULL @buffer */
124 _hidden int isds_asprintf(char **buffer, const char *format, ...) {
125 int ret;
126 va_list ap;
127 va_start(ap, format);
128 ret = isds_vasprintf(buffer, format, ap);
129 va_end(ap);
130 return ret;
133 /* Converts a block from charset to charset.
134 * @from is input charset of @input block as known to iconv
135 * @to is output charset @input will be converted to @output
136 * @input is block in @from charset/encoding of length @input_length
137 * @input_length is size of @input block in bytes
138 * @output is automatically allocated block of data converted from @input. No
139 * NUL is apended. Can be NULL, if resulting size is 0. You must free it.
140 * @return size of @output in bytes. In case of error returns (size_t) -1 and
141 * deallocates @output if this function allocated it in this call. */
142 _hidden size_t _isds_any2any(const char *from, const char *to,
143 const void *input, size_t input_length, void **output) {
144 iconv_t state;
145 char *buffer = NULL, *new_buffer;
146 size_t buffer_length = 0, buffer_used = 0;
147 char *inbuf, *outbuf;
148 size_t inleft, outleft;
150 if (output != NULL) *output = NULL;
151 if (from == NULL || to == NULL || input == NULL || output == NULL)
152 return (size_t) -1;
154 state = iconv_open(to, from);
155 if (state == (iconv_t) -1) return (size_t) -1;
157 /* Get the initial output buffer length */
158 buffer_length = input_length;
160 inbuf = (char *) input;
161 inleft = input_length;
163 while (inleft > 0) {
164 /* Extend buffer */
165 new_buffer = realloc(buffer, buffer_length);
166 if (!new_buffer) {
167 zfree(buffer);
168 buffer_used = (size_t) -1;
169 goto leave;
171 buffer = new_buffer;
173 /* FIXME */
174 outbuf = buffer + buffer_used;
175 outleft = buffer_length - buffer_used;
177 /* Convert chunk of data */
178 if ((size_t) -1 == iconv(state, &inbuf, &inleft, &outbuf, &outleft) &&
179 errno != E2BIG) {
180 zfree(buffer);
181 buffer_used = (size_t) -1;
182 goto leave;
185 /* Update positions */
186 buffer_length += 1024;
187 buffer_used = outbuf - buffer;
190 leave:
191 iconv_close(state);
192 if (buffer_used == 0) zfree(buffer);
193 *output = buffer;
194 return buffer_used;
197 /* Converts UTF8 string into locale encoded string.
198 * @utf string int UTF-8 terminated by zero byte
199 * @return allocated string encoded in locale specific encoding. You must free
200 * it. In case of error or NULL @utf returns NULL. */
201 _hidden char *_isds_utf82locale(const char *utf) {
202 char *output, *bigger_output;
203 size_t length;
205 if (utf == NULL) return NULL;
207 length = _isds_any2any("UTF-8", nl_langinfo(CODESET), utf, strlen(utf),
208 (void **) &output);
209 if (length == (size_t) -1) return NULL;
211 bigger_output = realloc(output, length + 1);
212 if (bigger_output == NULL) {
213 zfree(output);
214 } else {
215 output = bigger_output;
216 output[length] = '\0';
219 return output;
223 /* Encode given data into MIME Base64 encoded zero terminated string.
224 * @plain are input data (binary stream)
225 * @length is length of @plain data in bytes
226 * @return allocated string of base64 encoded plain data or NULL in case of
227 * error. You must free it. */
228 _hidden char *_isds_b64encode(const void *plain, const size_t length) {
230 base64_encodestate state;
231 size_t code_length;
232 char *buffer, *new_buffer;
234 if (!plain) {
235 if (length) return NULL;
236 /* Empty input is valid input */
237 plain = "";
240 _isds_base64_init_encodestate(&state);
242 /* Allocate buffer
243 * (4 is padding, 1 is final new line, and 1 is string terminator) */
244 buffer = malloc(length * 2 + 4 + 1 + 1);
245 if (!buffer) return NULL;
247 /* Encode plain data */
248 code_length = _isds_base64_encode_block(plain, length, (int8_t *)buffer,
249 &state);
250 code_length += _isds_base64_encode_blockend(((int8_t*)buffer) + code_length,
251 &state);
253 /* Terminate string */
254 buffer[code_length++] = '\0';
256 /* Shrink the buffer */
257 new_buffer = realloc(buffer, code_length);
258 if (new_buffer) buffer = new_buffer;
260 return buffer;
264 /* Decode given data from MIME Base64 encoded zero terminated string to binary
265 * stream. Invalid Base64 symbols are skipped.
266 * @encoded are input data (Base64 zero terminated string)
267 * @plain are automatically reallocated output data (binary stream). You must
268 * free it. Will be freed in case of error.
269 * @return length of @plain data in bytes or (size_t) -1 in case of memory
270 * allocation failure. */
271 _hidden size_t _isds_b64decode(const char *encoded, void **plain) {
273 base64_decodestate state;
274 size_t encoded_length;
275 size_t plain_length;
276 char *buffer;
278 if (NULL == encoded || NULL == plain) {
279 if (NULL != plain) zfree(*plain);
280 return ((size_t) -1);
283 encoded_length = strlen(encoded);
284 _isds_base64_init_decodestate(&state);
286 /* Divert empty input */
287 if (encoded_length == 0) {
288 zfree(*plain);
289 return 0;
292 /* Allocate buffer */
293 buffer = realloc(*plain, encoded_length);
294 if (NULL == buffer) {
295 zfree(*plain);
296 return ((size_t) -1);
298 *plain = buffer;
300 /* Decode encoded data */
301 plain_length = _isds_base64_decode_block((const int8_t *)encoded,
302 encoded_length, *plain, &state);
303 if (plain_length >= (size_t) -1) {
304 zfree(*plain);
305 return((size_t) -1);
308 /* Shrink the buffer */
309 if (0 == plain_length) {
310 zfree(*plain);
311 } else {
312 buffer = realloc(*plain, plain_length);
313 /* realloc() can move pointer even when shrinking */
314 if (NULL != buffer) *plain = buffer;
317 return plain_length;
321 /* Convert hexadecimal digit to integer. Return negative value if character is
322 * not valid hexadecimal digit. */
323 _hidden int _isds_hex2i(char digit) {
324 if (digit >= '0' && digit <= '9')
325 return digit - '0';
326 if (digit >= 'a' && digit <= 'f')
327 return digit - 'a' + 10;
328 if (digit >= 'A' && digit <= 'F')
329 return digit - 'A' + 10;
330 return -1;
334 /* Convert size_t to int. */
335 _hidden int _isds_sizet2int(size_t val) {
336 return (val <= INT_MAX) ? (int) val : -1;