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