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