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