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