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