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