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