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