translate: check offset assigns if func is used
[ia32rtools.git] / tools / protoparse.h
1
2 struct parsed_proto;
3
4 struct parsed_type {
5         char *name;
6         unsigned int is_array:1;
7         unsigned int is_ptr:1;
8         unsigned int is_struct:1; // split for args
9         unsigned int is_retreg:1; // register to return
10 };
11
12 struct parsed_proto_arg {
13         char *reg;
14         struct parsed_type type;
15         struct parsed_proto *fptr;
16         void *datap;
17 };
18
19 struct parsed_proto {
20         char name[256];
21         union {
22                 struct parsed_type ret_type;
23                 struct parsed_type type;
24         };
25         struct parsed_proto_arg arg[16];
26         int argc;
27         int argc_stack;
28         int argc_reg;
29         unsigned int is_func:1;
30         unsigned int is_stdcall:1;
31         unsigned int is_fastcall:1;
32         unsigned int is_vararg:1;     // vararg func
33         unsigned int is_fptr:1;
34         unsigned int is_noreturn:1;
35         unsigned int is_unresolved:1;
36         unsigned int is_userstack:1;
37         unsigned int is_arg:1;        // decl in func arg
38         unsigned int has_structarg:1;
39         unsigned int has_retreg:1;
40 };
41
42 static const char *hdrfn;
43 static int hdrfline = 0;
44
45 static void pp_copy_arg(struct parsed_proto_arg *d,
46         const struct parsed_proto_arg *s);
47
48 static int b_pp_c_handler(char *proto, const char *fname);
49
50 static int do_protostrs(FILE *fhdr, const char *fname)
51 {
52         const char *finc_name;
53         const char *hdrfn_saved;
54         char protostr[256];
55         char path[256];
56         char fname_inc[256];
57         FILE *finc;
58         int line = 0;
59         int ret;
60         char *p;
61
62         hdrfn_saved = hdrfn;
63         hdrfn = fname;
64
65         while (fgets(protostr, sizeof(protostr), fhdr))
66         {
67                 line++;
68                 if (strncmp(protostr, "//#include ", 11) == 0) {
69                         finc_name = protostr + 11;
70                         p = strpbrk(finc_name, "\r\n ");
71                         if (p != NULL)
72                                 *p = 0;
73
74                         path[0] = 0;
75                         p = strrchr(hdrfn_saved, '/');
76                         if (p) {
77                                 memcpy(path, hdrfn_saved,
78                                         p - hdrfn_saved + 1);
79                                 path[p - hdrfn_saved + 1] = 0;
80                         }
81                         snprintf(fname_inc, sizeof(fname_inc), "%s%s", 
82                                 path, finc_name);
83                         finc = fopen(fname_inc, "r");
84                         if (finc == NULL) {
85                                 printf("%s:%d: can't open '%s'\n",
86                                         fname_inc, line, finc_name);
87                                 continue;
88                         }
89                         ret = do_protostrs(finc, finc_name);
90                         fclose(finc);
91                         if (ret < 0)
92                                 break;
93                         continue;
94                 }
95                 if (strncmp(sskip(protostr), "//", 2) == 0)
96                         continue;
97
98                 p = protostr + strlen(protostr);
99                 for (p--; p >= protostr && my_isblank(*p); --p)
100                         *p = 0;
101                 if (p < protostr)
102                         continue;
103
104                 hdrfline = line;
105
106                 ret = b_pp_c_handler(protostr, hdrfn);
107                 if (ret < 0)
108                         break;
109         }
110
111         hdrfn = hdrfn_saved;
112
113         if (feof(fhdr))
114                 return 0;
115
116         return -1;
117 }
118
119 static int get_regparm(char *dst, size_t dlen, char *p, int *retreg)
120 {
121         int i = 0, o;
122
123         *retreg = 0;
124
125         if (*p != '<')
126                 return 0;
127
128         i++;
129         if (p[i] == '*') {
130                 *retreg = 1;
131                 i++;
132         }
133
134         for (o = 0; o < dlen; i++) {
135                 if (p[i] == 0)
136                         return 0;
137                 if (p[i] == '>')
138                         break;
139                 dst[o++] = p[i];
140         }
141         dst[o] = 0;
142         return i + 1;
143 }
144
145 // hmh..
146 static const char *known_type_mod[] = {
147         "const",
148         "signed",
149         "unsigned",
150         "struct",
151         "enum",
152         "CONST",
153         "volatile",
154 };
155
156 static const char *known_ptr_types[] = {
157         "FARPROC",
158         "WNDPROC",
159         "HACCEL",
160         "HANDLE",
161         "HBITMAP",
162         "HCURSOR",
163         "HDC",
164         "HFONT",
165         "HGDIOBJ",
166         "HGLOBAL",
167         "HICON",
168         "HINSTANCE",
169         "HIMC", // DWORD in mingw, ptr in wine..
170         "HMODULE",
171         "HPALETTE",
172         "HRGN",
173         "HRSRC",
174         "HKEY",
175         "HMENU",
176         "HWAVEOUT",
177         "HWND",
178         "PBYTE",
179         "PCRITICAL_SECTION",
180         "PDWORD",
181         "PFILETIME",
182         "PHKEY",
183         "PLONG",
184         "PMEMORY_BASIC_INFORMATION",
185         "PUINT",
186         "PVOID",
187         "PCVOID",
188         "PWORD",
189         "DLGPROC",
190         "TIMERPROC",
191         "WNDENUMPROC",
192         "va_list",
193         "__VALIST",
194 };
195
196 static const char *ignored_keywords[] = {
197         "extern",
198         "WINBASEAPI",
199         "WINUSERAPI",
200         "WINGDIAPI",
201         "WINADVAPI",
202 };
203
204 // returns ptr to char after type ends
205 static int typecmp(const char *n, const char *t)
206 {
207         for (; *t != 0; n++, t++) {
208                 while (n[0] == ' ' && (n[1] == ' ' || n[1] == '*'))
209                         n++;
210                 while (t[0] == ' ' && (t[1] == ' ' || t[1] == '*'))
211                         t++;
212                 if (*n != *t)
213                         return *n - *t;
214         }
215
216         return 0;
217 }
218
219 static const char *skip_type_mod(const char *n)
220 {
221         int len;
222         int i;
223
224         for (i = 0; i < ARRAY_SIZE(known_type_mod); i++) {
225                 len = strlen(known_type_mod[i]);
226                 if (strncmp(n, known_type_mod[i], len) != 0)
227                         continue;
228                 if (!my_isblank(n[len]))
229                         continue;
230
231                 n += len;
232                 while (my_isblank(*n))
233                         n++;
234                 i = 0;
235         }
236
237         return n;
238 }
239
240 static int check_type(const char *name, struct parsed_type *type)
241 {
242         const char *n, *n1;
243         int ret = -1;
244         int i;
245
246         n = skip_type_mod(name);
247
248         for (i = 0; i < ARRAY_SIZE(known_ptr_types); i++) {
249                 if (typecmp(n, known_ptr_types[i]))
250                         continue;
251
252                 type->is_ptr = 1;
253                 break;
254         }
255
256         if (n[0] == 'L' && n[1] == 'P' && strncmp(n, "LPARAM", 6))
257                 type->is_ptr = 1;
258
259         // assume single word
260         while (!my_isblank(*n) && !my_issep(*n))
261                 n++;
262
263         while (1) {
264                 n1 = n;
265                 while (my_isblank(*n))
266                         n++;
267                 if (*n == '*') {
268                         type->is_ptr = 1;
269                         n++;
270                         continue;
271                 }
272                 break;
273         }
274
275         ret = n1 - name;
276         type->name = strndup(name, ret);
277         if (IS(type->name, "VOID"))
278                 memcpy(type->name, "void", 4);
279
280         return ret;
281 }
282
283 /* args are always expanded to 32bit */
284 static const char *map_reg(const char *reg)
285 {
286         const char *regs_f[] = { "eax", "ebx", "ecx", "edx", "esi", "edi" };
287         const char *regs_w[] = { "ax",  "bx",  "cx",  "dx",  "si",  "di" };
288         const char *regs_b[] = { "al",  "bl",  "cl",  "dl" };
289         int i;
290
291         for (i = 0; i < ARRAY_SIZE(regs_w); i++)
292                 if (IS(reg, regs_w[i]))
293                         return regs_f[i];
294
295         for (i = 0; i < ARRAY_SIZE(regs_b); i++)
296                 if (IS(reg, regs_b[i]))
297                         return regs_f[i];
298
299         return reg;
300 }
301
302 static int check_struct_arg(struct parsed_proto_arg *arg)
303 {
304         if (IS(arg->type.name, "POINT"))
305                 return 2 - 1;
306
307         return 0;
308 }
309
310 static int parse_protostr(char *protostr, struct parsed_proto *pp)
311 {
312         struct parsed_proto_arg *arg;
313         char regparm[16];
314         char buf[256];
315         char cconv[32];
316         int is_retreg;
317         int xarg = 0;
318         char *p, *p1;
319         int i, l;
320         int ret;
321
322         p = sskip(protostr);
323         if (p[0] == '/' && p[1] == '/') {
324                 printf("%s:%d: commented out?\n", hdrfn, hdrfline);
325                 p = sskip(p + 2);
326         }
327
328         // strip unneeded stuff
329         for (p1 = p; p1[0] != 0 && p1[1] != 0; p1++) {
330                 if ((p1[0] == '/' && p1[1] == '*')
331                  || (p1[0] == '*' && p1[1] == '/'))
332                         p1[0] = p1[1] = ' ';
333         }
334
335         if (!strncmp(p, "DECLSPEC_NORETURN ", 18)) {
336                 pp->is_noreturn = 1;
337                 p = sskip(p + 18);
338         }
339
340         for (i = 0; i < ARRAY_SIZE(ignored_keywords); i++) {
341                 l = strlen(ignored_keywords[i]);
342                 if (!strncmp(p, ignored_keywords[i], l) && my_isblank(p[l]))
343                         p = sskip(p + l + 1);
344         }
345
346         ret = check_type(p, &pp->ret_type);
347         if (ret <= 0) {
348                 printf("%s:%d:%zd: unhandled return in '%s'\n",
349                         hdrfn, hdrfline, (p - protostr) + 1, protostr);
350                 return -1;
351         }
352         p = sskip(p + ret);
353
354         if (!strncmp(p, "noreturn ", 9)) {
355                 pp->is_noreturn = 1;
356                 p = sskip(p + 9);
357         }
358
359         if (!strchr(p, ')')) {
360                 p = next_idt(buf, sizeof(buf), p);
361                 p = sskip(p);
362                 if (buf[0] == 0) {
363                         printf("%s:%d:%zd: var name missing\n",
364                                 hdrfn, hdrfline, (p - protostr) + 1);
365                         return -1;
366                 }
367                 strcpy(pp->name, buf);
368
369                 p1 = strchr(p, ']');
370                 if (p1 != NULL) {
371                         p = p1 + 1;
372                         pp->ret_type.is_array = 1;
373                 }
374                 return p - protostr;
375         }
376
377         pp->is_func = 1;
378
379         if (*p == '(') {
380                 pp->is_fptr = 1;
381                 p = sskip(p + 1);
382         }
383
384         p = next_word(cconv, sizeof(cconv), p);
385         p = sskip(p);
386         if (cconv[0] == 0) {
387                 printf("%s:%d:%zd: cconv missing\n",
388                         hdrfn, hdrfline, (p - protostr) + 1);
389                 return -1;
390         }
391         if      (IS(cconv, "__cdecl"))
392                 pp->is_stdcall = 0;
393         else if (IS(cconv, "__stdcall"))
394                 pp->is_stdcall = 1;
395         else if (IS(cconv, "__fastcall")) {
396                 pp->is_fastcall = 1;
397                 pp->is_stdcall = 1; // sort of..
398         }
399         else if (IS(cconv, "__thiscall"))
400                 pp->is_stdcall = 1;
401         else if (IS(cconv, "__userpurge"))
402                 pp->is_stdcall = 1; // IDA
403         else if (IS(cconv, "__usercall"))
404                 pp->is_stdcall = 0; // IDA
405         else if (IS(cconv, "__userstack")) {
406                 pp->is_stdcall = 0; // custom
407                 pp->is_userstack = 1;
408         }
409         else if (IS(cconv, "WINAPI"))
410                 pp->is_stdcall = 1;
411         else {
412                 printf("%s:%d:%zd: unhandled cconv: '%s'\n",
413                         hdrfn, hdrfline, (p - protostr) + 1, cconv);
414                 return -1;
415         }
416
417         if (pp->is_fptr) {
418                 if (*p != '*') {
419                         printf("%s:%d:%zd: '*' expected\n",
420                                 hdrfn, hdrfline, (p - protostr) + 1);
421                         return -1;
422                 }
423                 p++;
424                 // XXX: skipping extra asterisks, for now
425                 while (*p == '*')
426                         p++;
427                 p = sskip(p);
428         }
429
430         p = next_idt(buf, sizeof(buf), p);
431         p = sskip(p);
432         if (buf[0] == 0) {
433                 //printf("%s:%d:%zd: func name missing\n",
434                 //      hdrfn, hdrfline, (p - protostr) + 1);
435                 //return -1;
436         }
437         strcpy(pp->name, buf);
438
439         ret = get_regparm(regparm, sizeof(regparm), p, &is_retreg);
440         if (ret > 0) {
441                 if (!IS(regparm, "eax") && !IS(regparm, "ax")
442                  && !IS(regparm, "al") && !IS(regparm, "edx:eax"))
443                 {
444                         printf("%s:%d:%zd: bad regparm: %s\n",
445                                 hdrfn, hdrfline, (p - protostr) + 1, regparm);
446                         return -1;
447                 }
448                 p += ret;
449                 p = sskip(p);
450         }
451
452         if (pp->is_fptr) {
453                 if (*p == '[') {
454                         // not really ret_type is array, but ohwell
455                         pp->ret_type.is_array = 1;
456                         p = strchr(p + 1, ']');
457                         if (p == NULL) {
458                                 printf("%s:%d:%zd: ']' expected\n",
459                                  hdrfn, hdrfline, (p - protostr) + 1);
460                                 return -1;
461                         }
462                         p = sskip(p + 1);
463                 }
464                 if (*p != ')') {
465                         printf("%s:%d:%zd: ')' expected\n",
466                                 hdrfn, hdrfline, (p - protostr) + 1);
467                         return -1;
468                 }
469                 p = sskip(p + 1);
470         }
471
472         if (*p != '(') {
473                 printf("%s:%d:%zd: '(' expected, got '%c'\n",
474                                 hdrfn, hdrfline, (p - protostr) + 1, *p);
475                 return -1;
476         }
477         p++;
478
479         // check for x(void)
480         p = sskip(p);
481         if ((!strncmp(p, "void", 4) || !strncmp(p, "VOID", 4))
482            && *sskip(p + 4) == ')')
483                 p += 4;
484
485         while (1) {
486                 p = sskip(p);
487                 if (*p == ')') {
488                         p++;
489                         break;
490                 }
491                 if (xarg > 0) {
492                         if (*p != ',') {
493                                 printf("%s:%d:%zd: ',' expected\n",
494                                  hdrfn, hdrfline, (p - protostr) + 1);
495                                 return -1;
496                         }
497                         p = sskip(p + 1);
498                 }
499
500                 if (!strncmp(p, "...", 3)) {
501                         pp->is_vararg = 1;
502                         p = sskip(p + 3);
503                         if (*p == ')') {
504                                 p++;
505                                 break;
506                         }
507                         printf("%s:%d:%zd: ')' expected\n",
508                                 hdrfn, hdrfline, (p - protostr) + 1);
509                         return -1;
510                 }
511
512                 arg = &pp->arg[xarg];
513                 xarg++;
514
515                 p1 = p;
516                 ret = check_type(p, &arg->type);
517                 if (ret <= 0) {
518                         printf("%s:%d:%zd: unhandled type for arg%d\n",
519                                 hdrfn, hdrfline, (p - protostr) + 1, xarg);
520                         return -1;
521                 }
522                 p = sskip(p + ret);
523
524                 if (*p == '(') {
525                         // func ptr
526                         arg->fptr = calloc(1, sizeof(*arg->fptr));
527                         ret = parse_protostr(p1, arg->fptr);
528                         if (ret < 0) {
529                                 printf("%s:%d:%zd: funcarg parse failed\n",
530                                         hdrfn, hdrfline, p1 - protostr);
531                                 return -1;
532                         }
533                         arg->fptr->is_arg = 1;
534                         // we don't use actual names right now..
535                         snprintf(arg->fptr->name,
536                                 sizeof(arg->fptr->name), "a%d", xarg);
537                         // we'll treat it as void * for non-calls
538                         arg->type.name = strdup("void *");
539                         arg->type.is_ptr = 1;
540
541                         p = p1 + ret;
542                 }
543
544                 p = next_idt(buf, sizeof(buf), p);
545                 p = sskip(p);
546 #if 0
547                 if (buf[0] == 0) {
548                         printf("%s:%d:%zd: idt missing for arg%d\n",
549                                 hdrfn, hdrfline, (p - protostr) + 1, xarg);
550                         return -1;
551                 }
552 #endif
553                 arg->reg = NULL;
554
555                 ret = get_regparm(regparm, sizeof(regparm), p, &is_retreg);
556                 if (ret > 0) {
557                         p += ret;
558                         p = sskip(p);
559
560                         arg->reg = strdup(map_reg(regparm));
561                         arg->type.is_retreg = is_retreg;
562                         pp->has_retreg |= is_retreg;
563                 }
564
565                 if (strstr(arg->type.name, "int64")
566                     || IS(arg->type.name, "double"))
567                 {
568                         // hack..
569                         free(arg->type.name);
570                         arg->type.name = strdup("int");
571                         pp_copy_arg(&pp->arg[xarg], arg);
572                         xarg++;
573                 }
574
575                 ret = check_struct_arg(arg);
576                 if (ret > 0) {
577                         pp->has_structarg = 1;
578                         arg->type.is_struct = 1;
579                         free(arg->type.name);
580                         arg->type.name = strdup("int");
581                         for (l = 0; l < ret; l++) {
582                                 pp_copy_arg(&pp->arg[xarg], arg);
583                                 xarg++;
584                         }
585                 }
586         }
587
588         if (xarg > 0 && (IS(cconv, "__fastcall") || IS(cconv, "__thiscall"))) {
589                 if (pp->arg[0].reg != NULL) {
590                         printf("%s:%d: %s with arg1 spec %s?\n",
591                                 hdrfn, hdrfline, cconv, pp->arg[0].reg);
592                 }
593                 pp->arg[0].reg = strdup("ecx");
594         }
595
596         if (xarg > 1 && IS(cconv, "__fastcall")) {
597                 if (pp->arg[1].reg != NULL) {
598                         printf("%s:%d: %s with arg2 spec %s?\n",
599                                 hdrfn, hdrfline, cconv, pp->arg[1].reg);
600                 }
601                 pp->arg[1].reg = strdup("edx");
602         }
603
604         pp->argc = xarg;
605
606         for (i = 0; i < pp->argc; i++) {
607                 if (pp->arg[i].reg == NULL)
608                         pp->argc_stack++;
609                 else
610                         pp->argc_reg++;
611         }
612
613         if (pp->argc == 1 && pp->arg[0].reg != NULL
614             && IS(pp->arg[0].reg, "ecx"))
615         {
616                 pp->is_fastcall = 1;
617         }
618         else if (pp->argc_reg == 2
619           && pp->arg[0].reg != NULL && IS(pp->arg[0].reg, "ecx")
620           && pp->arg[1].reg != NULL && IS(pp->arg[1].reg, "edx"))
621         {
622                 pp->is_fastcall = 1;
623         }
624
625         if (pp->is_vararg && (pp->is_stdcall || pp->is_fastcall)) {
626                 printf("%s:%d: vararg %s?\n", hdrfn, hdrfline, cconv);
627                 return -1;
628         }
629
630         return p - protostr;
631 }
632
633 static int pp_name_cmp(const void *p1, const void *p2)
634 {
635         const struct parsed_proto *pp1 = p1, *pp2 = p2;
636         return strcmp(pp1->name, pp2->name);
637 }
638
639 static struct parsed_proto *pp_cache;
640 static int pp_cache_size;
641 static int pp_cache_alloc;
642
643 static int b_pp_c_handler(char *proto, const char *fname)
644 {
645         int ret;
646
647         if (pp_cache_size >= pp_cache_alloc) {
648                 pp_cache_alloc = pp_cache_alloc * 2 + 64;
649                 pp_cache = realloc(pp_cache, pp_cache_alloc
650                                 * sizeof(pp_cache[0]));
651                 my_assert_not(pp_cache, NULL);
652                 memset(pp_cache + pp_cache_size, 0,
653                         (pp_cache_alloc - pp_cache_size)
654                          * sizeof(pp_cache[0]));
655         }
656
657         ret = parse_protostr(proto, &pp_cache[pp_cache_size]);
658         if (ret < 0)
659                 return -1;
660
661         pp_cache_size++;
662         return 0;
663 }
664
665 static void build_pp_cache(FILE *fhdr)
666 {
667         long pos;
668         int ret;
669
670         pos = ftell(fhdr);
671         rewind(fhdr);
672
673         ret = do_protostrs(fhdr, hdrfn);
674         if (ret < 0)
675                 exit(1);
676
677         qsort(pp_cache, pp_cache_size, sizeof(pp_cache[0]), pp_name_cmp);
678         fseek(fhdr, pos, SEEK_SET);
679 }
680
681 static const struct parsed_proto *proto_parse(FILE *fhdr, const char *sym,
682         int quiet)
683 {
684         const struct parsed_proto *pp_ret;
685         struct parsed_proto pp_search;
686         char *p;
687
688         if (pp_cache == NULL)
689                 build_pp_cache(fhdr);
690
691         if (sym[0] == '_') // && strncmp(fname, "stdc", 4) == 0)
692                 sym++;
693
694         strcpy(pp_search.name, sym);
695         p = strchr(pp_search.name, '@');
696         if (p != NULL)
697                 *p = 0;
698
699         pp_ret = bsearch(&pp_search, pp_cache, pp_cache_size,
700                         sizeof(pp_cache[0]), pp_name_cmp);
701         if (pp_ret == NULL && !quiet)
702                 printf("%s: sym '%s' is missing\n", hdrfn, sym);
703
704         return pp_ret;
705 }
706
707 static void pp_copy_arg(struct parsed_proto_arg *d,
708         const struct parsed_proto_arg *s)
709 {
710         memcpy(d, s, sizeof(*d));
711
712         if (s->reg != NULL) {
713                 d->reg = strdup(s->reg);
714                 my_assert_not(d->reg, NULL);
715         }
716         if (s->type.name != NULL) {
717                 d->type.name = strdup(s->type.name);
718                 my_assert_not(d->type.name, NULL);
719         }
720         if (s->fptr != NULL) {
721                 d->fptr = malloc(sizeof(*d->fptr));
722                 my_assert_not(d->fptr, NULL);
723                 memcpy(d->fptr, s->fptr, sizeof(*d->fptr));
724         }
725 }
726
727 struct parsed_proto *proto_clone(const struct parsed_proto *pp_c)
728 {
729         struct parsed_proto *pp;
730         int i;
731
732         pp = malloc(sizeof(*pp));
733         my_assert_not(pp, NULL);
734         memcpy(pp, pp_c, sizeof(*pp)); // lazy..
735
736         // do the actual deep copy..
737         for (i = 0; i < pp_c->argc; i++)
738                 pp_copy_arg(&pp->arg[i], &pp_c->arg[i]);
739         if (pp_c->ret_type.name != NULL)
740                 pp->ret_type.name = strdup(pp_c->ret_type.name);
741
742         return pp;
743 }
744
745
746 static inline int pp_cmp_func(const struct parsed_proto *pp1,
747   const struct parsed_proto *pp2)
748 {
749   int i;
750
751   if (pp1->argc != pp2->argc || pp1->argc_reg != pp2->argc_reg)
752     return 1;
753   else {
754     for (i = 0; i < pp1->argc; i++) {
755       if ((pp1->arg[i].reg != NULL) != (pp2->arg[i].reg != NULL))
756         return 1;
757
758       if ((pp1->arg[i].reg != NULL)
759         && !IS(pp1->arg[i].reg, pp2->arg[i].reg))
760       {
761         return 1;
762       }
763     }
764   }
765
766   return 0;
767 }
768
769 static inline void pp_print(char *buf, size_t buf_size,
770   const struct parsed_proto *pp)
771 {
772   size_t l;
773   int i;
774
775   snprintf(buf, buf_size, "%s %s(", pp->ret_type.name, pp->name);
776   l = strlen(buf);
777
778   for (i = 0; i < pp->argc_reg; i++) {
779     snprintf(buf + l, buf_size - l, "%s%s",
780       i == 0 ? "" : ", ", pp->arg[i].reg);
781     l = strlen(buf);
782   }
783   if (pp->argc_stack > 0) {
784     snprintf(buf + l, buf_size - l, "%s{%d stack}",
785       i == 0 ? "" : ", ", pp->argc_stack);
786     l = strlen(buf);
787   }
788   snprintf(buf + l, buf_size - l, ")");
789 }
790
791 static inline void proto_release(struct parsed_proto *pp)
792 {
793         int i;
794
795         for (i = 0; i < pp->argc; i++) {
796                 if (pp->arg[i].reg != NULL)
797                         free(pp->arg[i].reg);
798                 if (pp->arg[i].type.name != NULL)
799                         free(pp->arg[i].type.name);
800                 if (pp->arg[i].fptr != NULL)
801                         free(pp->arg[i].fptr);
802         }
803         if (pp->ret_type.name != NULL)
804                 free(pp->ret_type.name);
805         free(pp);
806 }