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