various fixes
[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",
3ebea2cf 146 "PLONG",
147 "PDWORD",
148 "PVOID",
149 "PCVOID",
e56ab892 150 "DLGPROC",
cdfaeed7 151 "TIMERPROC",
152 "WNDENUMPROC",
4f12f671 153 "va_list",
154 "__VALIST",
3ebea2cf 155};
156
157static const char *ignored_keywords[] = {
158 "extern",
159 "WINBASEAPI",
160 "WINUSERAPI",
4f12f671 161 "WINGDIAPI",
162 "WINADVAPI",
c36e914d 163};
164
64c59faf 165// returns ptr to char after type ends
3ebea2cf 166static int typecmp(const char *n, const char *t)
39b168b8 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)
3ebea2cf 174 return *n - *t;
39b168b8 175 }
176
3ebea2cf 177 return 0;
39b168b8 178}
179
3ebea2cf 180static const char *skip_type_mod(const char *n)
c36e914d 181{
64c59faf 182 int len;
39b168b8 183 int i;
c36e914d 184
64c59faf 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;
bd96f656 189 if (!my_isblank(n[len]))
190 continue;
64c59faf 191
192 n += len;
193 while (my_isblank(*n))
194 n++;
195 i = 0;
196 }
197
3ebea2cf 198 return n;
199}
200
201static 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]))
64c59faf 211 continue;
212
3ebea2cf 213 type->is_ptr = 1;
214 break;
c36e914d 215 }
216
a652aa9f 217 if (n[0] == 'L' && n[1] == 'P' && strncmp(n, "LPARAM", 6))
3ebea2cf 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;
c36e914d 239}
240
241/* args are always expanded to 32bit */
242static 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
a652aa9f 260static 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
c36e914d 268static int parse_protostr(char *protostr, struct parsed_proto *pp)
269{
39b168b8 270 struct parsed_proto_arg *arg;
c36e914d 271 char regparm[16];
272 char buf[256];
273 char cconv[32];
c36e914d 274 int xarg = 0;
39b168b8 275 char *p, *p1;
3ebea2cf 276 int i, l;
c36e914d 277 int ret;
c36e914d 278
06c5d854 279 p = sskip(protostr);
c36e914d 280 if (p[0] == '/' && p[1] == '/') {
06c5d854 281 printf("%s:%d: commented out?\n", hdrfn, hdrfline);
c36e914d 282 p = sskip(p + 2);
283 }
284
06c5d854 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
e56ab892 292 if (!strncmp(p, "DECLSPEC_NORETURN ", 18)) {
293 pp->is_noreturn = 1;
294 p = sskip(p + 18);
295 }
296
3ebea2cf 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 }
06c5d854 302
3ebea2cf 303 ret = check_type(p, &pp->ret_type);
304 if (ret <= 0) {
63df67be 305 printf("%s:%d:%zd: unhandled return in '%s'\n",
c36e914d 306 hdrfn, hdrfline, (p - protostr) + 1, protostr);
39b168b8 307 return -1;
c36e914d 308 }
64c59faf 309 p = sskip(p + ret);
c36e914d 310
06c5d854 311 if (!strchr(p, ')')) {
312 p = next_idt(buf, sizeof(buf), p);
313 p = sskip(p);
314 if (buf[0] == 0) {
63df67be 315 printf("%s:%d:%zd: var name missing\n",
06c5d854 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;
3ebea2cf 324 pp->ret_type.is_array = 1;
06c5d854 325 }
326 return p - protostr;
327 }
328
329 pp->is_func = 1;
330
39b168b8 331 if (*p == '(') {
332 pp->is_fptr = 1;
333 p = sskip(p + 1);
334 }
335
c36e914d 336 p = next_word(cconv, sizeof(cconv), p);
337 p = sskip(p);
338 if (cconv[0] == 0) {
63df67be 339 printf("%s:%d:%zd: cconv missing\n",
c36e914d 340 hdrfn, hdrfline, (p - protostr) + 1);
39b168b8 341 return -1;
c36e914d 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"))
de50b98b 352 pp->is_stdcall = 1; // IDA
c36e914d 353 else if (IS(cconv, "__usercall"))
de50b98b 354 pp->is_stdcall = 0; // IDA
64c59faf 355 else if (IS(cconv, "WINAPI"))
356 pp->is_stdcall = 1;
c36e914d 357 else {
63df67be 358 printf("%s:%d:%zd: unhandled cconv: '%s'\n",
c36e914d 359 hdrfn, hdrfline, (p - protostr) + 1, cconv);
39b168b8 360 return -1;
361 }
362
363 if (pp->is_fptr) {
364 if (*p != '*') {
63df67be 365 printf("%s:%d:%zd: '*' expected\n",
39b168b8 366 hdrfn, hdrfline, (p - protostr) + 1);
367 return -1;
368 }
bd96f656 369 p++;
370 // XXX: skipping extra asterisks, for now
371 while (*p == '*')
372 p++;
373 p = sskip(p);
c36e914d 374 }
375
376 p = next_idt(buf, sizeof(buf), p);
377 p = sskip(p);
378 if (buf[0] == 0) {
de50b98b 379 //printf("%s:%d:%zd: func name missing\n",
380 // hdrfn, hdrfline, (p - protostr) + 1);
381 //return -1;
c36e914d 382 }
39b168b8 383 strcpy(pp->name, buf);
c36e914d 384
385 ret = get_regparm(regparm, sizeof(regparm), p);
386 if (ret > 0) {
387 if (!IS(regparm, "eax") && !IS(regparm, "ax")
2b43685d 388 && !IS(regparm, "al") && !IS(regparm, "edx:eax"))
c36e914d 389 {
63df67be 390 printf("%s:%d:%zd: bad regparm: %s\n",
c36e914d 391 hdrfn, hdrfline, (p - protostr) + 1, regparm);
39b168b8 392 return -1;
c36e914d 393 }
394 p += ret;
395 p = sskip(p);
396 }
397
39b168b8 398 if (pp->is_fptr) {
bd96f656 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 }
39b168b8 410 if (*p != ')') {
63df67be 411 printf("%s:%d:%zd: ')' expected\n",
39b168b8 412 hdrfn, hdrfline, (p - protostr) + 1);
413 return -1;
414 }
415 p = sskip(p + 1);
416 }
417
c36e914d 418 if (*p != '(') {
63df67be 419 printf("%s:%d:%zd: '(' expected, got '%c'\n",
c36e914d 420 hdrfn, hdrfline, (p - protostr) + 1, *p);
39b168b8 421 return -1;
c36e914d 422 }
423 p++;
424
39b168b8 425 // check for x(void)
426 p = sskip(p);
06c5d854 427 if ((!strncmp(p, "void", 4) || !strncmp(p, "VOID", 4))
428 && *sskip(p + 4) == ')')
39b168b8 429 p += 4;
430
c36e914d 431 while (1) {
432 p = sskip(p);
39b168b8 433 if (*p == ')') {
434 p++;
c36e914d 435 break;
39b168b8 436 }
840257f6 437 if (xarg > 0) {
438 if (*p != ',') {
439 printf("%s:%d:%zd: ',' expected\n",
440 hdrfn, hdrfline, (p - protostr) + 1);
441 return -1;
442 }
c36e914d 443 p = sskip(p + 1);
840257f6 444 }
c36e914d 445
7ba45c34 446 if (!strncmp(p, "...", 3)) {
447 pp->is_vararg = 1;
448 p = sskip(p + 3);
449 if (*p == ')') {
450 p++;
451 break;
452 }
63df67be 453 printf("%s:%d:%zd: ')' expected\n",
7ba45c34 454 hdrfn, hdrfline, (p - protostr) + 1);
455 return -1;
456 }
457
39b168b8 458 arg = &pp->arg[xarg];
c36e914d 459 xarg++;
460
39b168b8 461 p1 = p;
3ebea2cf 462 ret = check_type(p, &arg->type);
463 if (ret <= 0) {
63df67be 464 printf("%s:%d:%zd: unhandled type for arg%d\n",
c36e914d 465 hdrfn, hdrfline, (p - protostr) + 1, xarg);
39b168b8 466 return -1;
c36e914d 467 }
64c59faf 468 p = sskip(p + ret);
c36e914d 469
39b168b8 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) {
63df67be 475 printf("%s:%d:%zd: funcarg parse failed\n",
39b168b8 476 hdrfn, hdrfline, p1 - protostr);
477 return -1;
478 }
479 // we'll treat it as void * for non-calls
a652aa9f 480 arg->type.name = strdup("void *");
3ebea2cf 481 arg->type.is_ptr = 1;
39b168b8 482
483 p = p1 + ret;
484 }
485
c36e914d 486 p = next_idt(buf, sizeof(buf), p);
487 p = sskip(p);
488#if 0
489 if (buf[0] == 0) {
63df67be 490 printf("%s:%d:%zd: idt missing for arg%d\n",
c36e914d 491 hdrfn, hdrfline, (p - protostr) + 1, xarg);
39b168b8 492 return -1;
c36e914d 493 }
494#endif
39b168b8 495 arg->reg = NULL;
c36e914d 496
497 ret = get_regparm(regparm, sizeof(regparm), p);
498 if (ret > 0) {
499 p += ret;
500 p = sskip(p);
501
39b168b8 502 arg->reg = strdup(map_reg(regparm));
c36e914d 503 }
a652aa9f 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 }
c36e914d 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
7ba45c34 534 if (pp->is_vararg && pp->is_stdcall) {
535 printf("%s:%d: vararg stdcall?\n", hdrfn, hdrfline);
536 return -1;
537 }
538
c36e914d 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
39b168b8 548 return p - protostr;
c36e914d 549}
550
bd96f656 551static 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
557static struct parsed_proto *pp_cache;
558static int pp_cache_size;
559static int pp_cache_alloc;
560
561static 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
583static void build_pp_cache(FILE *fhdr)
c36e914d 584{
c36e914d 585 int ret;
586
bd96f656 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
596static 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++;
c36e914d 606
bd96f656 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)
c36e914d 611 printf("%s: sym '%s' is missing\n", hdrfn, sym);
bd96f656 612
613 return pp_ret;
614}
615
a652aa9f 616static 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
bd96f656 636struct 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..
a652aa9f 646 for (i = 0; i < pp_c->argc; i++)
647 pp_copy_arg(&pp->arg[i], &pp_c->arg[i]);
bd96f656 648 if (pp_c->ret_type.name != NULL)
649 pp->ret_type.name = strdup(pp_c->ret_type.name);
c36e914d 650
bd96f656 651 return pp;
c36e914d 652}
653
bd96f656 654static inline void proto_release(struct parsed_proto *pp)
c36e914d 655{
656 int i;
657
658 for (i = 0; i < pp->argc; i++) {
39b168b8 659 if (pp->arg[i].reg != NULL)
c36e914d 660 free(pp->arg[i].reg);
3ebea2cf 661 if (pp->arg[i].type.name != NULL)
662 free(pp->arg[i].type.name);
39b168b8 663 if (pp->arg[i].fptr != NULL)
664 free(pp->arg[i].fptr);
c36e914d 665 }
3ebea2cf 666 if (pp->ret_type.name != NULL)
667 free(pp->ret_type.name);
bd96f656 668 free(pp);
c36e914d 669}