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