protoparse: improve ret guessing
[ia32rtools.git] / tools / protoparse.h
... / ...
CommitLineData
1/*
2 * ia32rtools
3 * (C) notaz, 2013,2014
4 *
5 * This work is licensed under the terms of 3-clause BSD license.
6 * See COPYING file in the top-level directory.
7 */
8
9struct parsed_proto;
10
11struct parsed_type {
12 char *name;
13 unsigned int is_array:1;
14 unsigned int is_ptr:1;
15 unsigned int is_struct:1; // split for args
16 unsigned int is_retreg:1; // register to return to caller
17 unsigned int is_va_list:1;
18};
19
20struct parsed_proto_arg {
21 char *reg;
22 struct parsed_type type;
23 struct parsed_proto *fptr;
24 void *datap;
25};
26
27struct parsed_proto {
28 char name[256];
29 union {
30 struct parsed_type ret_type;
31 struct parsed_type type;
32 };
33 struct parsed_proto_arg arg[16];
34 int argc;
35 int argc_stack;
36 int argc_reg;
37 unsigned int is_func:1;
38 unsigned int is_stdcall:1;
39 unsigned int is_fastcall:1;
40 unsigned int is_vararg:1; // vararg func
41 unsigned int is_fptr:1;
42 unsigned int is_noreturn:1;
43 unsigned int is_unresolved:1;
44 unsigned int is_userstack:1;
45 unsigned int is_include:1; // not from top-level header
46 unsigned int is_osinc:1; // OS/system library func
47 unsigned int is_cinc:1; // crt library func
48 unsigned int is_arg:1; // declared in some func arg
49 unsigned int has_structarg:1;
50 unsigned int has_retreg:1;
51};
52
53static const char *hdrfn;
54static int hdrfline = 0;
55
56static void pp_copy_arg(struct parsed_proto_arg *d,
57 const struct parsed_proto_arg *s);
58
59static int b_pp_c_handler(char *proto, const char *fname,
60 int is_include, int is_osinc, int is_cinc);
61
62static int do_protostrs(FILE *fhdr, const char *fname, int is_include)
63{
64 const char *finc_name;
65 const char *hdrfn_saved;
66 char protostr[256];
67 char path[256];
68 char fname_inc[256];
69 int is_osinc;
70 int is_cinc;
71 FILE *finc;
72 int line = 0;
73 int ret;
74 char *p;
75
76 hdrfn_saved = hdrfn;
77 hdrfn = fname;
78
79 is_cinc = strstr(fname, "stdc.hlist") != NULL;
80 is_osinc = is_cinc || strstr(fname, "stdc.hlist") != NULL;
81
82 while (fgets(protostr, sizeof(protostr), fhdr))
83 {
84 line++;
85 if (strncmp(protostr, "//#include ", 11) == 0) {
86 finc_name = protostr + 11;
87 p = strpbrk(finc_name, "\r\n ");
88 if (p != NULL)
89 *p = 0;
90
91 path[0] = 0;
92 p = strrchr(hdrfn_saved, '/');
93 if (p) {
94 memcpy(path, hdrfn_saved,
95 p - hdrfn_saved + 1);
96 path[p - hdrfn_saved + 1] = 0;
97 }
98 snprintf(fname_inc, sizeof(fname_inc), "%s%s",
99 path, finc_name);
100 finc = fopen(fname_inc, "r");
101 if (finc == NULL) {
102 printf("%s:%d: can't open '%s'\n",
103 fname_inc, line, finc_name);
104 continue;
105 }
106 ret = do_protostrs(finc, finc_name, 1);
107 fclose(finc);
108 if (ret < 0)
109 break;
110 continue;
111 }
112 if (strncmp(sskip(protostr), "//", 2) == 0)
113 continue;
114
115 p = protostr + strlen(protostr);
116 for (p--; p >= protostr && my_isblank(*p); --p)
117 *p = 0;
118 if (p < protostr)
119 continue;
120
121 hdrfline = line;
122
123 ret = b_pp_c_handler(protostr, hdrfn, is_include,
124 is_osinc, is_cinc);
125 if (ret < 0)
126 break;
127 }
128
129 hdrfn = hdrfn_saved;
130
131 if (feof(fhdr))
132 return 0;
133
134 return -1;
135}
136
137static int get_regparm(char *dst, size_t dlen, char *p, int *retreg)
138{
139 int i = 0, o;
140
141 *retreg = 0;
142
143 if (*p != '<')
144 return 0;
145
146 i++;
147 if (p[i] == '*') {
148 *retreg = 1;
149 i++;
150 }
151
152 for (o = 0; o < dlen; i++) {
153 if (p[i] == 0)
154 return 0;
155 if (p[i] == '>')
156 break;
157 dst[o++] = p[i];
158 }
159 dst[o] = 0;
160 return i + 1;
161}
162
163// hmh..
164static const char *known_type_mod[] = {
165 "const",
166 "signed",
167 "unsigned",
168 "struct",
169 "enum",
170 "CONST",
171 "volatile",
172};
173
174static const char *known_ptr_types[] = {
175 "FARPROC",
176 "WNDPROC",
177 "LINECALLBACK",
178 "HACCEL",
179 "HANDLE",
180 "HBITMAP",
181 "HCALL",
182 "HCURSOR",
183 "HDC",
184 "HFONT",
185 "HGDIOBJ",
186 "HGLOBAL",
187 "HICON",
188 "HINSTANCE",
189 "HIMC", // DWORD in mingw, ptr in wine..
190 "HLINE",
191 "HLINEAPP",
192 "HLOCAL",
193 "HMODULE",
194 "HPALETTE",
195 "HRGN",
196 "HRSRC",
197 "HKEY",
198 "HMENU",
199 "HWAVEOUT",
200 "HWND",
201 "PBYTE",
202 "PCRITICAL_SECTION",
203 "PDWORD",
204 "PFILETIME",
205 "PLARGE_INTEGER",
206 "PHKEY",
207 "PLONG",
208 "PMEMORY_BASIC_INFORMATION",
209 "PUINT",
210 "PVOID",
211 "PCVOID",
212 "PWORD",
213 "DLGPROC",
214 "TIMERPROC",
215 "WNDENUMPROC",
216 "va_list",
217 "__VALIST",
218};
219
220static const char *ignored_keywords[] = {
221 "extern",
222 "WINBASEAPI",
223 "WINUSERAPI",
224 "WINGDIAPI",
225 "WINADVAPI",
226};
227
228// returns ptr to char after type ends
229static int typecmp(const char *n, const char *t)
230{
231 for (; *t != 0; n++, t++) {
232 while (n[0] == ' ' && (n[1] == ' ' || n[1] == '*'))
233 n++;
234 while (t[0] == ' ' && (t[1] == ' ' || t[1] == '*'))
235 t++;
236 if (*n != *t)
237 return *n - *t;
238 }
239
240 return 0;
241}
242
243static const char *skip_type_mod(const char *n)
244{
245 int len;
246 int i;
247
248 for (i = 0; i < ARRAY_SIZE(known_type_mod); i++) {
249 len = strlen(known_type_mod[i]);
250 if (strncmp(n, known_type_mod[i], len) != 0)
251 continue;
252 if (!my_isblank(n[len]))
253 continue;
254
255 n += len;
256 while (my_isblank(*n))
257 n++;
258 i = 0;
259 }
260
261 return n;
262}
263
264static int check_type(const char *name, struct parsed_type *type)
265{
266 const char *n, *n1;
267 int ret = -1;
268 int i;
269
270 n = skip_type_mod(name);
271
272 for (i = 0; i < ARRAY_SIZE(known_ptr_types); i++) {
273 if (typecmp(n, known_ptr_types[i]))
274 continue;
275
276 type->is_ptr = 1;
277 break;
278 }
279
280 if (n[0] == 'L' && n[1] == 'P' && strncmp(n, "LPARAM", 6))
281 type->is_ptr = 1;
282
283 // assume single word
284 while (!my_isblank(*n) && !my_issep(*n))
285 n++;
286
287 while (1) {
288 n1 = n;
289 while (my_isblank(*n))
290 n++;
291 if (*n == '*') {
292 type->is_ptr = 1;
293 n++;
294 continue;
295 }
296 break;
297 }
298
299 ret = n1 - name;
300 type->name = strndup(name, ret);
301 if (IS(type->name, "__VALIST") || IS(type->name, "va_list"))
302 type->is_va_list = 1;
303 if (IS(type->name, "VOID"))
304 memcpy(type->name, "void", 4);
305
306 return ret;
307}
308
309/* args are always expanded to 32bit */
310static const char *map_reg(const char *reg)
311{
312 const char *regs_f[] = { "eax", "ebx", "ecx", "edx", "esi", "edi" };
313 const char *regs_w[] = { "ax", "bx", "cx", "dx", "si", "di" };
314 const char *regs_b[] = { "al", "bl", "cl", "dl" };
315 int i;
316
317 for (i = 0; i < ARRAY_SIZE(regs_w); i++)
318 if (IS(reg, regs_w[i]))
319 return regs_f[i];
320
321 for (i = 0; i < ARRAY_SIZE(regs_b); i++)
322 if (IS(reg, regs_b[i]))
323 return regs_f[i];
324
325 return reg;
326}
327
328static int check_struct_arg(struct parsed_proto_arg *arg)
329{
330 if (IS(arg->type.name, "POINT"))
331 return 2 - 1;
332
333 return 0;
334}
335
336static int parse_protostr(char *protostr, struct parsed_proto *pp)
337{
338 struct parsed_proto_arg *arg;
339 char regparm[16];
340 char buf[256];
341 char cconv[32];
342 int is_retreg;
343 int xarg = 0;
344 char *p, *p1;
345 int i, l;
346 int ret;
347
348 p = sskip(protostr);
349 if (p[0] == '/' && p[1] == '/') {
350 printf("%s:%d: commented out?\n", hdrfn, hdrfline);
351 p = sskip(p + 2);
352 }
353
354 // strip unneeded stuff
355 for (p1 = p; p1[0] != 0 && p1[1] != 0; p1++) {
356 if ((p1[0] == '/' && p1[1] == '*')
357 || (p1[0] == '*' && p1[1] == '/'))
358 p1[0] = p1[1] = ' ';
359 }
360
361 if (!strncmp(p, "DECLSPEC_NORETURN ", 18)) {
362 pp->is_noreturn = 1;
363 p = sskip(p + 18);
364 }
365
366 for (i = 0; i < ARRAY_SIZE(ignored_keywords); i++) {
367 l = strlen(ignored_keywords[i]);
368 if (!strncmp(p, ignored_keywords[i], l) && my_isblank(p[l]))
369 p = sskip(p + l + 1);
370 }
371
372 ret = check_type(p, &pp->ret_type);
373 if (ret <= 0) {
374 printf("%s:%d:%zd: unhandled return in '%s'\n",
375 hdrfn, hdrfline, (p - protostr) + 1, protostr);
376 return -1;
377 }
378 p = sskip(p + ret);
379
380 if (!strncmp(p, "noreturn ", 9)) {
381 pp->is_noreturn = 1;
382 p = sskip(p + 9);
383 }
384
385 if (!strchr(p, ')')) {
386 p = next_idt(buf, sizeof(buf), p);
387 p = sskip(p);
388 if (buf[0] == 0) {
389 printf("%s:%d:%zd: var name missing\n",
390 hdrfn, hdrfline, (p - protostr) + 1);
391 return -1;
392 }
393 strcpy(pp->name, buf);
394
395 p1 = strchr(p, ']');
396 if (p1 != NULL) {
397 p = p1 + 1;
398 pp->ret_type.is_array = 1;
399 }
400 return p - protostr;
401 }
402
403 pp->is_func = 1;
404
405 if (*p == '(') {
406 pp->is_fptr = 1;
407 p = sskip(p + 1);
408 }
409
410 p = next_word(cconv, sizeof(cconv), p);
411 p = sskip(p);
412 if (cconv[0] == 0) {
413 printf("%s:%d:%zd: cconv missing\n",
414 hdrfn, hdrfline, (p - protostr) + 1);
415 return -1;
416 }
417 if (IS(cconv, "__cdecl"))
418 pp->is_stdcall = 0;
419 else if (IS(cconv, "__stdcall"))
420 pp->is_stdcall = 1;
421 else if (IS(cconv, "__fastcall")) {
422 pp->is_fastcall = 1;
423 pp->is_stdcall = 1; // sort of..
424 }
425 else if (IS(cconv, "__thiscall"))
426 pp->is_stdcall = 1;
427 else if (IS(cconv, "__userpurge"))
428 pp->is_stdcall = 1; // IDA
429 else if (IS(cconv, "__usercall"))
430 pp->is_stdcall = 0; // IDA
431 else if (IS(cconv, "__userstack")) {
432 pp->is_stdcall = 0; // custom
433 pp->is_userstack = 1;
434 }
435 else if (IS(cconv, "WINAPI"))
436 pp->is_stdcall = 1;
437 else {
438 printf("%s:%d:%zd: unhandled cconv: '%s'\n",
439 hdrfn, hdrfline, (p - protostr) + 1, cconv);
440 return -1;
441 }
442
443 if (pp->is_fptr) {
444 if (*p != '*') {
445 printf("%s:%d:%zd: '*' expected\n",
446 hdrfn, hdrfline, (p - protostr) + 1);
447 return -1;
448 }
449 p++;
450 // XXX: skipping extra asterisks, for now
451 while (*p == '*')
452 p++;
453 p = sskip(p);
454 }
455
456 p = next_idt(buf, sizeof(buf), p);
457 p = sskip(p);
458 if (buf[0] == 0) {
459 //printf("%s:%d:%zd: func name missing\n",
460 // hdrfn, hdrfline, (p - protostr) + 1);
461 //return -1;
462 }
463 strcpy(pp->name, buf);
464
465 ret = get_regparm(regparm, sizeof(regparm), p, &is_retreg);
466 if (ret > 0) {
467 if (!IS(regparm, "eax") && !IS(regparm, "ax")
468 && !IS(regparm, "al") && !IS(regparm, "edx:eax"))
469 {
470 printf("%s:%d:%zd: bad regparm: %s\n",
471 hdrfn, hdrfline, (p - protostr) + 1, regparm);
472 return -1;
473 }
474 p += ret;
475 p = sskip(p);
476 }
477
478 if (pp->is_fptr) {
479 if (*p == '[') {
480 // not really ret_type is array, but ohwell
481 pp->ret_type.is_array = 1;
482 p = strchr(p + 1, ']');
483 if (p == NULL) {
484 printf("%s:%d:%zd: ']' expected\n",
485 hdrfn, hdrfline, (p - protostr) + 1);
486 return -1;
487 }
488 p = sskip(p + 1);
489 }
490 if (*p != ')') {
491 printf("%s:%d:%zd: ')' expected\n",
492 hdrfn, hdrfline, (p - protostr) + 1);
493 return -1;
494 }
495 p = sskip(p + 1);
496 }
497
498 if (*p != '(') {
499 printf("%s:%d:%zd: '(' expected, got '%c'\n",
500 hdrfn, hdrfline, (p - protostr) + 1, *p);
501 return -1;
502 }
503 p++;
504
505 // check for x(void)
506 p = sskip(p);
507 if ((!strncmp(p, "void", 4) || !strncmp(p, "VOID", 4))
508 && *sskip(p + 4) == ')')
509 p += 4;
510
511 while (1) {
512 p = sskip(p);
513 if (*p == ')') {
514 p++;
515 break;
516 }
517 if (xarg > 0) {
518 if (*p != ',') {
519 printf("%s:%d:%zd: ',' expected\n",
520 hdrfn, hdrfline, (p - protostr) + 1);
521 return -1;
522 }
523 p = sskip(p + 1);
524 }
525
526 if (!strncmp(p, "...", 3)) {
527 pp->is_vararg = 1;
528 p = sskip(p + 3);
529 if (*p == ')') {
530 p++;
531 break;
532 }
533 printf("%s:%d:%zd: ')' expected\n",
534 hdrfn, hdrfline, (p - protostr) + 1);
535 return -1;
536 }
537
538 arg = &pp->arg[xarg];
539 xarg++;
540
541 p1 = p;
542 ret = check_type(p, &arg->type);
543 if (ret <= 0) {
544 printf("%s:%d:%zd: unhandled type for arg%d\n",
545 hdrfn, hdrfline, (p - protostr) + 1, xarg);
546 return -1;
547 }
548 p = sskip(p + ret);
549
550 if (*p == '(') {
551 // func ptr
552 arg->fptr = calloc(1, sizeof(*arg->fptr));
553 ret = parse_protostr(p1, arg->fptr);
554 if (ret < 0) {
555 printf("%s:%d:%zd: funcarg parse failed\n",
556 hdrfn, hdrfline, p1 - protostr);
557 return -1;
558 }
559 arg->fptr->is_arg = 1;
560 // we don't use actual names right now..
561 snprintf(arg->fptr->name,
562 sizeof(arg->fptr->name), "a%d", xarg);
563 // we'll treat it as void * for non-calls
564 arg->type.name = strdup("void *");
565 arg->type.is_ptr = 1;
566
567 p = p1 + ret;
568 }
569
570 p = next_idt(buf, sizeof(buf), p);
571 p = sskip(p);
572#if 0
573 if (buf[0] == 0) {
574 printf("%s:%d:%zd: idt missing for arg%d\n",
575 hdrfn, hdrfline, (p - protostr) + 1, xarg);
576 return -1;
577 }
578#endif
579 arg->reg = NULL;
580
581 ret = get_regparm(regparm, sizeof(regparm), p, &is_retreg);
582 if (ret > 0) {
583 p += ret;
584 p = sskip(p);
585
586 arg->reg = strdup(map_reg(regparm));
587 arg->type.is_retreg = is_retreg;
588 pp->has_retreg |= is_retreg;
589 }
590
591 if (strstr(arg->type.name, "int64")
592 || IS(arg->type.name, "double"))
593 {
594 // hack..
595 free(arg->type.name);
596 arg->type.name = strdup("int");
597 pp_copy_arg(&pp->arg[xarg], arg);
598 xarg++;
599 }
600
601 ret = check_struct_arg(arg);
602 if (ret > 0) {
603 pp->has_structarg = 1;
604 arg->type.is_struct = 1;
605 free(arg->type.name);
606 arg->type.name = strdup("int");
607 for (l = 0; l < ret; l++) {
608 pp_copy_arg(&pp->arg[xarg], arg);
609 xarg++;
610 }
611 }
612 }
613
614 if (xarg > 0 && (IS(cconv, "__fastcall") || IS(cconv, "__thiscall"))) {
615 if (pp->arg[0].reg != NULL) {
616 printf("%s:%d: %s with arg1 spec %s?\n",
617 hdrfn, hdrfline, cconv, pp->arg[0].reg);
618 }
619 pp->arg[0].reg = strdup("ecx");
620 }
621
622 if (xarg > 1 && IS(cconv, "__fastcall")) {
623 if (pp->arg[1].reg != NULL) {
624 printf("%s:%d: %s with arg2 spec %s?\n",
625 hdrfn, hdrfline, cconv, pp->arg[1].reg);
626 }
627 pp->arg[1].reg = strdup("edx");
628 }
629
630 pp->argc = xarg;
631
632 for (i = 0; i < pp->argc; i++) {
633 if (pp->arg[i].reg == NULL)
634 pp->argc_stack++;
635 else
636 pp->argc_reg++;
637 }
638
639 if (pp->argc == 1 && pp->arg[0].reg != NULL
640 && IS(pp->arg[0].reg, "ecx"))
641 {
642 pp->is_fastcall = 1;
643 }
644 else if (pp->argc_reg == 2
645 && pp->arg[0].reg != NULL && IS(pp->arg[0].reg, "ecx")
646 && pp->arg[1].reg != NULL && IS(pp->arg[1].reg, "edx"))
647 {
648 pp->is_fastcall = 1;
649 }
650
651 if (pp->is_vararg && (pp->is_stdcall || pp->is_fastcall)) {
652 printf("%s:%d: vararg %s?\n", hdrfn, hdrfline, cconv);
653 return -1;
654 }
655
656 return p - protostr;
657}
658
659static int pp_name_cmp(const void *p1, const void *p2)
660{
661 const struct parsed_proto *pp1 = p1, *pp2 = p2;
662 return strcmp(pp1->name, pp2->name);
663}
664
665static struct parsed_proto *pp_cache;
666static int pp_cache_size;
667static int pp_cache_alloc;
668
669static int b_pp_c_handler(char *proto, const char *fname,
670 int is_include, int is_osinc, int is_cinc)
671{
672 int ret;
673
674 if (pp_cache_size >= pp_cache_alloc) {
675 pp_cache_alloc = pp_cache_alloc * 2 + 64;
676 pp_cache = realloc(pp_cache, pp_cache_alloc
677 * sizeof(pp_cache[0]));
678 my_assert_not(pp_cache, NULL);
679 memset(pp_cache + pp_cache_size, 0,
680 (pp_cache_alloc - pp_cache_size)
681 * sizeof(pp_cache[0]));
682 }
683
684 ret = parse_protostr(proto, &pp_cache[pp_cache_size]);
685 if (ret < 0)
686 return -1;
687
688 pp_cache[pp_cache_size].is_include = is_include;
689 pp_cache[pp_cache_size].is_osinc = is_osinc;
690 pp_cache[pp_cache_size].is_cinc = is_cinc;
691 pp_cache_size++;
692 return 0;
693}
694
695static void build_pp_cache(FILE *fhdr)
696{
697 long pos;
698 int ret;
699
700 pos = ftell(fhdr);
701 rewind(fhdr);
702
703 ret = do_protostrs(fhdr, hdrfn, 0);
704 if (ret < 0)
705 exit(1);
706
707 qsort(pp_cache, pp_cache_size, sizeof(pp_cache[0]), pp_name_cmp);
708 fseek(fhdr, pos, SEEK_SET);
709}
710
711static const struct parsed_proto *proto_parse(FILE *fhdr, const char *sym,
712 int quiet)
713{
714 const struct parsed_proto *pp_ret;
715 struct parsed_proto pp_search;
716 char *p;
717
718 if (pp_cache == NULL)
719 build_pp_cache(fhdr);
720
721 if (sym[0] == '_') // && strncmp(fname, "stdc", 4) == 0)
722 sym++;
723
724 strcpy(pp_search.name, sym);
725 p = strchr(pp_search.name, '@');
726 if (p != NULL)
727 *p = 0;
728
729 pp_ret = bsearch(&pp_search, pp_cache, pp_cache_size,
730 sizeof(pp_cache[0]), pp_name_cmp);
731 if (pp_ret == NULL && !quiet)
732 printf("%s: sym '%s' is missing\n", hdrfn, sym);
733
734 return pp_ret;
735}
736
737static void pp_copy_arg(struct parsed_proto_arg *d,
738 const struct parsed_proto_arg *s)
739{
740 memcpy(d, s, sizeof(*d));
741
742 if (s->reg != NULL) {
743 d->reg = strdup(s->reg);
744 my_assert_not(d->reg, NULL);
745 }
746 if (s->type.name != NULL) {
747 d->type.name = strdup(s->type.name);
748 my_assert_not(d->type.name, NULL);
749 }
750 if (s->fptr != NULL) {
751 d->fptr = malloc(sizeof(*d->fptr));
752 my_assert_not(d->fptr, NULL);
753 memcpy(d->fptr, s->fptr, sizeof(*d->fptr));
754 }
755}
756
757struct parsed_proto *proto_clone(const struct parsed_proto *pp_c)
758{
759 struct parsed_proto *pp;
760 int i;
761
762 pp = malloc(sizeof(*pp));
763 my_assert_not(pp, NULL);
764 memcpy(pp, pp_c, sizeof(*pp)); // lazy..
765
766 // do the actual deep copy..
767 for (i = 0; i < pp_c->argc; i++)
768 pp_copy_arg(&pp->arg[i], &pp_c->arg[i]);
769 if (pp_c->ret_type.name != NULL)
770 pp->ret_type.name = strdup(pp_c->ret_type.name);
771
772 return pp;
773}
774
775
776static inline int pp_cmp_func(const struct parsed_proto *pp1,
777 const struct parsed_proto *pp2)
778{
779 int i;
780
781 if (pp1->argc != pp2->argc || pp1->argc_reg != pp2->argc_reg)
782 return 1;
783 else {
784 for (i = 0; i < pp1->argc; i++) {
785 if ((pp1->arg[i].reg != NULL) != (pp2->arg[i].reg != NULL))
786 return 1;
787
788 if ((pp1->arg[i].reg != NULL)
789 && !IS(pp1->arg[i].reg, pp2->arg[i].reg))
790 {
791 return 1;
792 }
793 }
794 }
795
796 return 0;
797}
798
799static inline void pp_print(char *buf, size_t buf_size,
800 const struct parsed_proto *pp)
801{
802 size_t l;
803 int i;
804
805 snprintf(buf, buf_size, "%s %s(", pp->ret_type.name, pp->name);
806 l = strlen(buf);
807
808 for (i = 0; i < pp->argc_reg; i++) {
809 snprintf(buf + l, buf_size - l, "%s%s",
810 i == 0 ? "" : ", ", pp->arg[i].reg);
811 l = strlen(buf);
812 }
813 if (pp->argc_stack > 0) {
814 snprintf(buf + l, buf_size - l, "%s{%d stack}",
815 i == 0 ? "" : ", ", pp->argc_stack);
816 l = strlen(buf);
817 }
818 snprintf(buf + l, buf_size - l, ")");
819}
820
821static inline void proto_release(struct parsed_proto *pp)
822{
823 int i;
824
825 for (i = 0; i < pp->argc; i++) {
826 if (pp->arg[i].reg != NULL)
827 free(pp->arg[i].reg);
828 if (pp->arg[i].type.name != NULL)
829 free(pp->arg[i].type.name);
830 if (pp->arg[i].fptr != NULL)
831 free(pp->arg[i].fptr);
832 }
833 if (pp->ret_type.name != NULL)
834 free(pp->ret_type.name);
835 free(pp);
836}