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