translate: support for data imports
[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[16];
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 "HWAVEOUT",
220 "HWND",
221 "PAPPBARDATA",
222 "PBYTE",
223 "PCRITICAL_SECTION",
224 "PDEVMODEA",
225 "PDWORD",
226 "PFILETIME",
227 "PLARGE_INTEGER",
228 "PHANDLE",
229 "PHKEY",
230 "PLONG",
231 "PMEMORY_BASIC_INFORMATION",
232 "PUINT",
233 "PULARGE_INTEGER",
234 "PULONG_PTR",
235 "PVOID",
236 "PCVOID",
237 "PWORD",
238 "REFCLSID",
239 "REFGUID",
240 "REFIID",
241 "HOOKPROC",
242 "DLGPROC",
243 "TIMERPROC",
244 "WNDENUMPROC",
245 "va_list",
246 "__VALIST",
247};
248
249static const char *ignored_keywords[] = {
250 "extern",
251 "WINBASEAPI",
252 "WINUSERAPI",
253 "WINGDIAPI",
254 "WINADVAPI",
255};
256
257static int typecmp(const char *n, const char *t)
258{
259 for (; *t != 0; n++, t++) {
260 while (n[0] == ' ' && (n[1] == ' ' || n[1] == '*'))
261 n++;
262 while (t[0] == ' ' && (t[1] == ' ' || t[1] == '*'))
263 t++;
264 if (*n != *t)
265 return *n - *t;
266 }
267
268 return 0;
269}
270
271static const char *skip_type_mod(const char *n)
272{
273 int len;
274 int i;
275
276 for (i = 0; i < ARRAY_SIZE(known_type_mod); i++) {
277 len = strlen(known_type_mod[i]);
278 if (strncmp(n, known_type_mod[i], len) != 0)
279 continue;
280 if (!my_isblank(n[len]))
281 continue;
282
283 n += len;
284 while (my_isblank(*n))
285 n++;
286 i = 0;
287 }
288
289 return n;
290}
291
292static int check_type(const char *name, struct parsed_type *type)
293{
294 const char *n, *n1;
295 int ret = -1;
296 int i;
297
298 n = skip_type_mod(name);
299
300 if (!strncmp(n, "struct", 6) && my_isblank(n[6])) {
301 type->is_struct = 1;
302
303 n += 6;
304 while (my_isblank(*n))
305 n++;
306 }
307
308 for (i = 0; i < ARRAY_SIZE(known_ptr_types); i++) {
309 if (typecmp(n, known_ptr_types[i]))
310 continue;
311
312 type->is_ptr = 1;
313 break;
314 }
315
316 if (n[0] == 'L' && n[1] == 'P' && strncmp(n, "LPARAM", 6))
317 type->is_ptr = 1;
318
319 // assume single word
320 while (!my_isblank(*n) && !my_issep(*n))
321 n++;
322
323 while (1) {
324 n1 = n;
325 while (my_isblank(*n))
326 n++;
327 if (*n == '*') {
328 type->is_ptr = 1;
329 n++;
330 continue;
331 }
332 break;
333 }
334
335 ret = n1 - name;
336 type->name = strndup(name, ret);
337 if (IS(type->name, "__VALIST") || IS(type->name, "va_list"))
338 type->is_va_list = 1;
339 if (IS(type->name, "VOID"))
340 memcpy(type->name, "void", 4);
341
342 return ret;
343}
344
345/* args are always expanded to 32bit */
346static const char *map_reg(const char *reg)
347{
348 const char *regs_f[] = { "eax", "ebx", "ecx", "edx", "esi", "edi" };
349 const char *regs_w[] = { "ax", "bx", "cx", "dx", "si", "di" };
350 const char *regs_b[] = { "al", "bl", "cl", "dl" };
351 int i;
352
353 for (i = 0; i < ARRAY_SIZE(regs_w); i++)
354 if (IS(reg, regs_w[i]))
355 return regs_f[i];
356
357 for (i = 0; i < ARRAY_SIZE(regs_b); i++)
358 if (IS(reg, regs_b[i]))
359 return regs_f[i];
360
361 return reg;
362}
363
364static int check_struct_arg(struct parsed_proto_arg *arg)
365{
366 if (IS(arg->type.name, "POINT"))
367 return 2 - 1;
368
369 return 0;
370}
371
372static int parse_protostr(char *protostr, struct parsed_proto *pp);
373
374static int parse_arg(char **p_, struct parsed_proto_arg *arg, int xarg)
375{
376 char buf[256];
377 char *p = *p_;
378 char *pe;
379 int ret;
380
381 arg->pp = calloc(1, sizeof(*arg->pp));
382 my_assert_not(arg->pp, NULL);
383 arg->pp->is_arg = 1;
384
385 pe = p;
386 while (1) {
387 pe = strpbrk(pe, ",()");
388 if (pe == NULL)
389 return -1;
390 if (*pe == ',' || *pe == ')')
391 break;
392 pe = strchr(pe, ')');
393 if (pe == NULL)
394 return -1;
395 pe++;
396 }
397
398 if (pe - p > sizeof(buf) - 1)
399 return -1;
400 memcpy(buf, p, pe - p);
401 buf[pe - p] = 0;
402
403 ret = parse_protostr(buf, arg->pp);
404 if (ret < 0)
405 return -1;
406
407 if (IS_START(arg->pp->name, "guess"))
408 arg->pp->is_guessed = 1;
409
410 // we don't use actual names right now...
411 snprintf(arg->pp->name, sizeof(arg->pp->name), "a%d", xarg);
412
413 if (!arg->type.is_struct)
414 // we'll treat it as void * for non-calls
415 arg->type.name = strdup("void *");
416 arg->type.is_ptr = 1;
417
418 p += ret;
419 *p_ = p;
420 return 0;
421}
422
423static int parse_protostr(char *protostr, struct parsed_proto *pp)
424{
425 struct parsed_proto_arg *arg;
426 char regparm[16];
427 char buf[256];
428 char cconv[32];
429 int is_retreg;
430 char *p, *p1;
431 int xarg = 0;
432 int i, l;
433 int ret;
434
435 p = sskip(protostr);
436 if (p[0] == '/' && p[1] == '/') {
437 printf("%s:%d: commented out?\n", hdrfn, hdrfline);
438 p = sskip(p + 2);
439 }
440
441 // allow start of line comment
442 if (p[0] == '/' && p[1] == '*') {
443 p = strstr(p + 2, "*/");
444 if (p == NULL) {
445 printf("%s:%d: multiline comments unsupported\n",
446 hdrfn, hdrfline);
447 return -1;
448 }
449 p = sskip(p + 2);
450 }
451
452 // we need remaining hints in comments, so strip / *
453 for (p1 = p; p1[0] != 0 && p1[1] != 0; p1++) {
454 if ((p1[0] == '/' && p1[1] == '*')
455 || (p1[0] == '*' && p1[1] == '/'))
456 p1[0] = p1[1] = ' ';
457 }
458
459 if (!strncmp(p, "DECLSPEC_NORETURN ", 18)) {
460 pp->is_noreturn = 1;
461 p = sskip(p + 18);
462 }
463
464 for (i = 0; i < ARRAY_SIZE(ignored_keywords); i++) {
465 l = strlen(ignored_keywords[i]);
466 if (!strncmp(p, ignored_keywords[i], l) && my_isblank(p[l]))
467 p = sskip(p + l + 1);
468 }
469
470 if (IS_START(p, "DECL_IMPORT ")) {
471 pp->is_import = 1;
472 p = sskip(p + 12);
473 }
474
475 ret = check_type(p, &pp->ret_type);
476 if (ret <= 0) {
477 printf("%s:%d:%zd: unhandled return in '%s'\n",
478 hdrfn, hdrfline, (p - protostr) + 1, protostr);
479 return -1;
480 }
481 p = sskip(p + ret);
482
483 if (!strncmp(p, "noreturn ", 9)) {
484 pp->is_noreturn = 1;
485 p = sskip(p + 9);
486 }
487
488 if (!strchr(p, ')')) {
489 p = next_idt(buf, sizeof(buf), p);
490 p = sskip(p);
491 if (!pp->is_arg && buf[0] == 0) {
492 printf("%s:%d:%zd: var name is missing\n",
493 hdrfn, hdrfline, (p - protostr) + 1);
494 return -1;
495 }
496 strcpy(pp->name, buf);
497
498 p1 = strchr(p, ']');
499 if (p1 != NULL) {
500 p = p1 + 1;
501 pp->ret_type.is_array = 1;
502 }
503 return p - protostr;
504 }
505
506 pp->is_func = 1;
507
508 if (*p == '(') {
509 pp->is_fptr = 1;
510 p = sskip(p + 1);
511 }
512
513 p = next_word(cconv, sizeof(cconv), p);
514 p = sskip(p);
515 if (cconv[0] == 0) {
516 printf("%s:%d:%zd: cconv missing\n",
517 hdrfn, hdrfline, (p - protostr) + 1);
518 return -1;
519 }
520 if (IS(cconv, "__cdecl"))
521 pp->is_stdcall = 0;
522 else if (IS(cconv, "__stdcall"))
523 pp->is_stdcall = 1;
524 else if (IS(cconv, "__fastcall")) {
525 pp->is_fastcall = 1;
526 pp->is_stdcall = 1; // sort of..
527 }
528 else if (IS(cconv, "__thiscall"))
529 pp->is_stdcall = 1;
530 else if (IS(cconv, "__userpurge"))
531 pp->is_stdcall = 1; // IDA
532 else if (IS(cconv, "__usercall"))
533 pp->is_stdcall = 0; // IDA
534 else if (IS(cconv, "__userstack")) {
535 pp->is_stdcall = 0; // custom
536 pp->is_userstack = 1;
537 }
538 else if (IS(cconv, "WINAPI") || IS(cconv, "PASCAL"))
539 pp->is_stdcall = 1;
540 else {
541 printf("%s:%d:%zd: unhandled cconv: '%s'\n",
542 hdrfn, hdrfline, (p - protostr) + 1, cconv);
543 return -1;
544 }
545
546 if (pp->is_fptr) {
547 if (*p != '*') {
548 printf("%s:%d:%zd: '*' expected\n",
549 hdrfn, hdrfline, (p - protostr) + 1);
550 return -1;
551 }
552 p++;
553 // XXX: skipping extra asterisks, for now
554 while (*p == '*')
555 p++;
556 p = sskip(p);
557 }
558
559 p = next_idt(buf, sizeof(buf), p);
560 p = sskip(p);
561 if (buf[0] == 0) {
562 //printf("%s:%d:%zd: func name missing\n",
563 // hdrfn, hdrfline, (p - protostr) + 1);
564 //return -1;
565 }
566 strcpy(pp->name, buf);
567
568 ret = get_regparm(regparm, sizeof(regparm), p, &is_retreg);
569 if (ret > 0) {
570 if (!IS(regparm, "eax") && !IS(regparm, "ax")
571 && !IS(regparm, "al") && !IS(regparm, "edx:eax"))
572 {
573 printf("%s:%d:%zd: bad regparm: %s\n",
574 hdrfn, hdrfline, (p - protostr) + 1, regparm);
575 return -1;
576 }
577 p += ret;
578 p = sskip(p);
579 }
580
581 if (pp->is_fptr) {
582 if (*p == '[') {
583 // not really ret_type is array, but ohwell
584 pp->ret_type.is_array = 1;
585 p = strchr(p + 1, ']');
586 if (p == NULL) {
587 printf("%s:%d:%zd: ']' expected\n",
588 hdrfn, hdrfline, (p - protostr) + 1);
589 return -1;
590 }
591 p = sskip(p + 1);
592 }
593 if (*p != ')') {
594 printf("%s:%d:%zd: ')' expected\n",
595 hdrfn, hdrfline, (p - protostr) + 1);
596 return -1;
597 }
598 p = sskip(p + 1);
599 }
600
601 if (*p != '(') {
602 printf("%s:%d:%zd: '(' expected, got '%c'\n",
603 hdrfn, hdrfline, (p - protostr) + 1, *p);
604 return -1;
605 }
606 p++;
607
608 // check for x(void)
609 p = sskip(p);
610 if ((!strncmp(p, "void", 4) || !strncmp(p, "VOID", 4))
611 && *sskip(p + 4) == ')')
612 p += 4;
613
614 while (1) {
615 p = sskip(p);
616 if (*p == ')') {
617 p++;
618 break;
619 }
620 if (xarg > 0) {
621 if (*p != ',') {
622 printf("%s:%d:%zd: ',' expected\n",
623 hdrfn, hdrfline, (p - protostr) + 1);
624 return -1;
625 }
626 p = sskip(p + 1);
627 }
628
629 if (!strncmp(p, "...", 3)) {
630 pp->is_vararg = 1;
631 p = sskip(p + 3);
632 if (*p == ')') {
633 p++;
634 break;
635 }
636 printf("%s:%d:%zd: ')' expected\n",
637 hdrfn, hdrfline, (p - protostr) + 1);
638 return -1;
639 }
640
641 arg = &pp->arg[xarg];
642 xarg++;
643
644 p1 = p;
645 ret = check_type(p, &arg->type);
646 if (ret <= 0) {
647 printf("%s:%d:%zd: unhandled type for arg%d\n",
648 hdrfn, hdrfline, (p - protostr) + 1, xarg);
649 return -1;
650 }
651 p = sskip(p + ret);
652
653 if (*p == '(' || arg->type.is_struct) {
654 // func ptr or struct
655 ret = parse_arg(&p1, arg, xarg);
656 if (ret < 0) {
657 printf("%s:%d:%zd: funcarg parse failed\n",
658 hdrfn, hdrfline, p1 - protostr);
659 return -1;
660 }
661 p = p1;
662 }
663
664 p = next_idt(buf, sizeof(buf), p);
665 p = sskip(p);
666#if 0
667 if (buf[0] == 0) {
668 printf("%s:%d:%zd: idt missing for arg%d\n",
669 hdrfn, hdrfline, (p - protostr) + 1, xarg);
670 return -1;
671 }
672#endif
673 arg->reg = NULL;
674
675 ret = get_regparm(regparm, sizeof(regparm), p, &is_retreg);
676 if (ret > 0) {
677 p += ret;
678 p = sskip(p);
679
680 arg->reg = strdup(map_reg(regparm));
681 arg->type.is_retreg = is_retreg;
682 pp->has_retreg |= is_retreg;
683 }
684
685 if (IS(arg->type.name, "float")
686 || IS(arg->type.name, "double"))
687 {
688 arg->type.is_float = 1;
689 }
690
691 if (!arg->type.is_ptr && (strstr(arg->type.name, "int64")
692 || IS(arg->type.name, "double")))
693 {
694 arg->type.is_64bit = 1;
695 // hack..
696 pp_copy_arg(&pp->arg[xarg], arg);
697 arg = &pp->arg[xarg];
698 xarg++;
699 free(arg->type.name);
700 arg->type.name = strdup("dummy");
701 }
702
703 ret = check_struct_arg(arg);
704 if (ret > 0) {
705 pp->has_structarg = 1;
706 arg->type.is_struct = 1;
707 free(arg->type.name);
708 arg->type.name = strdup("int");
709 for (l = 0; l < ret; l++) {
710 pp_copy_arg(&pp->arg[xarg], arg);
711 xarg++;
712 }
713 }
714 }
715
716 if (xarg > 0 && (IS(cconv, "__fastcall") || IS(cconv, "__thiscall"))) {
717 if (pp->arg[0].reg != NULL) {
718 printf("%s:%d: %s with arg1 spec %s?\n",
719 hdrfn, hdrfline, cconv, pp->arg[0].reg);
720 }
721 pp->arg[0].reg = strdup("ecx");
722 }
723
724 if (xarg > 1 && IS(cconv, "__fastcall")) {
725 if (pp->arg[1].reg != NULL) {
726 printf("%s:%d: %s with arg2 spec %s?\n",
727 hdrfn, hdrfline, cconv, pp->arg[1].reg);
728 }
729 pp->arg[1].reg = strdup("edx");
730 }
731
732 pp->argc = xarg;
733
734 for (i = 0; i < pp->argc; i++) {
735 if (pp->arg[i].reg == NULL)
736 pp->argc_stack++;
737 else
738 pp->argc_reg++;
739 }
740
741 if (pp->argc == 1 && pp->arg[0].reg != NULL
742 && IS(pp->arg[0].reg, "ecx"))
743 {
744 pp->is_fastcall = 1;
745 }
746 else if (pp->argc_reg == 2
747 && pp->arg[0].reg != NULL && IS(pp->arg[0].reg, "ecx")
748 && pp->arg[1].reg != NULL && IS(pp->arg[1].reg, "edx"))
749 {
750 pp->is_fastcall = 1;
751 }
752
753 if (pp->is_vararg && (pp->is_stdcall || pp->is_fastcall)) {
754 printf("%s:%d: vararg %s?\n", hdrfn, hdrfline, cconv);
755 return -1;
756 }
757
758 return p - protostr;
759}
760
761static int pp_name_cmp(const void *p1, const void *p2)
762{
763 const struct parsed_proto *pp1 = p1, *pp2 = p2;
764 return strcmp(pp1->name, pp2->name);
765}
766
767static int ps_name_cmp(const void *p1, const void *p2)
768{
769 const struct parsed_struct *ps1 = p1, *ps2 = p2;
770 return strcmp(ps1->name, ps2->name);
771}
772
773// parsed struct cache
774static struct parsed_struct *ps_cache;
775static int ps_cache_size;
776static int ps_cache_alloc;
777
778static int struct_handler(FILE *fhdr, char *proto, int *line)
779{
780 struct parsed_struct *ps;
781 char lstr[256], *p;
782 int offset = 0;
783 int m = 0;
784 int ret;
785
786 if (ps_cache_size >= ps_cache_alloc) {
787 ps_cache_alloc = ps_cache_alloc * 2 + 64;
788 ps_cache = realloc(ps_cache, ps_cache_alloc
789 * sizeof(ps_cache[0]));
790 my_assert_not(ps_cache, NULL);
791 memset(ps_cache + ps_cache_size, 0,
792 (ps_cache_alloc - ps_cache_size)
793 * sizeof(ps_cache[0]));
794 }
795
796 ps = &ps_cache[ps_cache_size++];
797 ret = sscanf(proto, "struct %255s {", ps->name);
798 if (ret != 1) {
799 printf("%s:%d: struct parse failed\n", hdrfn, *line);
800 return -1;
801 }
802
803 while (fgets(lstr, sizeof(lstr), fhdr))
804 {
805 (*line)++;
806
807 p = sskip(lstr);
808 if (p[0] == '/' && p[1] == '/')
809 continue;
810 if (p[0] == '}')
811 break;
812
813 if (m >= ARRAY_SIZE(ps->members)) {
814 printf("%s:%d: too many struct members\n",
815 hdrfn, *line);
816 return -1;
817 }
818
819 hdrfline = *line;
820 ret = parse_protostr(p, &ps->members[m].pp);
821 if (ret < 0) {
822 printf("%s:%d: struct member #%d/%02x "
823 "doesn't parse\n", hdrfn, *line,
824 m, offset);
825 return -1;
826 }
827 ps->members[m].offset = offset;
828 offset += 4;
829 m++;
830 }
831
832 ps->member_count = m;
833
834 return 0;
835}
836
837// parsed proto cache
838static struct parsed_proto *pp_cache;
839static int pp_cache_size;
840static int pp_cache_alloc;
841
842static int b_pp_c_handler(char *proto, const char *fname,
843 int is_include, int is_osinc, int is_cinc)
844{
845 int ret;
846
847 if (pp_cache_size >= pp_cache_alloc) {
848 pp_cache_alloc = pp_cache_alloc * 2 + 64;
849 pp_cache = realloc(pp_cache, pp_cache_alloc
850 * sizeof(pp_cache[0]));
851 my_assert_not(pp_cache, NULL);
852 memset(pp_cache + pp_cache_size, 0,
853 (pp_cache_alloc - pp_cache_size)
854 * sizeof(pp_cache[0]));
855 }
856
857 ret = parse_protostr(proto, &pp_cache[pp_cache_size]);
858 if (ret < 0)
859 return -1;
860
861 pp_cache[pp_cache_size].is_include = is_include;
862 pp_cache[pp_cache_size].is_osinc = is_osinc;
863 pp_cache[pp_cache_size].is_cinc = is_cinc;
864 pp_cache_size++;
865 return 0;
866}
867
868static void build_caches(FILE *fhdr)
869{
870 long pos;
871 int ret;
872
873 pos = ftell(fhdr);
874 rewind(fhdr);
875
876 ret = do_protostrs(fhdr, hdrfn, 0);
877 if (ret < 0)
878 exit(1);
879
880 qsort(pp_cache, pp_cache_size, sizeof(pp_cache[0]), pp_name_cmp);
881 qsort(ps_cache, ps_cache_size, sizeof(ps_cache[0]), ps_name_cmp);
882 fseek(fhdr, pos, SEEK_SET);
883}
884
885static const struct parsed_proto *proto_parse(FILE *fhdr, const char *sym,
886 int quiet)
887{
888 const struct parsed_proto *pp_ret;
889 struct parsed_proto pp_search;
890 char *p;
891
892 if (pp_cache == NULL)
893 build_caches(fhdr);
894
895 // ugh...
896 if (sym[0] == '_' && !IS_START(sym, "__W"))
897 sym++;
898
899 strcpy(pp_search.name, sym);
900 p = strchr(pp_search.name, '@');
901 if (p != NULL)
902 *p = 0;
903
904 pp_ret = bsearch(&pp_search, pp_cache, pp_cache_size,
905 sizeof(pp_cache[0]), pp_name_cmp);
906 if (pp_ret == NULL && !quiet)
907 printf("%s: sym '%s' is missing\n", hdrfn, sym);
908
909 return pp_ret;
910}
911
912static const struct parsed_proto *proto_lookup_struct(FILE *fhdr,
913 const char *type, int offset)
914{
915 struct parsed_struct ps_search, *ps;
916 int m;
917
918 if (pp_cache == NULL)
919 build_caches(fhdr);
920 if (ps_cache_size == 0)
921 return NULL;
922
923 while (my_isblank(*type))
924 type++;
925 if (!strncmp(type, "struct", 6) && my_isblank(type[6]))
926 type += 7;
927
928 if (sscanf(type, "%255s", ps_search.name) != 1)
929 return NULL;
930
931 ps = bsearch(&ps_search, ps_cache, ps_cache_size,
932 sizeof(ps_cache[0]), ps_name_cmp);
933 if (ps == NULL) {
934 printf("%s: struct '%s' is missing\n",
935 hdrfn, ps_search.name);
936 return NULL;
937 }
938
939 for (m = 0; m < ps->member_count; m++) {
940 if (ps->members[m].offset == offset)
941 return &ps->members[m].pp;
942 }
943
944 return NULL;
945}
946
947static void pp_copy_arg(struct parsed_proto_arg *d,
948 const struct parsed_proto_arg *s)
949{
950 memcpy(d, s, sizeof(*d));
951
952 if (s->reg != NULL) {
953 d->reg = strdup(s->reg);
954 my_assert_not(d->reg, NULL);
955 }
956 if (s->type.name != NULL) {
957 d->type.name = strdup(s->type.name);
958 my_assert_not(d->type.name, NULL);
959 }
960 if (s->pp != NULL) {
961 d->pp = malloc(sizeof(*d->pp));
962 my_assert_not(d->pp, NULL);
963 memcpy(d->pp, s->pp, sizeof(*d->pp));
964 }
965}
966
967struct parsed_proto *proto_clone(const struct parsed_proto *pp_c)
968{
969 struct parsed_proto *pp;
970 int i;
971
972 pp = malloc(sizeof(*pp));
973 my_assert_not(pp, NULL);
974 memcpy(pp, pp_c, sizeof(*pp)); // lazy..
975
976 // do the actual deep copy..
977 for (i = 0; i < pp_c->argc; i++)
978 pp_copy_arg(&pp->arg[i], &pp_c->arg[i]);
979 if (pp_c->ret_type.name != NULL)
980 pp->ret_type.name = strdup(pp_c->ret_type.name);
981
982 return pp;
983}
984
985
986static inline int pp_cmp_func(const struct parsed_proto *pp1,
987 const struct parsed_proto *pp2)
988{
989 int i;
990
991 if (pp1->argc != pp2->argc || pp1->argc_reg != pp2->argc_reg)
992 return 1;
993 else {
994 for (i = 0; i < pp1->argc; i++) {
995 if ((pp1->arg[i].reg != NULL) != (pp2->arg[i].reg != NULL))
996 return 1;
997
998 if ((pp1->arg[i].reg != NULL)
999 && !IS(pp1->arg[i].reg, pp2->arg[i].reg))
1000 {
1001 return 1;
1002 }
1003 }
1004 }
1005
1006 return 0;
1007}
1008
1009static inline void pp_print(char *buf, size_t buf_size,
1010 const struct parsed_proto *pp)
1011{
1012 size_t l;
1013 int i;
1014
1015 snprintf(buf, buf_size, "%s %s(", pp->ret_type.name, pp->name);
1016 l = strlen(buf);
1017
1018 for (i = 0; i < pp->argc_reg; i++) {
1019 snprintf(buf + l, buf_size - l, "%s%s",
1020 i == 0 ? "" : ", ", pp->arg[i].reg);
1021 l = strlen(buf);
1022 }
1023 if (pp->argc_stack > 0) {
1024 snprintf(buf + l, buf_size - l, "%s{%d stack}",
1025 i == 0 ? "" : ", ", pp->argc_stack);
1026 l = strlen(buf);
1027 }
1028 snprintf(buf + l, buf_size - l, ")");
1029}
1030
1031static inline void proto_release(struct parsed_proto *pp)
1032{
1033 int i;
1034
1035 for (i = 0; i < pp->argc; i++) {
1036 if (pp->arg[i].reg != NULL)
1037 free(pp->arg[i].reg);
1038 if (pp->arg[i].type.name != NULL)
1039 free(pp->arg[i].type.name);
1040 if (pp->arg[i].pp != NULL)
1041 free(pp->arg[i].pp);
1042 }
1043 if (pp->ret_type.name != NULL)
1044 free(pp->ret_type.name);
1045 free(pp);
1046
1047 (void)proto_lookup_struct;
1048}