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