translate: support more dereference types
[ia32rtools.git] / tools / protoparse.h
CommitLineData
7637b6cc 1/*
2 * ia32rtools
3 * (C) notaz, 2013,2014
4 *
5 * This work is licensed under the terms of 3-clause BSD license.
6 * See COPYING file in the top-level directory.
7 */
c36e914d 8
39b168b8 9struct parsed_proto;
10
3ebea2cf 11struct parsed_type {
12 char *name;
13 unsigned int is_array:1;
14 unsigned int is_ptr:1;
a652aa9f 15 unsigned int is_struct:1; // split for args
5f70a34f 16 unsigned int is_retreg:1; // register to return to caller
17 unsigned int is_va_list:1;
3ebea2cf 18};
19
39b168b8 20struct parsed_proto_arg {
21 char *reg;
3ebea2cf 22 struct parsed_type type;
93b5bd18 23 struct parsed_proto *pp; // fptr or struct
39b168b8 24 void *datap;
25};
26
c36e914d 27struct parsed_proto {
39b168b8 28 char name[256];
3ebea2cf 29 union {
30 struct parsed_type ret_type;
31 struct parsed_type type;
32 };
39b168b8 33 struct parsed_proto_arg arg[16];
c36e914d 34 int argc;
35 int argc_stack;
36 int argc_reg;
06c5d854 37 unsigned int is_func:1;
39b168b8 38 unsigned int is_stdcall:1;
c0050df6 39 unsigned int is_fastcall:1;
efea2951 40 unsigned int is_vararg:1; // vararg func
39b168b8 41 unsigned int is_fptr:1;
e56ab892 42 unsigned int is_noreturn:1;
89ff3147 43 unsigned int is_unresolved:1;
1f84f6b3 44 unsigned int is_userstack:1;
61e29183 45 unsigned int is_include:1; // not from top-level header
46 unsigned int is_osinc:1; // OS/system library func
ebc4dc43 47 unsigned int is_cinc:1; // crt library func
179b79a9 48 unsigned int is_arg:1; // declared in some func arg
a652aa9f 49 unsigned int has_structarg:1;
1f84f6b3 50 unsigned int has_retreg:1;
c36e914d 51};
52
865f1aca 53struct parsed_struct {
54 char name[256];
55 struct {
56 int offset;
57 struct parsed_proto pp;
58 } members[64];
59 int member_count;
60};
61
c36e914d 62static const char *hdrfn;
63static int hdrfline = 0;
64
a652aa9f 65static void pp_copy_arg(struct parsed_proto_arg *d,
66 const struct parsed_proto_arg *s);
67
61e29183 68static int b_pp_c_handler(char *proto, const char *fname,
ebc4dc43 69 int is_include, int is_osinc, int is_cinc);
865f1aca 70static int struct_handler(FILE *fhdr, char *proto, int *line);
bd96f656 71
61e29183 72static int do_protostrs(FILE *fhdr, const char *fname, int is_include)
c36e914d 73{
06c5d854 74 const char *finc_name;
bd96f656 75 const char *hdrfn_saved;
76 char protostr[256];
36595fd2 77 char path[256];
78 char fname_inc[256];
61e29183 79 int is_osinc;
ebc4dc43 80 int is_cinc;
39b168b8 81 FILE *finc;
c36e914d 82 int line = 0;
39b168b8 83 int ret;
c36e914d 84 char *p;
85
bd96f656 86 hdrfn_saved = hdrfn;
87 hdrfn = fname;
39b168b8 88
ebc4dc43 89 is_cinc = strstr(fname, "stdc.hlist") != NULL;
edeadc28 90 is_osinc = is_cinc || strstr(fname, "win32.hlist") != NULL;
179b79a9 91
bd96f656 92 while (fgets(protostr, sizeof(protostr), fhdr))
c36e914d 93 {
94 line++;
bd96f656 95 if (strncmp(protostr, "//#include ", 11) == 0) {
96 finc_name = protostr + 11;
06c5d854 97 p = strpbrk(finc_name, "\r\n ");
39b168b8 98 if (p != NULL)
99 *p = 0;
100
36595fd2 101 path[0] = 0;
102 p = strrchr(hdrfn_saved, '/');
103 if (p) {
104 memcpy(path, hdrfn_saved,
105 p - hdrfn_saved + 1);
106 path[p - hdrfn_saved + 1] = 0;
107 }
108 snprintf(fname_inc, sizeof(fname_inc), "%s%s",
109 path, finc_name);
110 finc = fopen(fname_inc, "r");
39b168b8 111 if (finc == NULL) {
112 printf("%s:%d: can't open '%s'\n",
36595fd2 113 fname_inc, line, finc_name);
39b168b8 114 continue;
115 }
61e29183 116 ret = do_protostrs(finc, finc_name, 1);
39b168b8 117 fclose(finc);
bd96f656 118 if (ret < 0)
39b168b8 119 break;
120 continue;
121 }
bd96f656 122 if (strncmp(sskip(protostr), "//", 2) == 0)
123 continue;
124
125 p = protostr + strlen(protostr);
126 for (p--; p >= protostr && my_isblank(*p); --p)
127 *p = 0;
128 if (p < protostr)
06c5d854 129 continue;
39b168b8 130
bd96f656 131 hdrfline = line;
132
865f1aca 133 if (!strncmp(protostr, "struct", 6)
134 && strchr(protostr, '{') != NULL)
135 ret = struct_handler(fhdr, protostr, &line);
136 else
137 ret = b_pp_c_handler(protostr, hdrfn,
138 is_include, is_osinc, is_cinc);
bd96f656 139 if (ret < 0)
c36e914d 140 break;
141 }
c36e914d 142
bd96f656 143 hdrfn = hdrfn_saved;
c36e914d 144
bd96f656 145 if (feof(fhdr))
146 return 0;
c36e914d 147
bd96f656 148 return -1;
c36e914d 149}
150
1f84f6b3 151static int get_regparm(char *dst, size_t dlen, char *p, int *retreg)
c36e914d 152{
1f84f6b3 153 int i = 0, o;
154
155 *retreg = 0;
c36e914d 156
157 if (*p != '<')
158 return 0;
159
1f84f6b3 160 i++;
161 if (p[i] == '*') {
162 *retreg = 1;
163 i++;
164 }
165
166 for (o = 0; o < dlen; i++) {
c36e914d 167 if (p[i] == 0)
168 return 0;
169 if (p[i] == '>')
170 break;
171 dst[o++] = p[i];
172 }
173 dst[o] = 0;
174 return i + 1;
175}
176
177// hmh..
64c59faf 178static const char *known_type_mod[] = {
179 "const",
180 "signed",
181 "unsigned",
3ebea2cf 182 "enum",
840257f6 183 "CONST",
92804a48 184 "volatile",
64c59faf 185};
186
3ebea2cf 187static const char *known_ptr_types[] = {
da87ae38 188 "FARPROC",
92804a48 189 "WNDPROC",
bf2471e9 190 "LINECALLBACK",
e56ab892 191 "HACCEL",
c36e914d 192 "HANDLE",
e56ab892 193 "HBITMAP",
bf2471e9 194 "HCALL",
e56ab892 195 "HCURSOR",
3ebea2cf 196 "HDC",
a652aa9f 197 "HFONT",
3ebea2cf 198 "HGDIOBJ",
e56ab892 199 "HGLOBAL",
da87ae38 200 "HICON",
e56ab892 201 "HINSTANCE",
c7ed83dd 202 "HIMC", // DWORD in mingw, ptr in wine..
bf2471e9 203 "HLINE",
204 "HLINEAPP",
205 "HLOCAL",
e56ab892 206 "HMODULE",
da87ae38 207 "HPALETTE",
e56ab892 208 "HRGN",
209 "HRSRC",
210 "HKEY",
211 "HMENU",
7aca4698 212 "HWAVEOUT",
e56ab892 213 "HWND",
ef6b315d 214 "PBYTE",
a2c1d768 215 "PCRITICAL_SECTION",
3ebea2cf 216 "PDWORD",
92804a48 217 "PFILETIME",
bf2471e9 218 "PLARGE_INTEGER",
94d447fb 219 "PHKEY",
220 "PLONG",
a2c1d768 221 "PMEMORY_BASIC_INFORMATION",
46411e6c 222 "PUINT",
3ebea2cf 223 "PVOID",
224 "PCVOID",
ef6b315d 225 "PWORD",
e56ab892 226 "DLGPROC",
cdfaeed7 227 "TIMERPROC",
228 "WNDENUMPROC",
4f12f671 229 "va_list",
230 "__VALIST",
3ebea2cf 231};
232
233static const char *ignored_keywords[] = {
234 "extern",
235 "WINBASEAPI",
236 "WINUSERAPI",
4f12f671 237 "WINGDIAPI",
238 "WINADVAPI",
c36e914d 239};
240
3ebea2cf 241static int typecmp(const char *n, const char *t)
39b168b8 242{
243 for (; *t != 0; n++, t++) {
244 while (n[0] == ' ' && (n[1] == ' ' || n[1] == '*'))
245 n++;
246 while (t[0] == ' ' && (t[1] == ' ' || t[1] == '*'))
247 t++;
248 if (*n != *t)
3ebea2cf 249 return *n - *t;
39b168b8 250 }
251
3ebea2cf 252 return 0;
39b168b8 253}
254
3ebea2cf 255static const char *skip_type_mod(const char *n)
c36e914d 256{
64c59faf 257 int len;
39b168b8 258 int i;
c36e914d 259
64c59faf 260 for (i = 0; i < ARRAY_SIZE(known_type_mod); i++) {
261 len = strlen(known_type_mod[i]);
262 if (strncmp(n, known_type_mod[i], len) != 0)
263 continue;
bd96f656 264 if (!my_isblank(n[len]))
265 continue;
64c59faf 266
267 n += len;
268 while (my_isblank(*n))
269 n++;
270 i = 0;
271 }
272
3ebea2cf 273 return n;
274}
275
276static int check_type(const char *name, struct parsed_type *type)
277{
278 const char *n, *n1;
279 int ret = -1;
280 int i;
281
282 n = skip_type_mod(name);
283
865f1aca 284 if (!strncmp(n, "struct", 6) && my_isblank(n[6])) {
285 type->is_struct = 1;
286
287 n += 6;
288 while (my_isblank(*n))
289 n++;
290 }
291
3ebea2cf 292 for (i = 0; i < ARRAY_SIZE(known_ptr_types); i++) {
293 if (typecmp(n, known_ptr_types[i]))
64c59faf 294 continue;
295
3ebea2cf 296 type->is_ptr = 1;
297 break;
c36e914d 298 }
299
a652aa9f 300 if (n[0] == 'L' && n[1] == 'P' && strncmp(n, "LPARAM", 6))
3ebea2cf 301 type->is_ptr = 1;
302
303 // assume single word
304 while (!my_isblank(*n) && !my_issep(*n))
305 n++;
306
307 while (1) {
308 n1 = n;
309 while (my_isblank(*n))
310 n++;
311 if (*n == '*') {
312 type->is_ptr = 1;
313 n++;
314 continue;
315 }
316 break;
317 }
318
319 ret = n1 - name;
320 type->name = strndup(name, ret);
5f70a34f 321 if (IS(type->name, "__VALIST") || IS(type->name, "va_list"))
322 type->is_va_list = 1;
a2c1d768 323 if (IS(type->name, "VOID"))
324 memcpy(type->name, "void", 4);
325
3ebea2cf 326 return ret;
c36e914d 327}
328
329/* args are always expanded to 32bit */
330static const char *map_reg(const char *reg)
331{
332 const char *regs_f[] = { "eax", "ebx", "ecx", "edx", "esi", "edi" };
333 const char *regs_w[] = { "ax", "bx", "cx", "dx", "si", "di" };
334 const char *regs_b[] = { "al", "bl", "cl", "dl" };
335 int i;
336
337 for (i = 0; i < ARRAY_SIZE(regs_w); i++)
338 if (IS(reg, regs_w[i]))
339 return regs_f[i];
340
341 for (i = 0; i < ARRAY_SIZE(regs_b); i++)
342 if (IS(reg, regs_b[i]))
343 return regs_f[i];
344
345 return reg;
346}
347
a652aa9f 348static int check_struct_arg(struct parsed_proto_arg *arg)
349{
350 if (IS(arg->type.name, "POINT"))
351 return 2 - 1;
352
353 return 0;
354}
355
93b5bd18 356static int parse_protostr(char *protostr, struct parsed_proto *pp);
357
358static int parse_arg(char **p_, struct parsed_proto_arg *arg, int xarg)
359{
360 char buf[256];
361 char *p = *p_;
362 char *pe;
363 int ret;
364
365 arg->pp = calloc(1, sizeof(*arg->pp));
366 my_assert_not(arg->pp, NULL);
367 arg->pp->is_arg = 1;
368
369 pe = p;
370 while (1) {
371 pe = strpbrk(pe, ",()");
372 if (pe == NULL)
373 return -1;
374 if (*pe == ',' || *pe == ')')
375 break;
376 pe = strchr(pe, ')');
377 if (pe == NULL)
378 return -1;
379 pe++;
380 }
381
382 if (pe - p > sizeof(buf) - 1)
383 return -1;
384 memcpy(buf, p, pe - p);
385 buf[pe - p] = 0;
386
387 ret = parse_protostr(buf, arg->pp);
388 if (ret < 0)
389 return -1;
390
391 // we don't use actual names right now...
392 snprintf(arg->pp->name, sizeof(arg->pp->name), "a%d", xarg);
393
394 if (!arg->type.is_struct)
395 // we'll treat it as void * for non-calls
396 arg->type.name = strdup("void *");
397 arg->type.is_ptr = 1;
398
399 p += ret;
400 *p_ = p;
401 return 0;
402}
403
c36e914d 404static int parse_protostr(char *protostr, struct parsed_proto *pp)
405{
39b168b8 406 struct parsed_proto_arg *arg;
c36e914d 407 char regparm[16];
408 char buf[256];
409 char cconv[32];
1f84f6b3 410 int is_retreg;
39b168b8 411 char *p, *p1;
93b5bd18 412 int xarg = 0;
3ebea2cf 413 int i, l;
c36e914d 414 int ret;
c36e914d 415
06c5d854 416 p = sskip(protostr);
c36e914d 417 if (p[0] == '/' && p[1] == '/') {
06c5d854 418 printf("%s:%d: commented out?\n", hdrfn, hdrfline);
c36e914d 419 p = sskip(p + 2);
420 }
421
865f1aca 422 // allow start of line comment
423 if (p[0] == '/' && p[1] == '*') {
424 p = strstr(p + 2, "*/");
425 if (p == NULL) {
426 printf("%s:%d: multiline comments unsupported\n",
427 hdrfn, hdrfline);
428 return -1;
429 }
430 p = sskip(p + 2);
431 }
432
433 // we need remaining hints in comments, so strip / *
06c5d854 434 for (p1 = p; p1[0] != 0 && p1[1] != 0; p1++) {
435 if ((p1[0] == '/' && p1[1] == '*')
436 || (p1[0] == '*' && p1[1] == '/'))
437 p1[0] = p1[1] = ' ';
438 }
439
e56ab892 440 if (!strncmp(p, "DECLSPEC_NORETURN ", 18)) {
441 pp->is_noreturn = 1;
442 p = sskip(p + 18);
443 }
444
3ebea2cf 445 for (i = 0; i < ARRAY_SIZE(ignored_keywords); i++) {
446 l = strlen(ignored_keywords[i]);
447 if (!strncmp(p, ignored_keywords[i], l) && my_isblank(p[l]))
448 p = sskip(p + l + 1);
449 }
06c5d854 450
3ebea2cf 451 ret = check_type(p, &pp->ret_type);
452 if (ret <= 0) {
63df67be 453 printf("%s:%d:%zd: unhandled return in '%s'\n",
c36e914d 454 hdrfn, hdrfline, (p - protostr) + 1, protostr);
39b168b8 455 return -1;
c36e914d 456 }
64c59faf 457 p = sskip(p + ret);
c36e914d 458
da87ae38 459 if (!strncmp(p, "noreturn ", 9)) {
460 pp->is_noreturn = 1;
461 p = sskip(p + 9);
462 }
463
06c5d854 464 if (!strchr(p, ')')) {
465 p = next_idt(buf, sizeof(buf), p);
466 p = sskip(p);
93b5bd18 467 if (!pp->is_arg && buf[0] == 0) {
468 printf("%s:%d:%zd: var name is missing\n",
06c5d854 469 hdrfn, hdrfline, (p - protostr) + 1);
470 return -1;
471 }
472 strcpy(pp->name, buf);
473
474 p1 = strchr(p, ']');
475 if (p1 != NULL) {
476 p = p1 + 1;
3ebea2cf 477 pp->ret_type.is_array = 1;
06c5d854 478 }
479 return p - protostr;
480 }
481
482 pp->is_func = 1;
483
39b168b8 484 if (*p == '(') {
485 pp->is_fptr = 1;
486 p = sskip(p + 1);
487 }
488
c36e914d 489 p = next_word(cconv, sizeof(cconv), p);
490 p = sskip(p);
491 if (cconv[0] == 0) {
63df67be 492 printf("%s:%d:%zd: cconv missing\n",
c36e914d 493 hdrfn, hdrfline, (p - protostr) + 1);
39b168b8 494 return -1;
c36e914d 495 }
496 if (IS(cconv, "__cdecl"))
497 pp->is_stdcall = 0;
498 else if (IS(cconv, "__stdcall"))
499 pp->is_stdcall = 1;
c0050df6 500 else if (IS(cconv, "__fastcall")) {
501 pp->is_fastcall = 1;
502 pp->is_stdcall = 1; // sort of..
503 }
c36e914d 504 else if (IS(cconv, "__thiscall"))
505 pp->is_stdcall = 1;
506 else if (IS(cconv, "__userpurge"))
de50b98b 507 pp->is_stdcall = 1; // IDA
c36e914d 508 else if (IS(cconv, "__usercall"))
de50b98b 509 pp->is_stdcall = 0; // IDA
1f84f6b3 510 else if (IS(cconv, "__userstack")) {
511 pp->is_stdcall = 0; // custom
512 pp->is_userstack = 1;
513 }
64c59faf 514 else if (IS(cconv, "WINAPI"))
515 pp->is_stdcall = 1;
c36e914d 516 else {
63df67be 517 printf("%s:%d:%zd: unhandled cconv: '%s'\n",
c36e914d 518 hdrfn, hdrfline, (p - protostr) + 1, cconv);
39b168b8 519 return -1;
520 }
521
522 if (pp->is_fptr) {
523 if (*p != '*') {
63df67be 524 printf("%s:%d:%zd: '*' expected\n",
39b168b8 525 hdrfn, hdrfline, (p - protostr) + 1);
526 return -1;
527 }
bd96f656 528 p++;
529 // XXX: skipping extra asterisks, for now
530 while (*p == '*')
531 p++;
532 p = sskip(p);
c36e914d 533 }
534
535 p = next_idt(buf, sizeof(buf), p);
536 p = sskip(p);
537 if (buf[0] == 0) {
de50b98b 538 //printf("%s:%d:%zd: func name missing\n",
539 // hdrfn, hdrfline, (p - protostr) + 1);
540 //return -1;
c36e914d 541 }
39b168b8 542 strcpy(pp->name, buf);
c36e914d 543
1f84f6b3 544 ret = get_regparm(regparm, sizeof(regparm), p, &is_retreg);
c36e914d 545 if (ret > 0) {
546 if (!IS(regparm, "eax") && !IS(regparm, "ax")
2b43685d 547 && !IS(regparm, "al") && !IS(regparm, "edx:eax"))
c36e914d 548 {
63df67be 549 printf("%s:%d:%zd: bad regparm: %s\n",
c36e914d 550 hdrfn, hdrfline, (p - protostr) + 1, regparm);
39b168b8 551 return -1;
c36e914d 552 }
553 p += ret;
554 p = sskip(p);
555 }
556
39b168b8 557 if (pp->is_fptr) {
bd96f656 558 if (*p == '[') {
559 // not really ret_type is array, but ohwell
560 pp->ret_type.is_array = 1;
561 p = strchr(p + 1, ']');
562 if (p == NULL) {
563 printf("%s:%d:%zd: ']' expected\n",
564 hdrfn, hdrfline, (p - protostr) + 1);
565 return -1;
566 }
567 p = sskip(p + 1);
568 }
39b168b8 569 if (*p != ')') {
63df67be 570 printf("%s:%d:%zd: ')' expected\n",
39b168b8 571 hdrfn, hdrfline, (p - protostr) + 1);
572 return -1;
573 }
574 p = sskip(p + 1);
575 }
576
c36e914d 577 if (*p != '(') {
63df67be 578 printf("%s:%d:%zd: '(' expected, got '%c'\n",
c36e914d 579 hdrfn, hdrfline, (p - protostr) + 1, *p);
39b168b8 580 return -1;
c36e914d 581 }
582 p++;
583
39b168b8 584 // check for x(void)
585 p = sskip(p);
06c5d854 586 if ((!strncmp(p, "void", 4) || !strncmp(p, "VOID", 4))
587 && *sskip(p + 4) == ')')
39b168b8 588 p += 4;
589
c36e914d 590 while (1) {
591 p = sskip(p);
39b168b8 592 if (*p == ')') {
593 p++;
c36e914d 594 break;
39b168b8 595 }
840257f6 596 if (xarg > 0) {
597 if (*p != ',') {
598 printf("%s:%d:%zd: ',' expected\n",
599 hdrfn, hdrfline, (p - protostr) + 1);
600 return -1;
601 }
c36e914d 602 p = sskip(p + 1);
840257f6 603 }
c36e914d 604
7ba45c34 605 if (!strncmp(p, "...", 3)) {
606 pp->is_vararg = 1;
607 p = sskip(p + 3);
608 if (*p == ')') {
609 p++;
610 break;
611 }
63df67be 612 printf("%s:%d:%zd: ')' expected\n",
7ba45c34 613 hdrfn, hdrfline, (p - protostr) + 1);
614 return -1;
615 }
616
39b168b8 617 arg = &pp->arg[xarg];
c36e914d 618 xarg++;
619
39b168b8 620 p1 = p;
3ebea2cf 621 ret = check_type(p, &arg->type);
622 if (ret <= 0) {
63df67be 623 printf("%s:%d:%zd: unhandled type for arg%d\n",
c36e914d 624 hdrfn, hdrfline, (p - protostr) + 1, xarg);
39b168b8 625 return -1;
c36e914d 626 }
64c59faf 627 p = sskip(p + ret);
c36e914d 628
93b5bd18 629 if (*p == '(' || arg->type.is_struct) {
630 // func ptr or struct
631 ret = parse_arg(&p1, arg, xarg);
39b168b8 632 if (ret < 0) {
63df67be 633 printf("%s:%d:%zd: funcarg parse failed\n",
39b168b8 634 hdrfn, hdrfline, p1 - protostr);
635 return -1;
636 }
93b5bd18 637 p = p1;
39b168b8 638 }
639
c36e914d 640 p = next_idt(buf, sizeof(buf), p);
641 p = sskip(p);
642#if 0
643 if (buf[0] == 0) {
63df67be 644 printf("%s:%d:%zd: idt missing for arg%d\n",
c36e914d 645 hdrfn, hdrfline, (p - protostr) + 1, xarg);
39b168b8 646 return -1;
c36e914d 647 }
648#endif
39b168b8 649 arg->reg = NULL;
c36e914d 650
1f84f6b3 651 ret = get_regparm(regparm, sizeof(regparm), p, &is_retreg);
c36e914d 652 if (ret > 0) {
653 p += ret;
654 p = sskip(p);
655
39b168b8 656 arg->reg = strdup(map_reg(regparm));
1f84f6b3 657 arg->type.is_retreg = is_retreg;
658 pp->has_retreg |= is_retreg;
c36e914d 659 }
a652aa9f 660
7e50b291 661 if (strstr(arg->type.name, "int64")
662 || IS(arg->type.name, "double"))
663 {
664 // hack..
665 free(arg->type.name);
666 arg->type.name = strdup("int");
667 pp_copy_arg(&pp->arg[xarg], arg);
668 xarg++;
669 }
670
a652aa9f 671 ret = check_struct_arg(arg);
672 if (ret > 0) {
673 pp->has_structarg = 1;
674 arg->type.is_struct = 1;
675 free(arg->type.name);
676 arg->type.name = strdup("int");
677 for (l = 0; l < ret; l++) {
678 pp_copy_arg(&pp->arg[xarg], arg);
679 xarg++;
680 }
681 }
c36e914d 682 }
683
684 if (xarg > 0 && (IS(cconv, "__fastcall") || IS(cconv, "__thiscall"))) {
685 if (pp->arg[0].reg != NULL) {
686 printf("%s:%d: %s with arg1 spec %s?\n",
687 hdrfn, hdrfline, cconv, pp->arg[0].reg);
688 }
689 pp->arg[0].reg = strdup("ecx");
690 }
691
692 if (xarg > 1 && IS(cconv, "__fastcall")) {
693 if (pp->arg[1].reg != NULL) {
694 printf("%s:%d: %s with arg2 spec %s?\n",
695 hdrfn, hdrfline, cconv, pp->arg[1].reg);
696 }
697 pp->arg[1].reg = strdup("edx");
698 }
699
700 pp->argc = xarg;
701
702 for (i = 0; i < pp->argc; i++) {
703 if (pp->arg[i].reg == NULL)
704 pp->argc_stack++;
705 else
706 pp->argc_reg++;
707 }
708
c0050df6 709 if (pp->argc == 1 && pp->arg[0].reg != NULL
710 && IS(pp->arg[0].reg, "ecx"))
711 {
712 pp->is_fastcall = 1;
713 }
714 else if (pp->argc_reg == 2
715 && pp->arg[0].reg != NULL && IS(pp->arg[0].reg, "ecx")
716 && pp->arg[1].reg != NULL && IS(pp->arg[1].reg, "edx"))
717 {
718 pp->is_fastcall = 1;
719 }
720
721 if (pp->is_vararg && (pp->is_stdcall || pp->is_fastcall)) {
722 printf("%s:%d: vararg %s?\n", hdrfn, hdrfline, cconv);
723 return -1;
724 }
725
39b168b8 726 return p - protostr;
c36e914d 727}
728
bd96f656 729static int pp_name_cmp(const void *p1, const void *p2)
730{
731 const struct parsed_proto *pp1 = p1, *pp2 = p2;
732 return strcmp(pp1->name, pp2->name);
733}
734
865f1aca 735static int ps_name_cmp(const void *p1, const void *p2)
736{
737 const struct parsed_struct *ps1 = p1, *ps2 = p2;
738 return strcmp(ps1->name, ps2->name);
739}
740
741// parsed struct cache
742static struct parsed_struct *ps_cache;
743static int ps_cache_size;
744static int ps_cache_alloc;
745
746static int struct_handler(FILE *fhdr, char *proto, int *line)
747{
748 struct parsed_struct *ps;
749 char lstr[256], *p;
750 int offset = 0;
751 int m = 0;
752 int ret;
753
754 if (ps_cache_size >= ps_cache_alloc) {
755 ps_cache_alloc = ps_cache_alloc * 2 + 64;
756 ps_cache = realloc(ps_cache, ps_cache_alloc
757 * sizeof(ps_cache[0]));
758 my_assert_not(ps_cache, NULL);
759 memset(ps_cache + ps_cache_size, 0,
760 (ps_cache_alloc - ps_cache_size)
761 * sizeof(ps_cache[0]));
762 }
763
764 ps = &ps_cache[ps_cache_size++];
765 ret = sscanf(proto, "struct %255s {", ps->name);
766 if (ret != 1) {
767 printf("%s:%d: struct parse failed\n", hdrfn, *line);
768 return -1;
769 }
770
771 while (fgets(lstr, sizeof(lstr), fhdr))
772 {
773 (*line)++;
774
775 p = sskip(lstr);
776 if (p[0] == '/' && p[1] == '/')
777 continue;
778 if (p[0] == '}')
779 break;
780
781 if (m >= ARRAY_SIZE(ps->members)) {
782 printf("%s:%d: too many struct members\n",
783 hdrfn, *line);
784 return -1;
785 }
786
787 hdrfline = *line;
788 ret = parse_protostr(p, &ps->members[m].pp);
789 if (ret < 0) {
790 printf("%s:%d: struct member #%d/%02x "
791 "doesn't parse\n", hdrfn, *line,
792 m, offset);
793 return -1;
794 }
795 ps->members[m].offset = offset;
796 offset += 4;
797 m++;
798 }
799
800 ps->member_count = m;
801
802 return 0;
803}
804
805// parsed proto cache
bd96f656 806static struct parsed_proto *pp_cache;
807static int pp_cache_size;
808static int pp_cache_alloc;
809
61e29183 810static int b_pp_c_handler(char *proto, const char *fname,
ebc4dc43 811 int is_include, int is_osinc, int is_cinc)
bd96f656 812{
813 int ret;
814
815 if (pp_cache_size >= pp_cache_alloc) {
816 pp_cache_alloc = pp_cache_alloc * 2 + 64;
817 pp_cache = realloc(pp_cache, pp_cache_alloc
818 * sizeof(pp_cache[0]));
819 my_assert_not(pp_cache, NULL);
820 memset(pp_cache + pp_cache_size, 0,
821 (pp_cache_alloc - pp_cache_size)
822 * sizeof(pp_cache[0]));
823 }
824
825 ret = parse_protostr(proto, &pp_cache[pp_cache_size]);
826 if (ret < 0)
827 return -1;
828
61e29183 829 pp_cache[pp_cache_size].is_include = is_include;
830 pp_cache[pp_cache_size].is_osinc = is_osinc;
ebc4dc43 831 pp_cache[pp_cache_size].is_cinc = is_cinc;
bd96f656 832 pp_cache_size++;
833 return 0;
834}
835
865f1aca 836static void build_caches(FILE *fhdr)
c36e914d 837{
1f906263 838 long pos;
c36e914d 839 int ret;
840
1f906263 841 pos = ftell(fhdr);
bd96f656 842 rewind(fhdr);
843
61e29183 844 ret = do_protostrs(fhdr, hdrfn, 0);
bd96f656 845 if (ret < 0)
846 exit(1);
847
848 qsort(pp_cache, pp_cache_size, sizeof(pp_cache[0]), pp_name_cmp);
865f1aca 849 qsort(ps_cache, ps_cache_size, sizeof(ps_cache[0]), ps_name_cmp);
1f906263 850 fseek(fhdr, pos, SEEK_SET);
bd96f656 851}
852
36595fd2 853static const struct parsed_proto *proto_parse(FILE *fhdr, const char *sym,
854 int quiet)
bd96f656 855{
856 const struct parsed_proto *pp_ret;
857 struct parsed_proto pp_search;
7aca4698 858 char *p;
bd96f656 859
860 if (pp_cache == NULL)
865f1aca 861 build_caches(fhdr);
bd96f656 862
863 if (sym[0] == '_') // && strncmp(fname, "stdc", 4) == 0)
864 sym++;
c36e914d 865
bd96f656 866 strcpy(pp_search.name, sym);
7aca4698 867 p = strchr(pp_search.name, '@');
868 if (p != NULL)
869 *p = 0;
870
bd96f656 871 pp_ret = bsearch(&pp_search, pp_cache, pp_cache_size,
872 sizeof(pp_cache[0]), pp_name_cmp);
36595fd2 873 if (pp_ret == NULL && !quiet)
c36e914d 874 printf("%s: sym '%s' is missing\n", hdrfn, sym);
bd96f656 875
876 return pp_ret;
877}
878
865f1aca 879static const struct parsed_proto *proto_lookup_struct(FILE *fhdr,
880 const char *type, int offset)
881{
882 struct parsed_struct ps_search, *ps;
883 int m;
884
885 if (pp_cache == NULL)
886 build_caches(fhdr);
887 if (ps_cache_size == 0)
888 return NULL;
889
890 while (my_isblank(*type))
891 type++;
892 if (!strncmp(type, "struct", 6) && my_isblank(type[6]))
893 type += 7;
894
895 if (sscanf(type, "%255s", ps_search.name) != 1)
896 return NULL;
897
898 ps = bsearch(&ps_search, ps_cache, ps_cache_size,
899 sizeof(ps_cache[0]), ps_name_cmp);
900 if (ps == NULL) {
901 printf("%s: struct '%s' is missing\n",
902 hdrfn, ps_search.name);
903 return NULL;
904 }
905
906 for (m = 0; m < ps->member_count; m++) {
907 if (ps->members[m].offset == offset)
908 return &ps->members[m].pp;
909 }
910
911 return NULL;
912}
913
a652aa9f 914static void pp_copy_arg(struct parsed_proto_arg *d,
915 const struct parsed_proto_arg *s)
916{
917 memcpy(d, s, sizeof(*d));
918
919 if (s->reg != NULL) {
920 d->reg = strdup(s->reg);
921 my_assert_not(d->reg, NULL);
922 }
923 if (s->type.name != NULL) {
924 d->type.name = strdup(s->type.name);
925 my_assert_not(d->type.name, NULL);
926 }
93b5bd18 927 if (s->pp != NULL) {
928 d->pp = malloc(sizeof(*d->pp));
929 my_assert_not(d->pp, NULL);
930 memcpy(d->pp, s->pp, sizeof(*d->pp));
a652aa9f 931 }
932}
933
bd96f656 934struct parsed_proto *proto_clone(const struct parsed_proto *pp_c)
935{
936 struct parsed_proto *pp;
937 int i;
938
939 pp = malloc(sizeof(*pp));
940 my_assert_not(pp, NULL);
941 memcpy(pp, pp_c, sizeof(*pp)); // lazy..
942
943 // do the actual deep copy..
a652aa9f 944 for (i = 0; i < pp_c->argc; i++)
945 pp_copy_arg(&pp->arg[i], &pp_c->arg[i]);
bd96f656 946 if (pp_c->ret_type.name != NULL)
947 pp->ret_type.name = strdup(pp_c->ret_type.name);
c36e914d 948
bd96f656 949 return pp;
c36e914d 950}
951
27ebfaed 952
953static inline int pp_cmp_func(const struct parsed_proto *pp1,
954 const struct parsed_proto *pp2)
955{
956 int i;
957
958 if (pp1->argc != pp2->argc || pp1->argc_reg != pp2->argc_reg)
959 return 1;
960 else {
961 for (i = 0; i < pp1->argc; i++) {
962 if ((pp1->arg[i].reg != NULL) != (pp2->arg[i].reg != NULL))
963 return 1;
964
965 if ((pp1->arg[i].reg != NULL)
966 && !IS(pp1->arg[i].reg, pp2->arg[i].reg))
967 {
968 return 1;
969 }
970 }
971 }
972
973 return 0;
974}
975
b74c31e3 976static inline void pp_print(char *buf, size_t buf_size,
977 const struct parsed_proto *pp)
978{
979 size_t l;
980 int i;
981
982 snprintf(buf, buf_size, "%s %s(", pp->ret_type.name, pp->name);
983 l = strlen(buf);
984
985 for (i = 0; i < pp->argc_reg; i++) {
986 snprintf(buf + l, buf_size - l, "%s%s",
987 i == 0 ? "" : ", ", pp->arg[i].reg);
988 l = strlen(buf);
989 }
990 if (pp->argc_stack > 0) {
991 snprintf(buf + l, buf_size - l, "%s{%d stack}",
992 i == 0 ? "" : ", ", pp->argc_stack);
993 l = strlen(buf);
994 }
995 snprintf(buf + l, buf_size - l, ")");
996}
997
bd96f656 998static inline void proto_release(struct parsed_proto *pp)
c36e914d 999{
1000 int i;
1001
1002 for (i = 0; i < pp->argc; i++) {
39b168b8 1003 if (pp->arg[i].reg != NULL)
c36e914d 1004 free(pp->arg[i].reg);
3ebea2cf 1005 if (pp->arg[i].type.name != NULL)
1006 free(pp->arg[i].type.name);
93b5bd18 1007 if (pp->arg[i].pp != NULL)
1008 free(pp->arg[i].pp);
c36e914d 1009 }
3ebea2cf 1010 if (pp->ret_type.name != NULL)
1011 free(pp->ret_type.name);
bd96f656 1012 free(pp);
865f1aca 1013
1014 (void)proto_lookup_struct;
c36e914d 1015}