1/* $NetBSD$ */ 2 3/* 4 * Copyright (c) 1997 - 2006 Kungliga Tekniska H��gskolan 5 * (Royal Institute of Technology, Stockholm, Sweden). 6 * All rights reserved. 7 * 8 * Redistribution and use in source and binary forms, with or without 9 * modification, are permitted provided that the following conditions 10 * are met: 11 * 12 * 1. Redistributions of source code must retain the above copyright 13 * notice, this list of conditions and the following disclaimer. 14 * 15 * 2. Redistributions in binary form must reproduce the above copyright 16 * notice, this list of conditions and the following disclaimer in the 17 * documentation and/or other materials provided with the distribution. 18 * 19 * 3. Neither the name of the Institute nor the names of its contributors 20 * may be used to endorse or promote products derived from this software 21 * without specific prior written permission. 22 * 23 * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND 24 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 25 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 26 * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE 27 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 28 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 29 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 30 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 31 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 32 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 33 * SUCH DAMAGE. 34 */ 35 36#include "gen_locl.h" 37#include "lex.h" 38 39__RCSID("$NetBSD$"); 40 41static void 42decode_primitive (const char *typename, const char *name, const char *forwstr) 43{ 44#if 0 45 fprintf (codefile, 46 "e = decode_%s(p, len, %s, &l);\n" 47 "%s;\n", 48 typename, 49 name, 50 forwstr); 51#else 52 fprintf (codefile, 53 "e = der_get_%s(p, len, %s, &l);\n" 54 "if(e) %s;\np += l; len -= l; ret += l;\n", 55 typename, 56 name, 57 forwstr); 58#endif 59} 60 61static void 62find_tag (const Type *t, 63 Der_class *cl, Der_type *ty, unsigned *tag) 64{ 65 switch (t->type) { 66 case TBitString: 67 *cl = ASN1_C_UNIV; 68 *ty = PRIM; 69 *tag = UT_BitString; 70 break; 71 case TBoolean: 72 *cl = ASN1_C_UNIV; 73 *ty = PRIM; 74 *tag = UT_Boolean; 75 break; 76 case TChoice: 77 errx(1, "Cannot have recursive CHOICE"); 78 case TEnumerated: 79 *cl = ASN1_C_UNIV; 80 *ty = PRIM; 81 *tag = UT_Enumerated; 82 break; 83 case TGeneralString: 84 *cl = ASN1_C_UNIV; 85 *ty = PRIM; 86 *tag = UT_GeneralString; 87 break; 88 case TTeletexString: 89 *cl = ASN1_C_UNIV; 90 *ty = PRIM; 91 *tag = UT_TeletexString; 92 break; 93 case TGeneralizedTime: 94 *cl = ASN1_C_UNIV; 95 *ty = PRIM; 96 *tag = UT_GeneralizedTime; 97 break; 98 case TIA5String: 99 *cl = ASN1_C_UNIV; 100 *ty = PRIM; 101 *tag = UT_IA5String; 102 break; 103 case TInteger: 104 *cl = ASN1_C_UNIV; 105 *ty = PRIM; 106 *tag = UT_Integer; 107 break; 108 case TNull: 109 *cl = ASN1_C_UNIV; 110 *ty = PRIM; 111 *tag = UT_Null; 112 break; 113 case TOID: 114 *cl = ASN1_C_UNIV; 115 *ty = PRIM; 116 *tag = UT_OID; 117 break; 118 case TOctetString: 119 *cl = ASN1_C_UNIV; 120 *ty = PRIM; 121 *tag = UT_OctetString; 122 break; 123 case TPrintableString: 124 *cl = ASN1_C_UNIV; 125 *ty = PRIM; 126 *tag = UT_PrintableString; 127 break; 128 case TSequence: 129 case TSequenceOf: 130 *cl = ASN1_C_UNIV; 131 *ty = CONS; 132 *tag = UT_Sequence; 133 break; 134 case TSet: 135 case TSetOf: 136 *cl = ASN1_C_UNIV; 137 *ty = CONS; 138 *tag = UT_Set; 139 break; 140 case TTag: 141 *cl = t->tag.tagclass; 142 *ty = is_primitive_type(t->subtype->type) ? PRIM : CONS; 143 *tag = t->tag.tagvalue; 144 break; 145 case TType: 146 if ((t->symbol->stype == Stype && t->symbol->type == NULL) 147 || t->symbol->stype == SUndefined) { 148 lex_error_message("%s is imported or still undefined, " 149 " can't generate tag checking data in CHOICE " 150 "without this information", 151 t->symbol->name); 152 exit(1); 153 } 154 find_tag(t->symbol->type, cl, ty, tag); 155 return; 156 case TUTCTime: 157 *cl = ASN1_C_UNIV; 158 *ty = PRIM; 159 *tag = UT_UTCTime; 160 break; 161 case TUTF8String: 162 *cl = ASN1_C_UNIV; 163 *ty = PRIM; 164 *tag = UT_UTF8String; 165 break; 166 case TBMPString: 167 *cl = ASN1_C_UNIV; 168 *ty = PRIM; 169 *tag = UT_BMPString; 170 break; 171 case TUniversalString: 172 *cl = ASN1_C_UNIV; 173 *ty = PRIM; 174 *tag = UT_UniversalString; 175 break; 176 case TVisibleString: 177 *cl = ASN1_C_UNIV; 178 *ty = PRIM; 179 *tag = UT_VisibleString; 180 break; 181 default: 182 abort(); 183 } 184} 185 186static void 187range_check(const char *name, 188 const char *length, 189 const char *forwstr, 190 struct range *r) 191{ 192 if (r->min == r->max + 2 || r->min < r->max) 193 fprintf (codefile, 194 "if ((%s)->%s > %d) {\n" 195 "e = ASN1_MAX_CONSTRAINT; %s;\n" 196 "}\n", 197 name, length, r->max, forwstr); 198 if (r->min - 1 == r->max || r->min < r->max) 199 fprintf (codefile, 200 "if ((%s)->%s < %d) {\n" 201 "e = ASN1_MIN_CONSTRAINT; %s;\n" 202 "}\n", 203 name, length, r->min, forwstr); 204 if (r->max == r->min) 205 fprintf (codefile, 206 "if ((%s)->%s != %d) {\n" 207 "e = ASN1_EXACT_CONSTRAINT; %s;\n" 208 "}\n", 209 name, length, r->min, forwstr); 210} 211 212static int 213decode_type (const char *name, const Type *t, int optional, 214 const char *forwstr, const char *tmpstr, const char *dertype) 215{ 216 switch (t->type) { 217 case TType: { 218 if (optional) 219 fprintf(codefile, 220 "%s = calloc(1, sizeof(*%s));\n" 221 "if (%s == NULL) %s;\n", 222 name, name, name, forwstr); 223 fprintf (codefile, 224 "e = decode_%s(p, len, %s, &l);\n", 225 t->symbol->gen_name, name); 226 if (optional) { 227 fprintf (codefile, 228 "if(e) {\n" 229 "free(%s);\n" 230 "%s = NULL;\n" 231 "} else {\n" 232 "p += l; len -= l; ret += l;\n" 233 "}\n", 234 name, name); 235 } else { 236 fprintf (codefile, 237 "if(e) %s;\n", 238 forwstr); 239 fprintf (codefile, 240 "p += l; len -= l; ret += l;\n"); 241 } 242 break; 243 } 244 case TInteger: 245 if(t->members) { 246 fprintf(codefile, 247 "{\n" 248 "int enumint;\n"); 249 decode_primitive ("integer", "&enumint", forwstr); 250 fprintf(codefile, 251 "*%s = enumint;\n" 252 "}\n", 253 name); 254 } else if (t->range == NULL) { 255 decode_primitive ("heim_integer", name, forwstr); 256 } else if (t->range->min == INT_MIN && t->range->max == INT_MAX) { 257 decode_primitive ("integer", name, forwstr); 258 } else if (t->range->min == 0 && t->range->max == UINT_MAX) { 259 decode_primitive ("unsigned", name, forwstr); 260 } else if (t->range->min == 0 && t->range->max == INT_MAX) { 261 decode_primitive ("unsigned", name, forwstr); 262 } else 263 errx(1, "%s: unsupported range %d -> %d", 264 name, t->range->min, t->range->max); 265 break; 266 case TBoolean: 267 decode_primitive ("boolean", name, forwstr); 268 break; 269 case TEnumerated: 270 decode_primitive ("enumerated", name, forwstr); 271 break; 272 case TOctetString: 273 if (dertype) { 274 fprintf(codefile, 275 "if (%s == CONS) {\n", 276 dertype); 277 decode_primitive("octet_string_ber", name, forwstr); 278 fprintf(codefile, 279 "} else {\n"); 280 } 281 decode_primitive ("octet_string", name, forwstr); 282 if (dertype) 283 fprintf(codefile, "}\n"); 284 if (t->range) 285 range_check(name, "length", forwstr, t->range); 286 break; 287 case TBitString: { 288 Member *m; 289 int pos = 0; 290 291 if (ASN1_TAILQ_EMPTY(t->members)) { 292 decode_primitive ("bit_string", name, forwstr); 293 break; 294 } 295 fprintf(codefile, 296 "if (len < 1) return ASN1_OVERRUN;\n" 297 "p++; len--; ret++;\n"); 298 fprintf(codefile, 299 "do {\n" 300 "if (len < 1) break;\n"); 301 ASN1_TAILQ_FOREACH(m, t->members, members) { 302 while (m->val / 8 > pos / 8) { 303 fprintf (codefile, 304 "p++; len--; ret++;\n" 305 "if (len < 1) break;\n"); 306 pos += 8; 307 } 308 fprintf (codefile, 309 "(%s)->%s = (*p >> %d) & 1;\n", 310 name, m->gen_name, 7 - m->val % 8); 311 } 312 fprintf(codefile, 313 "} while(0);\n"); 314 fprintf (codefile, 315 "p += len; ret += len;\n"); 316 break; 317 } 318 case TSequence: { 319 Member *m; 320 321 if (t->members == NULL) 322 break; 323 324 ASN1_TAILQ_FOREACH(m, t->members, members) { 325 char *s = NULL; 326 327 if (m->ellipsis) 328 continue; 329 330 if (asprintf (&s, "%s(%s)->%s", m->optional ? "" : "&", 331 name, m->gen_name) < 0 || s == NULL) 332 errx(1, "malloc"); 333 decode_type (s, m->type, m->optional, forwstr, m->gen_name, NULL); 334 free (s); 335 } 336 337 break; 338 } 339 case TSet: { 340 Member *m; 341 unsigned int memno; 342 343 if(t->members == NULL) 344 break; 345 346 fprintf(codefile, "{\n"); 347 fprintf(codefile, "unsigned int members = 0;\n"); 348 fprintf(codefile, "while(len > 0) {\n"); 349 fprintf(codefile, 350 "Der_class class;\n" 351 "Der_type type;\n" 352 "int tag;\n" 353 "e = der_get_tag (p, len, &class, &type, &tag, NULL);\n" 354 "if(e) %s;\n", forwstr); 355 fprintf(codefile, "switch (MAKE_TAG(class, type, tag)) {\n"); 356 memno = 0; 357 ASN1_TAILQ_FOREACH(m, t->members, members) { 358 char *s; 359 360 assert(m->type->type == TTag); 361 362 fprintf(codefile, "case MAKE_TAG(%s, %s, %s):\n", 363 classname(m->type->tag.tagclass), 364 is_primitive_type(m->type->subtype->type) ? "PRIM" : "CONS", 365 valuename(m->type->tag.tagclass, m->type->tag.tagvalue)); 366 367 if (asprintf (&s, "%s(%s)->%s", m->optional ? "" : "&", name, m->gen_name) < 0 || s == NULL) 368 errx(1, "malloc"); 369 if(m->optional) 370 fprintf(codefile, 371 "%s = calloc(1, sizeof(*%s));\n" 372 "if (%s == NULL) { e = ENOMEM; %s; }\n", 373 s, s, s, forwstr); 374 decode_type (s, m->type, 0, forwstr, m->gen_name, NULL); 375 free (s); 376 377 fprintf(codefile, "members |= (1 << %d);\n", memno); 378 memno++; 379 fprintf(codefile, "break;\n"); 380 } 381 fprintf(codefile, 382 "default:\n" 383 "return ASN1_MISPLACED_FIELD;\n" 384 "break;\n"); 385 fprintf(codefile, "}\n"); 386 fprintf(codefile, "}\n"); 387 memno = 0; 388 ASN1_TAILQ_FOREACH(m, t->members, members) { 389 char *s; 390 391 if (asprintf (&s, "%s->%s", name, m->gen_name) < 0 || s == NULL) 392 errx(1, "malloc"); 393 fprintf(codefile, "if((members & (1 << %d)) == 0)\n", memno); 394 if(m->optional) 395 fprintf(codefile, "%s = NULL;\n", s); 396 else if(m->defval) 397 gen_assign_defval(s, m->defval); 398 else 399 fprintf(codefile, "return ASN1_MISSING_FIELD;\n"); 400 free(s); 401 memno++; 402 } 403 fprintf(codefile, "}\n"); 404 break; 405 } 406 case TSetOf: 407 case TSequenceOf: { 408 char *n = NULL; 409 char *sname = NULL; 410 411 fprintf (codefile, 412 "{\n" 413 "size_t %s_origlen = len;\n" 414 "size_t %s_oldret = ret;\n" 415 "size_t %s_olen = 0;\n" 416 "void *%s_tmp;\n" 417 "ret = 0;\n" 418 "(%s)->len = 0;\n" 419 "(%s)->val = NULL;\n", 420 tmpstr, 421 tmpstr, 422 tmpstr, 423 tmpstr, 424 name, 425 name); 426 427 fprintf (codefile, 428 "while(ret < %s_origlen) {\n" 429 "size_t %s_nlen = %s_olen + sizeof(*((%s)->val));\n" 430 "if (%s_olen > %s_nlen) { e = ASN1_OVERFLOW; %s; }\n" 431 "%s_olen = %s_nlen;\n" 432 "%s_tmp = realloc((%s)->val, %s_olen);\n" 433 "if (%s_tmp == NULL) { e = ENOMEM; %s; }\n" 434 "(%s)->val = %s_tmp;\n", 435 tmpstr, 436 tmpstr, tmpstr, name, 437 tmpstr, tmpstr, forwstr, 438 tmpstr, tmpstr, 439 tmpstr, name, tmpstr, 440 tmpstr, forwstr, 441 name, tmpstr); 442 443 if (asprintf (&n, "&(%s)->val[(%s)->len]", name, name) < 0 || n == NULL) 444 errx(1, "malloc"); 445 if (asprintf (&sname, "%s_s_of", tmpstr) < 0 || sname == NULL) 446 errx(1, "malloc"); 447 decode_type (n, t->subtype, 0, forwstr, sname, NULL); 448 fprintf (codefile, 449 "(%s)->len++;\n" 450 "len = %s_origlen - ret;\n" 451 "}\n" 452 "ret += %s_oldret;\n" 453 "}\n", 454 name, 455 tmpstr, tmpstr); 456 if (t->range) 457 range_check(name, "len", forwstr, t->range); 458 free (n); 459 free (sname); 460 break; 461 } 462 case TGeneralizedTime: 463 decode_primitive ("generalized_time", name, forwstr); 464 break; 465 case TGeneralString: 466 decode_primitive ("general_string", name, forwstr); 467 break; 468 case TTeletexString: 469 decode_primitive ("general_string", name, forwstr); 470 break; 471 case TTag:{ 472 char *tname = NULL, *typestring = NULL; 473 char *ide = NULL; 474 475 if (asprintf(&typestring, "%s_type", tmpstr) < 0 || typestring == NULL) 476 errx(1, "malloc"); 477 478 fprintf(codefile, 479 "{\n" 480 "size_t %s_datalen, %s_oldlen;\n" 481 "Der_type %s;\n", 482 tmpstr, tmpstr, typestring); 483 if(support_ber) 484 fprintf(codefile, 485 "int is_indefinite;\n"); 486 487 fprintf(codefile, "e = der_match_tag_and_length(p, len, %s, &%s, %s, " 488 "&%s_datalen, &l);\n", 489 classname(t->tag.tagclass), 490 typestring, 491 valuename(t->tag.tagclass, t->tag.tagvalue), 492 tmpstr); 493 494 /* XXX hardcode for now */ 495 if (support_ber && t->subtype->type == TOctetString) { 496 ide = typestring; 497 } else { 498 fprintf(codefile, 499 "if (e == 0 && %s != %s) { e = ASN1_BAD_ID; }\n", 500 typestring, 501 is_primitive_type(t->subtype->type) ? "PRIM" : "CONS"); 502 } 503 504 if(optional) { 505 fprintf(codefile, 506 "if(e) {\n" 507 "%s = NULL;\n" 508 "} else {\n" 509 "%s = calloc(1, sizeof(*%s));\n" 510 "if (%s == NULL) { e = ENOMEM; %s; }\n", 511 name, name, name, name, forwstr); 512 } else { 513 fprintf(codefile, "if(e) %s;\n", forwstr); 514 } 515 fprintf (codefile, 516 "p += l; len -= l; ret += l;\n" 517 "%s_oldlen = len;\n", 518 tmpstr); 519 if(support_ber) 520 fprintf (codefile, 521 "if((is_indefinite = _heim_fix_dce(%s_datalen, &len)) < 0)\n" 522 "{ e = ASN1_BAD_FORMAT; %s; }\n" 523 "if (is_indefinite) { if (len < 2) { e = ASN1_OVERRUN; %s; } len -= 2; }", 524 tmpstr, forwstr, forwstr); 525 else 526 fprintf(codefile, 527 "if (%s_datalen > len) { e = ASN1_OVERRUN; %s; }\n" 528 "len = %s_datalen;\n", tmpstr, forwstr, tmpstr); 529 if (asprintf (&tname, "%s_Tag", tmpstr) < 0 || tname == NULL) 530 errx(1, "malloc"); 531 decode_type (name, t->subtype, 0, forwstr, tname, ide); 532 if(support_ber) 533 fprintf(codefile, 534 "if(is_indefinite){\n" 535 "len += 2;\n" 536 "e = der_match_tag_and_length(p, len, " 537 "(Der_class)0, &%s, UT_EndOfContent, " 538 "&%s_datalen, &l);\n" 539 "if(e) %s;\n" 540 "p += l; len -= l; ret += l;\n" 541 "if (%s != (Der_type)0) { e = ASN1_BAD_ID; %s; }\n" 542 "} else \n", 543 typestring, 544 tmpstr, 545 forwstr, 546 typestring, forwstr); 547 fprintf(codefile, 548 "len = %s_oldlen - %s_datalen;\n", 549 tmpstr, tmpstr); 550 if(optional) 551 fprintf(codefile, 552 "}\n"); 553 fprintf(codefile, 554 "}\n"); 555 free(tname); 556 free(typestring); 557 break; 558 } 559 case TChoice: { 560 Member *m, *have_ellipsis = NULL; 561 const char *els = ""; 562 563 if (t->members == NULL) 564 break; 565 566 ASN1_TAILQ_FOREACH(m, t->members, members) { 567 const Type *tt = m->type; 568 char *s = NULL; 569 Der_class cl; 570 Der_type ty; 571 unsigned tag; 572 573 if (m->ellipsis) { 574 have_ellipsis = m; 575 continue; 576 } 577 578 find_tag(tt, &cl, &ty, &tag); 579 580 fprintf(codefile, 581 "%sif (der_match_tag(p, len, %s, %s, %s, NULL) == 0) {\n", 582 els, 583 classname(cl), 584 ty ? "CONS" : "PRIM", 585 valuename(cl, tag)); 586 if (asprintf (&s, "%s(%s)->u.%s", m->optional ? "" : "&", 587 name, m->gen_name) < 0 || s == NULL) 588 errx(1, "malloc"); 589 decode_type (s, m->type, m->optional, forwstr, m->gen_name, NULL); 590 fprintf(codefile, 591 "(%s)->element = %s;\n", 592 name, m->label); 593 free(s); 594 fprintf(codefile, 595 "}\n"); 596 els = "else "; 597 } 598 if (have_ellipsis) { 599 fprintf(codefile, 600 "else {\n" 601 "(%s)->u.%s.data = calloc(1, len);\n" 602 "if ((%s)->u.%s.data == NULL) {\n" 603 "e = ENOMEM; %s;\n" 604 "}\n" 605 "(%s)->u.%s.length = len;\n" 606 "memcpy((%s)->u.%s.data, p, len);\n" 607 "(%s)->element = %s;\n" 608 "p += len;\n" 609 "ret += len;\n" 610 "len -= len;\n" 611 "}\n", 612 name, have_ellipsis->gen_name, 613 name, have_ellipsis->gen_name, 614 forwstr, 615 name, have_ellipsis->gen_name, 616 name, have_ellipsis->gen_name, 617 name, have_ellipsis->label); 618 } else { 619 fprintf(codefile, 620 "else {\n" 621 "e = ASN1_PARSE_ERROR;\n" 622 "%s;\n" 623 "}\n", 624 forwstr); 625 } 626 break; 627 } 628 case TUTCTime: 629 decode_primitive ("utctime", name, forwstr); 630 break; 631 case TUTF8String: 632 decode_primitive ("utf8string", name, forwstr); 633 break; 634 case TPrintableString: 635 decode_primitive ("printable_string", name, forwstr); 636 break; 637 case TIA5String: 638 decode_primitive ("ia5_string", name, forwstr); 639 break; 640 case TBMPString: 641 decode_primitive ("bmp_string", name, forwstr); 642 break; 643 case TUniversalString: 644 decode_primitive ("universal_string", name, forwstr); 645 break; 646 case TVisibleString: 647 decode_primitive ("visible_string", name, forwstr); 648 break; 649 case TNull: 650 fprintf (codefile, "/* NULL */\n"); 651 break; 652 case TOID: 653 decode_primitive ("oid", name, forwstr); 654 break; 655 default : 656 abort (); 657 } 658 return 0; 659} 660 661void 662generate_type_decode (const Symbol *s) 663{ 664 int preserve = preserve_type(s->name) ? TRUE : FALSE; 665 666 fprintf (codefile, "int ASN1CALL\n" 667 "decode_%s(const unsigned char *p," 668 " size_t len, %s *data, size_t *size)\n" 669 "{\n", 670 s->gen_name, s->gen_name); 671 672 switch (s->type->type) { 673 case TInteger: 674 case TBoolean: 675 case TOctetString: 676 case TOID: 677 case TGeneralizedTime: 678 case TGeneralString: 679 case TTeletexString: 680 case TUTF8String: 681 case TPrintableString: 682 case TIA5String: 683 case TBMPString: 684 case TUniversalString: 685 case TVisibleString: 686 case TUTCTime: 687 case TNull: 688 case TEnumerated: 689 case TBitString: 690 case TSequence: 691 case TSequenceOf: 692 case TSet: 693 case TSetOf: 694 case TTag: 695 case TType: 696 case TChoice: 697 fprintf (codefile, 698 "size_t ret = 0;\n" 699 "size_t l;\n" 700 "int e;\n"); 701 if (preserve) 702 fprintf (codefile, "const unsigned char *begin = p;\n"); 703 704 fprintf (codefile, "\n"); 705 fprintf (codefile, "memset(data, 0, sizeof(*data));\n"); /* hack to avoid `unused variable' */ 706 707 decode_type ("data", s->type, 0, "goto fail", "Top", NULL); 708 if (preserve) 709 fprintf (codefile, 710 "data->_save.data = calloc(1, ret);\n" 711 "if (data->_save.data == NULL) { \n" 712 "e = ENOMEM; goto fail; \n" 713 "}\n" 714 "data->_save.length = ret;\n" 715 "memcpy(data->_save.data, begin, ret);\n"); 716 fprintf (codefile, 717 "if(size) *size = ret;\n" 718 "return 0;\n"); 719 fprintf (codefile, 720 "fail:\n" 721 "free_%s(data);\n" 722 "return e;\n", 723 s->gen_name); 724 break; 725 default: 726 abort (); 727 } 728 fprintf (codefile, "}\n\n"); 729} 730