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