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