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