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