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