|
|
1.1 ! root 1: #define DEBUG ! 2: ! 3: #include "awk.h" ! 4: #include "ctype.h" ! 5: #include "stdio.h" ! 6: #include "y.tab.h" ! 7: ! 8: extern Node *op2(); ! 9: #define MAXLIN 256 ! 10: ! 11: #define type(v) v->nobj ! 12: #define left(v) v->narg[0] ! 13: #define right(v) v->narg[1] ! 14: #define parent(v) v->nnext ! 15: ! 16: #define LEAF case CCL: case NCCL: case CHAR: case DOT: case FINAL: case ALL: ! 17: #define UNARY case STAR: case PLUS: case QUEST: ! 18: ! 19: /* encoding in tree Nodes: ! 20: leaf (CCL, NCCL, CHAR, DOT, FINAL, ALL): left is index, right contains value or pointer to value ! 21: unary (STAR, PLUS, QUEST): left is child, right is null ! 22: binary (CAT, OR): left and right are children ! 23: parent contains pointer to parent ! 24: */ ! 25: ! 26: ! 27: char chars[MAXLIN]; ! 28: int setvec[MAXLIN]; ! 29: Node *point[MAXLIN]; ! 30: ! 31: int rtok; ! 32: int rlxval; ! 33: char *prestr; ! 34: ! 35: int setcnt; ! 36: static int line; ! 37: ! 38: char *patbeg; ! 39: int patlen; ! 40: ! 41: fa *makedfa(p, anchor) /* returns dfa for tree pointed to by p */ ! 42: Node *p; /* anchor = 1 for anchored matches, else 0 */ ! 43: int anchor; ! 44: { ! 45: Node *p1; ! 46: fa *f; ! 47: int i; ! 48: fcell *pf; ! 49: ! 50: p1 = op2(CAT, op2(STAR, op2(ALL, (Node *) 0, (Node *) 0), (Node *) 0), p); ! 51: /* put ALL STAR in front of reg. exp. */ ! 52: p1 = op2(CAT, p1, op2(FINAL, (Node *) 0, (Node *) 0)); ! 53: /* put FINAL after reg. exp. */ ! 54: ! 55: line = 0; ! 56: penter(p1); /* enter parent pointers and leaf indices */ ! 57: if ((f = (fa *) Calloc (1, sizeof(fa) + (line-1)*sizeof(rrow))) == NULL) ! 58: overflo("no room for fa"); ! 59: cfoll(f, p1); /* set up follow sets */ ! 60: freetr(p1); ! 61: f->accept = line-1; ! 62: /* ! 63: printf("retab %o:\n", f->re); ! 64: printf(" ltype lval lfollow\n"); ! 65: for (i=0; i<line; i++) { ! 66: printf("%d %d %c %o\n", i, f->re[i].ltype, f->re[i].lval, f->re[i].lfollow); ! 67: pf = f->re[i].lfollow; ! 68: while (pf != 0) { ! 69: printf(" %o: %d %o\n", pf, pf->info, pf->link); ! 70: pf = pf->link; ! 71: } ! 72: } ! 73: */ ! 74: f->initstat = makeinit(f, anchor); ! 75: f->reset = 0; ! 76: return f; ! 77: } ! 78: ! 79: int makeinit(f, anchor) ! 80: fa *f; ! 81: int anchor; ! 82: { ! 83: register i; ! 84: fcell *pf; ! 85: ! 86: f->curstat = 2; ! 87: f->out[2] = 0; ! 88: pf = f->posns[2] = f->re[0].lfollow; ! 89: while (pf != 0) { ! 90: if (pf->info == f->accept) { ! 91: f->out[2] = 1; ! 92: break; ! 93: } ! 94: pf = pf->link; ! 95: } ! 96: for (i=0; i<NCHARS; i++) ! 97: f->gototab[2][i] = 0; ! 98: f->curstat = cgoto(f, 2, HAT); ! 99: if (anchor) { ! 100: f->posns[1] = 0; ! 101: f->posns[0] = f->posns[2] = f->posns[2]->link; ! 102: f->out[0] = f->out[2]; ! 103: if (f->curstat != 2) ! 104: f->posns[f->curstat] = f->posns[f->curstat]->link; ! 105: } ! 106: return f->curstat; ! 107: } ! 108: ! 109: penter(p) /* set up parent pointers and leaf indices */ ! 110: Node *p; ! 111: { ! 112: switch(type(p)) { ! 113: LEAF ! 114: left(p) = (Node *) line; ! 115: point[line++] = p; ! 116: break; ! 117: UNARY ! 118: penter(left(p)); ! 119: parent(left(p)) = p; ! 120: break; ! 121: case CAT: ! 122: case OR: ! 123: penter(left(p)); ! 124: penter(right(p)); ! 125: parent(left(p)) = p; ! 126: parent(right(p)) = p; ! 127: break; ! 128: default: ! 129: error(FATAL, "unknown type %d in penter\n", type(p)); ! 130: break; ! 131: } ! 132: } ! 133: ! 134: freetr(p) /* free parse tree and follow sets */ ! 135: Node *p; ! 136: { ! 137: switch(type(p)) { ! 138: LEAF ! 139: xfree(p); ! 140: break; ! 141: UNARY ! 142: freetr(left(p)); ! 143: xfree(p); ! 144: break; ! 145: case CAT: ! 146: case OR: ! 147: freetr(left(p)); ! 148: freetr(right(p)); ! 149: xfree(p); ! 150: break; ! 151: default: ! 152: error(FATAL, "unknown type %d in freetr", type(p)); ! 153: break; ! 154: } ! 155: } ! 156: ! 157: char *cclenter(p) ! 158: register char *p; ! 159: { ! 160: register i, c; ! 161: char *op; ! 162: ! 163: op = p; ! 164: i = 0; ! 165: while ((c = *p++) != 0) { ! 166: if (c == '-' && i > 0 && chars[i-1] != 0) { ! 167: if (*p != 0) { ! 168: c = chars[i-1]; ! 169: while (c < *p) { ! 170: if (i >= MAXLIN) ! 171: overflo("character class too big"); ! 172: chars[i++] = ++c; ! 173: } ! 174: p++; ! 175: continue; ! 176: } ! 177: } ! 178: if (i >= MAXLIN) ! 179: overflo("character class too big"); ! 180: chars[i++] = c; ! 181: } ! 182: chars[i++] = '\0'; ! 183: dprintf("cclenter: in = |%s|, out = |%s|\n", op, chars, NULL); ! 184: xfree(op); ! 185: return(tostring(chars)); ! 186: } ! 187: ! 188: overflo(s) ! 189: char *s; ! 190: { ! 191: error(FATAL, "regular expression too big: %s", s); ! 192: } ! 193: ! 194: cfoll(f, v) /* enter follow set of each leaf of vertex v into lfollow[leaf] */ ! 195: fa *f; ! 196: register Node *v; ! 197: { ! 198: register i; ! 199: fcell **prev; ! 200: fcell *p; ! 201: ! 202: switch(type(v)) { ! 203: LEAF ! 204: f->re[(int) left(v)].ltype = type(v); ! 205: f->re[(int) left(v)].lval = (int) right(v); ! 206: for (i=0; i<line; i++) ! 207: setvec[i] = 0; ! 208: follow(v); ! 209: prev = &(f->re[(int) left(v)].lfollow); ! 210: for (i=0; i<line; i++) ! 211: if (setvec[i] == 1) { ! 212: if ((p = (fcell *) Malloc(sizeof(struct fcell))) == NULL) ! 213: overflo("follow set overflow"); ! 214: p->info = i; ! 215: *prev = p; ! 216: prev = &(p->link); ! 217: } ! 218: *prev = (fcell *) 0; ! 219: break; ! 220: UNARY ! 221: cfoll(f,left(v)); ! 222: break; ! 223: case CAT: ! 224: case OR: ! 225: cfoll(f,left(v)); ! 226: cfoll(f,right(v)); ! 227: break; ! 228: default: ! 229: error(FATAL, "unknown type %d in cfoll", type(v)); ! 230: } ! 231: } ! 232: ! 233: first(p) /* collects initially active leaves of p into setvec */ ! 234: register Node *p; /* returns 0 or 1 depending on whether p matches empty string */ ! 235: { ! 236: register b; ! 237: ! 238: switch(type(p)) { ! 239: LEAF ! 240: if (setvec[(int) left(p)] != 1) { ! 241: setvec[(int) left(p)] = 1; ! 242: setcnt++; ! 243: } ! 244: if (type(p) == CCL && (*(char *) right(p)) == '\0') ! 245: return(0); /* empty CCL */ ! 246: else return(1); ! 247: case PLUS: ! 248: if (first(left(p)) == 0) return(0); ! 249: return(1); ! 250: case STAR: ! 251: case QUEST: ! 252: first(left(p)); ! 253: return(0); ! 254: case CAT: ! 255: if (first(left(p)) == 0 && first(right(p)) == 0) return(0); ! 256: return(1); ! 257: case OR: ! 258: b = first(right(p)); ! 259: if (first(left(p)) == 0 || b == 0) return(0); ! 260: return(1); ! 261: } ! 262: error(FATAL, "unknown type %d in first\n", type(p)); ! 263: return(-1); ! 264: } ! 265: ! 266: follow(v) ! 267: Node *v; /* collects leaves that can follow v into setvec */ ! 268: { ! 269: Node *p; ! 270: ! 271: if (type(v) == FINAL) ! 272: return; ! 273: p = parent(v); ! 274: switch (type(p)) { ! 275: case STAR: ! 276: case PLUS: first(v); ! 277: follow(p); ! 278: return; ! 279: ! 280: case OR: ! 281: case QUEST: follow(p); ! 282: return; ! 283: ! 284: case CAT: if (v == left(p)) { /* v is left child of p */ ! 285: if (first(right(p)) == 0) { ! 286: follow(p); ! 287: return; ! 288: } ! 289: } ! 290: else /* v is right child */ ! 291: follow(p); ! 292: return; ! 293: } ! 294: } ! 295: ! 296: member(c, s) /* is c in s? */ ! 297: register char c, *s; ! 298: { ! 299: while (*s) ! 300: if (c == *s++) ! 301: return(1); ! 302: return(0); ! 303: } ! 304: ! 305: ! 306: match(f, p) ! 307: register fa *f; ! 308: register char *p; ! 309: { ! 310: register s,ns; ! 311: ! 312: s = (f->reset)?makeinit(f,0):f->initstat; ! 313: if (f->out[s]) ! 314: return(1); ! 315: do { ! 316: if (ns=f->gototab[s][*p]) ! 317: s=ns; ! 318: else ! 319: s=cgoto(f,s,*p); ! 320: if (f->out[s]) ! 321: return(1); ! 322: } while(*p++ != 0); ! 323: return(0); ! 324: } ! 325: ! 326: pmatch(f, p) ! 327: register fa *f; ! 328: register char *p; ! 329: { ! 330: register s, ns; ! 331: register char *q; ! 332: extern char *patbeg; ! 333: extern int patlen; ! 334: int i; ! 335: ! 336: s = (f->reset)?makeinit(f,1):f->initstat; ! 337: patlen = -1; ! 338: do { ! 339: q = p; ! 340: do { ! 341: /* ! 342: fcell *pp; ! 343: printf("pmatch: p = %o, *p = %c, q = %o, *q = %c\n", p, *p, q, *q); ! 344: printf("state %d: ", s); ! 345: pp = f->posns[s]; ! 346: while (pp != 0) { ! 347: printf(" %d", pp->info); ! 348: pp = pp->link; ! 349: } ! 350: printf(" out = %d\n", f->out[s]); ! 351: */ ! 352: if (f->out[s]) /* final state */ ! 353: patlen = q-p; ! 354: /* ! 355: printf(" g(%d, %c) = ", s, *q); ! 356: */ ! 357: if (ns=f->gototab[s][*q]) ! 358: s=ns; ! 359: else ! 360: s=cgoto(f,s,*q); ! 361: /* ! 362: printf("%d\n", s); ! 363: */ ! 364: if (s==1) /* no transition */ ! 365: if (patlen >= 0) { ! 366: patbeg = p; ! 367: return(1); ! 368: } ! 369: else ! 370: goto nextin; /* no match */ ! 371: } while (*q++ != 0); ! 372: if (f->out[s]) ! 373: patlen = q-p; ! 374: if (patlen >=0 ) { ! 375: patbeg = p; ! 376: return(1); ! 377: } ! 378: nextin: ! 379: s = 2; ! 380: if (f->reset) { ! 381: s = f->initstat = f->curstat = 2; ! 382: f->posns[2] = f->posns[0]; ! 383: f->out[2] = f->out[0]; ! 384: for (i=0; i<NCHARS; i++) ! 385: f->gototab[2][i] = 0; ! 386: } ! 387: } while (*p++ != 0); ! 388: return (0); ! 389: } ! 390: ! 391: nematch(f, p) ! 392: register fa *f; ! 393: register char *p; ! 394: { ! 395: register s, ns; ! 396: register char *q; ! 397: extern char *patbeg; ! 398: extern int patlen; ! 399: int i; ! 400: ! 401: s = (f->reset)?makeinit(f,1):f->initstat; ! 402: patlen = -1; ! 403: while (*p) { ! 404: q = p; ! 405: do { ! 406: if (f->out[s]) /* final state */ ! 407: patlen = q-p; ! 408: if (ns=f->gototab[s][*q]) ! 409: s=ns; ! 410: else ! 411: s=cgoto(f,s,*q); ! 412: if (s==1) /* no transition */ ! 413: if (patlen > 0) { ! 414: patbeg = p; ! 415: return(1); ! 416: } ! 417: else ! 418: goto nnextin; /* no nonempty match */ ! 419: } while (*q++ != 0); ! 420: if (f->out[s]) ! 421: patlen = q-p; ! 422: if (patlen >0 ) { ! 423: patbeg = p; ! 424: return(1); ! 425: } ! 426: nnextin: ! 427: s = 2; ! 428: if (f->reset) { ! 429: s = f->initstat = f->curstat = 2; ! 430: f->posns[2] = f->posns[0]; ! 431: f->out[2] = f->out[0]; ! 432: for (i=0; i<NCHARS; i++) ! 433: f->gototab[2][i] = 0; ! 434: } ! 435: p++; ! 436: } ! 437: return (0); ! 438: } ! 439: ! 440: Node *regexp(), *primary(), *concat(), *alt(), *unary(); ! 441: ! 442: Node *reparse(p) ! 443: char *p; ! 444: { ! 445: /* parses regular expression pointed to by p */ ! 446: /* uses relex() to scan regular expression */ ! 447: Node *np; ! 448: ! 449: dprintf("reparse <%s>\n", p); ! 450: prestr = p; /* prestr points to string to be parsed */ ! 451: rtok = relex(); ! 452: if (rtok == '\0') ! 453: error(FATAL, "empty regular expression"); ! 454: np = regexp(); ! 455: if (rtok == '\0') return(np); ! 456: else ! 457: error(FATAL, "syntax error in regular expression"); ! 458: } ! 459: Node *regexp(){ ! 460: return (alt(concat(primary()))); ! 461: } ! 462: Node *primary(){ ! 463: Node *np; ! 464: switch(rtok){ ! 465: case CHAR: ! 466: np = op2(CHAR, (Node *) 0, rlxval); ! 467: rtok = relex(); ! 468: return (unary(np)); ! 469: case ALL: ! 470: rtok = relex(); ! 471: return (unary(op2(ALL, (Node *) 0, (Node *) 0))); ! 472: case DOT: ! 473: rtok = relex(); ! 474: return (unary(op2(DOT, (Node *) 0, (Node *) 0))); ! 475: case CCL: ! 476: np = op2(CCL, (Node *) 0, cclenter(rlxval)); ! 477: rtok = relex(); ! 478: return (unary(np)); ! 479: case NCCL: ! 480: np = op2(NCCL, (Node *) 0, cclenter(rlxval)); ! 481: rtok = relex(); ! 482: return (unary(np)); ! 483: case '^': ! 484: rtok = relex(); ! 485: return (unary(op2(CHAR, (Node *) 0, HAT))); ! 486: case '$': ! 487: rtok = relex(); ! 488: return (unary(op2(CHAR, (Node *) 0, (Node *) 0))); ! 489: case '(': ! 490: rtok = relex(); ! 491: if (rtok == ')') { /* special pleading for () */ ! 492: rtok = relex(); ! 493: return unary(op2(CCL, (Node *) 0, tostring(""))); ! 494: } ! 495: np = regexp(); ! 496: if (rtok==')') { ! 497: rtok = relex(); ! 498: return (unary(np)); ! 499: } ! 500: else ! 501: error(FATAL, "syntax error in regular expression"); ! 502: } ! 503: } ! 504: Node *concat(np) ! 505: Node *np; ! 506: { ! 507: switch(rtok){ ! 508: case CHAR: case DOT: case ALL: case CCL: case NCCL: case '$': case '(': ! 509: return (concat(op2(CAT, np, primary()))); ! 510: default: ! 511: return (np); ! 512: } ! 513: } ! 514: Node *alt(np) ! 515: Node *np; ! 516: { ! 517: if (rtok == OR) { ! 518: rtok = relex(); ! 519: return (alt(op2(OR, np, concat(primary())))); ! 520: } ! 521: return (np); ! 522: } ! 523: Node *unary(np) ! 524: Node *np; ! 525: { ! 526: switch(rtok){ ! 527: case STAR: ! 528: rtok = relex(); ! 529: return (unary(op2(STAR, np, (Node *) 0))); ! 530: case PLUS: ! 531: rtok = relex(); ! 532: return (unary(op2(PLUS, np, (Node *) 0))); ! 533: case QUEST: ! 534: rtok = relex(); ! 535: return (unary(op2(QUEST, np, (Node *) 0))); ! 536: default: ! 537: return (np); ! 538: } ! 539: } ! 540: ! 541: relex() /* lexical analyzer for reparse */ ! 542: { ! 543: extern int rlxval; ! 544: register int c; ! 545: char cbuf[150]; ! 546: int clen, cflag; ! 547: switch (c = *prestr++) { ! 548: case '|': return OR; ! 549: case '*': return STAR; ! 550: case '+': return PLUS; ! 551: case '?': return QUEST; ! 552: case '.': return DOT; ! 553: case '\0': return '\0'; ! 554: case '^': ! 555: case '$': ! 556: case '(': ! 557: case ')': ! 558: return c; ! 559: case '\\': ! 560: if ((c = *prestr++) == 't') ! 561: c = '\t'; ! 562: else if (c == 'n') ! 563: c = '\n'; ! 564: else if (c == 'f') ! 565: c = '\f'; ! 566: else if (c == 'r') ! 567: c = '\r'; ! 568: else if (c == 'b') ! 569: c = '\b'; ! 570: else if (c == '\\') ! 571: c = '\\'; ! 572: else if (isdigit(c)) { ! 573: int n = c - '0'; ! 574: if (isdigit(*prestr)) { ! 575: n = 8 * n + *prestr++ - '0'; ! 576: if (isdigit(*prestr)) ! 577: n = 8 * n + *prestr++ - '0'; ! 578: } ! 579: c = n; ! 580: } /* else it's now in c */ ! 581: rlxval = c; ! 582: return CHAR; ! 583: default: ! 584: rlxval = c; ! 585: return CHAR; ! 586: case '[': ! 587: clen = 0; ! 588: if (*prestr == '^') { ! 589: cflag = 1; ! 590: prestr++; ! 591: } ! 592: else ! 593: cflag = 0; ! 594: for (;;) { ! 595: if ((c = *prestr++) == '\\') { ! 596: if ((c = *prestr++) == 't') ! 597: cbuf[clen++] = '\t'; ! 598: else if (c == 'n') ! 599: cbuf[clen++] = '\n'; ! 600: else if (c == 'f') ! 601: cbuf[clen++] = '\f'; ! 602: else if (c == 'r') ! 603: cbuf[clen++] = '\r'; ! 604: else if (c == 'b') ! 605: cbuf[clen++] = '\b'; ! 606: else if (c == '\\') ! 607: cbuf[clen++] = '\\'; ! 608: else if (isdigit(c)) { ! 609: int n = c - '0'; ! 610: if (isdigit(*prestr)) { ! 611: n = 8 * n + *prestr++ - '0'; ! 612: if (isdigit(*prestr)) ! 613: n = 8 * n + *prestr++ - '0'; ! 614: } ! 615: cbuf[clen++] = n; ! 616: } else ! 617: cbuf[clen++] = c; ! 618: } else if (c == ']') { ! 619: cbuf[clen] = 0; ! 620: rlxval = (int) tostring(cbuf); ! 621: if (cflag == 0) ! 622: return CCL; ! 623: else ! 624: return NCCL; ! 625: } else if (c == '\n') { ! 626: error(FATAL, "newline in character class"); ! 627: } else if (c == '\0') { ! 628: error(FATAL, "non-terminated character class"); ! 629: } else ! 630: cbuf[clen++] = c; ! 631: } ! 632: } ! 633: } ! 634: ! 635: ! 636: int cgoto(f, s, c) ! 637: fa *f; ! 638: int s; ! 639: char c; ! 640: { ! 641: register int i, j, k; ! 642: register fcell *p, *q; ! 643: fcell *listbeg; ! 644: fcell **prev; ! 645: int curvec[MAXLIN]; ! 646: int fline; ! 647: ! 648: fline = f->accept; ! 649: for (i=0; i<=fline; i++) ! 650: curvec[i] = 0; ! 651: /* compute positions of state s into curvec */ ! 652: p = f->posns[s]; ! 653: while (p != 0) { ! 654: curvec[p->info] = 1; ! 655: p = p->link; ! 656: } ! 657: for (i=0; i<=fline; i++) ! 658: setvec[i] = 0; ! 659: /* compute positions of gototab[s,c] into setvec */ ! 660: for (i=0; i<=fline; i++) ! 661: if (curvec[i]) ! 662: if ((k = f->re[i].ltype) != FINAL) { ! 663: if (k == CHAR && c == f->re[i].lval ! 664: || k == DOT && c != 0 && c != HAT ! 665: || k == ALL && c != 0 ! 666: || k == CCL && member(c, (char *) f->re[i].lval) ! 667: || k == NCCL && !member(c, (char *) f->re[i].lval) && c != 0) { ! 668: p = f->re[i].lfollow; ! 669: while (p != 0) { ! 670: setvec[p->info] = 1; ! 671: p = p->link; ! 672: } ! 673: } ! 674: } ! 675: /* determine if setvec is a previous state */ ! 676: prev = &listbeg; ! 677: for (i=0; i<=fline; i++) { ! 678: if (setvec[i]) { ! 679: if ((p = (fcell *) Malloc(sizeof(struct fcell))) == NULL) ! 680: overflo("out of space in cgoto"); ! 681: p->info = i; ! 682: *prev = p; ! 683: prev = &p->link; ! 684: } ! 685: } ! 686: *prev = (fcell *) 0; ! 687: for (i=1; i<= f->curstat; i++) { ! 688: p = f->posns[i]; ! 689: q = listbeg; ! 690: while (p != 0) { ! 691: if ((p->info != q->info) || (q == 0)) ! 692: goto different; ! 693: p = p->link; ! 694: q = q->link; ! 695: } ! 696: if (q != 0) ! 697: goto different; ! 698: /* setvec is state i */ ! 699: f->gototab[s][c] = i; ! 700: /* printf("g[%d][%c] = %d\n", s, c, i); */ ! 701: p = listbeg; ! 702: while (p != 0) { ! 703: q = p->link; ! 704: Free(p); ! 705: p = q; ! 706: } ! 707: return i; ! 708: different:; ! 709: } ! 710: /* setvec is notin current set of states */ ! 711: if (f->curstat >= NSTATES-1) { ! 712: f->curstat = 2; ! 713: f->reset = 1; ! 714: } ! 715: else ! 716: ++(f->curstat); ! 717: for (i=0; i<NCHARS; i++) ! 718: f->gototab[f->curstat][i] = 0; ! 719: f->posns[f->curstat] = listbeg; ! 720: f->gototab[s][c] = f->curstat; ! 721: /* printf("g[%d, %c] = %d\n", s, c, f->curstat); */ ! 722: if (setvec[fline]) ! 723: f->out[f->curstat] = 1; ! 724: else ! 725: f->out[f->curstat] = 0; ! 726: return f->curstat; ! 727: } ! 728: ! 729: freefa(f) ! 730: struct fa *f; ! 731: { ! 732: register fcell *p, *q; ! 733: register int i; ! 734: ! 735: /* free posns */ ! 736: for (i=0; i < NSTATES; i++) { ! 737: p = f->posns[i]; ! 738: while (p != 0) { ! 739: q = p->link; ! 740: Free(p); ! 741: p = q; ! 742: } ! 743: } ! 744: /* free re */ ! 745: for (i=0; i<line; i++) { ! 746: p = f->re[i].lfollow; ! 747: while (p != 0) { ! 748: q = p->link; ! 749: Free(p); ! 750: p = q; ! 751: } ! 752: } ! 753: Free(f); ! 754: }
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.