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