silc_buffer_sstrformat to preserve buffer locations.
[crypto.git] / lib / silcutil / silcbuffmt.c
1 /*
2
3   silcbuffmt.c
4
5   Author: Pekka Riikonen <priikone@silcnet.org>
6
7   Copyright (C) 1997 - 2006 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__, __x__, __req__)                             \
28 do {                                                                        \
29   if (__req__ > silc_buffer_len((__x__)))                                   \
30     if (!silc_buffer_srealloc_size((__s__), (__x__),                        \
31                                    silc_buffer_truelen((__x__)) + __req__)) \
32       goto fail;                                                            \
33   flen += __req__;                                                          \
34 } while(0)
35
36 /* Check that there is data to be unformatted */
37 #define UNFORMAT_HAS_SPACE(__x__, __req__)      \
38 do {                                            \
39   if (__req__ > silc_buffer_len((__x__)))       \
40     goto fail;                                  \
41   if ((__req__ + 1) <= 0)                       \
42     goto fail;                                  \
43 } while(0)
44
45
46 /******************************* Formatting *********************************/
47
48 int silc_buffer_format(SilcBuffer dst, ...)
49 {
50   va_list ap;
51   int ret;
52
53   va_start(ap, dst);
54   ret = silc_buffer_sformat_vp(NULL, dst, ap);
55   va_end(ap);
56
57   return ret;
58 }
59
60 int silc_buffer_format_vp(SilcBuffer dst, va_list ap)
61 {
62   return silc_buffer_sformat_vp(NULL, dst, ap);
63 }
64
65 int silc_buffer_sformat(SilcStack stack, SilcBuffer dst, ...)
66 {
67   va_list ap;
68   int ret;
69
70   va_start(ap, dst);
71   ret = silc_buffer_sformat_vp(stack, dst, ap);
72   va_end(ap);
73
74   return ret;
75 }
76
77 int silc_buffer_sformat_vp(SilcStack stack, SilcBuffer dst, va_list ap)
78 {
79   SilcBufferParamType fmt;
80   int flen = 0;
81   SilcBool advance = FALSE;
82
83   /* Parse the arguments by formatting type. */
84   while (1) {
85     fmt = va_arg(ap, SilcBufferParamType);
86
87     switch(fmt) {
88     case SILC_BUFFER_PARAM_OFFSET:
89       {
90         int offst = va_arg(ap, int);
91         if (!offst)
92           break;
93         if (offst > 1) {
94           if (offst > silc_buffer_len(dst))
95             goto fail;
96           silc_buffer_pull(dst, offst);
97           flen += offst;
98         } else {
99           silc_buffer_push(dst, -(offst));
100           flen += -(offst);
101         }
102         break;
103       }
104     case SILC_BUFFER_PARAM_SI8_CHAR:
105       {
106         char x = (char)va_arg(ap, int);
107         FORMAT_HAS_SPACE(stack, dst, 1);
108         silc_buffer_put(dst, &x, 1);
109         silc_buffer_pull(dst, 1);
110         break;
111       }
112     case SILC_BUFFER_PARAM_UI8_CHAR:
113       {
114         unsigned char x = (unsigned char)va_arg(ap, int);
115         FORMAT_HAS_SPACE(stack, dst, 1);
116         silc_buffer_put(dst, &x, 1);
117         silc_buffer_pull(dst, 1);
118         break;
119       }
120     case SILC_BUFFER_PARAM_SI16_SHORT:
121       {
122         unsigned char xf[2];
123         SilcInt16 x = (SilcInt16)va_arg(ap, int);
124         FORMAT_HAS_SPACE(stack, dst, 2);
125         SILC_PUT16_MSB(x, xf);
126         silc_buffer_put(dst, xf, 2);
127         silc_buffer_pull(dst, 2);
128         break;
129       }
130     case SILC_BUFFER_PARAM_UI16_SHORT:
131       {
132         unsigned char xf[2];
133         SilcUInt16 x = (SilcUInt16)va_arg(ap, int);
134         FORMAT_HAS_SPACE(stack, dst, 2);
135         SILC_PUT16_MSB(x, xf);
136         silc_buffer_put(dst, xf, 2);
137         silc_buffer_pull(dst, 2);
138         break;
139       }
140     case SILC_BUFFER_PARAM_SI32_INT:
141       {
142         unsigned char xf[4];
143         SilcInt32 x = va_arg(ap, SilcInt32);
144         FORMAT_HAS_SPACE(stack, dst, 4);
145         SILC_PUT32_MSB(x, xf);
146         silc_buffer_put(dst, xf, 4);
147         silc_buffer_pull(dst, 4);
148         break;
149       }
150     case SILC_BUFFER_PARAM_UI32_INT:
151       {
152         unsigned char xf[4];
153         SilcUInt32 x = va_arg(ap, SilcUInt32);
154         FORMAT_HAS_SPACE(stack, dst, 4);
155         SILC_PUT32_MSB(x, xf);
156         silc_buffer_put(dst, xf, 4);
157         silc_buffer_pull(dst, 4);
158         break;
159       }
160     case SILC_BUFFER_PARAM_SI64_INT:
161       {
162         unsigned char xf[8];
163         SilcInt64 x = va_arg(ap, SilcInt64);
164         FORMAT_HAS_SPACE(stack, dst, sizeof(SilcInt64));
165         SILC_PUT64_MSB(x, xf);
166         silc_buffer_put(dst, xf, sizeof(SilcInt64));
167         silc_buffer_pull(dst, sizeof(SilcInt64));
168         break;
169       }
170     case SILC_BUFFER_PARAM_UI64_INT:
171       {
172         unsigned char xf[8];
173         SilcUInt64 x = va_arg(ap, SilcUInt64);
174         FORMAT_HAS_SPACE(stack, dst, sizeof(SilcUInt64));
175         SILC_PUT64_MSB(x, xf);
176         silc_buffer_put(dst, xf, sizeof(SilcUInt64));
177         silc_buffer_pull(dst, sizeof(SilcUInt64));
178         break;
179       }
180     case SILC_BUFFER_PARAM_UI8_STRING:
181     case SILC_BUFFER_PARAM_UI16_STRING:
182     case SILC_BUFFER_PARAM_UI32_STRING:
183     case SILC_BUFFER_PARAM_UI8_STRING_ALLOC:
184     case SILC_BUFFER_PARAM_UI16_STRING_ALLOC:
185     case SILC_BUFFER_PARAM_UI32_STRING_ALLOC:
186       {
187         unsigned char *x = va_arg(ap, unsigned char *);
188         SilcUInt32 tmp_len = strlen(x);
189         FORMAT_HAS_SPACE(stack, dst, tmp_len);
190         silc_buffer_put(dst, x, tmp_len);
191         silc_buffer_pull(dst, tmp_len);
192         break;
193       }
194     case SILC_BUFFER_PARAM_UI8_NSTRING:
195     case SILC_BUFFER_PARAM_UI16_NSTRING:
196     case SILC_BUFFER_PARAM_UI32_NSTRING:
197     case SILC_BUFFER_PARAM_UI_XNSTRING:
198     case SILC_BUFFER_PARAM_DATA:
199     case SILC_BUFFER_PARAM_UI8_NSTRING_ALLOC:
200     case SILC_BUFFER_PARAM_UI16_NSTRING_ALLOC:
201     case SILC_BUFFER_PARAM_UI32_NSTRING_ALLOC:
202     case SILC_BUFFER_PARAM_UI_XNSTRING_ALLOC:
203     case SILC_BUFFER_PARAM_DATA_ALLOC:
204       {
205         unsigned char *x = va_arg(ap, unsigned char *);
206         SilcUInt32 tmp_len = va_arg(ap, SilcUInt32);
207         if (x && tmp_len) {
208           FORMAT_HAS_SPACE(stack, dst, tmp_len);
209           silc_buffer_put(dst, x, tmp_len);
210           silc_buffer_pull(dst, tmp_len);
211         }
212         break;
213       }
214     case SILC_BUFFER_PARAM_END:
215       goto ok;
216       break;
217     case SILC_BUFFER_PARAM_ADVANCE:
218       advance = TRUE;
219       break;
220     default:
221       SILC_LOG_DEBUG(("Bad buffer formatting type `%d'. Could not "
222                       "format the data.", fmt));
223       goto fail;
224       break;
225     }
226   }
227
228  fail:
229   SILC_LOG_DEBUG(("Error occured while formatting data"));
230   if (!advance)
231     silc_buffer_push(dst, flen);
232   return -1;
233
234  ok:
235   /* Push the buffer back to where it belongs. */
236   if (!advance)
237     silc_buffer_push(dst, flen);
238   return flen;
239 }
240
241
242 /****************************** Unformatting ********************************/
243
244 int silc_buffer_unformat(SilcBuffer src, ...)
245 {
246   va_list ap;
247   int ret;
248
249   va_start(ap, src);
250   ret = silc_buffer_unformat_vp(src, ap);
251   va_end(ap);
252
253   return ret;
254 }
255
256 int silc_buffer_unformat_vp(SilcBuffer src, va_list ap)
257 {
258   SilcBufferParamType fmt;
259   unsigned char *start_ptr = src->data;
260   int len = 0;
261
262   /* Parse the arguments by formatting type. */
263   while(1) {
264     fmt = va_arg(ap, SilcBufferParamType);
265
266     switch(fmt) {
267     case SILC_BUFFER_PARAM_OFFSET:
268       {
269         int offst = va_arg(ap, int);
270         if (!offst)
271           break;
272         if (offst > 1) {
273           UNFORMAT_HAS_SPACE(src, offst);
274           silc_buffer_pull(src, offst);
275         } else {
276           silc_buffer_push(src, -(offst));
277         }
278         break;
279       }
280     case SILC_BUFFER_PARAM_SI8_CHAR:
281       {
282         char *x = va_arg(ap, char *);
283         UNFORMAT_HAS_SPACE(src, 1);
284         if (x)
285           *x = src->data[0];
286         silc_buffer_pull(src, 1);
287         break;
288       }
289     case SILC_BUFFER_PARAM_UI8_CHAR:
290       {
291         unsigned char *x = va_arg(ap, unsigned char *);
292         UNFORMAT_HAS_SPACE(src, 1);
293         if (x)
294           *x = src->data[0];
295         silc_buffer_pull(src, 1);
296         break;
297       }
298     case SILC_BUFFER_PARAM_SI16_SHORT:
299       {
300         SilcInt16 *x = va_arg(ap, SilcInt16 *);
301         UNFORMAT_HAS_SPACE(src, 2);
302         if (x)
303           SILC_GET16_MSB(*x, src->data);
304         silc_buffer_pull(src, 2);
305         break;
306       }
307     case SILC_BUFFER_PARAM_UI16_SHORT:
308       {
309         SilcUInt16 *x = va_arg(ap, SilcUInt16 *);
310         UNFORMAT_HAS_SPACE(src, 2);
311         if (x)
312           SILC_GET16_MSB(*x, src->data);
313         silc_buffer_pull(src, 2);
314         break;
315       }
316     case SILC_BUFFER_PARAM_SI32_INT:
317       {
318         SilcInt32 *x = va_arg(ap, SilcInt32 *);
319         UNFORMAT_HAS_SPACE(src, 4);
320         if (x)
321           SILC_GET32_MSB(*x, src->data);
322         silc_buffer_pull(src, 4);
323         break;
324       }
325     case SILC_BUFFER_PARAM_UI32_INT:
326       {
327         SilcUInt32 *x = va_arg(ap, SilcUInt32 *);
328         UNFORMAT_HAS_SPACE(src, 4);
329         if (x)
330           SILC_GET32_MSB(*x, src->data);
331         silc_buffer_pull(src, 4);
332         break;
333       }
334     case SILC_BUFFER_PARAM_SI64_INT:
335       {
336         SilcInt64 *x = va_arg(ap, SilcInt64 *);
337         UNFORMAT_HAS_SPACE(src, sizeof(SilcInt64));
338         if (x)
339           SILC_GET64_MSB(*x, src->data);
340         silc_buffer_pull(src, sizeof(SilcInt64));
341         break;
342       }
343     case SILC_BUFFER_PARAM_UI64_INT:
344       {
345         SilcUInt64 *x = va_arg(ap, SilcUInt64 *);
346         UNFORMAT_HAS_SPACE(src, sizeof(SilcUInt64));
347         if (x)
348           SILC_GET64_MSB(*x, src->data);
349         silc_buffer_pull(src, sizeof(SilcUInt64));
350         break;
351       }
352     case SILC_BUFFER_PARAM_UI8_STRING:
353       {
354         SilcUInt8 len2;
355         unsigned char **x = va_arg(ap, unsigned char **);
356         UNFORMAT_HAS_SPACE(src, 1);
357         len2 = (SilcUInt8)src->data[0];
358         silc_buffer_pull(src, 1);
359         UNFORMAT_HAS_SPACE(src, len2);
360         if (x)
361           *x = src->data;
362         silc_buffer_pull(src, len2);
363         break;
364       }
365     case SILC_BUFFER_PARAM_UI16_STRING:
366       {
367         SilcUInt16 len2;
368         unsigned char **x = va_arg(ap, unsigned char **);
369         UNFORMAT_HAS_SPACE(src, 2);
370         SILC_GET16_MSB(len2, src->data);
371         silc_buffer_pull(src, 2);
372         UNFORMAT_HAS_SPACE(src, len2);
373         if (x)
374           *x = src->data;
375         silc_buffer_pull(src, len2);
376         break;
377       }
378     case SILC_BUFFER_PARAM_UI8_STRING_ALLOC:
379       {
380         SilcUInt8 len2;
381         unsigned char **x = va_arg(ap, unsigned char **);
382         UNFORMAT_HAS_SPACE(src, 1);
383         len2 = (SilcUInt8)src->data[0];
384         silc_buffer_pull(src, 1);
385         UNFORMAT_HAS_SPACE(src, len2);
386         if (x && len2) {
387           *x = silc_calloc(len2 + 1, sizeof(unsigned char));
388           memcpy(*x, src->data, len2);
389         }
390         silc_buffer_pull(src, len2);
391         break;
392       }
393     case SILC_BUFFER_PARAM_UI16_STRING_ALLOC:
394       {
395         SilcUInt16 len2;
396         unsigned char **x = va_arg(ap, unsigned char **);
397         UNFORMAT_HAS_SPACE(src, 2);
398         SILC_GET16_MSB(len2, src->data);
399         silc_buffer_pull(src, 2);
400         UNFORMAT_HAS_SPACE(src, len2);
401         if (x && len2) {
402           *x = silc_calloc(len2 + 1, sizeof(unsigned char));
403           memcpy(*x, src->data, len2);
404         }
405         silc_buffer_pull(src, len2);
406         break;
407       }
408     case SILC_BUFFER_PARAM_UI32_STRING:
409       {
410         SilcUInt32 len2;
411         unsigned char **x = va_arg(ap, unsigned char **);
412         UNFORMAT_HAS_SPACE(src, 4);
413         SILC_GET32_MSB(len2, src->data);
414         silc_buffer_pull(src, 4);
415         UNFORMAT_HAS_SPACE(src, len2);
416         if (x)
417           *x = src->data;
418         silc_buffer_pull(src, len2);
419         break;
420       }
421     case SILC_BUFFER_PARAM_UI32_STRING_ALLOC:
422       {
423         SilcUInt32 len2;
424         unsigned char **x = va_arg(ap, unsigned char **);
425         UNFORMAT_HAS_SPACE(src, 4);
426         SILC_GET32_MSB(len2, src->data);
427         silc_buffer_pull(src, 4);
428         UNFORMAT_HAS_SPACE(src, len2);
429         if (x && len2) {
430           *x = silc_calloc(len2 + 1, sizeof(unsigned char));
431           memcpy(*x, src->data, len2);
432         }
433         silc_buffer_pull(src, len2);
434         break;
435       }
436     case SILC_BUFFER_PARAM_UI8_NSTRING:
437       {
438         SilcUInt8 len2;
439         unsigned char **x = va_arg(ap, unsigned char **);
440         SilcUInt8 *len = va_arg(ap, SilcUInt8 *);
441         UNFORMAT_HAS_SPACE(src, 1);
442         len2 = (SilcUInt8)src->data[0];
443         silc_buffer_pull(src, 1);
444         UNFORMAT_HAS_SPACE(src, len2);
445         if (len)
446           *len = len2;
447         if (x)
448           *x = src->data;
449         silc_buffer_pull(src, len2);
450         break;
451       }
452     case SILC_BUFFER_PARAM_UI16_NSTRING:
453       {
454         SilcUInt16 len2;
455         unsigned char **x = va_arg(ap, unsigned char **);
456         SilcUInt16 *len = va_arg(ap, SilcUInt16 *);
457         UNFORMAT_HAS_SPACE(src, 2);
458         SILC_GET16_MSB(len2, src->data);
459         silc_buffer_pull(src, 2);
460         UNFORMAT_HAS_SPACE(src, len2);
461         if (len)
462           *len = len2;
463         if (x)
464           *x = src->data;
465         silc_buffer_pull(src, len2);
466         break;
467       }
468     case SILC_BUFFER_PARAM_UI8_NSTRING_ALLOC:
469       {
470         SilcUInt8 len2;
471         unsigned char **x = va_arg(ap, unsigned char **);
472         SilcUInt8 *len = va_arg(ap, SilcUInt8 *);
473         UNFORMAT_HAS_SPACE(src, 1);
474         len2 = (SilcUInt8)src->data[0];
475         silc_buffer_pull(src, 1);
476         UNFORMAT_HAS_SPACE(src, len2);
477         if (len)
478           *len = len2;
479         if (x && len2) {
480           *x = silc_calloc(len2 + 1, sizeof(unsigned char));
481           memcpy(*x, src->data, len2);
482         }
483         silc_buffer_pull(src, len2);
484         break;
485       }
486     case SILC_BUFFER_PARAM_UI16_NSTRING_ALLOC:
487       {
488         SilcUInt16 len2;
489         unsigned char **x = va_arg(ap, unsigned char **);
490         SilcUInt16 *len = va_arg(ap, SilcUInt16 *);
491         UNFORMAT_HAS_SPACE(src, 2);
492         SILC_GET16_MSB(len2, src->data);
493         silc_buffer_pull(src, 2);
494         UNFORMAT_HAS_SPACE(src, len2);
495         if (len)
496           *len = len2;
497         if (x && len2) {
498           *x = silc_calloc(len2 + 1, sizeof(unsigned char));
499           memcpy(*x, src->data, len2);
500         }
501         silc_buffer_pull(src, len2);
502         break;
503       }
504     case SILC_BUFFER_PARAM_UI32_NSTRING:
505       {
506         SilcUInt32 len2;
507         unsigned char **x = va_arg(ap, unsigned char **);
508         SilcUInt32 *len = va_arg(ap, SilcUInt32 *);
509         UNFORMAT_HAS_SPACE(src, 4);
510         SILC_GET32_MSB(len2, src->data);
511         silc_buffer_pull(src, 4);
512         UNFORMAT_HAS_SPACE(src, len2);
513         if (len)
514           *len = len2;
515         if (x)
516           *x = src->data;
517         silc_buffer_pull(src, len2);
518         break;
519       }
520     case SILC_BUFFER_PARAM_UI_XNSTRING:
521     case SILC_BUFFER_PARAM_DATA:
522       {
523         unsigned char **x = va_arg(ap, unsigned char **);
524         SilcUInt32 len = va_arg(ap, SilcUInt32);
525         UNFORMAT_HAS_SPACE(src, len);
526         if (len && x)
527           *x = src->data;
528         silc_buffer_pull(src, len);
529         break;
530       }
531     case SILC_BUFFER_PARAM_UI_XNSTRING_ALLOC:
532     case SILC_BUFFER_PARAM_DATA_ALLOC:
533       {
534         unsigned char **x = va_arg(ap, unsigned char **);
535         SilcUInt32 len = va_arg(ap, SilcUInt32);
536         UNFORMAT_HAS_SPACE(src, len);
537         if (len && x) {
538           *x = silc_calloc(len + 1, sizeof(unsigned char));
539           memcpy(*x, src->data, len);
540         }
541         silc_buffer_pull(src, len);
542         break;
543       }
544     case SILC_BUFFER_PARAM_END:
545       goto ok;
546       break;
547     case SILC_BUFFER_PARAM_ADVANCE:
548       break;
549     default:
550       SILC_LOG_DEBUG(("Bad buffer formatting type `%d'. Could not "
551                       "format the data.", fmt));
552       goto fail;
553       break;
554     }
555   }
556
557  fail:
558   SILC_LOG_DEBUG(("Error occured while unformatting buffer"));
559   len = src->data - start_ptr;
560   silc_buffer_push(src, len);
561   return -1;
562
563  ok:
564   /* Push the buffer back to the start. */
565   len = src->data - start_ptr;
566   silc_buffer_push(src, len);
567   return len;
568 }
569
570
571 /**************************** Utility functions *****************************/
572
573 /* Formats strings into a buffer */
574
575 int silc_buffer_strformat(SilcBuffer dst, ...)
576 {
577   int len = silc_buffer_truelen(dst);
578   int hlen = silc_buffer_headlen(dst);
579   va_list va;
580
581   va_start(va, dst);
582
583   /* Parse the arguments by formatting type. */
584   while(1) {
585     char *string = va_arg(va, char *);
586     unsigned char *d;
587     SilcInt32 slen;
588
589     if (!string)
590       continue;
591     if (string == (char *)SILC_BUFFER_PARAM_END)
592       goto ok;
593
594     slen = strlen(string);
595     d = silc_realloc(dst->head, sizeof(*dst->head) * (slen + len + 1));
596     if (!d)
597       return -1;
598     dst->head = d;
599     memcpy(dst->head + len, string, slen);
600     len += slen;
601     dst->head[len] = '\0';
602   }
603
604   SILC_LOG_DEBUG(("Error occured while formatting buffer"));
605   va_end(va);
606   return -1;
607
608  ok:
609   dst->end = dst->head + len;
610   dst->data = dst->head + hlen;
611   dst->tail = dst->end;
612
613   va_end(va);
614   return len;
615 }
616
617 /* Formats strings into a buffer.  Allocates memory from SilcStack. */
618
619 int silc_buffer_sstrformat(SilcStack stack, SilcBuffer dst, ...)
620 {
621   int len = silc_buffer_truelen(dst);
622   int hlen = silc_buffer_headlen(dst);
623   va_list va;
624
625   va_start(va, dst);
626
627   /* Parse the arguments by formatting type. */
628   while(1) {
629     char *string = va_arg(va, char *);
630     unsigned char *d;
631     SilcInt32 slen;
632
633     if (!string)
634       continue;
635     if (string == (char *)SILC_BUFFER_PARAM_END)
636       goto ok;
637
638     slen = strlen(string);
639     d = silc_srealloc_ua(stack, len + 1, dst->head,
640                          sizeof(*dst->head) * (slen + len + 1));
641     if (!d)
642       return -1;
643     dst->head = d;
644     memcpy(dst->head + len, string, slen);
645     len += slen;
646     dst->head[len] = '\0';
647   }
648
649   SILC_LOG_DEBUG(("Error occured while formatting buffer"));
650   va_end(va);
651   return -1;
652
653  ok:
654   dst->end = dst->head + len;
655   dst->data = dst->head + hlen;
656   dst->tail = dst->end;
657
658   va_end(va);
659   return len;
660 }