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