Check-in Lasker-2.2.3 tar ball from samba.org
[capablanca.git] / lasker-2.2.3 / src / parsers / genparser.c
1 /*
2    Copyright (C) Andrew Tridgell <genstruct@tridgell.net> 2002
3    
4    This program is free software; you can redistribute it and/or modify
5    it under the terms of the GNU General Public License as published by
6    the Free Software Foundation; either version 2 of the License, or
7    (at your option) any later version.
8    
9    This program is distributed in the hope that it will be useful,
10    but WITHOUT ANY WARRANTY; without even the implied warranty of
11    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12    GNU General Public License for more details.
13    
14    You should have received a copy of the GNU General Public License
15    along with this program; if not, write to the Free Software
16    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
17 */
18
19 /*
20   automatic marshalling/unmarshalling system for C structures
21 */
22
23 #if STANDALONE
24 #define _GNU_SOURCE
25 #include <stdio.h>
26 #include <stdlib.h>
27 #include <time.h>
28 #include <string.h>
29 #include <ctype.h>
30 #include <stddef.h>
31 #include <stdarg.h>
32 #include <errno.h>
33 #include "genparser.h"
34 #else
35 #include "includes.h"
36 #endif
37
38 /* see if a range of memory is all zero. Used to prevent dumping of zero elements */
39 static int all_zero(const char *ptr, unsigned size)
40 {
41         int i;
42         if (!ptr) return 1;
43         for (i=0;i<size;i++) {
44                 if (ptr[i]) return 0;
45         }
46         return 1;
47 }
48
49 /* encode a buffer of bytes into a escaped string */
50 static char *encode_bytes(const char *ptr, unsigned len)
51 {
52         const char *hexdig = "0123456789abcdef";
53         char *ret, *p;
54         unsigned i;
55         ret = malloc(len*3 + 1); /* worst case size */
56         if (!ret) return NULL;
57         for (p=ret,i=0;i<len;i++) {
58                 if (isalnum(ptr[i]) || isspace(ptr[i]) ||
59                     (ispunct(ptr[i]) && !strchr("\\{}", ptr[i]))) {
60                         *p++ = ptr[i];
61                 } else {
62                         unsigned char c = *(unsigned char *)(ptr+i);
63                         if (c == 0 && all_zero(ptr+i, len-i)) break;
64                         p[0] = '\\';
65                         p[1] = hexdig[c>>4];
66                         p[2] = hexdig[c&0xF];
67                         p += 3;
68                 }
69         }
70
71         *p = 0;
72
73         return ret;
74 }
75
76 /* decode an escaped string from encode_bytes() into a buffer */
77 static char *decode_bytes(const char *s, unsigned *len) 
78 {
79         char *ret, *p;
80         unsigned i;
81         ret = calloc(1, strlen(s)+1); /* worst case length */
82
83         if (*s == '{') s++;
84
85         for (p=ret,i=0;s[i];i++) {
86                 if (s[i] == '}') {
87                         break;
88                 } else if (s[i] == '\\') {
89                         unsigned v;
90                         if (sscanf(&s[i+1], "%02x", &v) != 1 || v > 255) {
91                                 free(ret);
92                                 return NULL;
93                         }
94                         *(unsigned char *)p = v;
95                         p++;
96                         i += 2;
97                 } else {
98                         *p++ = s[i];
99                 }
100         }
101         *p = 0;
102
103         (*len) = (unsigned)(p - ret);
104         
105         return ret;
106 }
107
108 /* the add*() functions deal with adding things to a struct
109    parse_string */
110
111 /* allocate more space if needed */
112 static int addgen_alloc(struct parse_string *p, int n)
113 {
114         if (p->length + n <= p->allocated) return 0;
115         p->allocated = p->length + n + 200;
116         p->s = realloc(p->s, p->allocated);
117         if (!p->s) {
118                 errno = ENOMEM;
119                 return -1;
120         }
121         return 0;
122 }
123
124 /* add a character to the buffer */
125 static int addchar(struct parse_string *p, char c)
126 {
127         if (addgen_alloc(p, 2) != 0) {
128                 return -1;
129         }
130         p->s[p->length++] = c;
131         p->s[p->length] = 0;
132         return 0;
133 }
134
135 /* add a string to the buffer */
136 static int addstr(struct parse_string *p, const char *s)
137 {
138         int len = strlen(s);
139         if (addgen_alloc(p, len+1) != 0) {
140                 return -1;
141         }
142         memcpy(p->s + p->length, s, len+1);
143         p->length += len;
144         return 0;
145 }
146
147 /* add a string to the buffer with a tab prefix */
148 static int addtabbed(struct parse_string *p, const char *s, unsigned indent)
149 {
150         int len = strlen(s);
151         if (addgen_alloc(p, indent+len+1) != 0) {
152                 return -1;
153         }
154         while (indent--) {
155                 p->s[p->length++] = '\t';
156         }
157         memcpy(p->s + p->length, s, len+1);
158         p->length += len;
159         return 0;
160 }
161
162 /* note! this can only be used for results up to 60 chars wide! */
163 static int addshort(struct parse_string *p, const char *fmt, ...)
164 {
165         char buf[60];
166         int n;
167         va_list ap;
168         va_start(ap, fmt);
169         n = vsnprintf(buf, sizeof(buf), fmt, ap);
170         va_end(ap);
171         if (addgen_alloc(p, n + 1) != 0) {
172                 return -1;
173         }
174         if (n != 0) {
175                 memcpy(p->s + p->length, buf, n);
176         }
177         p->length += n;
178         p->s[p->length] = 0;
179         return 0;
180 }
181
182 /* 
183    this is here to make it easier for people to write dump functions 
184    for their own types
185  */
186 int gen_addgen(struct parse_string *p, const char *fmt, ...)
187 {
188         char *buf = NULL;
189         int n;
190         va_list ap;
191         va_start(ap, fmt);
192         n = vasprintf(&buf, fmt, ap);
193         va_end(ap);
194         if (addgen_alloc(p, n + 1) != 0) {
195                 if (buf) free(buf);
196                 return -1;
197         }
198         if (n != 0) {
199                 memcpy(p->s + p->length, buf, n);
200         }
201         p->length += n;
202         p->s[p->length] = 0;
203         if (buf) free(buf);
204         return 0;
205 }
206
207 /* dump a enumerated type */
208 int gen_dump_enum(const struct enum_struct *einfo,
209                   struct parse_string *p, 
210                   const char *ptr,
211                   unsigned indent)
212 {
213         unsigned v = *(unsigned *)ptr;
214         int i;
215         for (i=0;einfo[i].name;i++) {
216                 if (v == einfo[i].value) {
217                         addstr(p, einfo[i].name);
218                         return 0;
219                 }
220         }
221         /* hmm, maybe we should just fail? */
222         return gen_dump_unsigned(p, ptr, indent);
223 }
224
225 /* dump a single non-array element, hanlding struct and enum */
226 static int gen_dump_one(struct parse_string *p, 
227                          const struct parse_struct *pinfo,
228                          const char *ptr,
229                          unsigned indent)
230 {
231         if (pinfo->dump_fn == gen_dump_char && pinfo->ptr_count == 1) {
232                 char *s = encode_bytes(ptr, strlen(ptr));
233                 if (addchar(p,'{') ||
234                     addstr(p, s) ||
235                     addstr(p, "}")) {
236                         free(s);
237                         return -1;
238                 }
239                 return 0;
240         }
241
242         return pinfo->dump_fn(p, ptr, indent);
243 }
244
245 /* handle dumping of an array of arbitrary type */
246 static int gen_dump_array(struct parse_string *p,
247                           const struct parse_struct *pinfo, 
248                           const char *ptr,
249                           int array_len,
250                           int indent)
251 {
252         int i, count=0;
253
254         /* special handling of fixed length strings */
255         if (array_len != 0 && 
256             pinfo->ptr_count == 0 &&
257             pinfo->dump_fn == gen_dump_char) {
258                 char *s = encode_bytes(ptr, array_len);
259                 if (!s) return -1;
260                 if (addtabbed(p, pinfo->name, indent) ||
261                     addstr(p, " = {") ||
262                     addstr(p, s) ||
263                     addstr(p, "}\n")) {
264                         free(s);
265                         return -1;
266                 }
267                 free(s);
268                 return 0;
269         }
270
271         for (i=0;i<array_len;i++) {
272                 const char *p2 = ptr;
273                 unsigned size = pinfo->size;
274
275                 /* generic pointer dereference */
276                 if (pinfo->ptr_count) {
277                         p2 = *(const char **)ptr;
278                         size = sizeof(void *);
279                 }
280                 
281                 if ((count || pinfo->ptr_count) && 
282                     !(pinfo->flags & FLAG_ALWAYS) &&
283                     all_zero(ptr, size)) {
284                         ptr += size;
285                         continue;
286                 }
287                 if (count == 0) {
288                         if (addtabbed(p, pinfo->name, indent) ||
289                             addshort(p, " = %u:", i)) {
290                                 return -1;
291                         }
292                 } else {
293                         if (addshort(p, ", %u:", i) != 0) {
294                                 return -1;
295                         }
296                 }
297                 if (gen_dump_one(p, pinfo, p2, indent) != 0) {
298                         return -1;
299                 }
300                 ptr += size;
301                 count++;
302         }
303         if (count) {
304                 return addstr(p, "\n");
305         }
306         return 0;
307 }
308
309 /* find a variable by name in a loaded structure and return its value
310    as an integer. Used to support dynamic arrays */
311 static int find_var(const struct parse_struct *pinfo,
312                     const char *data,
313                     const char *var)
314 {
315         int i;
316         const char *ptr;
317
318         /* this allows for constant lengths */
319         if (isdigit(*var)) {
320                 return atoi(var);
321         }
322
323         for (i=0;pinfo[i].name;i++) {
324                 if (strcmp(pinfo[i].name, var) == 0) break;
325         }
326         if (!pinfo[i].name) return -1;
327
328         ptr = data + pinfo[i].offset;
329
330         switch (pinfo[i].size) {
331         case sizeof(int):
332                 return *(int *)ptr;
333         case sizeof(char):
334                 return *(char *)ptr;
335         }
336
337         return -1;
338 }
339
340
341 int gen_dump_struct(const struct parse_struct *pinfo,
342                     struct parse_string *p, 
343                     const char *ptr, 
344                     unsigned indent)
345 {
346         char *s = gen_dump(pinfo, ptr, indent+1);
347         if (!s) return -1;
348         if (addstr(p, "{\n") || 
349             addstr(p,s) || 
350             addtabbed(p,"}", indent)) {
351                 free(s);
352                 return -1;
353         }
354         free(s);
355         return 0;
356 }
357
358 static int gen_dump_string(struct parse_string *p,
359                            const struct parse_struct *pinfo, 
360                            const char *data, 
361                            unsigned indent)
362 {
363         const char *ptr = *(char **)data;
364         char *s = encode_bytes(ptr, strlen(ptr));
365         if (addtabbed(p, pinfo->name, indent) ||
366             addstr(p, " = ") ||
367             addchar(p,'{') ||
368             addstr(p, s) ||
369             addstr(p, "}\n")) {
370                 free(s);
371                 return -1;
372         }
373         return 0;
374 }
375
376 /* 
377    find the length of a nullterm array
378 */
379 static int len_nullterm(const char *ptr, int size, int array_len)
380 {
381         int len;
382
383         if (size == 1) {
384                 len = strnlen(ptr, array_len);
385         } else {
386                 for (len=0;len<array_len;len++) {
387                         if (all_zero(ptr+len*size, size)) break;
388                 }
389         }
390
391         if (len == 0) len = 1;
392
393         return len;
394 }
395
396
397 /* the generic dump routine. Scans the parse information for this structure
398    and processes it recursively */
399 char *gen_dump(const struct parse_struct *pinfo, 
400                const char *data, 
401                unsigned indent)
402 {
403         struct parse_string p;
404         int i;
405         
406         p.length = 0;
407         p.allocated = 0;
408         p.s = NULL;
409
410         if (addstr(&p, "") != 0) {
411                 return NULL;
412         }
413         
414         for (i=0;pinfo[i].name;i++) {
415                 const char *ptr = data + pinfo[i].offset;
416                 unsigned size = pinfo[i].size;
417
418                 if (pinfo[i].ptr_count) {
419                         size = sizeof(void *);
420                 }
421
422                 /* special handling for array types */
423                 if (pinfo[i].array_len) {
424                         unsigned len = pinfo[i].array_len;
425                         if (pinfo[i].flags & FLAG_NULLTERM) {
426                                 len = len_nullterm(ptr, size, len);
427                         }
428                         if (gen_dump_array(&p, &pinfo[i], ptr, 
429                                            len, indent)) {
430                                 goto failed;
431                         }
432                         continue;
433                 }
434
435                 /* and dynamically sized arrays */
436                 if (pinfo[i].dynamic_len) {
437                         int len = find_var(pinfo, data, pinfo[i].dynamic_len);
438                         struct parse_struct p2 = pinfo[i];
439                         if (len < 0) {
440                                 goto failed;
441                         }
442                         if (len > 0) {
443                                 if (pinfo[i].flags & FLAG_NULLTERM) {
444                                         len = len_nullterm(*(char **)ptr, 
445                                                            pinfo[i].size, len);
446                                 }
447                                 p2.ptr_count--;
448                                 p2.dynamic_len = NULL;
449                                 if (gen_dump_array(&p, &p2, *(char **)ptr, 
450                                                    len, indent) != 0) {
451                                         goto failed;
452                                 }
453                         }
454                         continue;
455                 }
456
457                 /* don't dump zero elements */
458                 if (!(pinfo[i].flags & FLAG_ALWAYS) && all_zero(ptr, size)) continue;
459
460                 /* assume char* is a null terminated string */
461                 if (pinfo[i].size == 1 && pinfo[i].ptr_count == 1 &&
462                     pinfo[i].dump_fn == gen_dump_char) {
463                         if (gen_dump_string(&p, &pinfo[i], ptr, indent) != 0) {
464                                 goto failed;
465                         }
466                         continue;
467                 }
468
469                 /* generic pointer dereference */
470                 if (pinfo[i].ptr_count) {
471                         ptr = *(const char **)ptr;
472                 }
473
474                 if (addtabbed(&p, pinfo[i].name, indent) ||
475                     addstr(&p, " = ") ||
476                     gen_dump_one(&p, &pinfo[i], ptr, indent) ||
477                     addstr(&p, "\n")) {
478                         goto failed;
479                 }
480         }
481         return p.s;
482
483 failed:
484         free(p.s);
485         return NULL;
486 }
487
488 /* search for a character in a string, skipping over sections within
489    matching braces */
490 static char *match_braces(char *s, char c)
491 {
492         int depth = 0;
493         while (*s) {
494                 switch (*s) {
495                 case '}':
496                         depth--;
497                         break;
498                 case '{':
499                         depth++;
500                         break;
501                 }
502                 if (depth == 0 && *s == c) {
503                         return s;
504                 }
505                 s++;
506         }
507         return s;
508 }
509
510 /* parse routine for enumerated types */
511 int gen_parse_enum(const struct enum_struct *einfo, 
512                    char *ptr, 
513                    const char *str)
514 {
515         unsigned v;
516         int i;
517
518         if (isdigit(*str)) {
519                 if (sscanf(str, "%u", &v) != 1) {
520                         errno = EINVAL;
521                         return -1;
522                 }
523                 *(unsigned *)ptr = v;
524                 return 0;
525         }
526
527         for (i=0;einfo[i].name;i++) {
528                 if (strcmp(einfo[i].name, str) == 0) {
529                         *(unsigned *)ptr = einfo[i].value;
530                         return 0;
531                 }
532         }
533
534         /* unknown enum value?? */
535         return -1;
536 }
537
538
539 /* parse all base types */
540 static int gen_parse_base(const struct parse_struct *pinfo, 
541                           char *ptr, 
542                           const char *str)
543 {
544         if (pinfo->parse_fn == gen_parse_char && pinfo->ptr_count==1) {
545                 unsigned len;
546                 char *s = decode_bytes(str, &len);
547                 if (!s) return -1;
548                 *(char **)ptr = s;
549                 return 0;
550         }
551
552         if (pinfo->ptr_count) {
553                 struct parse_struct p2 = *pinfo;
554                 *(void **)ptr = calloc(1, pinfo->ptr_count>1?sizeof(void *):pinfo->size);
555                 if (! *(void **)ptr) {
556                         return -1;
557                 }
558                 ptr = *(char **)ptr;
559                 p2.ptr_count--;
560                 return gen_parse_base(&p2, ptr, str);
561         }
562
563         return pinfo->parse_fn(ptr, str);
564 }
565
566 /* parse a generic array */
567 static int gen_parse_array(const struct parse_struct *pinfo, 
568                             char *ptr, 
569                             const char *str,
570                             int array_len)
571 {
572         char *p, *p2;
573         unsigned size = pinfo->size;
574
575         /* special handling of fixed length strings */
576         if (array_len != 0 && 
577             pinfo->ptr_count == 0 &&
578             pinfo->dump_fn == gen_dump_char) {
579                 unsigned len = 0;
580                 char *s = decode_bytes(str, &len);
581                 if (!s) return -1;
582                 memset(ptr, 0, array_len);
583                 memcpy(ptr, s, len);
584                 free(s);
585                 return 0;
586         }
587
588         if (pinfo->ptr_count) {
589                 size = sizeof(void *);
590         }
591
592         while (*str) {
593                 unsigned idx;
594                 int done;
595
596                 idx = atoi(str);
597                 p = strchr(str,':');
598                 if (!p) break;
599                 p++;
600                 p2 = match_braces(p, ',');
601                 done = (*p2 != ',');
602                 *p2 = 0;
603
604                 if (*p == '{') {
605                         p++;
606                         p[strlen(p)-1] = 0;
607                 }
608
609                 if (gen_parse_base(pinfo, ptr + idx*size, p) != 0) {
610                         return -1;
611                 }
612
613                 if (done) break;
614                 str = p2+1;
615         }
616
617         return 0;
618 }
619
620 /* parse one element, hanlding dynamic and static arrays */
621 static int gen_parse_one(const struct parse_struct *pinfo, 
622                          const char *name, 
623                          char *data, 
624                          const char *str)
625 {
626         int i;
627         for (i=0;pinfo[i].name;i++) {
628                 if (strcmp(pinfo[i].name, name) == 0) {
629                         break;
630                 }
631         }
632         if (pinfo[i].name == NULL) {
633                 return 0;
634         }
635
636         if (pinfo[i].array_len) {
637                 return gen_parse_array(&pinfo[i], data+pinfo[i].offset, 
638                                        str, pinfo[i].array_len);
639         }
640
641         if (pinfo[i].dynamic_len) {
642                 int len = find_var(pinfo, data, pinfo[i].dynamic_len);
643                 if (len < 0) {
644                         errno = EINVAL;
645                         return -1;
646                 }
647                 if (len > 0) {
648                         unsigned size;
649                         struct parse_struct p2 = pinfo[i];
650                         char *ptr;
651                         size = pinfo[i].ptr_count>1?sizeof(void*):pinfo[i].size;
652                         ptr = calloc(len, size);
653                         if (!ptr) {
654                                 errno = ENOMEM;
655                                 return -1;
656                         }
657                         *((char **)(data + pinfo[i].offset)) = ptr;
658                         p2.ptr_count--;
659                         p2.dynamic_len = NULL;
660                         return gen_parse_array(&p2, ptr, str, len);
661                 }
662                 return 0;
663         }
664
665         return gen_parse_base(&pinfo[i], data + pinfo[i].offset, str);
666 }
667
668 int gen_parse_struct(const struct parse_struct *pinfo, char *ptr, const char *str)
669 {
670         return gen_parse(pinfo, ptr, str);
671 }
672
673 /* the main parse routine */
674 int gen_parse(const struct parse_struct *pinfo, char *data, const char *s)
675 {
676         char *str, *s0;
677         
678         s0 = strdup(s);
679         str = s0;
680
681         while (*str) {
682                 char *p;
683                 char *name;
684                 char *value;
685
686                 /* skip leading whitespace */
687                 while (isspace(*str)) str++;
688
689                 p = strchr(str, '=');
690                 if (!p) break;
691                 value = p+1;
692                 while (p > str && isspace(*(p-1))) {
693                         p--;
694                 }
695
696                 *p = 0;
697                 name = str;
698
699                 while (isspace(*value)) value++;
700
701                 if (*value == '{') {
702                         str = match_braces(value, '}');
703                         value++;
704                 } else {
705                         str = match_braces(value, '\n');
706                 }
707
708                 *str++ = 0;
709                 
710                 if (gen_parse_one(pinfo, name, data, value) != 0) {
711                         free(s0);
712                         return -1;
713                 }
714         }
715
716         free(s0);
717         return 0;
718 }
719
720
721
722 /* for convenience supply some standard dumpers and parsers here */
723
724 int gen_parse_char(char *ptr, const char *str)
725 {
726         *(unsigned char *)ptr = atoi(str);
727         return 0;
728 }
729
730 int gen_parse_int(char *ptr, const char *str)
731 {
732         *(int *)ptr = atoi(str);
733         return 0;
734 }
735
736 int gen_parse_unsigned(char *ptr, const char *str)
737 {
738         *(unsigned *)ptr = strtoul(str, NULL, 10);
739         return 0;
740 }
741
742 int gen_parse_time_t(char *ptr, const char *str)
743 {
744         *(time_t *)ptr = strtoul(str, NULL, 10);
745         return 0;
746 }
747
748 int gen_parse_double(char *ptr, const char *str)
749 {
750         *(double *)ptr = atof(str);
751         return 0;
752 }
753
754 int gen_parse_float(char *ptr, const char *str)
755 {
756         *(float *)ptr = atof(str);
757         return 0;
758 }
759
760 int gen_dump_char(struct parse_string *p, const char *ptr, unsigned indent)
761 {
762         return addshort(p, "%u", *(unsigned char *)(ptr));
763 }
764
765 int gen_dump_int(struct parse_string *p, const char *ptr, unsigned indent)
766 {
767         return addshort(p, "%d", *(int *)(ptr));
768 }
769
770 int gen_dump_unsigned(struct parse_string *p, const char *ptr, unsigned indent)
771 {
772         return addshort(p, "%u", *(unsigned *)(ptr));
773 }
774
775 int gen_dump_time_t(struct parse_string *p, const char *ptr, unsigned indent)
776 {
777         return addshort(p, "%u", *(time_t *)(ptr));
778 }
779
780 int gen_dump_double(struct parse_string *p, const char *ptr, unsigned indent)
781 {
782         return addshort(p, "%lg", *(double *)(ptr));
783 }
784
785 int gen_dump_float(struct parse_string *p, const char *ptr, unsigned indent)
786 {
787         return addshort(p, "%g", *(float *)(ptr));
788 }