70d02c3507a2c2b436171009d4c6b1ec25615172
[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[32];
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         "SC_HANDLE",
243         "SERVICE_STATUS_HANDLE",
244         "HOOKPROC",
245         "DLGPROC",
246         "TIMERPROC",
247         "WNDENUMPROC",
248         "va_list",
249         "__VALIST",
250 };
251
252 static const char *ignored_keywords[] = {
253         "extern",
254         "WINBASEAPI",
255         "WINUSERAPI",
256         "WINGDIAPI",
257         "WINADVAPI",
258 };
259
260 static int typecmp(const char *n, const char *t)
261 {
262         for (; *t != 0; n++, t++) {
263                 while (n[0] == ' ' && (n[1] == ' ' || n[1] == '*'))
264                         n++;
265                 while (t[0] == ' ' && (t[1] == ' ' || t[1] == '*'))
266                         t++;
267                 if (*n != *t)
268                         return *n - *t;
269         }
270
271         return 0;
272 }
273
274 static const char *skip_type_mod(const char *n)
275 {
276         int len;
277         int i;
278
279         for (i = 0; i < ARRAY_SIZE(known_type_mod); i++) {
280                 len = strlen(known_type_mod[i]);
281                 if (strncmp(n, known_type_mod[i], len) != 0)
282                         continue;
283                 if (!my_isblank(n[len]))
284                         continue;
285
286                 n += len;
287                 while (my_isblank(*n))
288                         n++;
289                 i = 0;
290         }
291
292         return n;
293 }
294
295 static int check_type(const char *name, struct parsed_type *type)
296 {
297         const char *n, *n1;
298         int ret = -1;
299         int i;
300
301         n = skip_type_mod(name);
302
303         if (!strncmp(n, "struct", 6) && my_isblank(n[6])) {
304                 type->is_struct = 1;
305
306                 n += 6;
307                 while (my_isblank(*n))
308                         n++;
309         }
310
311         for (i = 0; i < ARRAY_SIZE(known_ptr_types); i++) {
312                 if (typecmp(n, known_ptr_types[i]))
313                         continue;
314
315                 type->is_ptr = 1;
316                 break;
317         }
318
319         if (n[0] == 'L' && n[1] == 'P' && strncmp(n, "LPARAM", 6))
320                 type->is_ptr = 1;
321
322         // assume single word
323         while (!my_isblank(*n) && !my_issep(*n))
324                 n++;
325
326         while (1) {
327                 n1 = n;
328                 while (my_isblank(*n))
329                         n++;
330                 if (*n == '*') {
331                         type->is_ptr = 1;
332                         n++;
333                         continue;
334                 }
335                 break;
336         }
337
338         ret = n1 - name;
339         type->name = strndup(name, ret);
340         if (IS(type->name, "__VALIST") || IS(type->name, "va_list"))
341                 type->is_va_list = 1;
342         if (IS(type->name, "VOID"))
343                 memcpy(type->name, "void", 4);
344
345         return ret;
346 }
347
348 /* args are always expanded to 32bit */
349 static const char *map_reg(const char *reg)
350 {
351         const char *regs_f[] = { "eax", "ebx", "ecx", "edx", "esi", "edi" };
352         const char *regs_w[] = { "ax",  "bx",  "cx",  "dx",  "si",  "di" };
353         const char *regs_b[] = { "al",  "bl",  "cl",  "dl" };
354         int i;
355
356         for (i = 0; i < ARRAY_SIZE(regs_w); i++)
357                 if (IS(reg, regs_w[i]))
358                         return regs_f[i];
359
360         for (i = 0; i < ARRAY_SIZE(regs_b); i++)
361                 if (IS(reg, regs_b[i]))
362                         return regs_f[i];
363
364         return reg;
365 }
366
367 static int check_struct_arg(struct parsed_proto_arg *arg)
368 {
369         if (IS(arg->type.name, "POINT"))
370                 return 2 - 1;
371
372         return 0;
373 }
374
375 static int parse_protostr(char *protostr, struct parsed_proto *pp);
376
377 static int parse_arg(char **p_, struct parsed_proto_arg *arg, int xarg)
378 {
379         char buf[256];
380         char *p = *p_;
381         char *pe;
382         int ret;
383
384         arg->pp = calloc(1, sizeof(*arg->pp));
385         my_assert_not(arg->pp, NULL);
386         arg->pp->is_arg = 1;
387
388         pe = p;
389         while (1) {
390                 pe = strpbrk(pe, ",()");
391                 if (pe == NULL)
392                         return -1;
393                 if (*pe == ',' || *pe == ')')
394                         break;
395                 pe = strchr(pe, ')');
396                 if (pe == NULL)
397                         return -1;
398                 pe++;
399         }
400
401         if (pe - p > sizeof(buf) - 1)
402                 return -1;
403         memcpy(buf, p, pe - p);
404         buf[pe - p] = 0;
405
406         ret = parse_protostr(buf, arg->pp);
407         if (ret < 0)
408                 return -1;
409
410         if (IS_START(arg->pp->name, "guess"))
411                 arg->pp->is_guessed = 1;
412
413         // we don't use actual names right now...
414         snprintf(arg->pp->name, sizeof(arg->pp->name), "a%d", xarg);
415
416         if (!arg->type.is_struct)
417                 // we'll treat it as void * for non-calls
418                 arg->type.name = strdup("void *");
419         arg->type.is_ptr = 1;
420
421         p += ret;
422         *p_ = p;
423         return 0;
424 }
425
426 static int parse_protostr(char *protostr, struct parsed_proto *pp)
427 {
428         struct parsed_proto_arg *arg;
429         char regparm[16];
430         char buf[256];
431         char cconv[32];
432         int is_retreg;
433         char *p, *p1;
434         int xarg = 0;
435         int i, l;
436         int ret;
437
438         p = sskip(protostr);
439         if (p[0] == '/' && p[1] == '/') {
440                 printf("%s:%d: commented out?\n", hdrfn, hdrfline);
441                 p = sskip(p + 2);
442         }
443
444         // allow start of line comment
445         if (p[0] == '/' && p[1] == '*') {
446                 p = strstr(p + 2, "*/");
447                 if (p == NULL) {
448                         printf("%s:%d: multiline comments unsupported\n",
449                                 hdrfn, hdrfline);
450                         return -1;
451                 }
452                 p = sskip(p + 2);
453         }
454
455         // we need remaining hints in comments, so strip / *
456         for (p1 = p; p1[0] != 0 && p1[1] != 0; p1++) {
457                 if ((p1[0] == '/' && p1[1] == '*')
458                  || (p1[0] == '*' && p1[1] == '/'))
459                         p1[0] = p1[1] = ' ';
460         }
461
462         if (!strncmp(p, "DECLSPEC_NORETURN ", 18)) {
463                 pp->is_noreturn = 1;
464                 p = sskip(p + 18);
465         }
466
467         for (i = 0; i < ARRAY_SIZE(ignored_keywords); i++) {
468                 l = strlen(ignored_keywords[i]);
469                 if (!strncmp(p, ignored_keywords[i], l) && my_isblank(p[l]))
470                         p = sskip(p + l + 1);
471         }
472
473         if (IS_START(p, "DECL_IMPORT ")) {
474                 pp->is_import = 1;
475                 p = sskip(p + 12);
476         }
477
478         ret = check_type(p, &pp->ret_type);
479         if (ret <= 0) {
480                 printf("%s:%d:%zd: unhandled return in '%s'\n",
481                         hdrfn, hdrfline, (p - protostr) + 1, protostr);
482                 return -1;
483         }
484         p = sskip(p + ret);
485
486         if (!strncmp(p, "noreturn ", 9)) {
487                 pp->is_noreturn = 1;
488                 p = sskip(p + 9);
489         }
490
491         if (!strchr(p, ')')) {
492                 p = next_idt(buf, sizeof(buf), p);
493                 p = sskip(p);
494                 if (!pp->is_arg && buf[0] == 0) {
495                         printf("%s:%d:%zd: var name is missing\n",
496                                 hdrfn, hdrfline, (p - protostr) + 1);
497                         return -1;
498                 }
499                 strcpy(pp->name, buf);
500
501                 p1 = strchr(p, ']');
502                 if (p1 != NULL) {
503                         p = p1 + 1;
504                         pp->ret_type.is_array = 1;
505                 }
506                 return p - protostr;
507         }
508
509         pp->is_func = 1;
510
511         if (*p == '(') {
512                 pp->is_fptr = 1;
513                 p = sskip(p + 1);
514         }
515
516         p = next_word(cconv, sizeof(cconv), p);
517         p = sskip(p);
518         if (cconv[0] == 0) {
519                 printf("%s:%d:%zd: cconv missing\n",
520                         hdrfn, hdrfline, (p - protostr) + 1);
521                 return -1;
522         }
523         if      (IS(cconv, "__cdecl"))
524                 pp->is_stdcall = 0;
525         else if (IS(cconv, "__stdcall"))
526                 pp->is_stdcall = 1;
527         else if (IS(cconv, "__fastcall")) {
528                 pp->is_fastcall = 1;
529                 pp->is_stdcall = 1; // sort of..
530         }
531         else if (IS(cconv, "__thiscall"))
532                 pp->is_stdcall = 1;
533         else if (IS(cconv, "__userpurge"))
534                 pp->is_stdcall = 1; // IDA
535         else if (IS(cconv, "__usercall"))
536                 pp->is_stdcall = 0; // IDA
537         else if (IS(cconv, "__userstack")) {
538                 pp->is_stdcall = 0; // custom
539                 pp->is_userstack = 1;
540         }
541         else if (IS(cconv, "WINAPI") || IS(cconv, "PASCAL"))
542                 pp->is_stdcall = 1;
543         else {
544                 printf("%s:%d:%zd: unhandled cconv: '%s'\n",
545                         hdrfn, hdrfline, (p - protostr) + 1, cconv);
546                 return -1;
547         }
548
549         if (pp->is_fptr) {
550                 if (*p != '*') {
551                         printf("%s:%d:%zd: '*' expected\n",
552                                 hdrfn, hdrfline, (p - protostr) + 1);
553                         return -1;
554                 }
555                 p++;
556                 // XXX: skipping extra asterisks, for now
557                 while (*p == '*')
558                         p++;
559                 p = sskip(p);
560         }
561
562         p = next_idt(buf, sizeof(buf), p);
563         p = sskip(p);
564         if (buf[0] == 0) {
565                 //printf("%s:%d:%zd: func name missing\n",
566                 //      hdrfn, hdrfline, (p - protostr) + 1);
567                 //return -1;
568         }
569         strcpy(pp->name, buf);
570
571         ret = get_regparm(regparm, sizeof(regparm), p, &is_retreg);
572         if (ret > 0) {
573                 if (!IS(regparm, "eax") && !IS(regparm, "ax")
574                  && !IS(regparm, "al") && !IS(regparm, "edx:eax"))
575                 {
576                         printf("%s:%d:%zd: bad regparm: %s\n",
577                                 hdrfn, hdrfline, (p - protostr) + 1, regparm);
578                         return -1;
579                 }
580                 p += ret;
581                 p = sskip(p);
582         }
583
584         if (pp->is_fptr) {
585                 if (*p == '[') {
586                         // not really ret_type is array, but ohwell
587                         pp->ret_type.is_array = 1;
588                         p = strchr(p + 1, ']');
589                         if (p == NULL) {
590                                 printf("%s:%d:%zd: ']' expected\n",
591                                  hdrfn, hdrfline, (p - protostr) + 1);
592                                 return -1;
593                         }
594                         p = sskip(p + 1);
595                 }
596                 if (*p != ')') {
597                         printf("%s:%d:%zd: ')' expected\n",
598                                 hdrfn, hdrfline, (p - protostr) + 1);
599                         return -1;
600                 }
601                 p = sskip(p + 1);
602         }
603
604         if (*p != '(') {
605                 printf("%s:%d:%zd: '(' expected, got '%c'\n",
606                                 hdrfn, hdrfline, (p - protostr) + 1, *p);
607                 return -1;
608         }
609         p++;
610
611         // check for x(void)
612         p = sskip(p);
613         if ((!strncmp(p, "void", 4) || !strncmp(p, "VOID", 4))
614            && *sskip(p + 4) == ')')
615                 p += 4;
616
617         while (1) {
618                 p = sskip(p);
619                 if (*p == ')') {
620                         p++;
621                         break;
622                 }
623                 if (xarg > 0) {
624                         if (*p != ',') {
625                                 printf("%s:%d:%zd: ',' expected\n",
626                                  hdrfn, hdrfline, (p - protostr) + 1);
627                                 return -1;
628                         }
629                         p = sskip(p + 1);
630                 }
631
632                 if (!strncmp(p, "...", 3)) {
633                         pp->is_vararg = 1;
634                         p = sskip(p + 3);
635                         if (*p == ')') {
636                                 p++;
637                                 break;
638                         }
639                         printf("%s:%d:%zd: ')' expected\n",
640                                 hdrfn, hdrfline, (p - protostr) + 1);
641                         return -1;
642                 }
643
644                 if (xarg >= ARRAY_SIZE(pp->arg)) {
645                         printf("%s:%d:%zd: too many args\n",
646                                 hdrfn, hdrfline, (p - protostr) + 1);
647                         return -1;
648                 }
649
650                 arg = &pp->arg[xarg];
651                 xarg++;
652
653                 p1 = p;
654                 ret = check_type(p, &arg->type);
655                 if (ret <= 0) {
656                         printf("%s:%d:%zd: unhandled type for arg%d\n",
657                                 hdrfn, hdrfline, (p - protostr) + 1, xarg);
658                         return -1;
659                 }
660                 p = sskip(p + ret);
661
662                 if (*p == '(' || arg->type.is_struct) {
663                         // func ptr or struct
664                         ret = parse_arg(&p1, arg, xarg);
665                         if (ret < 0) {
666                                 printf("%s:%d:%zd: funcarg parse failed\n",
667                                         hdrfn, hdrfline, p1 - protostr);
668                                 return -1;
669                         }
670                         p = p1;
671                 }
672
673                 p = next_idt(buf, sizeof(buf), p);
674                 p = sskip(p);
675 #if 0
676                 if (buf[0] == 0) {
677                         printf("%s:%d:%zd: idt missing for arg%d\n",
678                                 hdrfn, hdrfline, (p - protostr) + 1, xarg);
679                         return -1;
680                 }
681 #endif
682                 arg->reg = NULL;
683
684                 ret = get_regparm(regparm, sizeof(regparm), p, &is_retreg);
685                 if (ret > 0) {
686                         p += ret;
687                         p = sskip(p);
688
689                         arg->reg = strdup(map_reg(regparm));
690                         arg->type.is_retreg = is_retreg;
691                         pp->has_retreg |= is_retreg;
692                 }
693
694                 if (IS(arg->type.name, "float")
695                       || IS(arg->type.name, "double"))
696                 {
697                         arg->type.is_float = 1;
698                 }
699
700                 if (!arg->type.is_ptr && (strstr(arg->type.name, "int64")
701                       || IS(arg->type.name, "double")))
702                 {
703                         arg->type.is_64bit = 1;
704                         // hack..
705                         pp_copy_arg(&pp->arg[xarg], arg);
706                         arg = &pp->arg[xarg];
707                         xarg++;
708                         free(arg->type.name);
709                         arg->type.name = strdup("dummy");
710                 }
711
712                 ret = check_struct_arg(arg);
713                 if (ret > 0) {
714                         pp->has_structarg = 1;
715                         arg->type.is_struct = 1;
716                         free(arg->type.name);
717                         arg->type.name = strdup("int");
718                         for (l = 0; l < ret; l++) {
719                                 pp_copy_arg(&pp->arg[xarg], arg);
720                                 xarg++;
721                         }
722                 }
723         }
724
725         if (xarg > 0 && (IS(cconv, "__fastcall") || IS(cconv, "__thiscall"))) {
726                 if (pp->arg[0].reg != NULL) {
727                         printf("%s:%d: %s with arg1 spec %s?\n",
728                                 hdrfn, hdrfline, cconv, pp->arg[0].reg);
729                 }
730                 pp->arg[0].reg = strdup("ecx");
731         }
732
733         if (xarg > 1 && IS(cconv, "__fastcall")) {
734                 if (pp->arg[1].reg != NULL) {
735                         printf("%s:%d: %s with arg2 spec %s?\n",
736                                 hdrfn, hdrfline, cconv, pp->arg[1].reg);
737                 }
738                 pp->arg[1].reg = strdup("edx");
739         }
740
741         pp->argc = xarg;
742
743         for (i = 0; i < pp->argc; i++) {
744                 if (pp->arg[i].reg == NULL)
745                         pp->argc_stack++;
746                 else
747                         pp->argc_reg++;
748         }
749
750         if (pp->argc == 1 && pp->arg[0].reg != NULL
751             && IS(pp->arg[0].reg, "ecx"))
752         {
753                 pp->is_fastcall = 1;
754         }
755         else if (pp->argc_reg == 2
756           && pp->arg[0].reg != NULL && IS(pp->arg[0].reg, "ecx")
757           && pp->arg[1].reg != NULL && IS(pp->arg[1].reg, "edx"))
758         {
759                 pp->is_fastcall = 1;
760         }
761
762         if (pp->is_vararg && (pp->is_stdcall || pp->is_fastcall)) {
763                 printf("%s:%d: vararg %s?\n", hdrfn, hdrfline, cconv);
764                 return -1;
765         }
766
767         return p - protostr;
768 }
769
770 static int pp_name_cmp(const void *p1, const void *p2)
771 {
772         const struct parsed_proto *pp1 = p1, *pp2 = p2;
773         return strcmp(pp1->name, pp2->name);
774 }
775
776 static int ps_name_cmp(const void *p1, const void *p2)
777 {
778         const struct parsed_struct *ps1 = p1, *ps2 = p2;
779         return strcmp(ps1->name, ps2->name);
780 }
781
782 // parsed struct cache
783 static struct parsed_struct *ps_cache;
784 static int ps_cache_size;
785 static int ps_cache_alloc;
786
787 static int struct_handler(FILE *fhdr, char *proto, int *line)
788 {
789         struct parsed_struct *ps;
790         char lstr[256], *p;
791         int offset = 0;
792         int m = 0;
793         int ret;
794
795         if (ps_cache_size >= ps_cache_alloc) {
796                 ps_cache_alloc = ps_cache_alloc * 2 + 64;
797                 ps_cache = realloc(ps_cache, ps_cache_alloc
798                                 * sizeof(ps_cache[0]));
799                 my_assert_not(ps_cache, NULL);
800                 memset(ps_cache + ps_cache_size, 0,
801                         (ps_cache_alloc - ps_cache_size)
802                          * sizeof(ps_cache[0]));
803         }
804
805         ps = &ps_cache[ps_cache_size++];
806         ret = sscanf(proto, "struct %255s {", ps->name);
807         if (ret != 1) {
808                 printf("%s:%d: struct parse failed\n", hdrfn, *line);
809                 return -1;
810         }
811
812         while (fgets(lstr, sizeof(lstr), fhdr))
813         {
814                 (*line)++;
815
816                 p = sskip(lstr);
817                 if (p[0] == '/' && p[1] == '/')
818                         continue;
819                 if (p[0] == '}')
820                         break;
821
822                 if (m >= ARRAY_SIZE(ps->members)) {
823                         printf("%s:%d: too many struct members\n",
824                                 hdrfn, *line);
825                         return -1;
826                 }
827
828                 hdrfline = *line;
829                 ret = parse_protostr(p, &ps->members[m].pp);
830                 if (ret < 0) {
831                         printf("%s:%d: struct member #%d/%02x "
832                                 "doesn't parse\n", hdrfn, *line,
833                                 m, offset);
834                         return -1;
835                 }
836                 ps->members[m].offset = offset;
837                 offset += 4;
838                 m++;
839         }
840
841         ps->member_count = m;
842
843         return 0;
844 }
845
846 // parsed proto cache
847 static struct parsed_proto *pp_cache;
848 static int pp_cache_size;
849 static int pp_cache_alloc;
850
851 static int b_pp_c_handler(char *proto, const char *fname,
852         int is_include, int is_osinc, int is_cinc)
853 {
854         int ret;
855
856         if (pp_cache_size >= pp_cache_alloc) {
857                 pp_cache_alloc = pp_cache_alloc * 2 + 64;
858                 pp_cache = realloc(pp_cache, pp_cache_alloc
859                                 * sizeof(pp_cache[0]));
860                 my_assert_not(pp_cache, NULL);
861                 memset(pp_cache + pp_cache_size, 0,
862                         (pp_cache_alloc - pp_cache_size)
863                          * sizeof(pp_cache[0]));
864         }
865
866         ret = parse_protostr(proto, &pp_cache[pp_cache_size]);
867         if (ret < 0)
868                 return -1;
869
870         pp_cache[pp_cache_size].is_include = is_include;
871         pp_cache[pp_cache_size].is_osinc = is_osinc;
872         pp_cache[pp_cache_size].is_cinc = is_cinc;
873         pp_cache_size++;
874         return 0;
875 }
876
877 static void build_caches(FILE *fhdr)
878 {
879         long pos;
880         int ret;
881
882         pos = ftell(fhdr);
883         rewind(fhdr);
884
885         ret = do_protostrs(fhdr, hdrfn, 0);
886         if (ret < 0)
887                 exit(1);
888
889         qsort(pp_cache, pp_cache_size, sizeof(pp_cache[0]), pp_name_cmp);
890         qsort(ps_cache, ps_cache_size, sizeof(ps_cache[0]), ps_name_cmp);
891         fseek(fhdr, pos, SEEK_SET);
892 }
893
894 static const struct parsed_proto *proto_parse(FILE *fhdr, const char *sym,
895         int quiet)
896 {
897         const struct parsed_proto *pp_ret;
898         struct parsed_proto pp_search;
899         char *p;
900
901         if (pp_cache == NULL)
902                 build_caches(fhdr);
903
904         // ugh...
905         if (sym[0] == '_' && !IS_START(sym, "__W"))
906                 sym++;
907
908         strcpy(pp_search.name, sym);
909         p = strchr(pp_search.name, '@');
910         if (p != NULL)
911                 *p = 0;
912
913         pp_ret = bsearch(&pp_search, pp_cache, pp_cache_size,
914                         sizeof(pp_cache[0]), pp_name_cmp);
915         if (pp_ret == NULL && !quiet)
916                 printf("%s: sym '%s' is missing\n", hdrfn, sym);
917
918         return pp_ret;
919 }
920
921 static const struct parsed_proto *proto_lookup_struct(FILE *fhdr,
922         const char *type, int offset)
923 {
924         struct parsed_struct ps_search, *ps;
925         int m;
926
927         if (pp_cache == NULL)
928                 build_caches(fhdr);
929         if (ps_cache_size == 0)
930                 return NULL;
931
932         while (my_isblank(*type))
933                 type++;
934         if (!strncmp(type, "struct", 6) && my_isblank(type[6]))
935                 type += 7;
936
937         if (sscanf(type, "%255s", ps_search.name) != 1)
938                 return NULL;
939
940         ps = bsearch(&ps_search, ps_cache, ps_cache_size,
941                         sizeof(ps_cache[0]), ps_name_cmp);
942         if (ps == NULL) {
943                 printf("%s: struct '%s' is missing\n",
944                         hdrfn, ps_search.name);
945                 return NULL;
946         }
947
948         for (m = 0; m < ps->member_count; m++) {
949                 if (ps->members[m].offset == offset)
950                         return &ps->members[m].pp;
951         }
952
953         return NULL;
954 }
955
956 static void pp_copy_arg(struct parsed_proto_arg *d,
957         const struct parsed_proto_arg *s)
958 {
959         memcpy(d, s, sizeof(*d));
960
961         if (s->reg != NULL) {
962                 d->reg = strdup(s->reg);
963                 my_assert_not(d->reg, NULL);
964         }
965         if (s->type.name != NULL) {
966                 d->type.name = strdup(s->type.name);
967                 my_assert_not(d->type.name, NULL);
968         }
969         if (s->pp != NULL) {
970                 d->pp = malloc(sizeof(*d->pp));
971                 my_assert_not(d->pp, NULL);
972                 memcpy(d->pp, s->pp, sizeof(*d->pp));
973         }
974 }
975
976 struct parsed_proto *proto_clone(const struct parsed_proto *pp_c)
977 {
978         struct parsed_proto *pp;
979         int i;
980
981         pp = malloc(sizeof(*pp));
982         my_assert_not(pp, NULL);
983         memcpy(pp, pp_c, sizeof(*pp)); // lazy..
984
985         // do the actual deep copy..
986         for (i = 0; i < pp_c->argc; i++)
987                 pp_copy_arg(&pp->arg[i], &pp_c->arg[i]);
988         if (pp_c->ret_type.name != NULL)
989                 pp->ret_type.name = strdup(pp_c->ret_type.name);
990
991         return pp;
992 }
993
994
995 static inline int pp_cmp_func(const struct parsed_proto *pp1,
996   const struct parsed_proto *pp2)
997 {
998   int i;
999
1000   if (pp1->argc != pp2->argc || pp1->argc_reg != pp2->argc_reg)
1001     return 1;
1002   if (pp1->is_stdcall != pp2->is_stdcall)
1003     return 1;
1004
1005   // because of poor void return detection, return is not
1006   // checked for now to avoid heaps of false positives
1007
1008   for (i = 0; i < pp1->argc; i++) {
1009     if ((pp1->arg[i].reg != NULL) != (pp2->arg[i].reg != NULL))
1010       return 1;
1011
1012     if ((pp1->arg[i].reg != NULL)
1013       && !IS(pp1->arg[i].reg, pp2->arg[i].reg))
1014     {
1015       return 1;
1016     }
1017   }
1018
1019   return 0;
1020 }
1021
1022 static inline int pp_compatible_func(
1023   const struct parsed_proto *pp_site,
1024   const struct parsed_proto *pp_callee)
1025 {
1026   if (pp_cmp_func(pp_site, pp_callee) == 0)
1027     return 1;
1028
1029   if (pp_site->argc_stack == 0 && pp_site->is_fastcall
1030       && pp_callee->argc_stack == 0
1031       && (pp_callee->is_fastcall || pp_callee->argc_reg == 0)
1032       && pp_site->argc_reg > pp_callee->argc_reg)
1033     /* fascall compatible callee doesn't use all args -> ok */
1034     return 1;
1035
1036   return 0;
1037 }
1038
1039 static inline void pp_print(char *buf, size_t buf_size,
1040   const struct parsed_proto *pp)
1041 {
1042   size_t l;
1043   int i;
1044
1045   snprintf(buf, buf_size, "%s %s(", pp->ret_type.name, pp->name);
1046   l = strlen(buf);
1047
1048   for (i = 0; i < pp->argc_reg; i++) {
1049     snprintf(buf + l, buf_size - l, "%s%s",
1050       i == 0 ? "" : ", ", pp->arg[i].reg);
1051     l = strlen(buf);
1052   }
1053   if (pp->argc_stack > 0) {
1054     snprintf(buf + l, buf_size - l, "%s{%d stack}",
1055       i == 0 ? "" : ", ", pp->argc_stack);
1056     l = strlen(buf);
1057   }
1058   snprintf(buf + l, buf_size - l, ")");
1059 }
1060
1061 static inline void proto_release(struct parsed_proto *pp)
1062 {
1063         int i;
1064
1065         for (i = 0; i < pp->argc; i++) {
1066                 if (pp->arg[i].reg != NULL)
1067                         free(pp->arg[i].reg);
1068                 if (pp->arg[i].type.name != NULL)
1069                         free(pp->arg[i].type.name);
1070                 if (pp->arg[i].pp != NULL)
1071                         free(pp->arg[i].pp);
1072         }
1073         if (pp->ret_type.name != NULL)
1074                 free(pp->ret_type.name);
1075         free(pp);
1076
1077         (void)proto_lookup_struct;
1078 }