1: /*	@(#)sh.func.c	2.1	SCCS id keyword	*/
   2: /* Copyright (c) 1980 Regents of the University of California */
   3: #include "sh.h"
   4: 
   5: /*
   6:  * C shell
   7:  */
   8: 
   9: struct biltins *
  10: isbfunc(cp)
  11:     register char *cp;
  12: {
  13:     register char *dp;
  14:     register struct biltins *bp;
  15: 
  16:     if (lastchr(cp) == ':')
  17:         return ((struct biltins *) 1);
  18:     for (bp = bfunc; dp = bp->bname; bp++) {
  19:         if (dp[0] == cp[0] && eq(dp, cp))
  20:             return (bp);
  21:         if (dp[0] > cp[0])
  22:             break;
  23:     }
  24:     return (0);
  25: }
  26: 
  27: func(t)
  28:     register struct command *t;
  29: {
  30:     register struct biltins *bp;
  31:     int i;
  32: 
  33:     bp = bfunc;
  34:     if (lastchr(t->t_dcom[0]) == ':') {
  35:         xechoit(t->t_dcom);
  36:         if (!eq(t->t_dcom[0], ":") && t->t_dcom[1])
  37:             error("No args on labels");
  38:         return (1);
  39:     }
  40:     bp = isbfunc(t->t_dcom[0]);
  41:     if (bp == 0)
  42:         return (0);
  43:     /* timed builtins must go in background if output is pipe, or &'ed */
  44:     if (eq(bp->bname, "time"))
  45:         if ((t->t_dflg & FAND) || (t->t_dflg & FPOU))
  46:             return (0);
  47:     if (eq(bp->bname, "nohup") && t->t_dcom[1])
  48:         return (0);
  49:     xechoit(t->t_dcom);
  50:     setname(bp->bname);
  51:     i = blklen(t->t_dcom) - 1;
  52:     if (i < bp->minargs)
  53:         bferr("Too few arguments");
  54:     if (i > bp->maxargs)
  55:         bferr("Too many arguments");
  56:     i = (*bp->bfunct)(t->t_dcom, t);
  57:     /* time and nice may not do their deeds, all others guarantee too */
  58:     return (eq(bp->bname, "time") || eq(bp->bname, "nice") ? i : 1);
  59: }
  60: 
  61: doonintr(v)
  62:     char **v;
  63: {
  64:     register char *cp;
  65:     register char *vv = v[1];
  66: 
  67:     if (parintr == SIG_IGN)
  68:         return;
  69:     if (setintr && intty)
  70:         bferr("Can't from terminal");
  71:     cp = gointr, gointr = 0, xfree(cp);
  72:     if (vv == 0) {
  73:         signal(SIGINT, setintr ? SIG_IGN : SIG_DFL);
  74:         gointr = 0;
  75:     } else if (eq((vv = strip(vv)), "-")) {
  76:         signal(SIGINT, SIG_IGN);
  77:         gointr = "-";
  78:     } else {
  79:         gointr = savestr(vv);
  80:         signal(SIGINT, pintr);
  81:     }
  82: }
  83: 
  84: donohup()
  85: {
  86: 
  87:     if (intty)
  88:         bferr("Can't from terminal");
  89:     if (setintr == 0) {
  90:         signal(SIGHUP, SIG_IGN);
  91: #ifdef CC
  92:         submit(getpid());
  93: #endif
  94:     }
  95: }
  96: 
  97: dozip()
  98: {
  99: 
 100:     ;
 101: }
 102: 
 103: chngd(vp)
 104:     register char **vp;
 105: {
 106:     register int i;
 107:     register char *dp;
 108:     register char **cdp;
 109: 
 110:     vp++;
 111:     dp = *vp;
 112:     if (dp)
 113:         dp = globone(dp);
 114:     else {
 115:         dp = value("home");
 116:         if (*dp == 0)
 117:             bferr("No home");
 118:     }
 119:     i = chdir(dp);
 120:     if (i < 0 && dp[0] != '/') {
 121:         struct varent *c = adrof("cdpath");
 122: 
 123:         if (c == 0)
 124:             goto simple;
 125:         for (cdp = c->vec; *cdp; cdp++) {
 126:             char buf[BUFSIZ];
 127: 
 128:             strcpy(buf, *cdp);
 129:             strcat(buf, "/");
 130:             strcat(buf, dp);
 131:             i = chdir(buf);
 132:             if (i >= 0)
 133:                 goto simple;
 134:         }
 135:     }
 136: simple:
 137:     if (i < 0 && adrof(dp)) {
 138:         char *cp = value(dp);
 139: 
 140:         if (cp[0] == '/')
 141:             i = chdir(cp);
 142:     }
 143:     if (*vp)
 144:         xfree(dp);
 145:     if (i < 0)
 146:         Perror(dp);
 147: }
 148: 
 149: prvars()
 150: {
 151: 
 152:     plist(&shvhed);
 153: }
 154: 
 155: doalias(v)
 156:     register char **v;
 157: {
 158:     register struct varent *vp;
 159:     register char *p;
 160: 
 161:     v++;
 162:     p = *v++;
 163:     if (p == 0)
 164:         plist(&aliases);
 165:     else if (*v == 0) {
 166:         vp = adrof1(strip(p), &aliases);
 167:         if (vp)
 168:             blkpr(vp->vec), printf("\n");
 169:     } else {
 170:         if (eq(p, "alias") || eq(p, "unalias")) {
 171:             setname(p);
 172:             bferr("Too dangerous to alias that");
 173:         }
 174:         set1(strip(p), saveblk(v), &aliases);
 175:     }
 176: }
 177: 
 178: unalias(v)
 179:     char **v;
 180: {
 181: 
 182:     unset1(v, &aliases);
 183: }
 184: 
 185: dologout()
 186: {
 187: 
 188:     islogin();
 189:     goodbye();
 190: }
 191: 
 192: dologin(v)
 193:     char **v;
 194: {
 195: 
 196:     islogin();
 197:     execl("/bin/login", "login", v[1], 0);
 198:     exit(1);
 199: }
 200: 
 201: donewgrp(v)
 202:     char **v;
 203: {
 204: 
 205: #ifndef V6
 206:     execlp("newgrp", "newgrp", v[1], 0);
 207: #endif
 208:     execl("/bin/newgrp", "newgrp", v[1], 0);    /* just in case */
 209:     execl("/usr/bin/newgrp", "newgrp", v[1], 0);
 210: }
 211: 
 212: islogin()
 213: {
 214: 
 215:     if (loginsh)
 216:         return;
 217:     error("Not login shell");
 218: }
 219: 
 220: doif(v, kp)
 221:     char **v;
 222:     struct command *kp;
 223: {
 224:     register int i;
 225:     register char **vv;
 226: 
 227:     v++;
 228:     i = exp(&v);
 229:     vv = v;
 230:     if (*vv && eq(*vv, "then")) {
 231:         vv++;
 232:         if (*vv)
 233:             bferr("Improper then");
 234:         setname("then");
 235:         /*
 236: 		 * If expression was zero, then scan to else,
 237: 		 * otherwise just fall into following code.
 238: 		 */
 239:         if (!i)
 240:             search(ZIF, 0);
 241:         return;
 242:     }
 243:     /*
 244: 	 * Simple command attached to this if.
 245: 	 * Left shift the node in this tree, munging it
 246: 	 * so we can reexecute it.
 247: 	 */
 248:     if (i) {
 249:         lshift(kp->t_dcom, vv - kp->t_dcom);
 250:         reexecute(kp);
 251:         donefds();
 252:     }
 253: }
 254: 
 255: /*
 256:  * Reexecute a command, being careful not
 257:  * to redo i/o redirection, which is already set up.
 258:  */
 259: reexecute(kp)
 260:     register struct command *kp;
 261: {
 262: 
 263:     kp->t_dflg = FREDO;
 264:     execute(kp);
 265: }
 266: 
 267: doelse()
 268: {
 269: 
 270:     search(ZELSE, 0);
 271: }
 272: 
 273: dogoto(v)
 274:     char **v;
 275: {
 276:     register struct whyle *wp;
 277:     char *lp;
 278: 
 279:     /*
 280: 	 * While we still can, locate any unknown ends of existing loops.
 281: 	 * This obscure code is the WORST result of the fact that we
 282: 	 * don't really parse.
 283: 	 */
 284:     for (wp = whyles; wp; wp = wp->w_next)
 285:         if (wp->w_end == 0)
 286:             wp->w_end = search(ZBREAK, 0);
 287:         else
 288:             bseek(wp->w_end);
 289:     search(ZGOTO, 0, lp = globone(v[1]));
 290:     xfree(lp);
 291:     /*
 292: 	 * Eliminate loops which were exited.
 293: 	 */
 294:     wfree();
 295: }
 296: 
 297: doswitch(v)
 298:     register char **v;
 299: {
 300:     register char *cp, *lp;
 301: 
 302:     v++;
 303:     if (!*v || *(*v++) != '(')
 304:         goto syntax;
 305:     cp = **v == ')' ? "" : *v++;
 306:     if (*(*v++) != ')')
 307:         v--;
 308:     if (*v)
 309: syntax:
 310:         error("Syntax error");
 311:     search(ZSWITCH, 0, lp = globone(cp));
 312:     xfree(lp);
 313: }
 314: 
 315: dobreak()
 316: {
 317: 
 318:     if (whyles)
 319:         toend();
 320:     else
 321:         bferr("Not in while/foreach");
 322: }
 323: 
 324: doexit(v)
 325:     char **v;
 326: {
 327: 
 328:     /*
 329: 	 * Don't DEMAND parentheses here either.
 330: 	 */
 331:     v++;
 332:     if (*v) {
 333:         set("status", putn(exp(&v)));
 334:         if (*v)
 335:             bferr("Expression syntax");
 336:     }
 337:     btoeof();
 338:     if (intty)
 339:         close(SHIN);
 340: }
 341: 
 342: doforeach(v)
 343:     register char **v;
 344: {
 345:     register char *cp;
 346:     register struct whyle *nwp;
 347: 
 348:     v++;
 349:     cp = strip(*v);
 350:     while (*cp && letter(*cp))
 351:         cp++;
 352:     if (*cp || strlen(*v) >= 20)
 353:         bferr("Invalid variable");
 354:     cp = *v++;
 355:     if (v[0][0] != '(' || v[blklen(v) - 1][0] != ')')
 356:         bferr("Words not ()'ed");
 357:     v++;
 358:     gflag = 0, rscan(v, tglob);
 359:     v = glob(v);
 360:     if (v == 0)
 361:         bferr("No match");
 362:     nwp = (struct whyle *) calloc(1, sizeof *nwp);
 363:     nwp->w_fe = nwp->w_fe0 = v; gargv = 0;
 364:     nwp->w_start = btell();
 365:     nwp->w_fename = savestr(cp);
 366:     nwp->w_next = whyles;
 367:     whyles = nwp;
 368:     /*
 369: 	 * Pre-read the loop so as to be more
 370: 	 * comprehensible to a terminal user.
 371: 	 */
 372:     if (intty)
 373:         preread();
 374:     doagain();
 375: }
 376: 
 377: dowhile(v)
 378:     char **v;
 379: {
 380:     register int status;
 381:     register bool again = whyles && whyles->w_start == lineloc && whyles->w_fename == 0;
 382: 
 383:     v++;
 384:     /*
 385: 	 * Implement prereading here also, taking care not to
 386: 	 * evaluate the expression before the loop has been read up
 387: 	 * from a terminal.
 388: 	 */
 389:     if (intty && !again)
 390:         status = !exp0(&v, 1);
 391:     else
 392:         status = !exp(&v);
 393:     if (*v)
 394:         bferr("Expression syntax");
 395:     if (!again) {
 396:         register struct whyle *nwp = (struct whyle *) calloc(1, sizeof (*nwp));
 397: 
 398:         nwp->w_start = lineloc;
 399:         nwp->w_end = 0;
 400:         nwp->w_next = whyles;
 401:         whyles = nwp;
 402:         if (intty) {
 403:             /*
 404: 			 * The tty preread
 405: 			 */
 406:             preread();
 407:             doagain();
 408:             return;
 409:         }
 410:     }
 411:     if (status)
 412:         /* We ain't gonna loop no more, no more! */
 413:         toend();
 414: }
 415: 
 416: preread()
 417: {
 418:     register int (*oldint)();
 419: 
 420:     whyles->w_end = -1;
 421:     if (setintr)
 422:         oldint = signal(SIGINT, pintr);
 423:     search(ZBREAK, 0);
 424:     if (setintr)
 425:         signal(SIGINT, oldint);
 426:     whyles->w_end = btell();
 427: }
 428: 
 429: doend()
 430: {
 431: 
 432:     if (!whyles)
 433:         bferr("Not in while/foreach");
 434:     whyles->w_end = btell();
 435:     doagain();
 436: }
 437: 
 438: docontin()
 439: {
 440: 
 441:     if (!whyles)
 442:         bferr("Not in while/foreach");
 443:     doagain();
 444: }
 445: 
 446: doagain()
 447: {
 448: 
 449:     /* Repeating a while is simple */
 450:     if (whyles->w_fename == 0) {
 451:         bseek(whyles->w_start);
 452:         return;
 453:     }
 454:     /*
 455: 	 * The foreach variable list actually has a spurious word
 456: 	 * ")" at the end of the w_fe list.  Thus we are at the
 457: 	 * of the list if one word beyond this is 0.
 458: 	 */
 459:     if (!whyles->w_fe[1]) {
 460:         dobreak();
 461:         return;
 462:     }
 463:     set(whyles->w_fename, savestr(*whyles->w_fe++));
 464:     bseek(whyles->w_start);
 465: }
 466: 
 467: dorepeat(v, kp)
 468:     char **v;
 469:     struct command *kp;
 470: {
 471:     register int i;
 472:     register int (*saveintr)();
 473: 
 474:     i = getn(v[1]);
 475:     if (setintr)
 476:         saveintr = signal(SIGINT, SIG_IGN);
 477:     lshift(v, 2);
 478:     while (i > 0) {
 479:         if (setintr)
 480:             signal(SIGINT, pintr);
 481:         reexecute(kp);
 482:         --i;
 483:     }
 484:     donefds();
 485:     if (setintr)
 486:         signal(SIGINT, saveintr);
 487: }
 488: 
 489: doswbrk()
 490: {
 491: 
 492:     search(ZBRKSW, 0);
 493: }
 494: 
 495: srchx(cp)
 496:     register char *cp;
 497: {
 498:     register struct srch *sp;
 499: 
 500:     for (sp = srchn; sp->s_name; sp++)
 501:         if (eq(cp, sp->s_name))
 502:             return (sp->s_value);
 503:     return (-1);
 504: }
 505: 
 506: char    Stype;
 507: char    *Sgoal;
 508: 
 509: /*VARARGS2*/
 510: search(type, level, goal)
 511:     int type;
 512:     register int level;
 513:     char *goal;
 514: {
 515:     char wordbuf[BUFSIZ];
 516:     register char *aword = wordbuf;
 517:     register char *cp;
 518: 
 519:     Stype = type; Sgoal = goal;
 520:     if (type == ZGOTO)
 521:         bseek(0l);
 522:     do {
 523:         if (intty && fseekp == feobp)
 524:             printf("? "), flush();
 525:         aword[0] = 0, getword(aword);
 526:         switch (srchx(aword)) {
 527: 
 528:         case ZELSE:
 529:             if (level == 0 && type == ZIF)
 530:                 return;
 531:             break;
 532: 
 533:         case ZIF:
 534:             while (getword(aword))
 535:                 continue;
 536:             if ((type == ZIF || type == ZELSE) && eq(aword, "then"))
 537:                 level++;
 538:             break;
 539: 
 540:         case ZENDIF:
 541:             if (type == ZIF || type == ZELSE)
 542:                 level--;
 543:             break;
 544: 
 545:         case ZFOREACH:
 546:         case ZWHILE:
 547:             if (type == ZBREAK)
 548:                 level++;
 549:             break;
 550: 
 551:         case ZEND:
 552:             if (type == ZBREAK)
 553:                 level--;
 554:             break;
 555: 
 556:         case ZSWITCH:
 557:             if (type == ZSWITCH || type == ZBRKSW)
 558:                 level++;
 559:             break;
 560: 
 561:         case ZENDSW:
 562:             if (type == ZSWITCH || type == ZBRKSW)
 563:                 level--;
 564:             break;
 565: 
 566:         case ZLABEL:
 567:             if (type == ZGOTO && getword(aword) && eq(aword, goal))
 568:                 level = -1;
 569:             break;
 570: 
 571:         default:
 572:             if (type != ZGOTO && (type != ZSWITCH || level != 0))
 573:                 break;
 574:             if (lastchr(aword) != ':')
 575:                 break;
 576:             aword[strlen(aword) - 1] = 0;
 577:             if (type == ZGOTO && eq(aword, goal) || type == ZSWITCH && eq(aword, "default"))
 578:                 level = -1;
 579:             break;
 580: 
 581:         case ZCASE:
 582:             if (type != ZSWITCH || level != 0)
 583:                 break;
 584:             getword(aword);
 585:             if (lastchr(aword) == ':')
 586:                 aword[strlen(aword) - 1] = 0;
 587:             cp = strip(Dfix1(aword));
 588:             if (Gmatch(goal, cp))
 589:                 level = -1;
 590:             xfree(cp);
 591:             break;
 592: 
 593:         case ZDEFAULT:
 594:             if (type == ZSWITCH && level == 0)
 595:                 level = -1;
 596:             break;
 597:         }
 598:         getword(0);
 599:     } while (level >= 0);
 600: }
 601: 
 602: getword(wp)
 603:     register char *wp;
 604: {
 605:     register int found = 0;
 606:     register int c, d;
 607: 
 608:     c = readc(1);
 609:     d = 0;
 610:     do {
 611:         while (c == ' ' || c == '\t')
 612:             c = readc(1);
 613:         if (c < 0)
 614:             goto past;
 615:         if (c == '\n') {
 616:             if (wp)
 617:                 break;
 618:             return (0);
 619:         }
 620:         unreadc(c);
 621:         found = 1;
 622:         do {
 623:             c = readc(1);
 624:             if (c == '\\' && (c = readc(1)) == '\n')
 625:                 c = ' ';
 626:             if (any(c, "'\""))
 627:                 if (d == 0)
 628:                     d = c;
 629:                 else if (d == c)
 630:                     d = 0;
 631:             if (c < 0)
 632:                 goto past;
 633:             if (wp)
 634:                 *wp++ = c;
 635:         } while ((d || c != ' ' && c != '\t') && c != '\n');
 636:     } while (wp == 0);
 637:     unreadc(c);
 638:     if (found)
 639:         *--wp = 0;
 640:     return (found);
 641: 
 642: past:
 643:     switch (Stype) {
 644: 
 645:     case ZIF:
 646:         bferr("then/endif not found");
 647: 
 648:     case ZELSE:
 649:         bferr("endif not found");
 650: 
 651:     case ZBRKSW:
 652:     case ZSWITCH:
 653:         bferr("endsw not found");
 654: 
 655:     case ZBREAK:
 656:         bferr("end not found");
 657: 
 658:     case ZGOTO:
 659:         setname(Sgoal);
 660:         bferr("label not found");
 661:     }
 662:     /*NOTREACHED*/
 663: }
 664: 
 665: toend()
 666: {
 667: 
 668:     if (whyles->w_end == 0) {
 669:         search(ZBREAK, 0);
 670:         whyles->w_end = btell() - 1;
 671:     } else
 672:         bseek(whyles->w_end);
 673:     wfree();
 674: }
 675: 
 676: wfree()
 677: {
 678:     long o = btell();
 679: 
 680:     while (whyles) {
 681:         register struct whyle *wp = whyles;
 682:         register struct whyle *nwp = wp->w_next;
 683: 
 684:         if (o >= wp->w_start && (wp->w_end == 0 || o < wp->w_end))
 685:             break;
 686:         if (wp->w_fe0)
 687:             blkfree(wp->w_fe0);
 688:         if (wp->w_fename)
 689:             xfree(wp->w_fename);
 690:         xfree(wp);
 691:         whyles = nwp;
 692:     }
 693: }
 694: 
 695: doecho(v)
 696:     char **v;
 697: {
 698: 
 699:     echo(' ', v);
 700: }
 701: 
 702: doglob(v)
 703:     char **v;
 704: {
 705: 
 706:     echo(0, v);
 707:     flush();
 708: }
 709: 
 710: echo(sep, v)
 711:     char sep;
 712:     register char **v;
 713: {
 714:     register char *cp;
 715:     int (*saveintr)();
 716:     if (setintr)
 717:         saveintr = signal(SIGINT, pintr);
 718: 
 719:     v++;
 720:     if (*v == 0)
 721:         return;
 722:     gflag = 0; rscan(v, tglob);
 723:     if (gflag) {
 724:         v = glob(v);
 725:         if (v == 0)
 726:             bferr("No match");
 727:     } else
 728:         scan(v, trim);
 729:     while (cp = *v++) {
 730:         register int c;
 731: 
 732:         while (c = *cp++) {
 733:             if (sep == ' ' && *cp && c == '\\') {
 734:                 c = *cp++;
 735:                 if (c == 'c') {
 736:                     flush();
 737:                     return;
 738:                 } else if (c == 'n')
 739:                     c = '\n';
 740:                 else
 741:                     putchar('\\');
 742:             }
 743:             putchar(c | QUOTE);
 744:         }
 745:         if (*v)
 746:             putchar(sep | QUOTE);
 747:     }
 748:     if (sep)
 749:         putchar('\n');
 750:     if (setintr)
 751:         signal(SIGINT, saveintr);
 752:     if (gargv)
 753:         blkfree(gargv), gargv = 0;
 754: }
 755: 
 756: #ifndef V6
 757: char    **environ;
 758: 
 759: dosetenv(v)
 760:     register char **v;
 761: {
 762:     char *lp = globone(v[2]);
 763: 
 764:     setenv(v[1], lp);
 765:     if (eq(v[1], "PATH"))
 766:         importpath(lp);
 767:     xfree(lp);
 768: }
 769: 
 770: setenv(name, value)
 771:     char *name, *value;
 772: {
 773:     register char **ep = environ;
 774:     register char *cp, *dp;
 775:     char *blk[2], **oep = ep;
 776: 
 777:     for (; *ep; ep++) {
 778:         for (cp = name, dp = *ep; *cp && *cp == *dp; cp++, dp++)
 779:             continue;
 780:         if (*cp != 0 || *dp != '=')
 781:             continue;
 782:         cp = strspl("=", value);
 783:         xfree(*ep);
 784:         *ep = strspl(name, cp);
 785:         xfree(cp);
 786:         scan(ep, trim);
 787:         return;
 788:     }
 789:     blk[0] = strspl(name, "="); blk[1] = 0;
 790:     environ = blkspl(environ, blk);
 791:     xfree(oep);
 792:     setenv(name, value);
 793: }
 794: 
 795: doumask(v)
 796:     register char **v;
 797: {
 798:     register char *cp = v[1];
 799:     register int i;
 800: 
 801:     if (cp == 0) {
 802:         i = umask(0);
 803:         umask(i);
 804:         printf("%o\n", i);
 805:         return;
 806:     }
 807:     i = 0;
 808:     while (digit(*cp) && *cp != '8' && *cp != '9')
 809:         i = i * 8 + *cp++ - '0';
 810:     if (*cp || i < 0 || i > 0777)
 811:         bferr("Improper mask");
 812:     umask(i);
 813: }
 814: #endif

