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