plugin: more renaming
[ia32rtools.git] / tools / protoparse.h
... / ...
CommitLineData
1
2struct parsed_proto;
3
4struct parsed_type {
5 char *name;
6 unsigned int is_array:1;
7 unsigned int is_ptr:1;
8 unsigned int is_struct:1; // split for args
9};
10
11struct parsed_proto_arg {
12 char *reg;
13 struct parsed_type type;
14 struct parsed_proto *fptr;
15 void *datap;
16};
17
18struct parsed_proto {
19 char name[256];
20 union {
21 struct parsed_type ret_type;
22 struct parsed_type type;
23 };
24 struct parsed_proto_arg arg[16];
25 int argc;
26 int argc_stack;
27 int argc_reg;
28 unsigned int is_func:1;
29 unsigned int is_stdcall:1;
30 unsigned int is_fastcall:1;
31 unsigned int is_vararg:1;
32 unsigned int is_fptr:1;
33 unsigned int is_noreturn:1;
34 unsigned int is_unresolved:1;
35 unsigned int has_structarg:1;
36};
37
38static const char *hdrfn;
39static int hdrfline = 0;
40
41static void pp_copy_arg(struct parsed_proto_arg *d,
42 const struct parsed_proto_arg *s);
43
44static int b_pp_c_handler(char *proto, const char *fname);
45
46static int do_protostrs(FILE *fhdr, const char *fname)
47{
48 const char *finc_name;
49 const char *hdrfn_saved;
50 char protostr[256];
51 char path[256];
52 char fname_inc[256];
53 FILE *finc;
54 int line = 0;
55 int ret;
56 char *p;
57
58 hdrfn_saved = hdrfn;
59 hdrfn = fname;
60
61 while (fgets(protostr, sizeof(protostr), fhdr))
62 {
63 line++;
64 if (strncmp(protostr, "//#include ", 11) == 0) {
65 finc_name = protostr + 11;
66 p = strpbrk(finc_name, "\r\n ");
67 if (p != NULL)
68 *p = 0;
69
70 path[0] = 0;
71 p = strrchr(hdrfn_saved, '/');
72 if (p) {
73 memcpy(path, hdrfn_saved,
74 p - hdrfn_saved + 1);
75 path[p - hdrfn_saved + 1] = 0;
76 }
77 snprintf(fname_inc, sizeof(fname_inc), "%s%s",
78 path, finc_name);
79 finc = fopen(fname_inc, "r");
80 if (finc == NULL) {
81 printf("%s:%d: can't open '%s'\n",
82 fname_inc, line, finc_name);
83 continue;
84 }
85 ret = do_protostrs(finc, finc_name);
86 fclose(finc);
87 if (ret < 0)
88 break;
89 continue;
90 }
91 if (strncmp(sskip(protostr), "//", 2) == 0)
92 continue;
93
94 p = protostr + strlen(protostr);
95 for (p--; p >= protostr && my_isblank(*p); --p)
96 *p = 0;
97 if (p < protostr)
98 continue;
99
100 hdrfline = line;
101
102 ret = b_pp_c_handler(protostr, hdrfn);
103 if (ret < 0)
104 break;
105 }
106
107 hdrfn = hdrfn_saved;
108
109 if (feof(fhdr))
110 return 0;
111
112 return -1;
113}
114
115static int get_regparm(char *dst, size_t dlen, char *p)
116{
117 int i, o;
118
119 if (*p != '<')
120 return 0;
121
122 for (o = 0, i = 1; o < dlen; i++) {
123 if (p[i] == 0)
124 return 0;
125 if (p[i] == '>')
126 break;
127 dst[o++] = p[i];
128 }
129 dst[o] = 0;
130 return i + 1;
131}
132
133// hmh..
134static const char *known_type_mod[] = {
135 "const",
136 "signed",
137 "unsigned",
138 "struct",
139 "enum",
140 "CONST",
141};
142
143static const char *known_ptr_types[] = {
144 "FARPROC",
145 "HACCEL",
146 "HANDLE",
147 "HBITMAP",
148 "HCURSOR",
149 "HDC",
150 "HFONT",
151 "HGDIOBJ",
152 "HGLOBAL",
153 "HICON",
154 "HINSTANCE",
155 //"HIMC", // DWORD
156 "HMODULE",
157 "HPALETTE",
158 "HRGN",
159 "HRSRC",
160 "HKEY",
161 "HMENU",
162 "HWND",
163 "PCRITICAL_SECTION",
164 "PDWORD",
165 "PHKEY",
166 "PLONG",
167 "PMEMORY_BASIC_INFORMATION",
168 "PUINT",
169 "PVOID",
170 "PCVOID",
171 "DLGPROC",
172 "TIMERPROC",
173 "WNDENUMPROC",
174 "va_list",
175 "__VALIST",
176};
177
178static const char *ignored_keywords[] = {
179 "extern",
180 "WINBASEAPI",
181 "WINUSERAPI",
182 "WINGDIAPI",
183 "WINADVAPI",
184};
185
186// returns ptr to char after type ends
187static int typecmp(const char *n, const char *t)
188{
189 for (; *t != 0; n++, t++) {
190 while (n[0] == ' ' && (n[1] == ' ' || n[1] == '*'))
191 n++;
192 while (t[0] == ' ' && (t[1] == ' ' || t[1] == '*'))
193 t++;
194 if (*n != *t)
195 return *n - *t;
196 }
197
198 return 0;
199}
200
201static const char *skip_type_mod(const char *n)
202{
203 int len;
204 int i;
205
206 for (i = 0; i < ARRAY_SIZE(known_type_mod); i++) {
207 len = strlen(known_type_mod[i]);
208 if (strncmp(n, known_type_mod[i], len) != 0)
209 continue;
210 if (!my_isblank(n[len]))
211 continue;
212
213 n += len;
214 while (my_isblank(*n))
215 n++;
216 i = 0;
217 }
218
219 return n;
220}
221
222static int check_type(const char *name, struct parsed_type *type)
223{
224 const char *n, *n1;
225 int ret = -1;
226 int i;
227
228 n = skip_type_mod(name);
229
230 for (i = 0; i < ARRAY_SIZE(known_ptr_types); i++) {
231 if (typecmp(n, known_ptr_types[i]))
232 continue;
233
234 type->is_ptr = 1;
235 break;
236 }
237
238 if (n[0] == 'L' && n[1] == 'P' && strncmp(n, "LPARAM", 6))
239 type->is_ptr = 1;
240
241 // assume single word
242 while (!my_isblank(*n) && !my_issep(*n))
243 n++;
244
245 while (1) {
246 n1 = n;
247 while (my_isblank(*n))
248 n++;
249 if (*n == '*') {
250 type->is_ptr = 1;
251 n++;
252 continue;
253 }
254 break;
255 }
256
257 ret = n1 - name;
258 type->name = strndup(name, ret);
259 if (IS(type->name, "VOID"))
260 memcpy(type->name, "void", 4);
261
262 return ret;
263}
264
265/* args are always expanded to 32bit */
266static const char *map_reg(const char *reg)
267{
268 const char *regs_f[] = { "eax", "ebx", "ecx", "edx", "esi", "edi" };
269 const char *regs_w[] = { "ax", "bx", "cx", "dx", "si", "di" };
270 const char *regs_b[] = { "al", "bl", "cl", "dl" };
271 int i;
272
273 for (i = 0; i < ARRAY_SIZE(regs_w); i++)
274 if (IS(reg, regs_w[i]))
275 return regs_f[i];
276
277 for (i = 0; i < ARRAY_SIZE(regs_b); i++)
278 if (IS(reg, regs_b[i]))
279 return regs_f[i];
280
281 return reg;
282}
283
284static int check_struct_arg(struct parsed_proto_arg *arg)
285{
286 if (IS(arg->type.name, "POINT"))
287 return 2 - 1;
288
289 return 0;
290}
291
292static int parse_protostr(char *protostr, struct parsed_proto *pp)
293{
294 struct parsed_proto_arg *arg;
295 char regparm[16];
296 char buf[256];
297 char cconv[32];
298 int xarg = 0;
299 char *p, *p1;
300 int i, l;
301 int ret;
302
303 p = sskip(protostr);
304 if (p[0] == '/' && p[1] == '/') {
305 printf("%s:%d: commented out?\n", hdrfn, hdrfline);
306 p = sskip(p + 2);
307 }
308
309 // strip unneeded stuff
310 for (p1 = p; p1[0] != 0 && p1[1] != 0; p1++) {
311 if ((p1[0] == '/' && p1[1] == '*')
312 || (p1[0] == '*' && p1[1] == '/'))
313 p1[0] = p1[1] = ' ';
314 }
315
316 if (!strncmp(p, "DECLSPEC_NORETURN ", 18)) {
317 pp->is_noreturn = 1;
318 p = sskip(p + 18);
319 }
320
321 for (i = 0; i < ARRAY_SIZE(ignored_keywords); i++) {
322 l = strlen(ignored_keywords[i]);
323 if (!strncmp(p, ignored_keywords[i], l) && my_isblank(p[l]))
324 p = sskip(p + l + 1);
325 }
326
327 ret = check_type(p, &pp->ret_type);
328 if (ret <= 0) {
329 printf("%s:%d:%zd: unhandled return in '%s'\n",
330 hdrfn, hdrfline, (p - protostr) + 1, protostr);
331 return -1;
332 }
333 p = sskip(p + ret);
334
335 if (!strncmp(p, "noreturn ", 9)) {
336 pp->is_noreturn = 1;
337 p = sskip(p + 9);
338 }
339
340 if (!strchr(p, ')')) {
341 p = next_idt(buf, sizeof(buf), p);
342 p = sskip(p);
343 if (buf[0] == 0) {
344 printf("%s:%d:%zd: var name missing\n",
345 hdrfn, hdrfline, (p - protostr) + 1);
346 return -1;
347 }
348 strcpy(pp->name, buf);
349
350 p1 = strchr(p, ']');
351 if (p1 != NULL) {
352 p = p1 + 1;
353 pp->ret_type.is_array = 1;
354 }
355 return p - protostr;
356 }
357
358 pp->is_func = 1;
359
360 if (*p == '(') {
361 pp->is_fptr = 1;
362 p = sskip(p + 1);
363 }
364
365 p = next_word(cconv, sizeof(cconv), p);
366 p = sskip(p);
367 if (cconv[0] == 0) {
368 printf("%s:%d:%zd: cconv missing\n",
369 hdrfn, hdrfline, (p - protostr) + 1);
370 return -1;
371 }
372 if (IS(cconv, "__cdecl"))
373 pp->is_stdcall = 0;
374 else if (IS(cconv, "__stdcall"))
375 pp->is_stdcall = 1;
376 else if (IS(cconv, "__fastcall")) {
377 pp->is_fastcall = 1;
378 pp->is_stdcall = 1; // sort of..
379 }
380 else if (IS(cconv, "__thiscall"))
381 pp->is_stdcall = 1;
382 else if (IS(cconv, "__userpurge"))
383 pp->is_stdcall = 1; // IDA
384 else if (IS(cconv, "__usercall"))
385 pp->is_stdcall = 0; // IDA
386 else if (IS(cconv, "WINAPI"))
387 pp->is_stdcall = 1;
388 else {
389 printf("%s:%d:%zd: unhandled cconv: '%s'\n",
390 hdrfn, hdrfline, (p - protostr) + 1, cconv);
391 return -1;
392 }
393
394 if (pp->is_fptr) {
395 if (*p != '*') {
396 printf("%s:%d:%zd: '*' expected\n",
397 hdrfn, hdrfline, (p - protostr) + 1);
398 return -1;
399 }
400 p++;
401 // XXX: skipping extra asterisks, for now
402 while (*p == '*')
403 p++;
404 p = sskip(p);
405 }
406
407 p = next_idt(buf, sizeof(buf), p);
408 p = sskip(p);
409 if (buf[0] == 0) {
410 //printf("%s:%d:%zd: func name missing\n",
411 // hdrfn, hdrfline, (p - protostr) + 1);
412 //return -1;
413 }
414 strcpy(pp->name, buf);
415
416 ret = get_regparm(regparm, sizeof(regparm), p);
417 if (ret > 0) {
418 if (!IS(regparm, "eax") && !IS(regparm, "ax")
419 && !IS(regparm, "al") && !IS(regparm, "edx:eax"))
420 {
421 printf("%s:%d:%zd: bad regparm: %s\n",
422 hdrfn, hdrfline, (p - protostr) + 1, regparm);
423 return -1;
424 }
425 p += ret;
426 p = sskip(p);
427 }
428
429 if (pp->is_fptr) {
430 if (*p == '[') {
431 // not really ret_type is array, but ohwell
432 pp->ret_type.is_array = 1;
433 p = strchr(p + 1, ']');
434 if (p == NULL) {
435 printf("%s:%d:%zd: ']' expected\n",
436 hdrfn, hdrfline, (p - protostr) + 1);
437 return -1;
438 }
439 p = sskip(p + 1);
440 }
441 if (*p != ')') {
442 printf("%s:%d:%zd: ')' expected\n",
443 hdrfn, hdrfline, (p - protostr) + 1);
444 return -1;
445 }
446 p = sskip(p + 1);
447 }
448
449 if (*p != '(') {
450 printf("%s:%d:%zd: '(' expected, got '%c'\n",
451 hdrfn, hdrfline, (p - protostr) + 1, *p);
452 return -1;
453 }
454 p++;
455
456 // check for x(void)
457 p = sskip(p);
458 if ((!strncmp(p, "void", 4) || !strncmp(p, "VOID", 4))
459 && *sskip(p + 4) == ')')
460 p += 4;
461
462 while (1) {
463 p = sskip(p);
464 if (*p == ')') {
465 p++;
466 break;
467 }
468 if (xarg > 0) {
469 if (*p != ',') {
470 printf("%s:%d:%zd: ',' expected\n",
471 hdrfn, hdrfline, (p - protostr) + 1);
472 return -1;
473 }
474 p = sskip(p + 1);
475 }
476
477 if (!strncmp(p, "...", 3)) {
478 pp->is_vararg = 1;
479 p = sskip(p + 3);
480 if (*p == ')') {
481 p++;
482 break;
483 }
484 printf("%s:%d:%zd: ')' expected\n",
485 hdrfn, hdrfline, (p - protostr) + 1);
486 return -1;
487 }
488
489 arg = &pp->arg[xarg];
490 xarg++;
491
492 p1 = p;
493 ret = check_type(p, &arg->type);
494 if (ret <= 0) {
495 printf("%s:%d:%zd: unhandled type for arg%d\n",
496 hdrfn, hdrfline, (p - protostr) + 1, xarg);
497 return -1;
498 }
499 p = sskip(p + ret);
500
501 if (*p == '(') {
502 // func ptr
503 arg->fptr = calloc(1, sizeof(*arg->fptr));
504 ret = parse_protostr(p1, arg->fptr);
505 if (ret < 0) {
506 printf("%s:%d:%zd: funcarg parse failed\n",
507 hdrfn, hdrfline, p1 - protostr);
508 return -1;
509 }
510 // we'll treat it as void * for non-calls
511 arg->type.name = strdup("void *");
512 arg->type.is_ptr = 1;
513
514 p = p1 + ret;
515 }
516
517 p = next_idt(buf, sizeof(buf), p);
518 p = sskip(p);
519#if 0
520 if (buf[0] == 0) {
521 printf("%s:%d:%zd: idt missing for arg%d\n",
522 hdrfn, hdrfline, (p - protostr) + 1, xarg);
523 return -1;
524 }
525#endif
526 arg->reg = NULL;
527
528 ret = get_regparm(regparm, sizeof(regparm), p);
529 if (ret > 0) {
530 p += ret;
531 p = sskip(p);
532
533 arg->reg = strdup(map_reg(regparm));
534 }
535
536 ret = check_struct_arg(arg);
537 if (ret > 0) {
538 pp->has_structarg = 1;
539 arg->type.is_struct = 1;
540 free(arg->type.name);
541 arg->type.name = strdup("int");
542 for (l = 0; l < ret; l++) {
543 pp_copy_arg(&pp->arg[xarg], arg);
544 xarg++;
545 }
546 }
547 }
548
549 if (xarg > 0 && (IS(cconv, "__fastcall") || IS(cconv, "__thiscall"))) {
550 if (pp->arg[0].reg != NULL) {
551 printf("%s:%d: %s with arg1 spec %s?\n",
552 hdrfn, hdrfline, cconv, pp->arg[0].reg);
553 }
554 pp->arg[0].reg = strdup("ecx");
555 }
556
557 if (xarg > 1 && IS(cconv, "__fastcall")) {
558 if (pp->arg[1].reg != NULL) {
559 printf("%s:%d: %s with arg2 spec %s?\n",
560 hdrfn, hdrfline, cconv, pp->arg[1].reg);
561 }
562 pp->arg[1].reg = strdup("edx");
563 }
564
565 pp->argc = xarg;
566
567 for (i = 0; i < pp->argc; i++) {
568 if (pp->arg[i].reg == NULL)
569 pp->argc_stack++;
570 else
571 pp->argc_reg++;
572 }
573
574 if (pp->argc == 1 && pp->arg[0].reg != NULL
575 && IS(pp->arg[0].reg, "ecx"))
576 {
577 pp->is_fastcall = 1;
578 }
579 else if (pp->argc_reg == 2
580 && pp->arg[0].reg != NULL && IS(pp->arg[0].reg, "ecx")
581 && pp->arg[1].reg != NULL && IS(pp->arg[1].reg, "edx"))
582 {
583 pp->is_fastcall = 1;
584 }
585
586 if (pp->is_vararg && (pp->is_stdcall || pp->is_fastcall)) {
587 printf("%s:%d: vararg %s?\n", hdrfn, hdrfline, cconv);
588 return -1;
589 }
590
591 return p - protostr;
592}
593
594static int pp_name_cmp(const void *p1, const void *p2)
595{
596 const struct parsed_proto *pp1 = p1, *pp2 = p2;
597 return strcmp(pp1->name, pp2->name);
598}
599
600static struct parsed_proto *pp_cache;
601static int pp_cache_size;
602static int pp_cache_alloc;
603
604static int b_pp_c_handler(char *proto, const char *fname)
605{
606 int ret;
607
608 if (pp_cache_size >= pp_cache_alloc) {
609 pp_cache_alloc = pp_cache_alloc * 2 + 64;
610 pp_cache = realloc(pp_cache, pp_cache_alloc
611 * sizeof(pp_cache[0]));
612 my_assert_not(pp_cache, NULL);
613 memset(pp_cache + pp_cache_size, 0,
614 (pp_cache_alloc - pp_cache_size)
615 * sizeof(pp_cache[0]));
616 }
617
618 ret = parse_protostr(proto, &pp_cache[pp_cache_size]);
619 if (ret < 0)
620 return -1;
621
622 pp_cache_size++;
623 return 0;
624}
625
626static void build_pp_cache(FILE *fhdr)
627{
628 long pos;
629 int ret;
630
631 pos = ftell(fhdr);
632 rewind(fhdr);
633
634 ret = do_protostrs(fhdr, hdrfn);
635 if (ret < 0)
636 exit(1);
637
638 qsort(pp_cache, pp_cache_size, sizeof(pp_cache[0]), pp_name_cmp);
639 fseek(fhdr, pos, SEEK_SET);
640}
641
642static const struct parsed_proto *proto_parse(FILE *fhdr, const char *sym,
643 int quiet)
644{
645 const struct parsed_proto *pp_ret;
646 struct parsed_proto pp_search;
647
648 if (pp_cache == NULL)
649 build_pp_cache(fhdr);
650
651 if (sym[0] == '_') // && strncmp(fname, "stdc", 4) == 0)
652 sym++;
653
654 strcpy(pp_search.name, sym);
655 pp_ret = bsearch(&pp_search, pp_cache, pp_cache_size,
656 sizeof(pp_cache[0]), pp_name_cmp);
657 if (pp_ret == NULL && !quiet)
658 printf("%s: sym '%s' is missing\n", hdrfn, sym);
659
660 return pp_ret;
661}
662
663static void pp_copy_arg(struct parsed_proto_arg *d,
664 const struct parsed_proto_arg *s)
665{
666 memcpy(d, s, sizeof(*d));
667
668 if (s->reg != NULL) {
669 d->reg = strdup(s->reg);
670 my_assert_not(d->reg, NULL);
671 }
672 if (s->type.name != NULL) {
673 d->type.name = strdup(s->type.name);
674 my_assert_not(d->type.name, NULL);
675 }
676 if (s->fptr != NULL) {
677 d->fptr = malloc(sizeof(*d->fptr));
678 my_assert_not(d->fptr, NULL);
679 memcpy(d->fptr, s->fptr, sizeof(*d->fptr));
680 }
681}
682
683struct parsed_proto *proto_clone(const struct parsed_proto *pp_c)
684{
685 struct parsed_proto *pp;
686 int i;
687
688 pp = malloc(sizeof(*pp));
689 my_assert_not(pp, NULL);
690 memcpy(pp, pp_c, sizeof(*pp)); // lazy..
691
692 // do the actual deep copy..
693 for (i = 0; i < pp_c->argc; i++)
694 pp_copy_arg(&pp->arg[i], &pp_c->arg[i]);
695 if (pp_c->ret_type.name != NULL)
696 pp->ret_type.name = strdup(pp_c->ret_type.name);
697
698 return pp;
699}
700
701static inline void pp_print(char *buf, size_t buf_size,
702 const struct parsed_proto *pp)
703{
704 size_t l;
705 int i;
706
707 snprintf(buf, buf_size, "%s %s(", pp->ret_type.name, pp->name);
708 l = strlen(buf);
709
710 for (i = 0; i < pp->argc_reg; i++) {
711 snprintf(buf + l, buf_size - l, "%s%s",
712 i == 0 ? "" : ", ", pp->arg[i].reg);
713 l = strlen(buf);
714 }
715 if (pp->argc_stack > 0) {
716 snprintf(buf + l, buf_size - l, "%s{%d stack}",
717 i == 0 ? "" : ", ", pp->argc_stack);
718 l = strlen(buf);
719 }
720 snprintf(buf + l, buf_size - l, ")");
721}
722
723static inline void proto_release(struct parsed_proto *pp)
724{
725 int i;
726
727 for (i = 0; i < pp->argc; i++) {
728 if (pp->arg[i].reg != NULL)
729 free(pp->arg[i].reg);
730 if (pp->arg[i].type.name != NULL)
731 free(pp->arg[i].type.name);
732 if (pp->arg[i].fptr != NULL)
733 free(pp->arg[i].fptr);
734 }
735 if (pp->ret_type.name != NULL)
736 free(pp->ret_type.name);
737 free(pp);
738}