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