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