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