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