7f92c6bf0dc2f84fd6c138ed772436932b217b35
[silc.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;
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 saved_pos = 0, 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         if (match_nl) {
117         start_nl_match:
118           /* Match for '\n' in the buffer.  If not found, treat as line
119              without '\n' (buffer has only one line, or this is last line). */
120           saved_pos = silc_buffer_headlen(dst) + silc_buffer_len(dst);
121           if (silc_regex_buffer(dst, "\n", &match, NULL))
122             dst->tail = match.tail;
123         }
124
125       start_match:
126         /* Match */
127         ret = silc_regex_buffer(dst, regex, &match, NULL);
128         ret ^= (rflags & SILC_STR_REGEX_NOT) != 0;
129         if (!ret) {
130           if (!matched && rflags & SILC_STR_REGEX_MISMATCH) {
131             silc_set_errno(SILC_ERR_NOT_FOUND);
132             goto fail;
133           }
134           goto end_match;
135         }
136         matched++;
137
138         if (rflags & SILC_STR_REGEX_NOT)
139           match = *dst;
140
141         if (!(rflags & SILC_STR_REGEX_NO_ADVANCE)) {
142           /* Advance buffer after match */
143           flen += (match.data - dst->data);
144           if (!silc_buffer_pull(dst, (match.data - dst->data)))
145             goto fail;
146         }
147
148         if (rflags & SILC_STR_REGEX_INCLUSIVE) {
149           inclusive_pos = dst->tail - match.tail;
150           dst->tail = match.tail;
151         }
152
153         /* Recursively format */
154         silc_va_copy(cp, ap);
155         ret_len = silc_buffer_sformat_vp_i(stack, dst, cp, TRUE);
156         va_end(cp);
157         if (ret_len < 0)
158           goto fail;
159
160         if (rflags & SILC_STR_REGEX_INCLUSIVE)
161           if (!silc_buffer_pull_tail(dst, inclusive_pos))
162             goto fail;
163
164         /* Advance buffer after formatting */
165         flen += ret_len;
166         if (!silc_buffer_pull(dst, ret_len))
167           goto fail;
168
169         if (match_all && (!match_nl || silc_buffer_len(dst) > 1))
170           goto start_match;
171
172       end_match:
173         if (match_nl) {
174           /* Go to next line, it is at the end of the data area.  Adjust
175              the tail area of the target buffer to show rest of the buffer. */
176           flen += (dst->tail - dst->data);
177           if (!silc_buffer_pull(dst, (dst->tail - dst->data)))
178             goto fail;
179           if (!silc_buffer_pull_tail(dst, (saved_pos -
180                                            silc_buffer_headlen(dst) +
181                                            silc_buffer_len(dst))))
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_DELETE:
392       {
393         int n = va_arg(ap, int);
394
395         if (!process)
396           break;
397
398         if (n == -1) {
399           /* Move all data from tail to data area */
400           if (dst->data != dst->tail) {
401             memmove(dst->data, dst->tail, silc_buffer_taillen(dst));
402             memset(dst->end - silc_buffer_len(dst), 0, silc_buffer_len(dst));
403             silc_buffer_push_tail(dst, silc_buffer_len(dst));
404           }
405           break;
406         }
407
408         if (n > silc_buffer_len(dst))
409           goto fail;
410
411         memmove(dst->data, dst->data + n, (silc_buffer_len(dst) - n) +
412                 silc_buffer_taillen(dst));
413         memset(dst->end - n, 0, n);
414         silc_buffer_push_tail(dst, silc_buffer_len(dst) - n);
415
416         break;
417       }
418
419     case SILC_PARAM_OFFSET:
420       {
421         int offst = va_arg(ap, int);
422
423         if (!process)
424           break;
425
426         if (!offst)
427           break;
428
429         if (offst > 1) {
430           if (offst > silc_buffer_len(dst)) {
431             silc_set_errno(SILC_ERR_OVERFLOW);
432             goto fail;
433           }
434           silc_buffer_pull(dst, offst);
435           flen += offst;
436         } else {
437           silc_buffer_push(dst, -(offst));
438           flen += -(offst);
439         }
440         break;
441       }
442
443     case SILC_PARAM_OFFSET_START:
444       if (!process)
445         break;
446       if (!silc_buffer_push(dst, flen))
447         goto fail;
448       flen = 0;
449       break;
450
451     case SILC_PARAM_OFFSET_END:
452       if (!process)
453         break;
454       flen += silc_buffer_len(dst);
455       silc_buffer_pull(dst, silc_buffer_len(dst));
456       break;
457
458     case SILC_PARAM_ADVANCE:
459       if (!process)
460         break;
461       advance = TRUE;
462       break;
463
464     case SILC_PARAM_END:
465       goto ok;
466       break;
467
468     default:
469       SILC_LOG_DEBUG(("Bad buffer formatting type `%d'. Could not "
470                       "format the data.", fmt));
471       silc_set_errno_reason(SILC_ERR_INVALID_ARGUMENT,
472                             "Bad buffer formatting type %d", fmt);
473       goto fail;
474       break;
475     }
476   }
477
478  fail:
479   SILC_LOG_DEBUG(("Error occured while formatting data"));
480   if (process && !advance)
481     silc_buffer_push(dst, flen);
482   return -1;
483
484  ok:
485   /* Push the buffer back to where it belongs. */
486   if (process && !advance)
487     silc_buffer_push(dst, flen);
488   return flen;
489 }
490
491 int silc_buffer_format(SilcBuffer dst, ...)
492 {
493   va_list ap;
494   int ret;
495
496   va_start(ap, dst);
497   ret = silc_buffer_sformat_vp(NULL, dst, ap);
498   va_end(ap);
499
500   return ret;
501 }
502
503 int silc_buffer_format_vp(SilcBuffer dst, va_list ap)
504 {
505   return silc_buffer_sformat_vp(NULL, dst, ap);
506 }
507
508 int silc_buffer_sformat(SilcStack stack, SilcBuffer dst, ...)
509 {
510   va_list ap;
511   int ret;
512
513   va_start(ap, dst);
514   ret = silc_buffer_sformat_vp(stack, dst, ap);
515   va_end(ap);
516
517   return ret;
518 }
519
520 int silc_buffer_sformat_vp(SilcStack stack, SilcBuffer dst, va_list ap)
521 {
522   return silc_buffer_sformat_vp_i(stack, dst, ap, TRUE);
523 }
524
525 /****************************** Unformatting ********************************/
526
527 int silc_buffer_sunformat_vp_i(SilcStack stack, SilcBuffer src, va_list ap,
528                                SilcBool process)
529 {
530   SilcParam fmt;
531   unsigned char *start_ptr = src->data;
532   int len = 0;
533   SilcBool advance = FALSE;
534
535   /* Parse the arguments by formatting type. */
536   while (1) {
537     fmt = va_arg(ap, SilcParam);
538
539     SILC_LOG_DEBUG(("Buffer unformat type %d", fmt));
540
541     switch (fmt) {
542     case SILC_PARAM_FUNC:
543       {
544         SilcBufferUnformatFunc func;
545         void **val;
546         void *context;
547         int tmp_len;
548         func = va_arg(ap, SilcBufferUnformatFunc);
549         val = va_arg(ap, void **);
550         context = va_arg(ap, void *);
551
552         if (!process)
553           break;
554
555         tmp_len = func(stack, src, val, context);
556         if (tmp_len < 0)
557           goto fail;
558         if (tmp_len) {
559           UNFORMAT_HAS_SPACE(src, tmp_len);
560           silc_buffer_pull(src, tmp_len);
561         }
562       }
563       break;
564
565     case SILC_PARAM_REGEX:
566       {
567         const char *regex = va_arg(ap, char *);
568         SilcBufferRegexFlags rflags = va_arg(ap, SilcUInt32);
569         SilcBufferStruct match;
570         SilcBool match_all = (rflags & SILC_STR_REGEX_ALL) != 0;
571         SilcBool match_nl = (rflags & SILC_STR_REGEX_NL) != 0;
572         SilcBool ret;
573         SilcUInt32 saved_pos = 0, inclusive_pos = 0;
574         int matched = 0, ret_len;
575         va_list cp;
576
577         if (!process)
578           break;
579
580         if (!regex)
581           goto fail;
582
583
584         if (match_nl) {
585         start_nl_match:
586           /* Match for '\n' in the buffer.  If not found, treat as line
587              without '\n' (buffer has only one line, or this is last line). */
588           saved_pos = silc_buffer_headlen(src) + silc_buffer_len(src);
589           if (silc_regex_buffer(src, "\n", &match, NULL))
590             src->tail = match.tail;
591         }
592
593       start_match:
594         /* Match */
595         ret = silc_regex_buffer(src, regex, &match, NULL);
596         ret ^= (rflags & SILC_STR_REGEX_NOT) != 0;
597         if (!ret) {
598           if (!matched && rflags & SILC_STR_REGEX_MISMATCH) {
599             silc_set_errno(SILC_ERR_NOT_FOUND);
600             goto fail;
601           }
602           goto end_match;
603         }
604         matched++;
605
606         if (rflags & SILC_STR_REGEX_NOT)
607           match = *src;
608
609         if (!(rflags & SILC_STR_REGEX_NO_ADVANCE)) {
610           /* Advance buffer after match */
611           UNFORMAT_HAS_SPACE(src, (match.data - src->data));
612           if (!silc_buffer_pull(src, (match.data - src->data)))
613             goto fail;
614         }
615
616         if (rflags & SILC_STR_REGEX_INCLUSIVE) {
617           inclusive_pos = src->tail - match.tail;
618           src->tail = match.tail;
619         }
620
621         /* Recursively format */
622         silc_va_copy(cp, ap);
623         ret_len = silc_buffer_sunformat_vp_i(stack, src, cp, TRUE);
624         va_end(cp);
625         if (ret_len < 0)
626           goto fail;
627
628         if (rflags & SILC_STR_REGEX_INCLUSIVE)
629           if (!silc_buffer_pull_tail(src, inclusive_pos))
630             goto fail;
631
632         /* Advance buffer after formatting */
633         UNFORMAT_HAS_SPACE(src, ret_len);
634         if (!silc_buffer_pull(src, ret_len))
635           goto fail;
636
637         if (match_all && (!match_nl || silc_buffer_len(src) > 1))
638           goto start_match;
639
640       end_match:
641         if (match_nl) {
642           /* Go to next line, it is at the end of the data area.  Adjust
643              the tail area of the target buffer to show rest of the buffer. */
644           UNFORMAT_HAS_SPACE(src, src->tail - src->data);
645           if (!silc_buffer_pull(src, (src->tail - src->data)))
646             goto fail;
647           if (!silc_buffer_pull_tail(src, (saved_pos -
648                                            silc_buffer_headlen(src) +
649                                            silc_buffer_len(src))))
650             goto fail;
651
652           if (silc_buffer_len(src) > 0)
653             goto start_nl_match;
654         }
655
656         /* Skip to the next SILC_PARAM_END */
657         silc_buffer_sunformat_vp_i(NULL, src, ap, FALSE);
658       }
659       break;
660
661     case SILC_PARAM_UICHAR:
662       {
663         unsigned char **x = va_arg(ap, unsigned char **);
664         SilcUInt32 len2 = va_arg(ap, SilcUInt32);
665
666         if (!process)
667           break;
668
669         UNFORMAT_HAS_SPACE(src, len2);
670         if (silc_likely(len2 && x))
671           *x = src->data;
672         silc_buffer_pull(src, len2);
673         break;
674       }
675
676     case SILC_PARAM_UICHAR | SILC_PARAM_ALLOC:
677       {
678         unsigned char **x = va_arg(ap, unsigned char **);
679         SilcUInt32 len2 = va_arg(ap, SilcUInt32);
680
681         if (!process)
682           break;
683
684         UNFORMAT_HAS_SPACE(src, len2);
685         if (silc_likely(len2 && x)) {
686           *x = silc_scalloc(stack, len2 + 1, sizeof(unsigned char));
687           memcpy(*x, src->data, len2);
688         }
689         silc_buffer_pull(src, len2);
690         break;
691       }
692
693     case SILC_PARAM_UINT8:
694       {
695         unsigned char *x = va_arg(ap, unsigned char *);
696
697         if (!process)
698           break;
699
700         UNFORMAT_HAS_SPACE(src, 1);
701         if (silc_likely(x))
702           *x = src->data[0];
703         silc_buffer_pull(src, 1);
704         break;
705       }
706
707     case SILC_PARAM_UINT16:
708       {
709         SilcUInt16 *x = va_arg(ap, SilcUInt16 *);
710
711         if (!process)
712           break;
713
714         UNFORMAT_HAS_SPACE(src, 2);
715         if (silc_likely(x))
716           SILC_GET16_MSB(*x, src->data);
717         silc_buffer_pull(src, 2);
718         break;
719       }
720
721     case SILC_PARAM_UINT32:
722       {
723         SilcUInt32 *x = va_arg(ap, SilcUInt32 *);
724
725         if (!process)
726           break;
727
728         UNFORMAT_HAS_SPACE(src, 4);
729         if (silc_likely(x))
730           SILC_GET32_MSB(*x, src->data);
731         silc_buffer_pull(src, 4);
732         break;
733       }
734
735     case SILC_PARAM_UINT64:
736       {
737         SilcUInt64 *x = va_arg(ap, SilcUInt64 *);
738
739         if (!process)
740           break;
741
742         UNFORMAT_HAS_SPACE(src, sizeof(SilcUInt64));
743         if (silc_likely(x))
744           SILC_GET64_MSB(*x, src->data);
745         silc_buffer_pull(src, sizeof(SilcUInt64));
746         break;
747       }
748
749     case SILC_PARAM_SINT8:
750       {
751         char *x = va_arg(ap, char *);
752
753         if (!process)
754           break;
755
756         UNFORMAT_HAS_SPACE(src, 1);
757         if (silc_likely(x))
758           *x = src->data[0];
759         silc_buffer_pull(src, 1);
760         break;
761       }
762
763     case SILC_PARAM_SINT16:
764       {
765         SilcInt16 *x = va_arg(ap, SilcInt16 *);
766
767         if (!process)
768           break;
769
770         UNFORMAT_HAS_SPACE(src, 2);
771         if (silc_likely(x))
772           SILC_GET16_MSB(*x, src->data);
773         silc_buffer_pull(src, 2);
774         break;
775       }
776
777     case SILC_PARAM_SINT32:
778       {
779         SilcInt32 *x = va_arg(ap, SilcInt32 *);
780
781         if (!process)
782           break;
783
784         UNFORMAT_HAS_SPACE(src, 4);
785         if (silc_likely(x))
786           SILC_GET32_MSB(*x, src->data);
787         silc_buffer_pull(src, 4);
788         break;
789       }
790
791     case SILC_PARAM_SINT64:
792       {
793         SilcInt64 *x = va_arg(ap, SilcInt64 *);
794
795         if (!process)
796           break;
797
798         UNFORMAT_HAS_SPACE(src, sizeof(SilcInt64));
799         if (silc_likely(x))
800           SILC_GET64_MSB(*x, src->data);
801         silc_buffer_pull(src, sizeof(SilcInt64));
802         break;
803       }
804
805     case SILC_PARAM_UI8_STRING:
806       {
807         SilcUInt8 len2;
808         unsigned char **x = va_arg(ap, unsigned char **);
809
810         if (!process)
811           break;
812
813         UNFORMAT_HAS_SPACE(src, 1);
814         len2 = (SilcUInt8)src->data[0];
815         silc_buffer_pull(src, 1);
816         UNFORMAT_HAS_SPACE(src, len2);
817         if (silc_likely(x))
818           *x = src->data;
819         silc_buffer_pull(src, len2);
820         break;
821       }
822
823     case SILC_PARAM_UI8_STRING | SILC_PARAM_ALLOC:
824       {
825         SilcUInt8 len2;
826         unsigned char **x = va_arg(ap, unsigned char **);
827
828         if (!process)
829           break;
830
831         UNFORMAT_HAS_SPACE(src, 1);
832         len2 = (SilcUInt8)src->data[0];
833         silc_buffer_pull(src, 1);
834         UNFORMAT_HAS_SPACE(src, len2);
835         if (silc_likely(x && len2)) {
836           *x = silc_scalloc(stack, len2 + 1, sizeof(unsigned char));
837           memcpy(*x, src->data, len2);
838         }
839         silc_buffer_pull(src, len2);
840         break;
841       }
842
843     case SILC_PARAM_UI16_STRING:
844       {
845         SilcUInt16 len2;
846         unsigned char **x = va_arg(ap, unsigned char **);
847
848         if (!process)
849           break;
850
851         UNFORMAT_HAS_SPACE(src, 2);
852         SILC_GET16_MSB(len2, src->data);
853         silc_buffer_pull(src, 2);
854         UNFORMAT_HAS_SPACE(src, len2);
855         if (silc_likely(x))
856           *x = src->data;
857         silc_buffer_pull(src, len2);
858         break;
859       }
860
861     case SILC_PARAM_UI16_STRING | SILC_PARAM_ALLOC:
862       {
863         SilcUInt16 len2;
864         unsigned char **x = va_arg(ap, unsigned char **);
865
866         if (!process)
867           break;
868
869         UNFORMAT_HAS_SPACE(src, 2);
870         SILC_GET16_MSB(len2, src->data);
871         silc_buffer_pull(src, 2);
872         UNFORMAT_HAS_SPACE(src, len2);
873         if (silc_likely(x && len2)) {
874           *x = silc_scalloc(stack, len2 + 1, sizeof(unsigned char));
875           memcpy(*x, src->data, len2);
876         }
877         silc_buffer_pull(src, len2);
878         break;
879       }
880
881     case SILC_PARAM_UI32_STRING:
882       {
883         SilcUInt32 len2;
884         unsigned char **x = va_arg(ap, unsigned char **);
885
886         if (!process)
887           break;
888
889         UNFORMAT_HAS_SPACE(src, 4);
890         SILC_GET32_MSB(len2, src->data);
891         silc_buffer_pull(src, 4);
892         UNFORMAT_HAS_SPACE(src, len2);
893         if (silc_likely(x))
894           *x = src->data;
895         silc_buffer_pull(src, len2);
896         break;
897       }
898
899     case SILC_PARAM_UI32_STRING | SILC_PARAM_ALLOC:
900       {
901         SilcUInt32 len2;
902         unsigned char **x = va_arg(ap, unsigned char **);
903
904         if (!process)
905           break;
906
907         UNFORMAT_HAS_SPACE(src, 4);
908         SILC_GET32_MSB(len2, src->data);
909         silc_buffer_pull(src, 4);
910         UNFORMAT_HAS_SPACE(src, len2);
911         if (silc_likely(x && len2)) {
912           *x = silc_scalloc(stack, len2 + 1, sizeof(unsigned char));
913           memcpy(*x, src->data, len2);
914         }
915         silc_buffer_pull(src, len2);
916         break;
917       }
918
919     case SILC_PARAM_UI8_NSTRING:
920       {
921         SilcUInt8 len2;
922         unsigned char **x = va_arg(ap, unsigned char **);
923         SilcUInt8 *len3 = va_arg(ap, SilcUInt8 *);
924
925         if (!process)
926           break;
927
928         UNFORMAT_HAS_SPACE(src, 1);
929         len2 = (SilcUInt8)src->data[0];
930         silc_buffer_pull(src, 1);
931         UNFORMAT_HAS_SPACE(src, len2);
932         if (len3)
933           *len3 = len2;
934         if (x)
935           *x = src->data;
936         silc_buffer_pull(src, len2);
937         break;
938       }
939
940     case SILC_PARAM_UI8_NSTRING | SILC_PARAM_ALLOC:
941       {
942         SilcUInt8 len2;
943         unsigned char **x = va_arg(ap, unsigned char **);
944         SilcUInt8 *len3 = va_arg(ap, SilcUInt8 *);
945
946         if (!process)
947           break;
948
949         UNFORMAT_HAS_SPACE(src, 1);
950         len2 = (SilcUInt8)src->data[0];
951         silc_buffer_pull(src, 1);
952         UNFORMAT_HAS_SPACE(src, len2);
953         if (len3)
954           *len3 = len2;
955         if (x && len2) {
956           *x = silc_scalloc(stack, len2 + 1, sizeof(unsigned char));
957           memcpy(*x, src->data, len2);
958         }
959         silc_buffer_pull(src, len2);
960         break;
961       }
962
963     case SILC_PARAM_UI16_NSTRING:
964       {
965         SilcUInt16 len2;
966         unsigned char **x = va_arg(ap, unsigned char **);
967         SilcUInt16 *len3 = va_arg(ap, SilcUInt16 *);
968
969         if (!process)
970           break;
971
972         UNFORMAT_HAS_SPACE(src, 2);
973         SILC_GET16_MSB(len2, src->data);
974         silc_buffer_pull(src, 2);
975         UNFORMAT_HAS_SPACE(src, len2);
976         if (len3)
977           *len3 = len2;
978         if (x)
979           *x = src->data;
980         silc_buffer_pull(src, len2);
981         break;
982       }
983
984     case SILC_PARAM_UI16_NSTRING | SILC_PARAM_ALLOC:
985       {
986         SilcUInt16 len2;
987         unsigned char **x = va_arg(ap, unsigned char **);
988         SilcUInt16 *len3 = va_arg(ap, SilcUInt16 *);
989
990         if (!process)
991           break;
992
993         UNFORMAT_HAS_SPACE(src, 2);
994         SILC_GET16_MSB(len2, src->data);
995         silc_buffer_pull(src, 2);
996         UNFORMAT_HAS_SPACE(src, len2);
997         if (len3)
998           *len3 = len2;
999         if (x && len2) {
1000           *x = silc_scalloc(stack, len2 + 1, sizeof(unsigned char));
1001           memcpy(*x, src->data, len2);
1002         }
1003         silc_buffer_pull(src, len2);
1004         break;
1005       }
1006
1007     case SILC_PARAM_UI32_NSTRING:
1008       {
1009         SilcUInt32 len2;
1010         unsigned char **x = va_arg(ap, unsigned char **);
1011         SilcUInt32 *len3 = va_arg(ap, SilcUInt32 *);
1012
1013         if (!process)
1014           break;
1015
1016         UNFORMAT_HAS_SPACE(src, 4);
1017         SILC_GET32_MSB(len2, src->data);
1018         silc_buffer_pull(src, 4);
1019         UNFORMAT_HAS_SPACE(src, len2);
1020         if (len3)
1021           *len3 = len2;
1022         if (x)
1023           *x = src->data;
1024         silc_buffer_pull(src, len2);
1025         break;
1026       }
1027
1028     case SILC_PARAM_UI32_NSTRING | SILC_PARAM_ALLOC:
1029       {
1030         SilcUInt32 len2;
1031         unsigned char **x = va_arg(ap, unsigned char **);
1032         SilcUInt32 *len3 = va_arg(ap, SilcUInt32 *);
1033
1034         if (!process)
1035           break;
1036
1037         UNFORMAT_HAS_SPACE(src, 4);
1038         SILC_GET32_MSB(len2, src->data);
1039         silc_buffer_pull(src, 4);
1040         UNFORMAT_HAS_SPACE(src, len2);
1041         if (len3)
1042           *len3 = len2;
1043         if (silc_likely(x && len2)) {
1044           *x = silc_scalloc(stack, len2 + 1, sizeof(unsigned char));
1045           memcpy(*x, src->data, len2);
1046         }
1047         silc_buffer_pull(src, len2);
1048         break;
1049       }
1050
1051     case SILC_PARAM_BUFFER:
1052       {
1053         SilcBuffer x = va_arg(ap, SilcBuffer);
1054         SilcUInt32 len2;
1055
1056         if (!process)
1057           break;
1058
1059         UNFORMAT_HAS_SPACE(src, 4);
1060         SILC_GET32_MSB(len2, src->data);
1061         silc_buffer_pull(src, 4);
1062         UNFORMAT_HAS_SPACE(src, len2);
1063         silc_buffer_set(x, src->data, len2);
1064         silc_buffer_pull(src, len2);
1065       }
1066       break;
1067
1068     case SILC_PARAM_BUFFER | SILC_PARAM_ALLOC:
1069       {
1070         SilcBuffer x = va_arg(ap, SilcBuffer);
1071         SilcUInt32 len2;
1072
1073         if (!process)
1074           break;
1075
1076         UNFORMAT_HAS_SPACE(src, 4);
1077         SILC_GET32_MSB(len2, src->data);
1078         silc_buffer_pull(src, 4);
1079         UNFORMAT_HAS_SPACE(src, len2);
1080         silc_buffer_sformat(stack, x,
1081                             SILC_STR_DATA(src->data, len2),
1082                             SILC_STR_END);
1083         silc_buffer_pull(src, len2);
1084       }
1085       break;
1086
1087     case SILC_PARAM_OFFSET:
1088       {
1089         int offst = va_arg(ap, int);
1090
1091         if (!process)
1092           break;
1093
1094         if (!offst)
1095           break;
1096
1097         if (offst > 1) {
1098           UNFORMAT_HAS_SPACE(src, offst);
1099           silc_buffer_pull(src, offst);
1100         } else {
1101           silc_buffer_push(src, -(offst));
1102         }
1103         break;
1104       }
1105
1106     case SILC_PARAM_OFFSET_START:
1107       if (!process)
1108         break;
1109       silc_buffer_push(src, (src->data - start_ptr));
1110       break;
1111
1112     case SILC_PARAM_OFFSET_END:
1113       if (!process)
1114         break;
1115       silc_buffer_pull(src, silc_buffer_len(src));
1116       break;
1117
1118     case SILC_PARAM_ADVANCE:
1119       if (!process)
1120         break;
1121       advance = TRUE;
1122       break;
1123
1124     case SILC_PARAM_END:
1125       goto ok;
1126       break;
1127
1128     case SILC_PARAM_DELETE:
1129       break;
1130
1131     default:
1132       SILC_LOG_DEBUG(("Bad buffer formatting type `%d'. Could not "
1133                       "format the data.", fmt));
1134       silc_set_errno_reason(SILC_ERR_INVALID_ARGUMENT,
1135                             "Bad buffer formatting type %d", fmt);
1136       goto fail;
1137       break;
1138     }
1139   }
1140
1141  fail:
1142   SILC_LOG_DEBUG(("Error occured while unformatting buffer, type %d", fmt));
1143   if (process && !advance) {
1144     len = src->data - start_ptr;
1145     silc_buffer_push(src, len);
1146   }
1147   return -1;
1148
1149  ok:
1150   /* Push the buffer back to the start. */
1151   if (process && !advance) {
1152     len = src->data - start_ptr;
1153     silc_buffer_push(src, len);
1154   }
1155   return len;
1156 }
1157
1158 int silc_buffer_unformat(SilcBuffer src, ...)
1159 {
1160   va_list ap;
1161   int ret;
1162
1163   va_start(ap, src);
1164   ret = silc_buffer_sunformat_vp(NULL, src, ap);
1165   va_end(ap);
1166
1167   return ret;
1168 }
1169
1170 int silc_buffer_unformat_vp(SilcBuffer src, va_list ap)
1171 {
1172   return silc_buffer_sunformat_vp(NULL, src, ap);
1173 }
1174
1175 int silc_buffer_sunformat(SilcStack stack, SilcBuffer src, ...)
1176 {
1177   va_list ap;
1178   int ret;
1179
1180   va_start(ap, src);
1181   ret = silc_buffer_sunformat_vp(stack, src, ap);
1182   va_end(ap);
1183
1184   return ret;
1185 }
1186
1187 int silc_buffer_sunformat_vp(SilcStack stack, SilcBuffer src, va_list ap)
1188 {
1189   return silc_buffer_sunformat_vp_i(stack, src, ap, TRUE);
1190 }
1191
1192 /**************************** Utility functions *****************************/
1193
1194 /* Formats strings into a buffer */
1195
1196 int silc_buffer_strformat(SilcBuffer dst, ...)
1197 {
1198   int len = silc_buffer_truelen(dst);
1199   int hlen = silc_buffer_headlen(dst);
1200   va_list va;
1201
1202   va_start(va, dst);
1203
1204   /* Parse the arguments by formatting type. */
1205   while(1) {
1206     char *string = va_arg(va, char *);
1207     unsigned char *d;
1208     SilcInt32 slen;
1209
1210     if (!string)
1211       continue;
1212     if (string == (char *)SILC_PARAM_END)
1213       goto ok;
1214
1215     slen = strlen(string);
1216     d = silc_realloc(dst->head, sizeof(*dst->head) * (slen + len + 1));
1217     if (silc_unlikely(!d))
1218       return -1;
1219     dst->head = d;
1220     memcpy(dst->head + len, string, slen);
1221     len += slen;
1222     dst->head[len] = '\0';
1223   }
1224
1225   SILC_LOG_DEBUG(("Error occured while formatting buffer"));
1226   va_end(va);
1227   return -1;
1228
1229  ok:
1230   dst->end = dst->head + len;
1231   dst->data = dst->head + hlen;
1232   dst->tail = dst->end;
1233
1234   va_end(va);
1235   return len;
1236 }
1237
1238 /* Formats strings into a buffer.  Allocates memory from SilcStack. */
1239
1240 int silc_buffer_sstrformat(SilcStack stack, SilcBuffer dst, ...)
1241 {
1242   int len = silc_buffer_truelen(dst);
1243   int hlen = silc_buffer_headlen(dst);
1244   va_list va;
1245
1246   va_start(va, dst);
1247
1248   /* Parse the arguments by formatting type. */
1249   while(1) {
1250     char *string = va_arg(va, char *);
1251     unsigned char *d;
1252     SilcInt32 slen;
1253
1254     if (!string)
1255       continue;
1256     if (string == (char *)SILC_PARAM_END)
1257       goto ok;
1258
1259     slen = strlen(string);
1260     d = silc_srealloc(stack, len + 1, dst->head,
1261                       sizeof(*dst->head) * (slen + len + 1));
1262     if (silc_unlikely(!d))
1263       return -1;
1264     dst->head = d;
1265     memcpy(dst->head + len, string, slen);
1266     len += slen;
1267     dst->head[len] = '\0';
1268   }
1269
1270   SILC_LOG_DEBUG(("Error occured while formatting buffer"));
1271   va_end(va);
1272   return -1;
1273
1274  ok:
1275   dst->end = dst->head + len;
1276   dst->data = dst->head + hlen;
1277   dst->tail = dst->end;
1278
1279   va_end(va);
1280   return len;
1281 }