Defined functions

chngd defined in line 103; used 6 times
doagain defined in line 446; used 4 times
doalias defined in line 155; used 4 times
dobreak defined in line 315; used 5 times
docontin defined in line 438; used 4 times
doecho defined in line 695; used 4 times
doelse defined in line 267; used 4 times
doend defined in line 429; used 4 times
doexit defined in line 324; used 4 times
doforeach defined in line 342; used 4 times
doglob defined in line 702; used 4 times
dogoto defined in line 273; used 4 times
doif defined in line 220; used 4 times
dologin defined in line 192; used 4 times
dologout defined in line 185; used 4 times
donewgrp defined in line 201; used 4 times
donohup defined in line 84; used 4 times
doonintr defined in line 61; used 4 times
dorepeat defined in line 467; used 4 times
dosetenv defined in line 759; used 4 times
doswbrk defined in line 489; used 4 times
doswitch defined in line 297; used 4 times
doumask defined in line 795; used 4 times
dowhile defined in line 377; used 4 times
dozip defined in line 97; used 10 times
echo defined in line 710; used 2 times
getword defined in line 602; used 5 times
isbfunc defined in line 9; used 3 times
islogin defined in line 212; used 2 times
preread defined in line 416; used 2 times
prvars defined in line 149; used 2 times
reexecute defined in line 259; used 2 times
search defined in line 510; used 9 times
setenv defined in line 770; used 3 times
srchx defined in line 495; used 2 times
toend defined in line 665; used 2 times
unalias defined in line 178; used 4 times
wfree defined in line 676; used 3 times

Defined variables

Sgoal defined in line 507; used 2 times
Stype defined in line 506; used 2 times
environ defined in line 757; used 3 times
Last modified: 1980-09-13
Generated: 2016-12-26
Generated by src2html V0.67
page hit count: 1966
Valid CSS Valid XHTML 1.0 Strict