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