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