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