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