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