int64/double hack
[ia32rtools.git] / tools / protoparse.h
1
2 struct parsed_proto;
3
4 struct parsed_type {
5         char *name;
6         unsigned int is_array:1;
7         unsigned int is_ptr:1;
8         unsigned int is_struct:1; // split for args
9 };
10
11 struct parsed_proto_arg {
12         char *reg;
13         struct parsed_type type;
14         struct parsed_proto *fptr;
15         void *datap;
16 };
17
18 struct parsed_proto {
19         char name[256];
20         union {
21                 struct parsed_type ret_type;
22                 struct parsed_type type;
23         };
24         struct parsed_proto_arg arg[16];
25         int argc;
26         int argc_stack;
27         int argc_reg;
28         unsigned int is_func:1;
29         unsigned int is_stdcall:1;
30         unsigned int is_fastcall:1;
31         unsigned int is_vararg:1;
32         unsigned int is_fptr:1;
33         unsigned int is_noreturn:1;
34         unsigned int is_unresolved:1;
35         unsigned int has_structarg:1;
36 };
37
38 static const char *hdrfn;
39 static int hdrfline = 0;
40
41 static void pp_copy_arg(struct parsed_proto_arg *d,
42         const struct parsed_proto_arg *s);
43
44 static int b_pp_c_handler(char *proto, const char *fname);
45
46 static int do_protostrs(FILE *fhdr, const char *fname)
47 {
48         const char *finc_name;
49         const char *hdrfn_saved;
50         char protostr[256];
51         char path[256];
52         char fname_inc[256];
53         FILE *finc;
54         int line = 0;
55         int ret;
56         char *p;
57
58         hdrfn_saved = hdrfn;
59         hdrfn = fname;
60
61         while (fgets(protostr, sizeof(protostr), fhdr))
62         {
63                 line++;
64                 if (strncmp(protostr, "//#include ", 11) == 0) {
65                         finc_name = protostr + 11;
66                         p = strpbrk(finc_name, "\r\n ");
67                         if (p != NULL)
68                                 *p = 0;
69
70                         path[0] = 0;
71                         p = strrchr(hdrfn_saved, '/');
72                         if (p) {
73                                 memcpy(path, hdrfn_saved,
74                                         p - hdrfn_saved + 1);
75                                 path[p - hdrfn_saved + 1] = 0;
76                         }
77                         snprintf(fname_inc, sizeof(fname_inc), "%s%s", 
78                                 path, finc_name);
79                         finc = fopen(fname_inc, "r");
80                         if (finc == NULL) {
81                                 printf("%s:%d: can't open '%s'\n",
82                                         fname_inc, line, finc_name);
83                                 continue;
84                         }
85                         ret = do_protostrs(finc, finc_name);
86                         fclose(finc);
87                         if (ret < 0)
88                                 break;
89                         continue;
90                 }
91                 if (strncmp(sskip(protostr), "//", 2) == 0)
92                         continue;
93
94                 p = protostr + strlen(protostr);
95                 for (p--; p >= protostr && my_isblank(*p); --p)
96                         *p = 0;
97                 if (p < protostr)
98                         continue;
99
100                 hdrfline = line;
101
102                 ret = b_pp_c_handler(protostr, hdrfn);
103                 if (ret < 0)
104                         break;
105         }
106
107         hdrfn = hdrfn_saved;
108
109         if (feof(fhdr))
110                 return 0;
111
112         return -1;
113 }
114
115 static int get_regparm(char *dst, size_t dlen, char *p)
116 {
117         int i, o;
118
119         if (*p != '<')
120                 return 0;
121
122         for (o = 0, i = 1; o < dlen; i++) {
123                 if (p[i] == 0)
124                         return 0;
125                 if (p[i] == '>')
126                         break;
127                 dst[o++] = p[i];
128         }
129         dst[o] = 0;
130         return i + 1;
131 }
132
133 // hmh..
134 static const char *known_type_mod[] = {
135         "const",
136         "signed",
137         "unsigned",
138         "struct",
139         "enum",
140         "CONST",
141         "volatile",
142 };
143
144 static const char *known_ptr_types[] = {
145         "FARPROC",
146         "WNDPROC",
147         "HACCEL",
148         "HANDLE",
149         "HBITMAP",
150         "HCURSOR",
151         "HDC",
152         "HFONT",
153         "HGDIOBJ",
154         "HGLOBAL",
155         "HICON",
156         "HINSTANCE",
157         //"HIMC", // DWORD
158         "HMODULE",
159         "HPALETTE",
160         "HRGN",
161         "HRSRC",
162         "HKEY",
163         "HMENU",
164         "HWND",
165         "PCRITICAL_SECTION",
166         "PDWORD",
167         "PFILETIME",
168         "PHKEY",
169         "PLONG",
170         "PMEMORY_BASIC_INFORMATION",
171         "PUINT",
172         "PVOID",
173         "PCVOID",
174         "DLGPROC",
175         "TIMERPROC",
176         "WNDENUMPROC",
177         "va_list",
178         "__VALIST",
179 };
180
181 static const char *ignored_keywords[] = {
182         "extern",
183         "WINBASEAPI",
184         "WINUSERAPI",
185         "WINGDIAPI",
186         "WINADVAPI",
187 };
188
189 // returns ptr to char after type ends
190 static int typecmp(const char *n, const char *t)
191 {
192         for (; *t != 0; n++, t++) {
193                 while (n[0] == ' ' && (n[1] == ' ' || n[1] == '*'))
194                         n++;
195                 while (t[0] == ' ' && (t[1] == ' ' || t[1] == '*'))
196                         t++;
197                 if (*n != *t)
198                         return *n - *t;
199         }
200
201         return 0;
202 }
203
204 static const char *skip_type_mod(const char *n)
205 {
206         int len;
207         int i;
208
209         for (i = 0; i < ARRAY_SIZE(known_type_mod); i++) {
210                 len = strlen(known_type_mod[i]);
211                 if (strncmp(n, known_type_mod[i], len) != 0)
212                         continue;
213                 if (!my_isblank(n[len]))
214                         continue;
215
216                 n += len;
217                 while (my_isblank(*n))
218                         n++;
219                 i = 0;
220         }
221
222         return n;
223 }
224
225 static int check_type(const char *name, struct parsed_type *type)
226 {
227         const char *n, *n1;
228         int ret = -1;
229         int i;
230
231         n = skip_type_mod(name);
232
233         for (i = 0; i < ARRAY_SIZE(known_ptr_types); i++) {
234                 if (typecmp(n, known_ptr_types[i]))
235                         continue;
236
237                 type->is_ptr = 1;
238                 break;
239         }
240
241         if (n[0] == 'L' && n[1] == 'P' && strncmp(n, "LPARAM", 6))
242                 type->is_ptr = 1;
243
244         // assume single word
245         while (!my_isblank(*n) && !my_issep(*n))
246                 n++;
247
248         while (1) {
249                 n1 = n;
250                 while (my_isblank(*n))
251                         n++;
252                 if (*n == '*') {
253                         type->is_ptr = 1;
254                         n++;
255                         continue;
256                 }
257                 break;
258         }
259
260         ret = n1 - name;
261         type->name = strndup(name, ret);
262         if (IS(type->name, "VOID"))
263                 memcpy(type->name, "void", 4);
264
265         return ret;
266 }
267
268 /* args are always expanded to 32bit */
269 static const char *map_reg(const char *reg)
270 {
271         const char *regs_f[] = { "eax", "ebx", "ecx", "edx", "esi", "edi" };
272         const char *regs_w[] = { "ax",  "bx",  "cx",  "dx",  "si",  "di" };
273         const char *regs_b[] = { "al",  "bl",  "cl",  "dl" };
274         int i;
275
276         for (i = 0; i < ARRAY_SIZE(regs_w); i++)
277                 if (IS(reg, regs_w[i]))
278                         return regs_f[i];
279
280         for (i = 0; i < ARRAY_SIZE(regs_b); i++)
281                 if (IS(reg, regs_b[i]))
282                         return regs_f[i];
283
284         return reg;
285 }
286
287 static int check_struct_arg(struct parsed_proto_arg *arg)
288 {
289         if (IS(arg->type.name, "POINT"))
290                 return 2 - 1;
291
292         return 0;
293 }
294
295 static int parse_protostr(char *protostr, struct parsed_proto *pp)
296 {
297         struct parsed_proto_arg *arg;
298         char regparm[16];
299         char buf[256];
300         char cconv[32];
301         int xarg = 0;
302         char *p, *p1;
303         int i, l;
304         int ret;
305
306         p = sskip(protostr);
307         if (p[0] == '/' && p[1] == '/') {
308                 printf("%s:%d: commented out?\n", hdrfn, hdrfline);
309                 p = sskip(p + 2);
310         }
311
312         // strip unneeded stuff
313         for (p1 = p; p1[0] != 0 && p1[1] != 0; p1++) {
314                 if ((p1[0] == '/' && p1[1] == '*')
315                  || (p1[0] == '*' && p1[1] == '/'))
316                         p1[0] = p1[1] = ' ';
317         }
318
319         if (!strncmp(p, "DECLSPEC_NORETURN ", 18)) {
320                 pp->is_noreturn = 1;
321                 p = sskip(p + 18);
322         }
323
324         for (i = 0; i < ARRAY_SIZE(ignored_keywords); i++) {
325                 l = strlen(ignored_keywords[i]);
326                 if (!strncmp(p, ignored_keywords[i], l) && my_isblank(p[l]))
327                         p = sskip(p + l + 1);
328         }
329
330         ret = check_type(p, &pp->ret_type);
331         if (ret <= 0) {
332                 printf("%s:%d:%zd: unhandled return in '%s'\n",
333                         hdrfn, hdrfline, (p - protostr) + 1, protostr);
334                 return -1;
335         }
336         p = sskip(p + ret);
337
338         if (!strncmp(p, "noreturn ", 9)) {
339                 pp->is_noreturn = 1;
340                 p = sskip(p + 9);
341         }
342
343         if (!strchr(p, ')')) {
344                 p = next_idt(buf, sizeof(buf), p);
345                 p = sskip(p);
346                 if (buf[0] == 0) {
347                         printf("%s:%d:%zd: var name missing\n",
348                                 hdrfn, hdrfline, (p - protostr) + 1);
349                         return -1;
350                 }
351                 strcpy(pp->name, buf);
352
353                 p1 = strchr(p, ']');
354                 if (p1 != NULL) {
355                         p = p1 + 1;
356                         pp->ret_type.is_array = 1;
357                 }
358                 return p - protostr;
359         }
360
361         pp->is_func = 1;
362
363         if (*p == '(') {
364                 pp->is_fptr = 1;
365                 p = sskip(p + 1);
366         }
367
368         p = next_word(cconv, sizeof(cconv), p);
369         p = sskip(p);
370         if (cconv[0] == 0) {
371                 printf("%s:%d:%zd: cconv missing\n",
372                         hdrfn, hdrfline, (p - protostr) + 1);
373                 return -1;
374         }
375         if      (IS(cconv, "__cdecl"))
376                 pp->is_stdcall = 0;
377         else if (IS(cconv, "__stdcall"))
378                 pp->is_stdcall = 1;
379         else if (IS(cconv, "__fastcall")) {
380                 pp->is_fastcall = 1;
381                 pp->is_stdcall = 1; // sort of..
382         }
383         else if (IS(cconv, "__thiscall"))
384                 pp->is_stdcall = 1;
385         else if (IS(cconv, "__userpurge"))
386                 pp->is_stdcall = 1; // IDA
387         else if (IS(cconv, "__usercall"))
388                 pp->is_stdcall = 0; // IDA
389         else if (IS(cconv, "WINAPI"))
390                 pp->is_stdcall = 1;
391         else {
392                 printf("%s:%d:%zd: unhandled cconv: '%s'\n",
393                         hdrfn, hdrfline, (p - protostr) + 1, cconv);
394                 return -1;
395         }
396
397         if (pp->is_fptr) {
398                 if (*p != '*') {
399                         printf("%s:%d:%zd: '*' expected\n",
400                                 hdrfn, hdrfline, (p - protostr) + 1);
401                         return -1;
402                 }
403                 p++;
404                 // XXX: skipping extra asterisks, for now
405                 while (*p == '*')
406                         p++;
407                 p = sskip(p);
408         }
409
410         p = next_idt(buf, sizeof(buf), p);
411         p = sskip(p);
412         if (buf[0] == 0) {
413                 //printf("%s:%d:%zd: func name missing\n",
414                 //      hdrfn, hdrfline, (p - protostr) + 1);
415                 //return -1;
416         }
417         strcpy(pp->name, buf);
418
419         ret = get_regparm(regparm, sizeof(regparm), p);
420         if (ret > 0) {
421                 if (!IS(regparm, "eax") && !IS(regparm, "ax")
422                  && !IS(regparm, "al") && !IS(regparm, "edx:eax"))
423                 {
424                         printf("%s:%d:%zd: bad regparm: %s\n",
425                                 hdrfn, hdrfline, (p - protostr) + 1, regparm);
426                         return -1;
427                 }
428                 p += ret;
429                 p = sskip(p);
430         }
431
432         if (pp->is_fptr) {
433                 if (*p == '[') {
434                         // not really ret_type is array, but ohwell
435                         pp->ret_type.is_array = 1;
436                         p = strchr(p + 1, ']');
437                         if (p == NULL) {
438                                 printf("%s:%d:%zd: ']' expected\n",
439                                  hdrfn, hdrfline, (p - protostr) + 1);
440                                 return -1;
441                         }
442                         p = sskip(p + 1);
443                 }
444                 if (*p != ')') {
445                         printf("%s:%d:%zd: ')' expected\n",
446                                 hdrfn, hdrfline, (p - protostr) + 1);
447                         return -1;
448                 }
449                 p = sskip(p + 1);
450         }
451
452         if (*p != '(') {
453                 printf("%s:%d:%zd: '(' expected, got '%c'\n",
454                                 hdrfn, hdrfline, (p - protostr) + 1, *p);
455                 return -1;
456         }
457         p++;
458
459         // check for x(void)
460         p = sskip(p);
461         if ((!strncmp(p, "void", 4) || !strncmp(p, "VOID", 4))
462            && *sskip(p + 4) == ')')
463                 p += 4;
464
465         while (1) {
466                 p = sskip(p);
467                 if (*p == ')') {
468                         p++;
469                         break;
470                 }
471                 if (xarg > 0) {
472                         if (*p != ',') {
473                                 printf("%s:%d:%zd: ',' expected\n",
474                                  hdrfn, hdrfline, (p - protostr) + 1);
475                                 return -1;
476                         }
477                         p = sskip(p + 1);
478                 }
479
480                 if (!strncmp(p, "...", 3)) {
481                         pp->is_vararg = 1;
482                         p = sskip(p + 3);
483                         if (*p == ')') {
484                                 p++;
485                                 break;
486                         }
487                         printf("%s:%d:%zd: ')' expected\n",
488                                 hdrfn, hdrfline, (p - protostr) + 1);
489                         return -1;
490                 }
491
492                 arg = &pp->arg[xarg];
493                 xarg++;
494
495                 p1 = p;
496                 ret = check_type(p, &arg->type);
497                 if (ret <= 0) {
498                         printf("%s:%d:%zd: unhandled type for arg%d\n",
499                                 hdrfn, hdrfline, (p - protostr) + 1, xarg);
500                         return -1;
501                 }
502                 p = sskip(p + ret);
503
504                 if (*p == '(') {
505                         // func ptr
506                         arg->fptr = calloc(1, sizeof(*arg->fptr));
507                         ret = parse_protostr(p1, arg->fptr);
508                         if (ret < 0) {
509                                 printf("%s:%d:%zd: funcarg parse failed\n",
510                                         hdrfn, hdrfline, p1 - protostr);
511                                 return -1;
512                         }
513                         // we'll treat it as void * for non-calls
514                         arg->type.name = strdup("void *");
515                         arg->type.is_ptr = 1;
516
517                         p = p1 + ret;
518                 }
519
520                 p = next_idt(buf, sizeof(buf), p);
521                 p = sskip(p);
522 #if 0
523                 if (buf[0] == 0) {
524                         printf("%s:%d:%zd: idt missing for arg%d\n",
525                                 hdrfn, hdrfline, (p - protostr) + 1, xarg);
526                         return -1;
527                 }
528 #endif
529                 arg->reg = NULL;
530
531                 ret = get_regparm(regparm, sizeof(regparm), p);
532                 if (ret > 0) {
533                         p += ret;
534                         p = sskip(p);
535
536                         arg->reg = strdup(map_reg(regparm));
537                 }
538
539                 if (strstr(arg->type.name, "int64")
540                     || IS(arg->type.name, "double"))
541                 {
542                         // hack..
543                         free(arg->type.name);
544                         arg->type.name = strdup("int");
545                         pp_copy_arg(&pp->arg[xarg], arg);
546                         xarg++;
547                 }
548
549                 ret = check_struct_arg(arg);
550                 if (ret > 0) {
551                         pp->has_structarg = 1;
552                         arg->type.is_struct = 1;
553                         free(arg->type.name);
554                         arg->type.name = strdup("int");
555                         for (l = 0; l < ret; l++) {
556                                 pp_copy_arg(&pp->arg[xarg], arg);
557                                 xarg++;
558                         }
559                 }
560         }
561
562         if (xarg > 0 && (IS(cconv, "__fastcall") || IS(cconv, "__thiscall"))) {
563                 if (pp->arg[0].reg != NULL) {
564                         printf("%s:%d: %s with arg1 spec %s?\n",
565                                 hdrfn, hdrfline, cconv, pp->arg[0].reg);
566                 }
567                 pp->arg[0].reg = strdup("ecx");
568         }
569
570         if (xarg > 1 && IS(cconv, "__fastcall")) {
571                 if (pp->arg[1].reg != NULL) {
572                         printf("%s:%d: %s with arg2 spec %s?\n",
573                                 hdrfn, hdrfline, cconv, pp->arg[1].reg);
574                 }
575                 pp->arg[1].reg = strdup("edx");
576         }
577
578         pp->argc = xarg;
579
580         for (i = 0; i < pp->argc; i++) {
581                 if (pp->arg[i].reg == NULL)
582                         pp->argc_stack++;
583                 else
584                         pp->argc_reg++;
585         }
586
587         if (pp->argc == 1 && pp->arg[0].reg != NULL
588             && IS(pp->arg[0].reg, "ecx"))
589         {
590                 pp->is_fastcall = 1;
591         }
592         else if (pp->argc_reg == 2
593           && pp->arg[0].reg != NULL && IS(pp->arg[0].reg, "ecx")
594           && pp->arg[1].reg != NULL && IS(pp->arg[1].reg, "edx"))
595         {
596                 pp->is_fastcall = 1;
597         }
598
599         if (pp->is_vararg && (pp->is_stdcall || pp->is_fastcall)) {
600                 printf("%s:%d: vararg %s?\n", hdrfn, hdrfline, cconv);
601                 return -1;
602         }
603
604         return p - protostr;
605 }
606
607 static int pp_name_cmp(const void *p1, const void *p2)
608 {
609         const struct parsed_proto *pp1 = p1, *pp2 = p2;
610         return strcmp(pp1->name, pp2->name);
611 }
612
613 static struct parsed_proto *pp_cache;
614 static int pp_cache_size;
615 static int pp_cache_alloc;
616
617 static int b_pp_c_handler(char *proto, const char *fname)
618 {
619         int ret;
620
621         if (pp_cache_size >= pp_cache_alloc) {
622                 pp_cache_alloc = pp_cache_alloc * 2 + 64;
623                 pp_cache = realloc(pp_cache, pp_cache_alloc
624                                 * sizeof(pp_cache[0]));
625                 my_assert_not(pp_cache, NULL);
626                 memset(pp_cache + pp_cache_size, 0,
627                         (pp_cache_alloc - pp_cache_size)
628                          * sizeof(pp_cache[0]));
629         }
630
631         ret = parse_protostr(proto, &pp_cache[pp_cache_size]);
632         if (ret < 0)
633                 return -1;
634
635         pp_cache_size++;
636         return 0;
637 }
638
639 static void build_pp_cache(FILE *fhdr)
640 {
641         long pos;
642         int ret;
643
644         pos = ftell(fhdr);
645         rewind(fhdr);
646
647         ret = do_protostrs(fhdr, hdrfn);
648         if (ret < 0)
649                 exit(1);
650
651         qsort(pp_cache, pp_cache_size, sizeof(pp_cache[0]), pp_name_cmp);
652         fseek(fhdr, pos, SEEK_SET);
653 }
654
655 static const struct parsed_proto *proto_parse(FILE *fhdr, const char *sym,
656         int quiet)
657 {
658         const struct parsed_proto *pp_ret;
659         struct parsed_proto pp_search;
660
661         if (pp_cache == NULL)
662                 build_pp_cache(fhdr);
663
664         if (sym[0] == '_') // && strncmp(fname, "stdc", 4) == 0)
665                 sym++;
666
667         strcpy(pp_search.name, sym);
668         pp_ret = bsearch(&pp_search, pp_cache, pp_cache_size,
669                         sizeof(pp_cache[0]), pp_name_cmp);
670         if (pp_ret == NULL && !quiet)
671                 printf("%s: sym '%s' is missing\n", hdrfn, sym);
672
673         return pp_ret;
674 }
675
676 static void pp_copy_arg(struct parsed_proto_arg *d,
677         const struct parsed_proto_arg *s)
678 {
679         memcpy(d, s, sizeof(*d));
680
681         if (s->reg != NULL) {
682                 d->reg = strdup(s->reg);
683                 my_assert_not(d->reg, NULL);
684         }
685         if (s->type.name != NULL) {
686                 d->type.name = strdup(s->type.name);
687                 my_assert_not(d->type.name, NULL);
688         }
689         if (s->fptr != NULL) {
690                 d->fptr = malloc(sizeof(*d->fptr));
691                 my_assert_not(d->fptr, NULL);
692                 memcpy(d->fptr, s->fptr, sizeof(*d->fptr));
693         }
694 }
695
696 struct parsed_proto *proto_clone(const struct parsed_proto *pp_c)
697 {
698         struct parsed_proto *pp;
699         int i;
700
701         pp = malloc(sizeof(*pp));
702         my_assert_not(pp, NULL);
703         memcpy(pp, pp_c, sizeof(*pp)); // lazy..
704
705         // do the actual deep copy..
706         for (i = 0; i < pp_c->argc; i++)
707                 pp_copy_arg(&pp->arg[i], &pp_c->arg[i]);
708         if (pp_c->ret_type.name != NULL)
709                 pp->ret_type.name = strdup(pp_c->ret_type.name);
710
711         return pp;
712 }
713
714 static inline void pp_print(char *buf, size_t buf_size,
715   const struct parsed_proto *pp)
716 {
717   size_t l;
718   int i;
719
720   snprintf(buf, buf_size, "%s %s(", pp->ret_type.name, pp->name);
721   l = strlen(buf);
722
723   for (i = 0; i < pp->argc_reg; i++) {
724     snprintf(buf + l, buf_size - l, "%s%s",
725       i == 0 ? "" : ", ", pp->arg[i].reg);
726     l = strlen(buf);
727   }
728   if (pp->argc_stack > 0) {
729     snprintf(buf + l, buf_size - l, "%s{%d stack}",
730       i == 0 ? "" : ", ", pp->argc_stack);
731     l = strlen(buf);
732   }
733   snprintf(buf + l, buf_size - l, ")");
734 }
735
736 static inline void proto_release(struct parsed_proto *pp)
737 {
738         int i;
739
740         for (i = 0; i < pp->argc; i++) {
741                 if (pp->arg[i].reg != NULL)
742                         free(pp->arg[i].reg);
743                 if (pp->arg[i].type.name != NULL)
744                         free(pp->arg[i].type.name);
745                 if (pp->arg[i].fptr != NULL)
746                         free(pp->arg[i].fptr);
747         }
748         if (pp->ret_type.name != NULL)
749                 free(pp->ret_type.name);
750         free(pp);
751 }