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