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