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