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