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