win32.hlist, more types, fixes
[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_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_type_mod[] = {
107         "const",
108         "signed",
109         "unsigned",
110 };
111
112 static const char *known_types[] = {
113         "void *",
114         "char *",
115         "FILE *",
116         "int *",
117         "__int8 *",
118         "__int16 *",
119         "char",
120         "__int8",
121         "__int16",
122         "__int32",
123         "int",
124         "bool",
125         "void",
126         "BYTE",
127         "WORD",
128         "BOOL",
129         "DWORD",
130         "_DWORD",
131         "HMODULE",
132         "HANDLE",
133         "HWND",
134         "LANGID",
135         "LPCSTR",
136         "size_t",
137 };
138
139 // returns ptr to char after type ends
140 static const char *typecmp(const char *n, const char *t)
141 {
142         for (; *t != 0; n++, t++) {
143                 while (n[0] == ' ' && (n[1] == ' ' || n[1] == '*'))
144                         n++;
145                 while (t[0] == ' ' && (t[1] == ' ' || t[1] == '*'))
146                         t++;
147                 if (*n != *t)
148                         return NULL;
149         }
150
151         return n;
152 }
153
154 static char *check_type(const char *name, int *len_out)
155 {
156         const char *n = name, *n1;
157         int len;
158         int i;
159
160         *len_out = 0;
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         for (i = 0; i < ARRAY_SIZE(known_types); i++) {
174                 n1 = typecmp(n, known_types[i]);
175                 if (n1 == NULL)
176                         continue;
177
178                 *len_out = n1 - name;
179                 return strndup(name, *len_out);
180         }
181
182         return NULL;
183 }
184
185 /* args are always expanded to 32bit */
186 static const char *map_reg(const char *reg)
187 {
188         const char *regs_f[] = { "eax", "ebx", "ecx", "edx", "esi", "edi" };
189         const char *regs_w[] = { "ax",  "bx",  "cx",  "dx",  "si",  "di" };
190         const char *regs_b[] = { "al",  "bl",  "cl",  "dl" };
191         int i;
192
193         for (i = 0; i < ARRAY_SIZE(regs_w); i++)
194                 if (IS(reg, regs_w[i]))
195                         return regs_f[i];
196
197         for (i = 0; i < ARRAY_SIZE(regs_b); i++)
198                 if (IS(reg, regs_b[i]))
199                         return regs_f[i];
200
201         return reg;
202 }
203
204 static int parse_protostr(char *protostr, struct parsed_proto *pp)
205 {
206         struct parsed_proto_arg *arg;
207         char regparm[16];
208         char buf[256];
209         char cconv[32];
210         char *kt;
211         int xarg = 0;
212         char *p, *p1;
213         int ret;
214         int i;
215
216         p = sskip(protostr);
217         if (p[0] == '/' && p[1] == '/') {
218                 printf("%s:%d: commented out?\n", hdrfn, hdrfline);
219                 p = sskip(p + 2);
220         }
221
222         // strip unneeded stuff
223         for (p1 = p; p1[0] != 0 && p1[1] != 0; p1++) {
224                 if ((p1[0] == '/' && p1[1] == '*')
225                  || (p1[0] == '*' && p1[1] == '/'))
226                         p1[0] = p1[1] = ' ';
227         }
228
229         if (strncmp(p, "extern ", 7) == 0)
230                 p = sskip(p + 7);
231         if (strncmp(p, "WINBASEAPI ", 11) == 0)
232                 p = sskip(p + 11);
233
234         kt = check_type(p, &ret);
235         if (kt == NULL) {
236                 printf("%s:%d:%ld: unhandled return in '%s'\n",
237                         hdrfn, hdrfline, (p - protostr) + 1, protostr);
238                 return -1;
239         }
240         pp->ret_type = kt;
241         p = sskip(p + ret);
242
243         if (!strchr(p, ')')) {
244                 p = next_idt(buf, sizeof(buf), p);
245                 p = sskip(p);
246                 if (buf[0] == 0) {
247                         printf("%s:%d:%ld: var name missing\n",
248                                 hdrfn, hdrfline, (p - protostr) + 1);
249                         return -1;
250                 }
251                 strcpy(pp->name, buf);
252
253                 p1 = strchr(p, ']');
254                 if (p1 != NULL) {
255                         p = p1 + 1;
256                         pp->is_array = 1;
257                 }
258                 return p - protostr;
259         }
260
261         pp->is_func = 1;
262
263         if (*p == '(') {
264                 pp->is_fptr = 1;
265                 p = sskip(p + 1);
266         }
267
268         p = next_word(cconv, sizeof(cconv), p);
269         p = sskip(p);
270         if (cconv[0] == 0) {
271                 printf("%s:%d:%ld: cconv missing\n",
272                         hdrfn, hdrfline, (p - protostr) + 1);
273                 return -1;
274         }
275         if      (IS(cconv, "__cdecl"))
276                 pp->is_stdcall = 0;
277         else if (IS(cconv, "__stdcall"))
278                 pp->is_stdcall = 1;
279         else if (IS(cconv, "__fastcall"))
280                 pp->is_stdcall = 1;
281         else if (IS(cconv, "__thiscall"))
282                 pp->is_stdcall = 1;
283         else if (IS(cconv, "__userpurge"))
284                 pp->is_stdcall = 1; // in all cases seen..
285         else if (IS(cconv, "__usercall"))
286                 pp->is_stdcall = 0; // ..or is it?
287         else if (IS(cconv, "WINAPI"))
288                 pp->is_stdcall = 1;
289         else {
290                 printf("%s:%d:%ld: unhandled cconv: '%s'\n",
291                         hdrfn, hdrfline, (p - protostr) + 1, cconv);
292                 return -1;
293         }
294
295         if (pp->is_fptr) {
296                 if (*p != '*') {
297                         printf("%s:%d:%ld: '*' expected\n",
298                                 hdrfn, hdrfline, (p - protostr) + 1);
299                         return -1;
300                 }
301                 p = sskip(p + 1);
302         }
303
304         p = next_idt(buf, sizeof(buf), p);
305         p = sskip(p);
306         if (buf[0] == 0) {
307                 printf("%s:%d:%ld: func name missing\n",
308                         hdrfn, hdrfline, (p - protostr) + 1);
309                 return -1;
310         }
311         strcpy(pp->name, buf);
312
313         ret = get_regparm(regparm, sizeof(regparm), p);
314         if (ret > 0) {
315                 if (!IS(regparm, "eax") && !IS(regparm, "ax")
316                  && !IS(regparm, "al"))
317                 {
318                         printf("%s:%d:%ld: bad regparm: %s\n",
319                                 hdrfn, hdrfline, (p - protostr) + 1, regparm);
320                         return -1;
321                 }
322                 p += ret;
323                 p = sskip(p);
324         }
325
326         if (pp->is_fptr) {
327                 if (*p != ')') {
328                         printf("%s:%d:%ld: ')' expected\n",
329                                 hdrfn, hdrfline, (p - protostr) + 1);
330                         return -1;
331                 }
332                 p = sskip(p + 1);
333         }
334
335         if (*p != '(') {
336                 printf("%s:%d:%ld: '(' expected, got '%c'\n",
337                                 hdrfn, hdrfline, (p - protostr) + 1, *p);
338                 return -1;
339         }
340         p++;
341
342         // check for x(void)
343         p = sskip(p);
344         if ((!strncmp(p, "void", 4) || !strncmp(p, "VOID", 4))
345            && *sskip(p + 4) == ')')
346                 p += 4;
347
348         while (1) {
349                 p = sskip(p);
350                 if (*p == ')') {
351                         p++;
352                         break;
353                 }
354                 if (*p == ',')
355                         p = sskip(p + 1);
356
357                 arg = &pp->arg[xarg];
358                 xarg++;
359
360                 p1 = p;
361                 kt = check_type(p, &ret);
362                 if (kt == NULL) {
363                         printf("%s:%d:%ld: unhandled type for arg%d\n",
364                                 hdrfn, hdrfline, (p - protostr) + 1, xarg);
365                         return -1;
366                 }
367                 arg->type = kt;
368                 p = sskip(p + ret);
369
370                 if (*p == '(') {
371                         // func ptr
372                         arg->fptr = calloc(1, sizeof(*arg->fptr));
373                         ret = parse_protostr(p1, arg->fptr);
374                         if (ret < 0) {
375                                 printf("%s:%d:%ld: funcarg parse failed\n",
376                                         hdrfn, hdrfline, p1 - protostr);
377                                 return -1;
378                         }
379                         // we'll treat it as void * for non-calls
380                         arg->type = "void *";
381
382                         p = p1 + ret;
383                 }
384
385                 p = next_idt(buf, sizeof(buf), p);
386                 p = sskip(p);
387 #if 0
388                 if (buf[0] == 0) {
389                         printf("%s:%d:%ld: idt missing for arg%d\n",
390                                 hdrfn, hdrfline, (p - protostr) + 1, xarg);
391                         return -1;
392                 }
393 #endif
394                 arg->reg = NULL;
395
396                 ret = get_regparm(regparm, sizeof(regparm), p);
397                 if (ret > 0) {
398                         p += ret;
399                         p = sskip(p);
400
401                         arg->reg = strdup(map_reg(regparm));
402                 }
403         }
404
405         if (xarg > 0 && (IS(cconv, "__fastcall") || IS(cconv, "__thiscall"))) {
406                 if (pp->arg[0].reg != NULL) {
407                         printf("%s:%d: %s with arg1 spec %s?\n",
408                                 hdrfn, hdrfline, cconv, pp->arg[0].reg);
409                 }
410                 pp->arg[0].reg = strdup("ecx");
411         }
412
413         if (xarg > 1 && IS(cconv, "__fastcall")) {
414                 if (pp->arg[1].reg != NULL) {
415                         printf("%s:%d: %s with arg2 spec %s?\n",
416                                 hdrfn, hdrfline, cconv, pp->arg[1].reg);
417                 }
418                 pp->arg[1].reg = strdup("edx");
419         }
420
421         pp->argc = xarg;
422
423         for (i = 0; i < pp->argc; i++) {
424                 if (pp->arg[i].reg == NULL)
425                         pp->argc_stack++;
426                 else
427                         pp->argc_reg++;
428         }
429
430         return p - protostr;
431 }
432
433 static int proto_parse(FILE *fhdr, const char *sym, struct parsed_proto *pp)
434 {
435         char protostr[256];
436         int ret;
437
438         memset(pp, 0, sizeof(*pp));
439
440         ret = find_protostr(protostr, sizeof(protostr), fhdr, hdrfn, sym);
441         if (ret != 0) {
442                 printf("%s: sym '%s' is missing\n", hdrfn, sym);
443                 return ret;
444         }
445
446         return parse_protostr(protostr, pp) < 0 ? -1 : 0;
447 }
448
449 static void proto_release(struct parsed_proto *pp)
450 {
451         int i;
452
453         for (i = 0; i < pp->argc; i++) {
454                 if (pp->arg[i].reg != NULL)
455                         free(pp->arg[i].reg);
456                 if (pp->arg[i].type != NULL)
457                         free(pp->arg[i].type);
458                 if (pp->arg[i].fptr != NULL)
459                         free(pp->arg[i].fptr);
460         }
461         if (pp->ret_type != NULL)
462                 free(pp->ret_type);
463 }