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