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