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