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