Added SILC_STR_STRING and SILC_STR_STRING_APPEND and support for
[crypto.git] / lib / silcutil / silcbuffmt.c
1 /*
2
3   silcbuffmt.c
4
5   Author: Pekka Riikonen <priikone@silcnet.org>
6
7   Copyright (C) 1997 - 2008 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 "silc.h"
22
23 /************************** Types and definitions ***************************/
24
25 /* Check that buffer has enough room to format data in it, if not
26    allocate more. */
27 #define FORMAT_HAS_SPACE(s, b, req)                     \
28 do {                                                    \
29   if (silc_unlikely(!silc_buffer_senlarge(s, b, req)))  \
30     goto fail;                                          \
31   flen += req;                                          \
32 } while(0)
33
34 /* Check that buffer has enough room to format data in it, if not
35    allocate more.  This will append, thus not replacing any existing data. */
36 #define FORMAT_HAS_SPACE_APPEND(s, b, req)                              \
37 do {                                                                    \
38   if (silc_buffer_len(b) < req)                                         \
39     if (silc_unlikely(!silc_buffer_sappend(s, b, req - silc_buffer_len(b)))) \
40       goto fail;                                                        \
41   flen += req;                                                          \
42 } while(0)
43
44 /* Check that there is data to be unformatted */
45 #define UNFORMAT_HAS_SPACE(b, req)                      \
46 do {                                                    \
47   if (silc_unlikely(req > silc_buffer_len(b))) {        \
48     silc_set_errno(SILC_ERR_OVERFLOW);                  \
49     goto fail;                                          \
50   }                                                     \
51   if (silc_unlikely((req + 1) <= 0)) {                  \
52     silc_set_errno(SILC_ERR_UNDERFLOW);                 \
53     goto fail;                                          \
54   }                                                     \
55 } while(0)
56
57
58 /******************************* Formatting *********************************/
59
60 int silc_buffer_sformat_vp_i(SilcStack stack, SilcBuffer dst, va_list ap,
61                              SilcBool process)
62 {
63   SilcParam fmt;
64   int flen = 0;
65   SilcBool advance = FALSE;
66
67   /* Parse the arguments by formatting type. */
68   while (1) {
69     fmt = va_arg(ap, SilcParam);
70
71     SILC_LOG_DEBUG(("Buffer format type %d", fmt));
72
73     switch (fmt) {
74     case SILC_PARAM_FUNC:
75       {
76         SilcBufferFormatFunc func;
77         void *val;
78         void *context;
79         int tmp_len;
80
81         func = va_arg(ap, SilcBufferFormatFunc);
82         val = va_arg(ap, void *);
83         context = va_arg(ap, void *);
84
85         if (!process)
86           break;
87
88         tmp_len = func(stack, dst, val, context);
89         if (tmp_len < 0)
90           goto fail;
91         if (tmp_len) {
92           silc_buffer_pull(dst, tmp_len);
93           flen += tmp_len;
94         }
95         break;
96       }
97
98     case SILC_PARAM_REGEX:
99       {
100         const char *regex = va_arg(ap, char *);
101         SilcBufferRegexFlags rflags = va_arg(ap, SilcUInt32);
102         SilcBufferStruct match, saved;
103         SilcBool match_all = (rflags & SILC_STR_REGEX_ALL) != 0;
104         SilcBool match_nl = (rflags & SILC_STR_REGEX_NL) != 0;
105         SilcBool ret;
106         SilcUInt32 inclusive_pos = 0;
107         int matched = 0, ret_len;
108         va_list cp;
109
110         if (!process)
111           break;
112
113         if (!regex)
114           goto fail;
115
116         memset(&saved, 0, sizeof(saved));
117
118         if (match_nl) {
119         start_nl_match:
120           /* Match for '\n' in the buffer.  If not found, treat as line
121              without '\n' (buffer has only one line, or this is last line). */
122           saved = *dst;
123           if (silc_regex_buffer(dst, "\n", &match, NULL))
124             dst->tail = match.tail;
125         }
126
127       start_match:
128         /* Match */
129         ret = silc_regex_buffer(dst, regex, &match, NULL);
130         ret ^= (rflags & SILC_STR_REGEX_NOT) != 0;
131         if (!ret) {
132           if (!matched && rflags & SILC_STR_REGEX_MISMATCH) {
133             silc_set_errno(SILC_ERR_NOT_FOUND);
134             goto fail;
135           }
136           goto end_match;
137         }
138         matched++;
139
140         if (rflags & SILC_STR_REGEX_NOT)
141           match = *dst;
142
143         if (!(rflags & SILC_STR_REGEX_NO_ADVANCE)) {
144           /* Advance buffer after match */
145           flen += (match.data - dst->data);
146           if (!silc_buffer_pull(dst, (match.data - dst->data)))
147             goto fail;
148         }
149
150         if (rflags & SILC_STR_REGEX_INCLUSIVE) {
151           inclusive_pos = dst->tail - match.tail;
152           dst->tail = match.tail;
153         }
154
155         /* Recursively format */
156         silc_va_copy(cp, ap);
157         ret_len = silc_buffer_sformat_vp_i(stack, dst, cp, TRUE);
158         va_end(cp);
159         if (ret_len < 0)
160           goto fail;
161
162         if (rflags & SILC_STR_REGEX_INCLUSIVE)
163           if (!silc_buffer_pull_tail(dst, inclusive_pos))
164             goto fail;
165
166         /* Advance buffer after formatting */
167         flen += ret_len;
168         if (!silc_buffer_pull(dst, ret_len))
169           goto fail;
170
171         if (match_all && (!match_nl || silc_buffer_len(dst) > 1))
172           goto start_match;
173
174       end_match:
175         if (match_nl) {
176           /* Go to next line, it is at the end of the data area.  Adjust
177              the tail area of the target buffer to show rest of the buffer. */
178           flen += (dst->tail - dst->data);
179           if (!silc_buffer_pull(dst, (dst->tail - dst->data)))
180             goto fail;
181           if (!silc_buffer_pull_tail(dst, (saved.tail - dst->tail)))
182             goto fail;
183
184           if (silc_buffer_len(dst) > 0)
185             goto start_nl_match;
186         }
187
188         /* Skip to the next SILC_PARAM_END */
189         silc_buffer_sformat_vp_i(NULL, NULL, ap, FALSE);
190         break;
191       }
192
193     case SILC_PARAM_UI8_STRING:
194     case SILC_PARAM_UI16_STRING:
195     case SILC_PARAM_UI32_STRING:
196     case SILC_PARAM_UI8_STRING | SILC_PARAM_ALLOC:
197     case SILC_PARAM_UI16_STRING | SILC_PARAM_ALLOC:
198     case SILC_PARAM_UI32_STRING | SILC_PARAM_ALLOC:
199       {
200         char *x = va_arg(ap, char *);
201         SilcUInt32 tmp_len = x ? strlen(x) : 0;
202
203         if (!process)
204           break;
205
206         if (x && tmp_len) {
207           FORMAT_HAS_SPACE(stack, dst, tmp_len);
208           silc_buffer_put(dst, x, tmp_len);
209           silc_buffer_pull(dst, tmp_len);
210         }
211         break;
212       }
213
214     case SILC_PARAM_UI8_STRING | SILC_PARAM_APPEND:
215     case SILC_PARAM_UI16_STRING | SILC_PARAM_APPEND:
216     case SILC_PARAM_UI32_STRING | SILC_PARAM_APPEND:
217       {
218         char *x = va_arg(ap, char *);
219         SilcUInt32 tmp_len = x ? strlen(x) : 0;
220
221         if (!process)
222           break;
223
224         if (x && tmp_len) {
225           FORMAT_HAS_SPACE_APPEND(stack, dst, tmp_len);
226           silc_buffer_put(dst, x, tmp_len);
227           silc_buffer_pull(dst, tmp_len);
228         }
229         break;
230       }
231
232     case SILC_PARAM_UI8_NSTRING:
233     case SILC_PARAM_UI16_NSTRING:
234     case SILC_PARAM_UI32_NSTRING:
235     case SILC_PARAM_UICHAR:
236     case SILC_PARAM_UI8_NSTRING | SILC_PARAM_ALLOC:
237     case SILC_PARAM_UI16_NSTRING | SILC_PARAM_ALLOC:
238     case SILC_PARAM_UI32_NSTRING | SILC_PARAM_ALLOC:
239     case SILC_PARAM_UICHAR | SILC_PARAM_ALLOC:
240       {
241         unsigned char *x = va_arg(ap, unsigned char *);
242         SilcUInt32 tmp_len = va_arg(ap, SilcUInt32);
243
244         if (!process)
245           break;
246
247         if (x && tmp_len) {
248           FORMAT_HAS_SPACE(stack, dst, tmp_len);
249           silc_buffer_put(dst, x, tmp_len);
250           silc_buffer_pull(dst, tmp_len);
251         }
252         break;
253       }
254
255     case SILC_PARAM_UINT8:
256       {
257         unsigned char x = (unsigned char)va_arg(ap, int);
258
259         if (!process)
260           break;
261
262         FORMAT_HAS_SPACE(stack, dst, 1);
263         silc_buffer_put(dst, &x, 1);
264         silc_buffer_pull(dst, 1);
265         break;
266       }
267
268     case SILC_PARAM_UINT16:
269       {
270         unsigned char xf[2];
271         SilcUInt16 x = (SilcUInt16)va_arg(ap, int);
272
273         if (!process)
274           break;
275
276         FORMAT_HAS_SPACE(stack, dst, 2);
277         SILC_PUT16_MSB(x, xf);
278         silc_buffer_put(dst, xf, 2);
279         silc_buffer_pull(dst, 2);
280         break;
281       }
282
283     case SILC_PARAM_UINT32:
284       {
285         unsigned char xf[4];
286         SilcUInt32 x = va_arg(ap, SilcUInt32);
287
288         if (!process)
289           break;
290
291         FORMAT_HAS_SPACE(stack, dst, 4);
292         SILC_PUT32_MSB(x, xf);
293         silc_buffer_put(dst, xf, 4);
294         silc_buffer_pull(dst, 4);
295         break;
296       }
297
298     case SILC_PARAM_UINT64:
299       {
300         unsigned char xf[8];
301         SilcUInt64 x = va_arg(ap, SilcUInt64);
302
303         if (!process)
304           break;
305
306         FORMAT_HAS_SPACE(stack, dst, sizeof(SilcUInt64));
307         SILC_PUT64_MSB(x, xf);
308         silc_buffer_put(dst, xf, sizeof(SilcUInt64));
309         silc_buffer_pull(dst, sizeof(SilcUInt64));
310         break;
311       }
312
313     case SILC_PARAM_SINT8:
314       {
315         char x = (char)va_arg(ap, int);
316
317         if (!process)
318           break;
319
320         FORMAT_HAS_SPACE(stack, dst, 1);
321         silc_buffer_put(dst, (unsigned char *)&x, 1);
322         silc_buffer_pull(dst, 1);
323         break;
324       }
325
326     case SILC_PARAM_SINT16:
327       {
328         unsigned char xf[2];
329         SilcInt16 x = (SilcInt16)va_arg(ap, int);
330
331         if (!process)
332           break;
333
334         FORMAT_HAS_SPACE(stack, dst, 2);
335         SILC_PUT16_MSB(x, xf);
336         silc_buffer_put(dst, xf, 2);
337         silc_buffer_pull(dst, 2);
338         break;
339       }
340
341     case SILC_PARAM_SINT32:
342       {
343         unsigned char xf[4];
344         SilcInt32 x = va_arg(ap, SilcInt32);
345
346         if (!process)
347           break;
348
349         FORMAT_HAS_SPACE(stack, dst, 4);
350         SILC_PUT32_MSB(x, xf);
351         silc_buffer_put(dst, xf, 4);
352         silc_buffer_pull(dst, 4);
353         break;
354       }
355
356     case SILC_PARAM_SINT64:
357       {
358         unsigned char xf[8];
359         SilcInt64 x = va_arg(ap, SilcInt64);
360
361         if (!process)
362           break;
363
364         FORMAT_HAS_SPACE(stack, dst, sizeof(SilcInt64));
365         SILC_PUT64_MSB(x, xf);
366         silc_buffer_put(dst, xf, sizeof(SilcInt64));
367         silc_buffer_pull(dst, sizeof(SilcInt64));
368         break;
369       }
370
371     case SILC_PARAM_BUFFER:
372     case SILC_PARAM_BUFFER | SILC_PARAM_ALLOC:
373       {
374         SilcBuffer x = va_arg(ap, SilcBuffer);
375         unsigned char xf[4];
376
377         if (!process)
378           break;
379
380         if (x && silc_buffer_len(x)) {
381           FORMAT_HAS_SPACE(stack, dst, silc_buffer_len(x) + 4);
382           SILC_PUT32_MSB(silc_buffer_len(x), xf);
383           silc_buffer_put(dst, xf, 4);
384           silc_buffer_pull(dst, 4);
385           silc_buffer_put(dst, silc_buffer_data(x), silc_buffer_len(x));
386           silc_buffer_pull(dst, silc_buffer_len(x));
387         }
388       }
389       break;
390
391     case SILC_PARAM_OFFSET:
392       {
393         int offst = va_arg(ap, int);
394
395         if (!process)
396           break;
397
398         if (!offst)
399           break;
400
401         if (offst > 1) {
402           if (offst > silc_buffer_len(dst)) {
403             silc_set_errno(SILC_ERR_OVERFLOW);
404             goto fail;
405           }
406           silc_buffer_pull(dst, offst);
407           flen += offst;
408         } else {
409           silc_buffer_push(dst, -(offst));
410           flen += -(offst);
411         }
412         break;
413       }
414
415     case SILC_PARAM_OFFSET_START:
416       if (!process)
417         break;
418       if (!silc_buffer_push(dst, flen))
419         goto fail;
420       flen = 0;
421       break;
422
423     case SILC_PARAM_OFFSET_END:
424       if (!process)
425         break;
426       flen += silc_buffer_len(dst);
427       silc_buffer_pull(dst, silc_buffer_len(dst));
428       break;
429
430     case SILC_PARAM_ADVANCE:
431       if (!process)
432         break;
433       advance = TRUE;
434       break;
435
436     case SILC_PARAM_END:
437       goto ok;
438       break;
439
440     default:
441       SILC_LOG_DEBUG(("Bad buffer formatting type `%d'. Could not "
442                       "format the data.", fmt));
443       silc_set_errno_reason(SILC_ERR_INVALID_ARGUMENT,
444                             "Bad buffer formatting type %d", fmt);
445       goto fail;
446       break;
447     }
448   }
449
450  fail:
451   SILC_LOG_DEBUG(("Error occured while formatting data"));
452   if (process && !advance)
453     silc_buffer_push(dst, flen);
454   return -1;
455
456  ok:
457   /* Push the buffer back to where it belongs. */
458   if (process && !advance)
459     silc_buffer_push(dst, flen);
460   return flen;
461 }
462
463 int silc_buffer_format(SilcBuffer dst, ...)
464 {
465   va_list ap;
466   int ret;
467
468   va_start(ap, dst);
469   ret = silc_buffer_sformat_vp(NULL, dst, ap);
470   va_end(ap);
471
472   return ret;
473 }
474
475 int silc_buffer_format_vp(SilcBuffer dst, va_list ap)
476 {
477   return silc_buffer_sformat_vp(NULL, dst, ap);
478 }
479
480 int silc_buffer_sformat(SilcStack stack, SilcBuffer dst, ...)
481 {
482   va_list ap;
483   int ret;
484
485   va_start(ap, dst);
486   ret = silc_buffer_sformat_vp(stack, dst, ap);
487   va_end(ap);
488
489   return ret;
490 }
491
492 int silc_buffer_sformat_vp(SilcStack stack, SilcBuffer dst, va_list ap)
493 {
494   return silc_buffer_sformat_vp_i(stack, dst, ap, TRUE);
495 }
496
497 /****************************** Unformatting ********************************/
498
499 int silc_buffer_sunformat_vp_i(SilcStack stack, SilcBuffer src, va_list ap,
500                                SilcBool process)
501 {
502   SilcParam fmt;
503   unsigned char *start_ptr = src->data;
504   int len = 0;
505   SilcBool advance = FALSE;
506
507   /* Parse the arguments by formatting type. */
508   while (1) {
509     fmt = va_arg(ap, SilcParam);
510
511     SILC_LOG_DEBUG(("Buffer unformat type %d", fmt));
512
513     switch (fmt) {
514     case SILC_PARAM_FUNC:
515       {
516         SilcBufferUnformatFunc func;
517         void **val;
518         void *context;
519         int tmp_len;
520         func = va_arg(ap, SilcBufferUnformatFunc);
521         val = va_arg(ap, void **);
522         context = va_arg(ap, void *);
523
524         if (!process)
525           break;
526
527         tmp_len = func(stack, src, val, context);
528         if (tmp_len < 0)
529           goto fail;
530         if (tmp_len) {
531           UNFORMAT_HAS_SPACE(src, tmp_len);
532           silc_buffer_pull(src, tmp_len);
533         }
534       }
535       break;
536
537     case SILC_PARAM_REGEX:
538       {
539         const char *regex = va_arg(ap, char *);
540         SilcBufferRegexFlags rflags = va_arg(ap, SilcUInt32);
541         SilcBufferStruct match, saved;
542         SilcBool match_all = (rflags & SILC_STR_REGEX_ALL) != 0;
543         SilcBool match_nl = (rflags & SILC_STR_REGEX_NL) != 0;
544         SilcBool ret;
545         SilcUInt32 inclusive_pos = 0;
546         int matched = 0, ret_len;
547         va_list cp;
548
549         if (!process)
550           break;
551
552         if (!regex)
553           goto fail;
554
555         memset(&saved, 0, sizeof(saved));
556
557         if (match_nl) {
558         start_nl_match:
559           /* Match for '\n' in the buffer.  If not found, treat as line
560              without '\n' (buffer has only one line, or this is last line). */
561           saved = *src;
562           if (silc_regex_buffer(src, "\n", &match, NULL))
563             src->tail = match.tail;
564         }
565
566       start_match:
567         /* Match */
568         ret = silc_regex_buffer(src, regex, &match, NULL);
569         ret ^= (rflags & SILC_STR_REGEX_NOT) != 0;
570         if (!ret) {
571           if (!matched && rflags & SILC_STR_REGEX_MISMATCH) {
572             silc_set_errno(SILC_ERR_NOT_FOUND);
573             goto fail;
574           }
575           goto end_match;
576         }
577         matched++;
578
579         if (rflags & SILC_STR_REGEX_NOT)
580           match = *src;
581
582         if (!(rflags & SILC_STR_REGEX_NO_ADVANCE)) {
583           /* Advance buffer after match */
584           UNFORMAT_HAS_SPACE(src, (match.data - src->data));
585           if (!silc_buffer_pull(src, (match.data - src->data)))
586             goto fail;
587         }
588
589         if (rflags & SILC_STR_REGEX_INCLUSIVE) {
590           inclusive_pos = src->tail - match.tail;
591           src->tail = match.tail;
592         }
593
594         /* Recursively format */
595         silc_va_copy(cp, ap);
596         ret_len = silc_buffer_sunformat_vp_i(stack, src, cp, TRUE);
597         va_end(cp);
598         if (ret_len < 0)
599           goto fail;
600
601         if (rflags & SILC_STR_REGEX_INCLUSIVE)
602           if (!silc_buffer_pull_tail(src, inclusive_pos))
603             goto fail;
604
605         /* Advance buffer after formatting */
606         UNFORMAT_HAS_SPACE(src, ret_len);
607         if (!silc_buffer_pull(src, ret_len))
608           goto fail;
609
610         if (match_all && (!match_nl || silc_buffer_len(src) > 1))
611           goto start_match;
612
613       end_match:
614         if (match_nl) {
615           /* Go to next line, it is at the end of the data area.  Adjust
616              the tail area of the target buffer to show rest of the buffer. */
617           UNFORMAT_HAS_SPACE(src, src->tail - src->data);
618           if (!silc_buffer_pull(src, (src->tail - src->data)))
619             goto fail;
620           if (!silc_buffer_pull_tail(src, (saved.tail - src->tail)))
621             goto fail;
622
623           if (silc_buffer_len(src) > 0)
624             goto start_nl_match;
625         }
626
627         /* Skip to the next SILC_PARAM_END */
628         silc_buffer_sunformat_vp_i(NULL, src, ap, FALSE);
629       }
630       break;
631
632     case SILC_PARAM_UICHAR:
633       {
634         unsigned char **x = va_arg(ap, unsigned char **);
635         SilcUInt32 len2 = va_arg(ap, SilcUInt32);
636
637         if (!process)
638           break;
639
640         UNFORMAT_HAS_SPACE(src, len2);
641         if (silc_likely(len2 && x))
642           *x = src->data;
643         silc_buffer_pull(src, len2);
644         break;
645       }
646
647     case SILC_PARAM_UICHAR | SILC_PARAM_ALLOC:
648       {
649         unsigned char **x = va_arg(ap, unsigned char **);
650         SilcUInt32 len2 = va_arg(ap, SilcUInt32);
651
652         if (!process)
653           break;
654
655         UNFORMAT_HAS_SPACE(src, len2);
656         if (silc_likely(len2 && x)) {
657           *x = silc_scalloc(stack, len2 + 1, sizeof(unsigned char));
658           memcpy(*x, src->data, len2);
659         }
660         silc_buffer_pull(src, len2);
661         break;
662       }
663
664     case SILC_PARAM_UINT8:
665       {
666         unsigned char *x = va_arg(ap, unsigned char *);
667
668         if (!process)
669           break;
670
671         UNFORMAT_HAS_SPACE(src, 1);
672         if (silc_likely(x))
673           *x = src->data[0];
674         silc_buffer_pull(src, 1);
675         break;
676       }
677
678     case SILC_PARAM_UINT16:
679       {
680         SilcUInt16 *x = va_arg(ap, SilcUInt16 *);
681
682         if (!process)
683           break;
684
685         UNFORMAT_HAS_SPACE(src, 2);
686         if (silc_likely(x))
687           SILC_GET16_MSB(*x, src->data);
688         silc_buffer_pull(src, 2);
689         break;
690       }
691
692     case SILC_PARAM_UINT32:
693       {
694         SilcUInt32 *x = va_arg(ap, SilcUInt32 *);
695
696         if (!process)
697           break;
698
699         UNFORMAT_HAS_SPACE(src, 4);
700         if (silc_likely(x))
701           SILC_GET32_MSB(*x, src->data);
702         silc_buffer_pull(src, 4);
703         break;
704       }
705
706     case SILC_PARAM_UINT64:
707       {
708         SilcUInt64 *x = va_arg(ap, SilcUInt64 *);
709
710         if (!process)
711           break;
712
713         UNFORMAT_HAS_SPACE(src, sizeof(SilcUInt64));
714         if (silc_likely(x))
715           SILC_GET64_MSB(*x, src->data);
716         silc_buffer_pull(src, sizeof(SilcUInt64));
717         break;
718       }
719
720     case SILC_PARAM_SINT8:
721       {
722         char *x = va_arg(ap, char *);
723
724         if (!process)
725           break;
726
727         UNFORMAT_HAS_SPACE(src, 1);
728         if (silc_likely(x))
729           *x = src->data[0];
730         silc_buffer_pull(src, 1);
731         break;
732       }
733
734     case SILC_PARAM_SINT16:
735       {
736         SilcInt16 *x = va_arg(ap, SilcInt16 *);
737
738         if (!process)
739           break;
740
741         UNFORMAT_HAS_SPACE(src, 2);
742         if (silc_likely(x))
743           SILC_GET16_MSB(*x, src->data);
744         silc_buffer_pull(src, 2);
745         break;
746       }
747
748     case SILC_PARAM_SINT32:
749       {
750         SilcInt32 *x = va_arg(ap, SilcInt32 *);
751
752         if (!process)
753           break;
754
755         UNFORMAT_HAS_SPACE(src, 4);
756         if (silc_likely(x))
757           SILC_GET32_MSB(*x, src->data);
758         silc_buffer_pull(src, 4);
759         break;
760       }
761
762     case SILC_PARAM_SINT64:
763       {
764         SilcInt64 *x = va_arg(ap, SilcInt64 *);
765
766         if (!process)
767           break;
768
769         UNFORMAT_HAS_SPACE(src, sizeof(SilcInt64));
770         if (silc_likely(x))
771           SILC_GET64_MSB(*x, src->data);
772         silc_buffer_pull(src, sizeof(SilcInt64));
773         break;
774       }
775
776     case SILC_PARAM_UI8_STRING:
777       {
778         SilcUInt8 len2;
779         unsigned char **x = va_arg(ap, unsigned char **);
780
781         if (!process)
782           break;
783
784         UNFORMAT_HAS_SPACE(src, 1);
785         len2 = (SilcUInt8)src->data[0];
786         silc_buffer_pull(src, 1);
787         UNFORMAT_HAS_SPACE(src, len2);
788         if (silc_likely(x))
789           *x = src->data;
790         silc_buffer_pull(src, len2);
791         break;
792       }
793
794     case SILC_PARAM_UI8_STRING | SILC_PARAM_ALLOC:
795       {
796         SilcUInt8 len2;
797         unsigned char **x = va_arg(ap, unsigned char **);
798
799         if (!process)
800           break;
801
802         UNFORMAT_HAS_SPACE(src, 1);
803         len2 = (SilcUInt8)src->data[0];
804         silc_buffer_pull(src, 1);
805         UNFORMAT_HAS_SPACE(src, len2);
806         if (silc_likely(x && len2)) {
807           *x = silc_scalloc(stack, len2 + 1, sizeof(unsigned char));
808           memcpy(*x, src->data, len2);
809         }
810         silc_buffer_pull(src, len2);
811         break;
812       }
813
814     case SILC_PARAM_UI16_STRING:
815       {
816         SilcUInt16 len2;
817         unsigned char **x = va_arg(ap, unsigned char **);
818
819         if (!process)
820           break;
821
822         UNFORMAT_HAS_SPACE(src, 2);
823         SILC_GET16_MSB(len2, src->data);
824         silc_buffer_pull(src, 2);
825         UNFORMAT_HAS_SPACE(src, len2);
826         if (silc_likely(x))
827           *x = src->data;
828         silc_buffer_pull(src, len2);
829         break;
830       }
831
832     case SILC_PARAM_UI16_STRING | SILC_PARAM_ALLOC:
833       {
834         SilcUInt16 len2;
835         unsigned char **x = va_arg(ap, unsigned char **);
836
837         if (!process)
838           break;
839
840         UNFORMAT_HAS_SPACE(src, 2);
841         SILC_GET16_MSB(len2, src->data);
842         silc_buffer_pull(src, 2);
843         UNFORMAT_HAS_SPACE(src, len2);
844         if (silc_likely(x && len2)) {
845           *x = silc_scalloc(stack, len2 + 1, sizeof(unsigned char));
846           memcpy(*x, src->data, len2);
847         }
848         silc_buffer_pull(src, len2);
849         break;
850       }
851
852     case SILC_PARAM_UI32_STRING:
853       {
854         SilcUInt32 len2;
855         unsigned char **x = va_arg(ap, unsigned char **);
856
857         if (!process)
858           break;
859
860         UNFORMAT_HAS_SPACE(src, 4);
861         SILC_GET32_MSB(len2, src->data);
862         silc_buffer_pull(src, 4);
863         UNFORMAT_HAS_SPACE(src, len2);
864         if (silc_likely(x))
865           *x = src->data;
866         silc_buffer_pull(src, len2);
867         break;
868       }
869
870     case SILC_PARAM_UI32_STRING | SILC_PARAM_ALLOC:
871       {
872         SilcUInt32 len2;
873         unsigned char **x = va_arg(ap, unsigned char **);
874
875         if (!process)
876           break;
877
878         UNFORMAT_HAS_SPACE(src, 4);
879         SILC_GET32_MSB(len2, src->data);
880         silc_buffer_pull(src, 4);
881         UNFORMAT_HAS_SPACE(src, len2);
882         if (silc_likely(x && len2)) {
883           *x = silc_scalloc(stack, len2 + 1, sizeof(unsigned char));
884           memcpy(*x, src->data, len2);
885         }
886         silc_buffer_pull(src, len2);
887         break;
888       }
889
890     case SILC_PARAM_UI8_NSTRING:
891       {
892         SilcUInt8 len2;
893         unsigned char **x = va_arg(ap, unsigned char **);
894         SilcUInt8 *len3 = va_arg(ap, SilcUInt8 *);
895
896         if (!process)
897           break;
898
899         UNFORMAT_HAS_SPACE(src, 1);
900         len2 = (SilcUInt8)src->data[0];
901         silc_buffer_pull(src, 1);
902         UNFORMAT_HAS_SPACE(src, len2);
903         if (len3)
904           *len3 = len2;
905         if (x)
906           *x = src->data;
907         silc_buffer_pull(src, len2);
908         break;
909       }
910
911     case SILC_PARAM_UI8_NSTRING | SILC_PARAM_ALLOC:
912       {
913         SilcUInt8 len2;
914         unsigned char **x = va_arg(ap, unsigned char **);
915         SilcUInt8 *len3 = va_arg(ap, SilcUInt8 *);
916
917         if (!process)
918           break;
919
920         UNFORMAT_HAS_SPACE(src, 1);
921         len2 = (SilcUInt8)src->data[0];
922         silc_buffer_pull(src, 1);
923         UNFORMAT_HAS_SPACE(src, len2);
924         if (len3)
925           *len3 = len2;
926         if (x && len2) {
927           *x = silc_scalloc(stack, len2 + 1, sizeof(unsigned char));
928           memcpy(*x, src->data, len2);
929         }
930         silc_buffer_pull(src, len2);
931         break;
932       }
933
934     case SILC_PARAM_UI16_NSTRING:
935       {
936         SilcUInt16 len2;
937         unsigned char **x = va_arg(ap, unsigned char **);
938         SilcUInt16 *len3 = va_arg(ap, SilcUInt16 *);
939
940         if (!process)
941           break;
942
943         UNFORMAT_HAS_SPACE(src, 2);
944         SILC_GET16_MSB(len2, src->data);
945         silc_buffer_pull(src, 2);
946         UNFORMAT_HAS_SPACE(src, len2);
947         if (len3)
948           *len3 = len2;
949         if (x)
950           *x = src->data;
951         silc_buffer_pull(src, len2);
952         break;
953       }
954
955     case SILC_PARAM_UI16_NSTRING | SILC_PARAM_ALLOC:
956       {
957         SilcUInt16 len2;
958         unsigned char **x = va_arg(ap, unsigned char **);
959         SilcUInt16 *len3 = va_arg(ap, SilcUInt16 *);
960
961         if (!process)
962           break;
963
964         UNFORMAT_HAS_SPACE(src, 2);
965         SILC_GET16_MSB(len2, src->data);
966         silc_buffer_pull(src, 2);
967         UNFORMAT_HAS_SPACE(src, len2);
968         if (len3)
969           *len3 = len2;
970         if (x && len2) {
971           *x = silc_scalloc(stack, len2 + 1, sizeof(unsigned char));
972           memcpy(*x, src->data, len2);
973         }
974         silc_buffer_pull(src, len2);
975         break;
976       }
977
978     case SILC_PARAM_UI32_NSTRING:
979       {
980         SilcUInt32 len2;
981         unsigned char **x = va_arg(ap, unsigned char **);
982         SilcUInt32 *len3 = va_arg(ap, SilcUInt32 *);
983
984         if (!process)
985           break;
986
987         UNFORMAT_HAS_SPACE(src, 4);
988         SILC_GET32_MSB(len2, src->data);
989         silc_buffer_pull(src, 4);
990         UNFORMAT_HAS_SPACE(src, len2);
991         if (len3)
992           *len3 = len2;
993         if (x)
994           *x = src->data;
995         silc_buffer_pull(src, len2);
996         break;
997       }
998
999     case SILC_PARAM_UI32_NSTRING | SILC_PARAM_ALLOC:
1000       {
1001         SilcUInt32 len2;
1002         unsigned char **x = va_arg(ap, unsigned char **);
1003         SilcUInt32 *len3 = va_arg(ap, SilcUInt32 *);
1004
1005         if (!process)
1006           break;
1007
1008         UNFORMAT_HAS_SPACE(src, 4);
1009         SILC_GET32_MSB(len2, src->data);
1010         silc_buffer_pull(src, 4);
1011         UNFORMAT_HAS_SPACE(src, len2);
1012         if (len3)
1013           *len3 = len2;
1014         if (silc_likely(x && len2)) {
1015           *x = silc_scalloc(stack, len2 + 1, sizeof(unsigned char));
1016           memcpy(*x, src->data, len2);
1017         }
1018         silc_buffer_pull(src, len2);
1019         break;
1020       }
1021
1022     case SILC_PARAM_BUFFER:
1023       {
1024         SilcBuffer x = va_arg(ap, SilcBuffer);
1025         SilcUInt32 len2;
1026
1027         if (!process)
1028           break;
1029
1030         UNFORMAT_HAS_SPACE(src, 4);
1031         SILC_GET32_MSB(len2, src->data);
1032         silc_buffer_pull(src, 4);
1033         UNFORMAT_HAS_SPACE(src, len2);
1034         silc_buffer_set(x, src->data, len2);
1035         silc_buffer_pull(src, len2);
1036       }
1037       break;
1038
1039     case SILC_PARAM_BUFFER | SILC_PARAM_ALLOC:
1040       {
1041         SilcBuffer x = va_arg(ap, SilcBuffer);
1042         SilcUInt32 len2;
1043
1044         if (!process)
1045           break;
1046
1047         UNFORMAT_HAS_SPACE(src, 4);
1048         SILC_GET32_MSB(len2, src->data);
1049         silc_buffer_pull(src, 4);
1050         UNFORMAT_HAS_SPACE(src, len2);
1051         silc_buffer_sformat(stack, x,
1052                             SILC_STR_DATA(src->data, len2),
1053                             SILC_STR_END);
1054         silc_buffer_pull(src, len2);
1055       }
1056       break;
1057
1058     case SILC_PARAM_OFFSET:
1059       {
1060         int offst = va_arg(ap, int);
1061
1062         if (!process)
1063           break;
1064
1065         if (!offst)
1066           break;
1067
1068         if (offst > 1) {
1069           UNFORMAT_HAS_SPACE(src, offst);
1070           silc_buffer_pull(src, offst);
1071         } else {
1072           silc_buffer_push(src, -(offst));
1073         }
1074         break;
1075       }
1076
1077     case SILC_PARAM_OFFSET_START:
1078       if (!process)
1079         break;
1080       silc_buffer_push(src, (src->data - start_ptr));
1081       break;
1082
1083     case SILC_PARAM_OFFSET_END:
1084       if (!process)
1085         break;
1086       silc_buffer_pull(src, silc_buffer_len(src));
1087       break;
1088
1089     case SILC_PARAM_ADVANCE:
1090       if (!process)
1091         break;
1092       advance = TRUE;
1093       break;
1094
1095     case SILC_PARAM_END:
1096       goto ok;
1097       break;
1098
1099     default:
1100       SILC_LOG_DEBUG(("Bad buffer formatting type `%d'. Could not "
1101                       "format the data.", fmt));
1102       silc_set_errno_reason(SILC_ERR_INVALID_ARGUMENT,
1103                             "Bad buffer formatting type %d", fmt);
1104       goto fail;
1105       break;
1106     }
1107   }
1108
1109  fail:
1110   SILC_LOG_DEBUG(("Error occured while unformatting buffer, type %d", fmt));
1111   if (process && !advance) {
1112     len = src->data - start_ptr;
1113     silc_buffer_push(src, len);
1114   }
1115   return -1;
1116
1117  ok:
1118   /* Push the buffer back to the start. */
1119   if (process && !advance) {
1120     len = src->data - start_ptr;
1121     silc_buffer_push(src, len);
1122   }
1123   return len;
1124 }
1125
1126 int silc_buffer_unformat(SilcBuffer src, ...)
1127 {
1128   va_list ap;
1129   int ret;
1130
1131   va_start(ap, src);
1132   ret = silc_buffer_sunformat_vp(NULL, src, ap);
1133   va_end(ap);
1134
1135   return ret;
1136 }
1137
1138 int silc_buffer_unformat_vp(SilcBuffer src, va_list ap)
1139 {
1140   return silc_buffer_sunformat_vp(NULL, src, ap);
1141 }
1142
1143 int silc_buffer_sunformat(SilcStack stack, SilcBuffer src, ...)
1144 {
1145   va_list ap;
1146   int ret;
1147
1148   va_start(ap, src);
1149   ret = silc_buffer_sunformat_vp(stack, src, ap);
1150   va_end(ap);
1151
1152   return ret;
1153 }
1154
1155 int silc_buffer_sunformat_vp(SilcStack stack, SilcBuffer src, va_list ap)
1156 {
1157   return silc_buffer_sunformat_vp_i(stack, src, ap, TRUE);
1158 }
1159
1160 /**************************** Utility functions *****************************/
1161
1162 /* Formats strings into a buffer */
1163
1164 int silc_buffer_strformat(SilcBuffer dst, ...)
1165 {
1166   int len = silc_buffer_truelen(dst);
1167   int hlen = silc_buffer_headlen(dst);
1168   va_list va;
1169
1170   va_start(va, dst);
1171
1172   /* Parse the arguments by formatting type. */
1173   while(1) {
1174     char *string = va_arg(va, char *);
1175     unsigned char *d;
1176     SilcInt32 slen;
1177
1178     if (!string)
1179       continue;
1180     if (string == (char *)SILC_PARAM_END)
1181       goto ok;
1182
1183     slen = strlen(string);
1184     d = silc_realloc(dst->head, sizeof(*dst->head) * (slen + len + 1));
1185     if (silc_unlikely(!d))
1186       return -1;
1187     dst->head = d;
1188     memcpy(dst->head + len, string, slen);
1189     len += slen;
1190     dst->head[len] = '\0';
1191   }
1192
1193   SILC_LOG_DEBUG(("Error occured while formatting buffer"));
1194   va_end(va);
1195   return -1;
1196
1197  ok:
1198   dst->end = dst->head + len;
1199   dst->data = dst->head + hlen;
1200   dst->tail = dst->end;
1201
1202   va_end(va);
1203   return len;
1204 }
1205
1206 /* Formats strings into a buffer.  Allocates memory from SilcStack. */
1207
1208 int silc_buffer_sstrformat(SilcStack stack, SilcBuffer dst, ...)
1209 {
1210   int len = silc_buffer_truelen(dst);
1211   int hlen = silc_buffer_headlen(dst);
1212   va_list va;
1213
1214   va_start(va, dst);
1215
1216   /* Parse the arguments by formatting type. */
1217   while(1) {
1218     char *string = va_arg(va, char *);
1219     unsigned char *d;
1220     SilcInt32 slen;
1221
1222     if (!string)
1223       continue;
1224     if (string == (char *)SILC_PARAM_END)
1225       goto ok;
1226
1227     slen = strlen(string);
1228     d = silc_srealloc(stack, len + 1, dst->head,
1229                       sizeof(*dst->head) * (slen + len + 1));
1230     if (silc_unlikely(!d))
1231       return -1;
1232     dst->head = d;
1233     memcpy(dst->head + len, string, slen);
1234     len += slen;
1235     dst->head[len] = '\0';
1236   }
1237
1238   SILC_LOG_DEBUG(("Error occured while formatting buffer"));
1239   va_end(va);
1240   return -1;
1241
1242  ok:
1243   dst->end = dst->head + len;
1244   dst->data = dst->head + hlen;
1245   dst->tail = dst->end;
1246
1247   va_end(va);
1248   return len;
1249 }