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