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