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