Merged from silc_1_0_branch.
[silc.git] / lib / silcutil / silcbuffmt.c
1 /*
2
3   silcbuffmt.c
4
5   Author: Pekka Riikonen <priikone@silcnet.org>
6
7   Copyright (C) 1997 - 2002 Pekka Riikonen
8
9   This program is free software; you can redistribute it and/or modify
10   it under the terms of the GNU General Public License as published by
11   the Free Software Foundation; either version 2 of the License, or
12   (at your option) any later version.
13
14   This program is distributed in the hope that it will be useful,
15   but WITHOUT ANY WARRANTY; without even the implied warranty of
16   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17   GNU General Public License for more details.
18
19 */
20 /* $Id$ */
21
22 #include "silcincludes.h"
23
24 /* Macros to check whether there is enough free space to add the
25    required amount of data. For unformatting this means that there must
26    be the data that is to be extracted. */
27 #define FORMAT_HAS_SPACE(__x__, __req__)        \
28   do {                                          \
29     if (__req__ > (__x__)->len)                 \
30       goto fail;                                \
31   } while(0)
32 #define UNFORMAT_HAS_SPACE(__x__, __req__)      \
33   do {                                          \
34     if (__req__ > (__x__)->len)                 \
35       goto fail;                                \
36     if ((__req__ + 1) <= 0)                     \
37       goto fail;                                \
38   } while(0)
39
40 /* Formats the arguments sent and puts them into the buffer sent as
41    argument. The buffer must be initialized beforehand and it must have
42    enough free space to include the formatted data. If this function
43    fails caller should not trust the buffer anymore and should free it.
44    This function is used, for example, to create packets to send over
45    network. */
46
47 int silc_buffer_format(SilcBuffer dst, ...)
48 {
49   va_list ap;
50   int ret;
51
52   va_start(ap, dst);
53   ret = silc_buffer_format_vp(dst, ap);
54   va_end(ap);
55
56   return ret;
57 }
58
59 int silc_buffer_format_vp(SilcBuffer dst, va_list ap)
60 {
61   SilcBufferParamType fmt;
62   unsigned char *start_ptr = dst->data;
63   int len;
64
65   /* Parse the arguments by formatting type. */
66   while (1) {
67     fmt = va_arg(ap, SilcBufferParamType);
68
69     switch(fmt) {
70     case SILC_BUFFER_PARAM_SI8_CHAR:
71       {
72         char x = (char)va_arg(ap, int);
73         FORMAT_HAS_SPACE(dst, 1);
74         silc_buffer_put(dst, &x, 1);
75         silc_buffer_pull(dst, 1);
76         break;
77       }
78     case SILC_BUFFER_PARAM_UI8_CHAR:
79       {
80         unsigned char x = (unsigned char)va_arg(ap, int);
81         FORMAT_HAS_SPACE(dst, 1);
82         silc_buffer_put(dst, &x, 1);
83         silc_buffer_pull(dst, 1);
84         break;
85       }
86     case SILC_BUFFER_PARAM_SI16_SHORT:
87       {
88         unsigned char xf[2];
89         SilcInt16 x = (SilcInt16)va_arg(ap, int);
90         FORMAT_HAS_SPACE(dst, 2);
91         SILC_PUT16_MSB(x, xf);
92         silc_buffer_put(dst, xf, 2);
93         silc_buffer_pull(dst, 2);
94         break;
95       }
96     case SILC_BUFFER_PARAM_UI16_SHORT:
97       {
98         unsigned char xf[2];
99         SilcUInt16 x = (SilcUInt16)va_arg(ap, int);
100         FORMAT_HAS_SPACE(dst, 2);
101         SILC_PUT16_MSB(x, xf);
102         silc_buffer_put(dst, xf, 2);
103         silc_buffer_pull(dst, 2);
104         break;
105       }
106     case SILC_BUFFER_PARAM_SI32_INT:
107       {
108         unsigned char xf[4];
109         SilcInt32 x = va_arg(ap, SilcInt32);
110         FORMAT_HAS_SPACE(dst, 4);
111         SILC_PUT32_MSB(x, xf);
112         silc_buffer_put(dst, xf, 4);
113         silc_buffer_pull(dst, 4);
114         break;
115       }
116     case SILC_BUFFER_PARAM_UI32_INT:
117       {
118         unsigned char xf[4];
119         SilcUInt32 x = va_arg(ap, SilcUInt32);
120         FORMAT_HAS_SPACE(dst, 4);
121         SILC_PUT32_MSB(x, xf);
122         silc_buffer_put(dst, xf, 4);
123         silc_buffer_pull(dst, 4);
124         break;
125       }
126     case SILC_BUFFER_PARAM_SI64_INT:
127       {
128         unsigned char xf[8];
129         SilcInt64 x = va_arg(ap, SilcInt64);
130         FORMAT_HAS_SPACE(dst, sizeof(SilcInt64));
131         SILC_PUT64_MSB(x, xf);
132         silc_buffer_put(dst, xf, sizeof(SilcInt64));
133         silc_buffer_pull(dst, sizeof(SilcInt64));
134         break;
135       }
136     case SILC_BUFFER_PARAM_UI64_INT:
137       {
138         unsigned char xf[8];
139         SilcUInt64 x = va_arg(ap, SilcUInt64);
140         FORMAT_HAS_SPACE(dst, sizeof(SilcUInt64));
141         SILC_PUT64_MSB(x, xf);
142         silc_buffer_put(dst, xf, sizeof(SilcUInt64));
143         silc_buffer_pull(dst, sizeof(SilcUInt64));
144         break;
145       }
146     case SILC_BUFFER_PARAM_UI8_STRING:
147     case SILC_BUFFER_PARAM_UI16_STRING:
148     case SILC_BUFFER_PARAM_UI32_STRING:
149     case SILC_BUFFER_PARAM_UI8_STRING_ALLOC:
150     case SILC_BUFFER_PARAM_UI16_STRING_ALLOC:
151     case SILC_BUFFER_PARAM_UI32_STRING_ALLOC:
152       {
153         unsigned char *x = va_arg(ap, unsigned char *);
154         SilcUInt32 tmp_len = strlen(x);
155         FORMAT_HAS_SPACE(dst, tmp_len);
156         silc_buffer_put(dst, x, tmp_len);
157         silc_buffer_pull(dst, tmp_len);
158         break;
159       }
160     case SILC_BUFFER_PARAM_UI8_NSTRING:
161     case SILC_BUFFER_PARAM_UI16_NSTRING:
162     case SILC_BUFFER_PARAM_UI32_NSTRING:
163     case SILC_BUFFER_PARAM_UI_XNSTRING:
164     case SILC_BUFFER_PARAM_UI8_NSTRING_ALLOC:
165     case SILC_BUFFER_PARAM_UI16_NSTRING_ALLOC:
166     case SILC_BUFFER_PARAM_UI32_NSTRING_ALLOC:
167     case SILC_BUFFER_PARAM_UI_XNSTRING_ALLOC:
168       {
169         unsigned char *x = va_arg(ap, unsigned char *);
170         SilcUInt32 len = va_arg(ap, SilcUInt32);
171         if (x && len) {
172           FORMAT_HAS_SPACE(dst, len);
173           silc_buffer_put(dst, x, len);
174           silc_buffer_pull(dst, len);
175         }
176         break;
177       }
178     case SILC_BUFFER_PARAM_END:
179       goto ok;
180       break;
181     default:
182       SILC_LOG_DEBUG(("Bad buffer formatting type `%d'. Could not "
183                       "format the data.", fmt));
184       goto fail;
185       break;
186     }
187   }
188
189  fail:
190   SILC_LOG_DEBUG(("Error occured while formatting data"));
191   len = dst->data - start_ptr;
192   silc_buffer_push(dst, len);
193   return -1;
194
195  ok:
196   /* Push the buffer back to where it belongs. */
197   len = dst->data - start_ptr;
198   silc_buffer_push(dst, len);
199   return len;
200 }
201
202 /* Unformats the buffer sent as argument. The unformatted data is returned
203    to the variable argument list of pointers. The buffer must point to the
204    start of the data area to be unformatted. Buffer maybe be safely free'd
205    after this returns succesfully. */
206
207 int silc_buffer_unformat(SilcBuffer src, ...)
208 {
209   va_list ap;
210   int ret;
211
212   va_start(ap, src);
213   ret = silc_buffer_unformat_vp(src, ap);
214   va_end(ap);
215
216   return ret;
217 }
218
219 int silc_buffer_unformat_vp(SilcBuffer src, va_list ap)
220 {
221   SilcBufferParamType fmt;
222   unsigned char *start_ptr = src->data;
223   int len = 0;
224
225   /* Parse the arguments by formatting type. */
226   while(1) {
227     fmt = va_arg(ap, SilcBufferParamType);
228
229     switch(fmt) {
230     case SILC_BUFFER_PARAM_SI8_CHAR:
231       {
232         char *x = va_arg(ap, char *);
233         UNFORMAT_HAS_SPACE(src, 1);
234         if (x)
235           *x = src->data[0];
236         silc_buffer_pull(src, 1);
237         break;
238       }
239     case SILC_BUFFER_PARAM_UI8_CHAR:
240       {
241         unsigned char *x = va_arg(ap, unsigned char *);
242         UNFORMAT_HAS_SPACE(src, 1);
243         if (x)
244           *x = src->data[0];
245         silc_buffer_pull(src, 1);
246         break;
247       }
248     case SILC_BUFFER_PARAM_SI16_SHORT:
249       {
250         SilcInt16 *x = va_arg(ap, SilcInt16 *);
251         UNFORMAT_HAS_SPACE(src, 2);
252         if (x)
253           SILC_GET16_MSB(*x, src->data);
254         silc_buffer_pull(src, 2);
255         break;
256       }
257     case SILC_BUFFER_PARAM_UI16_SHORT:
258       {
259         SilcUInt16 *x = va_arg(ap, SilcUInt16 *);
260         UNFORMAT_HAS_SPACE(src, 2);
261         if (x)
262           SILC_GET16_MSB(*x, src->data);
263         silc_buffer_pull(src, 2);
264         break;
265       }
266     case SILC_BUFFER_PARAM_SI32_INT:
267       {
268         SilcInt32 *x = va_arg(ap, SilcInt32 *);
269         UNFORMAT_HAS_SPACE(src, 4);
270         if (x)
271           SILC_GET32_MSB(*x, src->data);
272         silc_buffer_pull(src, 4);
273         break;
274       }
275     case SILC_BUFFER_PARAM_UI32_INT:
276       {
277         SilcUInt32 *x = va_arg(ap, SilcUInt32 *);
278         UNFORMAT_HAS_SPACE(src, 4);
279         if (x)
280           SILC_GET32_MSB(*x, src->data);
281         silc_buffer_pull(src, 4);
282         break;
283       }
284     case SILC_BUFFER_PARAM_SI64_INT:
285       {
286         SilcInt64 *x = va_arg(ap, SilcInt64 *);
287         UNFORMAT_HAS_SPACE(src, sizeof(SilcInt64));
288         if (x)
289           SILC_GET64_MSB(*x, src->data);
290         silc_buffer_pull(src, sizeof(SilcInt64));
291         break;
292       }
293     case SILC_BUFFER_PARAM_UI64_INT:
294       {
295         SilcUInt64 *x = va_arg(ap, SilcUInt64 *);
296         UNFORMAT_HAS_SPACE(src, sizeof(SilcUInt64));
297         if (x)
298           SILC_GET64_MSB(*x, src->data);
299         silc_buffer_pull(src, sizeof(SilcUInt64));
300         break;
301       }
302     case SILC_BUFFER_PARAM_UI8_STRING:
303       {
304         SilcUInt8 len2;
305         unsigned char **x = va_arg(ap, unsigned char **);
306         UNFORMAT_HAS_SPACE(src, 1);
307         len2 = (SilcUInt8)src->data[0];
308         silc_buffer_pull(src, 1);
309         UNFORMAT_HAS_SPACE(src, len2);
310         if (x)
311           *x = src->data;
312         silc_buffer_pull(src, len2);
313         break;
314       }
315     case SILC_BUFFER_PARAM_UI16_STRING:
316       {
317         SilcUInt16 len2;
318         unsigned char **x = va_arg(ap, unsigned char **);
319         UNFORMAT_HAS_SPACE(src, 2);
320         SILC_GET16_MSB(len2, src->data);
321         silc_buffer_pull(src, 2);
322         UNFORMAT_HAS_SPACE(src, len2);
323         if (x)
324           *x = src->data;
325         silc_buffer_pull(src, len2);
326         break;
327       }
328     case SILC_BUFFER_PARAM_UI8_STRING_ALLOC:
329       {
330         SilcUInt8 len2;
331         unsigned char **x = va_arg(ap, unsigned char **);
332         UNFORMAT_HAS_SPACE(src, 1);
333         len2 = (SilcUInt8)src->data[0];
334         silc_buffer_pull(src, 1);
335         UNFORMAT_HAS_SPACE(src, len2);
336         if (x && len2) {
337           *x = silc_calloc(len2 + 1, sizeof(unsigned char));
338           memcpy(*x, src->data, len2);
339         }
340         silc_buffer_pull(src, len2);
341         break;
342       }
343     case SILC_BUFFER_PARAM_UI16_STRING_ALLOC:
344       {
345         SilcUInt16 len2;
346         unsigned char **x = va_arg(ap, unsigned char **);
347         UNFORMAT_HAS_SPACE(src, 2);
348         SILC_GET16_MSB(len2, src->data);
349         silc_buffer_pull(src, 2);
350         UNFORMAT_HAS_SPACE(src, len2);
351         if (x && len2) {
352           *x = silc_calloc(len2 + 1, sizeof(unsigned char));
353           memcpy(*x, src->data, len2);
354         }
355         silc_buffer_pull(src, len2);
356         break;
357       }
358     case SILC_BUFFER_PARAM_UI32_STRING:
359       {
360         SilcUInt32 len2;
361         unsigned char **x = va_arg(ap, unsigned char **);
362         UNFORMAT_HAS_SPACE(src, 4);
363         SILC_GET32_MSB(len2, src->data);
364         silc_buffer_pull(src, 4);
365         UNFORMAT_HAS_SPACE(src, len2);
366         if (x)
367           *x = src->data;
368         silc_buffer_pull(src, len2);
369         break;
370       }
371     case SILC_BUFFER_PARAM_UI32_STRING_ALLOC:
372       {
373         SilcUInt32 len2;
374         unsigned char **x = va_arg(ap, unsigned char **);
375         UNFORMAT_HAS_SPACE(src, 4);
376         SILC_GET32_MSB(len2, src->data);
377         silc_buffer_pull(src, 4);
378         UNFORMAT_HAS_SPACE(src, len2);
379         if (x && len2) {
380           *x = silc_calloc(len2 + 1, sizeof(unsigned char));
381           memcpy(*x, src->data, len2);
382         }
383         silc_buffer_pull(src, len2);
384         break;
385       }
386     case SILC_BUFFER_PARAM_UI8_NSTRING:
387       {
388         SilcUInt8 len2;
389         unsigned char **x = va_arg(ap, unsigned char **);
390         SilcUInt8 *len = va_arg(ap, SilcUInt8 *);
391         UNFORMAT_HAS_SPACE(src, 1);
392         len2 = (SilcUInt8)src->data[0];
393         silc_buffer_pull(src, 1);
394         UNFORMAT_HAS_SPACE(src, len2);
395         if (len)
396           *len = len2;
397         if (x)
398           *x = src->data;
399         silc_buffer_pull(src, len2);
400         break;
401       }
402     case SILC_BUFFER_PARAM_UI16_NSTRING:
403       {
404         SilcUInt16 len2;
405         unsigned char **x = va_arg(ap, unsigned char **);
406         SilcUInt16 *len = va_arg(ap, SilcUInt16 *);
407         UNFORMAT_HAS_SPACE(src, 2);
408         SILC_GET16_MSB(len2, src->data);
409         silc_buffer_pull(src, 2);
410         UNFORMAT_HAS_SPACE(src, len2);
411         if (len)
412           *len = len2;
413         if (x)
414           *x = src->data;
415         silc_buffer_pull(src, len2);
416         break;
417       }
418     case SILC_BUFFER_PARAM_UI8_NSTRING_ALLOC:
419       {
420         SilcUInt8 len2;
421         unsigned char **x = va_arg(ap, unsigned char **);
422         SilcUInt8 *len = va_arg(ap, SilcUInt8 *);
423         UNFORMAT_HAS_SPACE(src, 1);
424         len2 = (SilcUInt8)src->data[0];
425         silc_buffer_pull(src, 1);
426         UNFORMAT_HAS_SPACE(src, len2);
427         if (len)
428           *len = len2;
429         if (x && len2) {
430           *x = silc_calloc(len2 + 1, sizeof(unsigned char));
431           memcpy(*x, src->data, len2);
432         }
433         silc_buffer_pull(src, len2);
434         break;
435       }
436     case SILC_BUFFER_PARAM_UI16_NSTRING_ALLOC:
437       {
438         SilcUInt16 len2;
439         unsigned char **x = va_arg(ap, unsigned char **);
440         SilcUInt16 *len = va_arg(ap, SilcUInt16 *);
441         UNFORMAT_HAS_SPACE(src, 2);
442         SILC_GET16_MSB(len2, src->data);
443         silc_buffer_pull(src, 2);
444         UNFORMAT_HAS_SPACE(src, len2);
445         if (len)
446           *len = len2;
447         if (x && len2) {
448           *x = silc_calloc(len2 + 1, sizeof(unsigned char));
449           memcpy(*x, src->data, len2);
450         }
451         silc_buffer_pull(src, len2);
452         break;
453       }
454     case SILC_BUFFER_PARAM_UI32_NSTRING:
455       {
456         SilcUInt32 len2;
457         unsigned char **x = va_arg(ap, unsigned char **);
458         SilcUInt32 *len = va_arg(ap, SilcUInt32 *);
459         UNFORMAT_HAS_SPACE(src, 4);
460         SILC_GET32_MSB(len2, src->data);
461         silc_buffer_pull(src, 4);
462         UNFORMAT_HAS_SPACE(src, len2);
463         if (len)
464           *len = len2;
465         if (x)
466           *x = src->data;
467         silc_buffer_pull(src, len2);
468         break;
469       }
470     case SILC_BUFFER_PARAM_UI_XNSTRING:
471       {
472         unsigned char **x = va_arg(ap, unsigned char **);
473         SilcUInt32 len = va_arg(ap, SilcUInt32);
474         UNFORMAT_HAS_SPACE(src, len);
475         if (len && x)
476           *x = src->data;
477         silc_buffer_pull(src, len);
478         break;
479       }
480     case SILC_BUFFER_PARAM_UI_XNSTRING_ALLOC:
481       {
482         unsigned char **x = va_arg(ap, unsigned char **);
483         SilcUInt32 len = va_arg(ap, SilcUInt32);
484         UNFORMAT_HAS_SPACE(src, len);
485         if (len && x) {
486           *x = silc_calloc(len + 1, sizeof(unsigned char));
487           memcpy(*x, src->data, len);
488         }
489         silc_buffer_pull(src, len);
490         break;
491       }
492     case SILC_BUFFER_PARAM_END:
493       goto ok;
494       break;
495     default:
496       SILC_LOG_DEBUG(("Bad buffer formatting type `%d'. Could not "
497                       "format the data.", fmt));
498       goto fail;
499       break;
500     }
501   }
502
503  fail:
504   SILC_LOG_DEBUG(("Error occured while unformatting buffer"));
505   len = src->data - start_ptr;
506   silc_buffer_push(src, len);
507   return -1;
508
509  ok:
510   /* Push the buffer back to the start. */
511   len = src->data - start_ptr;
512   silc_buffer_push(src, len);
513   return len;
514 }
515
516 /* Formats strings into a buffer */
517
518 int silc_buffer_strformat(SilcBuffer dst, ...)
519 {
520   int len = dst->truelen;
521   va_list va;
522
523   va_start(va, dst);
524
525   /* Parse the arguments by formatting type. */
526   while(1) {
527     char *string = va_arg(va, char *);
528
529     if (!string)
530       continue;
531     if (string == (char *)SILC_BUFFER_PARAM_END)
532       goto ok;
533
534     dst->head = silc_realloc(dst->head, sizeof(*dst->head) *
535                              (strlen(string) + len));
536     memcpy(dst->head + len, string, strlen(string));
537     len += strlen(string);
538   }
539
540   SILC_LOG_DEBUG(("Error occured while formatting buffer"));
541   va_end(va);
542   return -1;
543
544  ok:
545   dst->end = dst->head + len;
546   dst->tail = dst->data = dst->end;
547   dst->len = 0;
548   dst->truelen = len;
549
550   va_end(va);
551   return len;
552 }