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