func chunks, improved arg collect, disasm
[ia32rtools.git] / tools / protoparse.h
1
2 struct parsed_proto;
3
4 struct parsed_type {
5         char *name;
6         unsigned int is_array:1;
7         unsigned int is_ptr:1;
8 };
9
10 struct parsed_proto_arg {
11         char *reg;
12         struct parsed_type type;
13         struct parsed_proto *fptr;
14         void *datap;
15 };
16
17 struct parsed_proto {
18         char name[256];
19         union {
20                 struct parsed_type ret_type;
21                 struct parsed_type type;
22         };
23         struct parsed_proto_arg arg[16];
24         int argc;
25         int argc_stack;
26         int argc_reg;
27         unsigned int is_func:1;
28         unsigned int is_stdcall:1;
29         unsigned int is_vararg:1;
30         unsigned int is_fptr:1;
31         unsigned int is_noreturn:1;
32 };
33
34 static const char *hdrfn;
35 static int hdrfline = 0;
36
37 static int find_protostr(char *dst, size_t dlen, FILE *fhdr,
38         const char *fname, const char *sym_)
39 {
40         const char *sym = sym_;
41         const char *finc_name;
42         FILE *finc;
43         int symlen;
44         int line = 0;
45         int ret;
46         char *p;
47
48         if (sym[0] == '_' && strncmp(fname, "stdc", 4) == 0)
49                 sym++;
50         symlen = strlen(sym);
51
52         rewind(fhdr);
53
54         while (fgets(dst, dlen, fhdr))
55         {
56                 line++;
57                 if (strncmp(dst, "//#include ", 11) == 0) {
58                         finc_name = dst + 11;
59                         p = strpbrk(finc_name, "\r\n ");
60                         if (p != NULL)
61                                 *p = 0;
62
63                         finc = fopen(finc_name, "r");
64                         if (finc == NULL) {
65                                 printf("%s:%d: can't open '%s'\n",
66                                         fname, line, finc_name);
67                                 continue;
68                         }
69                         ret = find_protostr(dst, dlen, finc,
70                                 finc_name, sym_);
71                         fclose(finc);
72                         if (ret == 0)
73                                 break;
74                         continue;
75                 }
76                 if (strncmp(sskip(dst), "//", 2) == 0)
77                         continue;
78
79                 p = strstr(dst, sym);
80                 if (p != NULL && p > dst
81                    && (my_isblank(p[-1]) || my_issep(p[-1]))
82                    && (my_isblank(p[symlen]) || my_issep(p[symlen])))
83                         break;
84         }
85         hdrfline = line;
86
87         if (feof(fhdr))
88                 return -1;
89
90         p = dst + strlen(dst);
91         for (p--; p > dst && my_isblank(*p); --p)
92                 *p = 0;
93
94         return 0;
95 }
96
97 static int get_regparm(char *dst, size_t dlen, char *p)
98 {
99         int i, o;
100
101         if (*p != '<')
102                 return 0;
103
104         for (o = 0, i = 1; o < dlen; i++) {
105                 if (p[i] == 0)
106                         return 0;
107                 if (p[i] == '>')
108                         break;
109                 dst[o++] = p[i];
110         }
111         dst[o] = 0;
112         return i + 1;
113 }
114
115 // hmh..
116 static const char *known_type_mod[] = {
117         "const",
118         "signed",
119         "unsigned",
120         "struct",
121         "enum",
122 };
123
124 static const char *known_ptr_types[] = {
125         "HACCEL",
126         "HANDLE",
127         "HBITMAP",
128         "HCURSOR",
129         "HDC",
130         "HGDIOBJ",
131         "HGLOBAL",
132         "HINSTANCE",
133         "HMODULE",
134         "HRGN",
135         "HRSRC",
136         "HKEY",
137         "HMENU",
138         "HWND",
139         "PLONG",
140         "PDWORD",
141         "PVOID",
142         "PCVOID",
143         "DLGPROC",
144         "va_list",
145         "__VALIST",
146 };
147
148 static const char *ignored_keywords[] = {
149         "extern",
150         "WINBASEAPI",
151         "WINUSERAPI",
152         "WINGDIAPI",
153         "WINADVAPI",
154 };
155
156 // returns ptr to char after type ends
157 static int typecmp(const char *n, const char *t)
158 {
159         for (; *t != 0; n++, t++) {
160                 while (n[0] == ' ' && (n[1] == ' ' || n[1] == '*'))
161                         n++;
162                 while (t[0] == ' ' && (t[1] == ' ' || t[1] == '*'))
163                         t++;
164                 if (*n != *t)
165                         return *n - *t;
166         }
167
168         return 0;
169 }
170
171 static const char *skip_type_mod(const char *n)
172 {
173         int len;
174         int i;
175
176         for (i = 0; i < ARRAY_SIZE(known_type_mod); i++) {
177                 len = strlen(known_type_mod[i]);
178                 if (strncmp(n, known_type_mod[i], len) != 0)
179                         continue;
180
181                 n += len;
182                 while (my_isblank(*n))
183                         n++;
184                 i = 0;
185         }
186
187         return n;
188 }
189
190 static int check_type(const char *name, struct parsed_type *type)
191 {
192         const char *n, *n1;
193         int ret = -1;
194         int i;
195
196         n = skip_type_mod(name);
197
198         for (i = 0; i < ARRAY_SIZE(known_ptr_types); i++) {
199                 if (typecmp(n, known_ptr_types[i]))
200                         continue;
201
202                 type->is_ptr = 1;
203                 break;
204         }
205
206         if (n[0] == 'L' && n[1] == 'P')
207                 type->is_ptr = 1;
208
209         // assume single word
210         while (!my_isblank(*n) && !my_issep(*n))
211                 n++;
212
213         while (1) {
214                 n1 = n;
215                 while (my_isblank(*n))
216                         n++;
217                 if (*n == '*') {
218                         type->is_ptr = 1;
219                         n++;
220                         continue;
221                 }
222                 break;
223         }
224
225         ret = n1 - name;
226         type->name = strndup(name, ret);
227         return ret;
228 }
229
230 /* args are always expanded to 32bit */
231 static const char *map_reg(const char *reg)
232 {
233         const char *regs_f[] = { "eax", "ebx", "ecx", "edx", "esi", "edi" };
234         const char *regs_w[] = { "ax",  "bx",  "cx",  "dx",  "si",  "di" };
235         const char *regs_b[] = { "al",  "bl",  "cl",  "dl" };
236         int i;
237
238         for (i = 0; i < ARRAY_SIZE(regs_w); i++)
239                 if (IS(reg, regs_w[i]))
240                         return regs_f[i];
241
242         for (i = 0; i < ARRAY_SIZE(regs_b); i++)
243                 if (IS(reg, regs_b[i]))
244                         return regs_f[i];
245
246         return reg;
247 }
248
249 static int parse_protostr(char *protostr, struct parsed_proto *pp)
250 {
251         struct parsed_proto_arg *arg;
252         char regparm[16];
253         char buf[256];
254         char cconv[32];
255         int xarg = 0;
256         char *p, *p1;
257         int i, l;
258         int ret;
259
260         p = sskip(protostr);
261         if (p[0] == '/' && p[1] == '/') {
262                 printf("%s:%d: commented out?\n", hdrfn, hdrfline);
263                 p = sskip(p + 2);
264         }
265
266         // strip unneeded stuff
267         for (p1 = p; p1[0] != 0 && p1[1] != 0; p1++) {
268                 if ((p1[0] == '/' && p1[1] == '*')
269                  || (p1[0] == '*' && p1[1] == '/'))
270                         p1[0] = p1[1] = ' ';
271         }
272
273         if (!strncmp(p, "DECLSPEC_NORETURN ", 18)) {
274                 pp->is_noreturn = 1;
275                 p = sskip(p + 18);
276         }
277
278         for (i = 0; i < ARRAY_SIZE(ignored_keywords); i++) {
279                 l = strlen(ignored_keywords[i]);
280                 if (!strncmp(p, ignored_keywords[i], l) && my_isblank(p[l]))
281                         p = sskip(p + l + 1);
282         }
283
284         ret = check_type(p, &pp->ret_type);
285         if (ret <= 0) {
286                 printf("%s:%d:%zd: unhandled return in '%s'\n",
287                         hdrfn, hdrfline, (p - protostr) + 1, protostr);
288                 return -1;
289         }
290         p = sskip(p + ret);
291
292         if (!strchr(p, ')')) {
293                 p = next_idt(buf, sizeof(buf), p);
294                 p = sskip(p);
295                 if (buf[0] == 0) {
296                         printf("%s:%d:%zd: var name missing\n",
297                                 hdrfn, hdrfline, (p - protostr) + 1);
298                         return -1;
299                 }
300                 strcpy(pp->name, buf);
301
302                 p1 = strchr(p, ']');
303                 if (p1 != NULL) {
304                         p = p1 + 1;
305                         pp->ret_type.is_array = 1;
306                 }
307                 return p - protostr;
308         }
309
310         pp->is_func = 1;
311
312         if (*p == '(') {
313                 pp->is_fptr = 1;
314                 p = sskip(p + 1);
315         }
316
317         p = next_word(cconv, sizeof(cconv), p);
318         p = sskip(p);
319         if (cconv[0] == 0) {
320                 printf("%s:%d:%zd: cconv missing\n",
321                         hdrfn, hdrfline, (p - protostr) + 1);
322                 return -1;
323         }
324         if      (IS(cconv, "__cdecl"))
325                 pp->is_stdcall = 0;
326         else if (IS(cconv, "__stdcall"))
327                 pp->is_stdcall = 1;
328         else if (IS(cconv, "__fastcall"))
329                 pp->is_stdcall = 1;
330         else if (IS(cconv, "__thiscall"))
331                 pp->is_stdcall = 1;
332         else if (IS(cconv, "__userpurge"))
333                 pp->is_stdcall = 1; // in all cases seen..
334         else if (IS(cconv, "__usercall"))
335                 pp->is_stdcall = 0; // ..or is it?
336         else if (IS(cconv, "WINAPI"))
337                 pp->is_stdcall = 1;
338         else {
339                 printf("%s:%d:%zd: unhandled cconv: '%s'\n",
340                         hdrfn, hdrfline, (p - protostr) + 1, cconv);
341                 return -1;
342         }
343
344         if (pp->is_fptr) {
345                 if (*p != '*') {
346                         printf("%s:%d:%zd: '*' expected\n",
347                                 hdrfn, hdrfline, (p - protostr) + 1);
348                         return -1;
349                 }
350                 p = sskip(p + 1);
351         }
352
353         p = next_idt(buf, sizeof(buf), p);
354         p = sskip(p);
355         if (buf[0] == 0) {
356                 printf("%s:%d:%zd: func name missing\n",
357                         hdrfn, hdrfline, (p - protostr) + 1);
358                 return -1;
359         }
360         strcpy(pp->name, buf);
361
362         ret = get_regparm(regparm, sizeof(regparm), p);
363         if (ret > 0) {
364                 if (!IS(regparm, "eax") && !IS(regparm, "ax")
365                  && !IS(regparm, "al"))
366                 {
367                         printf("%s:%d:%zd: bad regparm: %s\n",
368                                 hdrfn, hdrfline, (p - protostr) + 1, regparm);
369                         return -1;
370                 }
371                 p += ret;
372                 p = sskip(p);
373         }
374
375         if (pp->is_fptr) {
376                 if (*p != ')') {
377                         printf("%s:%d:%zd: ')' expected\n",
378                                 hdrfn, hdrfline, (p - protostr) + 1);
379                         return -1;
380                 }
381                 p = sskip(p + 1);
382         }
383
384         if (*p != '(') {
385                 printf("%s:%d:%zd: '(' expected, got '%c'\n",
386                                 hdrfn, hdrfline, (p - protostr) + 1, *p);
387                 return -1;
388         }
389         p++;
390
391         // check for x(void)
392         p = sskip(p);
393         if ((!strncmp(p, "void", 4) || !strncmp(p, "VOID", 4))
394            && *sskip(p + 4) == ')')
395                 p += 4;
396
397         while (1) {
398                 p = sskip(p);
399                 if (*p == ')') {
400                         p++;
401                         break;
402                 }
403                 if (*p == ',')
404                         p = sskip(p + 1);
405
406                 if (!strncmp(p, "...", 3)) {
407                         pp->is_vararg = 1;
408                         p = sskip(p + 3);
409                         if (*p == ')') {
410                                 p++;
411                                 break;
412                         }
413                         printf("%s:%d:%zd: ')' expected\n",
414                                 hdrfn, hdrfline, (p - protostr) + 1);
415                         return -1;
416                 }
417
418                 arg = &pp->arg[xarg];
419                 xarg++;
420
421                 p1 = p;
422                 ret = check_type(p, &arg->type);
423                 if (ret <= 0) {
424                         printf("%s:%d:%zd: unhandled type for arg%d\n",
425                                 hdrfn, hdrfline, (p - protostr) + 1, xarg);
426                         return -1;
427                 }
428                 p = sskip(p + ret);
429
430                 if (*p == '(') {
431                         // func ptr
432                         arg->fptr = calloc(1, sizeof(*arg->fptr));
433                         ret = parse_protostr(p1, arg->fptr);
434                         if (ret < 0) {
435                                 printf("%s:%d:%zd: funcarg parse failed\n",
436                                         hdrfn, hdrfline, p1 - protostr);
437                                 return -1;
438                         }
439                         // we'll treat it as void * for non-calls
440                         arg->type.name = "void *";
441                         arg->type.is_ptr = 1;
442
443                         p = p1 + ret;
444                 }
445
446                 p = next_idt(buf, sizeof(buf), p);
447                 p = sskip(p);
448 #if 0
449                 if (buf[0] == 0) {
450                         printf("%s:%d:%zd: idt missing for arg%d\n",
451                                 hdrfn, hdrfline, (p - protostr) + 1, xarg);
452                         return -1;
453                 }
454 #endif
455                 arg->reg = NULL;
456
457                 ret = get_regparm(regparm, sizeof(regparm), p);
458                 if (ret > 0) {
459                         p += ret;
460                         p = sskip(p);
461
462                         arg->reg = strdup(map_reg(regparm));
463                 }
464         }
465
466         if (xarg > 0 && (IS(cconv, "__fastcall") || IS(cconv, "__thiscall"))) {
467                 if (pp->arg[0].reg != NULL) {
468                         printf("%s:%d: %s with arg1 spec %s?\n",
469                                 hdrfn, hdrfline, cconv, pp->arg[0].reg);
470                 }
471                 pp->arg[0].reg = strdup("ecx");
472         }
473
474         if (xarg > 1 && IS(cconv, "__fastcall")) {
475                 if (pp->arg[1].reg != NULL) {
476                         printf("%s:%d: %s with arg2 spec %s?\n",
477                                 hdrfn, hdrfline, cconv, pp->arg[1].reg);
478                 }
479                 pp->arg[1].reg = strdup("edx");
480         }
481
482         if (pp->is_vararg && pp->is_stdcall) {
483                 printf("%s:%d: vararg stdcall?\n", hdrfn, hdrfline);
484                 return -1;
485         }
486
487         pp->argc = xarg;
488
489         for (i = 0; i < pp->argc; i++) {
490                 if (pp->arg[i].reg == NULL)
491                         pp->argc_stack++;
492                 else
493                         pp->argc_reg++;
494         }
495
496         return p - protostr;
497 }
498
499 static int proto_parse(FILE *fhdr, const char *sym, struct parsed_proto *pp)
500 {
501         char protostr[256];
502         int ret;
503
504         memset(pp, 0, sizeof(*pp));
505
506         ret = find_protostr(protostr, sizeof(protostr), fhdr, hdrfn, sym);
507         if (ret != 0) {
508                 printf("%s: sym '%s' is missing\n", hdrfn, sym);
509                 return ret;
510         }
511
512         return parse_protostr(protostr, pp) < 0 ? -1 : 0;
513 }
514
515 static void proto_release(struct parsed_proto *pp)
516 {
517         int i;
518
519         for (i = 0; i < pp->argc; i++) {
520                 if (pp->arg[i].reg != NULL)
521                         free(pp->arg[i].reg);
522                 if (pp->arg[i].type.name != NULL)
523                         free(pp->arg[i].type.name);
524                 if (pp->arg[i].fptr != NULL)
525                         free(pp->arg[i].fptr);
526         }
527         if (pp->ret_type.name != NULL)
528                 free(pp->ret_type.name);
529 }