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