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