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