improved type handling and cast generation
[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 };
135
136 static const char *ignored_keywords[] = {
137         "extern",
138         "WINBASEAPI",
139         "WINUSERAPI",
140 };
141
142 // returns ptr to char after type ends
143 static int typecmp(const char *n, const char *t)
144 {
145         for (; *t != 0; n++, t++) {
146                 while (n[0] == ' ' && (n[1] == ' ' || n[1] == '*'))
147                         n++;
148                 while (t[0] == ' ' && (t[1] == ' ' || t[1] == '*'))
149                         t++;
150                 if (*n != *t)
151                         return *n - *t;
152         }
153
154         return 0;
155 }
156
157 static const char *skip_type_mod(const char *n)
158 {
159         int len;
160         int i;
161
162         for (i = 0; i < ARRAY_SIZE(known_type_mod); i++) {
163                 len = strlen(known_type_mod[i]);
164                 if (strncmp(n, known_type_mod[i], len) != 0)
165                         continue;
166
167                 n += len;
168                 while (my_isblank(*n))
169                         n++;
170                 i = 0;
171         }
172
173         return n;
174 }
175
176 static int check_type(const char *name, struct parsed_type *type)
177 {
178         const char *n, *n1;
179         int ret = -1;
180         int i;
181
182         n = skip_type_mod(name);
183
184         for (i = 0; i < ARRAY_SIZE(known_ptr_types); i++) {
185                 if (typecmp(n, known_ptr_types[i]))
186                         continue;
187
188                 type->is_ptr = 1;
189                 break;
190         }
191
192         if (n[0] == 'L' && n[1] == 'P')
193                 type->is_ptr = 1;
194
195         // assume single word
196         while (!my_isblank(*n) && !my_issep(*n))
197                 n++;
198
199         while (1) {
200                 n1 = n;
201                 while (my_isblank(*n))
202                         n++;
203                 if (*n == '*') {
204                         type->is_ptr = 1;
205                         n++;
206                         continue;
207                 }
208                 break;
209         }
210
211         ret = n1 - name;
212         type->name = strndup(name, ret);
213         return ret;
214 }
215
216 /* args are always expanded to 32bit */
217 static const char *map_reg(const char *reg)
218 {
219         const char *regs_f[] = { "eax", "ebx", "ecx", "edx", "esi", "edi" };
220         const char *regs_w[] = { "ax",  "bx",  "cx",  "dx",  "si",  "di" };
221         const char *regs_b[] = { "al",  "bl",  "cl",  "dl" };
222         int i;
223
224         for (i = 0; i < ARRAY_SIZE(regs_w); i++)
225                 if (IS(reg, regs_w[i]))
226                         return regs_f[i];
227
228         for (i = 0; i < ARRAY_SIZE(regs_b); i++)
229                 if (IS(reg, regs_b[i]))
230                         return regs_f[i];
231
232         return reg;
233 }
234
235 static int parse_protostr(char *protostr, struct parsed_proto *pp)
236 {
237         struct parsed_proto_arg *arg;
238         char regparm[16];
239         char buf[256];
240         char cconv[32];
241         int xarg = 0;
242         char *p, *p1;
243         int i, l;
244         int ret;
245
246         p = sskip(protostr);
247         if (p[0] == '/' && p[1] == '/') {
248                 printf("%s:%d: commented out?\n", hdrfn, hdrfline);
249                 p = sskip(p + 2);
250         }
251
252         // strip unneeded stuff
253         for (p1 = p; p1[0] != 0 && p1[1] != 0; p1++) {
254                 if ((p1[0] == '/' && p1[1] == '*')
255                  || (p1[0] == '*' && p1[1] == '/'))
256                         p1[0] = p1[1] = ' ';
257         }
258
259         for (i = 0; i < ARRAY_SIZE(ignored_keywords); i++) {
260                 l = strlen(ignored_keywords[i]);
261                 if (!strncmp(p, ignored_keywords[i], l) && my_isblank(p[l]))
262                         p = sskip(p + l + 1);
263         }
264
265         ret = check_type(p, &pp->ret_type);
266         if (ret <= 0) {
267                 printf("%s:%d:%zd: unhandled return in '%s'\n",
268                         hdrfn, hdrfline, (p - protostr) + 1, protostr);
269                 return -1;
270         }
271         p = sskip(p + ret);
272
273         if (!strchr(p, ')')) {
274                 p = next_idt(buf, sizeof(buf), p);
275                 p = sskip(p);
276                 if (buf[0] == 0) {
277                         printf("%s:%d:%zd: var name missing\n",
278                                 hdrfn, hdrfline, (p - protostr) + 1);
279                         return -1;
280                 }
281                 strcpy(pp->name, buf);
282
283                 p1 = strchr(p, ']');
284                 if (p1 != NULL) {
285                         p = p1 + 1;
286                         pp->ret_type.is_array = 1;
287                 }
288                 return p - protostr;
289         }
290
291         pp->is_func = 1;
292
293         if (*p == '(') {
294                 pp->is_fptr = 1;
295                 p = sskip(p + 1);
296         }
297
298         p = next_word(cconv, sizeof(cconv), p);
299         p = sskip(p);
300         if (cconv[0] == 0) {
301                 printf("%s:%d:%zd: cconv missing\n",
302                         hdrfn, hdrfline, (p - protostr) + 1);
303                 return -1;
304         }
305         if      (IS(cconv, "__cdecl"))
306                 pp->is_stdcall = 0;
307         else if (IS(cconv, "__stdcall"))
308                 pp->is_stdcall = 1;
309         else if (IS(cconv, "__fastcall"))
310                 pp->is_stdcall = 1;
311         else if (IS(cconv, "__thiscall"))
312                 pp->is_stdcall = 1;
313         else if (IS(cconv, "__userpurge"))
314                 pp->is_stdcall = 1; // in all cases seen..
315         else if (IS(cconv, "__usercall"))
316                 pp->is_stdcall = 0; // ..or is it?
317         else if (IS(cconv, "WINAPI"))
318                 pp->is_stdcall = 1;
319         else {
320                 printf("%s:%d:%zd: unhandled cconv: '%s'\n",
321                         hdrfn, hdrfline, (p - protostr) + 1, cconv);
322                 return -1;
323         }
324
325         if (pp->is_fptr) {
326                 if (*p != '*') {
327                         printf("%s:%d:%zd: '*' expected\n",
328                                 hdrfn, hdrfline, (p - protostr) + 1);
329                         return -1;
330                 }
331                 p = sskip(p + 1);
332         }
333
334         p = next_idt(buf, sizeof(buf), p);
335         p = sskip(p);
336         if (buf[0] == 0) {
337                 printf("%s:%d:%zd: func name missing\n",
338                         hdrfn, hdrfline, (p - protostr) + 1);
339                 return -1;
340         }
341         strcpy(pp->name, buf);
342
343         ret = get_regparm(regparm, sizeof(regparm), p);
344         if (ret > 0) {
345                 if (!IS(regparm, "eax") && !IS(regparm, "ax")
346                  && !IS(regparm, "al"))
347                 {
348                         printf("%s:%d:%zd: bad regparm: %s\n",
349                                 hdrfn, hdrfline, (p - protostr) + 1, regparm);
350                         return -1;
351                 }
352                 p += ret;
353                 p = sskip(p);
354         }
355
356         if (pp->is_fptr) {
357                 if (*p != ')') {
358                         printf("%s:%d:%zd: ')' expected\n",
359                                 hdrfn, hdrfline, (p - protostr) + 1);
360                         return -1;
361                 }
362                 p = sskip(p + 1);
363         }
364
365         if (*p != '(') {
366                 printf("%s:%d:%zd: '(' expected, got '%c'\n",
367                                 hdrfn, hdrfline, (p - protostr) + 1, *p);
368                 return -1;
369         }
370         p++;
371
372         // check for x(void)
373         p = sskip(p);
374         if ((!strncmp(p, "void", 4) || !strncmp(p, "VOID", 4))
375            && *sskip(p + 4) == ')')
376                 p += 4;
377
378         while (1) {
379                 p = sskip(p);
380                 if (*p == ')') {
381                         p++;
382                         break;
383                 }
384                 if (*p == ',')
385                         p = sskip(p + 1);
386
387                 if (!strncmp(p, "...", 3)) {
388                         pp->is_vararg = 1;
389                         p = sskip(p + 3);
390                         if (*p == ')') {
391                                 p++;
392                                 break;
393                         }
394                         printf("%s:%d:%zd: ')' expected\n",
395                                 hdrfn, hdrfline, (p - protostr) + 1);
396                         return -1;
397                 }
398
399                 arg = &pp->arg[xarg];
400                 xarg++;
401
402                 p1 = p;
403                 ret = check_type(p, &arg->type);
404                 if (ret <= 0) {
405                         printf("%s:%d:%zd: unhandled type for arg%d\n",
406                                 hdrfn, hdrfline, (p - protostr) + 1, xarg);
407                         return -1;
408                 }
409                 p = sskip(p + ret);
410
411                 if (*p == '(') {
412                         // func ptr
413                         arg->fptr = calloc(1, sizeof(*arg->fptr));
414                         ret = parse_protostr(p1, arg->fptr);
415                         if (ret < 0) {
416                                 printf("%s:%d:%zd: funcarg parse failed\n",
417                                         hdrfn, hdrfline, p1 - protostr);
418                                 return -1;
419                         }
420                         // we'll treat it as void * for non-calls
421                         arg->type.name = "void *";
422                         arg->type.is_ptr = 1;
423
424                         p = p1 + ret;
425                 }
426
427                 p = next_idt(buf, sizeof(buf), p);
428                 p = sskip(p);
429 #if 0
430                 if (buf[0] == 0) {
431                         printf("%s:%d:%zd: idt missing for arg%d\n",
432                                 hdrfn, hdrfline, (p - protostr) + 1, xarg);
433                         return -1;
434                 }
435 #endif
436                 arg->reg = NULL;
437
438                 ret = get_regparm(regparm, sizeof(regparm), p);
439                 if (ret > 0) {
440                         p += ret;
441                         p = sskip(p);
442
443                         arg->reg = strdup(map_reg(regparm));
444                 }
445         }
446
447         if (xarg > 0 && (IS(cconv, "__fastcall") || IS(cconv, "__thiscall"))) {
448                 if (pp->arg[0].reg != NULL) {
449                         printf("%s:%d: %s with arg1 spec %s?\n",
450                                 hdrfn, hdrfline, cconv, pp->arg[0].reg);
451                 }
452                 pp->arg[0].reg = strdup("ecx");
453         }
454
455         if (xarg > 1 && IS(cconv, "__fastcall")) {
456                 if (pp->arg[1].reg != NULL) {
457                         printf("%s:%d: %s with arg2 spec %s?\n",
458                                 hdrfn, hdrfline, cconv, pp->arg[1].reg);
459                 }
460                 pp->arg[1].reg = strdup("edx");
461         }
462
463         if (pp->is_vararg && pp->is_stdcall) {
464                 printf("%s:%d: vararg stdcall?\n", hdrfn, hdrfline);
465                 return -1;
466         }
467
468         pp->argc = xarg;
469
470         for (i = 0; i < pp->argc; i++) {
471                 if (pp->arg[i].reg == NULL)
472                         pp->argc_stack++;
473                 else
474                         pp->argc_reg++;
475         }
476
477         return p - protostr;
478 }
479
480 static int proto_parse(FILE *fhdr, const char *sym, struct parsed_proto *pp)
481 {
482         char protostr[256];
483         int ret;
484
485         memset(pp, 0, sizeof(*pp));
486
487         ret = find_protostr(protostr, sizeof(protostr), fhdr, hdrfn, sym);
488         if (ret != 0) {
489                 printf("%s: sym '%s' is missing\n", hdrfn, sym);
490                 return ret;
491         }
492
493         return parse_protostr(protostr, pp) < 0 ? -1 : 0;
494 }
495
496 static void proto_release(struct parsed_proto *pp)
497 {
498         int i;
499
500         for (i = 0; i < pp->argc; i++) {
501                 if (pp->arg[i].reg != NULL)
502                         free(pp->arg[i].reg);
503                 if (pp->arg[i].type.name != NULL)
504                         free(pp->arg[i].type.name);
505                 if (pp->arg[i].fptr != NULL)
506                         free(pp->arg[i].fptr);
507         }
508         if (pp->ret_type.name != NULL)
509                 free(pp->ret_type.name);
510 }