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