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