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