1: /*
   2:  * Copyright (c) 1980 Regents of the University of California.
   3:  * All rights reserved.  The Berkeley software License Agreement
   4:  * specifies the terms and conditions for redistribution.
   5:  */
   6: 
   7: #ifndef lint
   8: static char *sccsid = "@(#)aux.c	5.4 (Berkeley) 1/13/86";
   9: #endif not lint
  10: 
  11: #include "rcv.h"
  12: #include <sys/stat.h>
  13: #include <ctype.h>
  14: 
  15: /*
  16:  * Mail -- a mail program
  17:  *
  18:  * Auxiliary functions.
  19:  */
  20: 
  21: /*
  22:  * Return a pointer to a dynamic copy of the argument.
  23:  */
  24: 
  25: char *
  26: savestr(str)
  27:     char *str;
  28: {
  29:     register char *cp, *cp2, *top;
  30: 
  31:     for (cp = str; *cp; cp++)
  32:         ;
  33:     top = salloc(cp-str + 1);
  34:     if (top == NOSTR)
  35:         return(NOSTR);
  36:     for (cp = str, cp2 = top; *cp; cp++)
  37:         *cp2++ = *cp;
  38:     *cp2 = 0;
  39:     return(top);
  40: }
  41: 
  42: /*
  43:  * Copy the name from the passed header line into the passed
  44:  * name buffer.  Null pad the name buffer.
  45:  */
  46: 
  47: copyname(linebuf, nbuf)
  48:     char *linebuf, *nbuf;
  49: {
  50:     register char *cp, *cp2;
  51: 
  52:     for (cp = linebuf + 5, cp2 = nbuf; *cp != ' ' && cp2-nbuf < 8; cp++)
  53:         *cp2++ = *cp;
  54:     while (cp2-nbuf < 8)
  55:         *cp2++ = 0;
  56: }
  57: 
  58: /*
  59:  * Announce a fatal error and die.
  60:  */
  61: 
  62: panic(str)
  63:     char *str;
  64: {
  65:     prs("panic: ");
  66:     prs(str);
  67:     prs("\n");
  68:     exit(1);
  69: }
  70: 
  71: /*
  72:  * Catch stdio errors and report them more nicely.
  73:  */
  74: 
  75: _error(str)
  76:     char *str;
  77: {
  78:     prs("Stdio Error: ");
  79:     prs(str);
  80:     prs("\n");
  81:     abort();
  82: }
  83: 
  84: /*
  85:  * Print a string on diagnostic output.
  86:  */
  87: 
  88: prs(str)
  89:     char *str;
  90: {
  91:     register char *s;
  92: 
  93:     for (s = str; *s; s++)
  94:         ;
  95:     write(2, str, s-str);
  96: }
  97: 
  98: /*
  99:  * Touch the named message by setting its MTOUCH flag.
 100:  * Touched messages have the effect of not being sent
 101:  * back to the system mailbox on exit.
 102:  */
 103: 
 104: touch(mesg)
 105: {
 106:     register struct message *mp;
 107: 
 108:     if (mesg < 1 || mesg > msgCount)
 109:         return;
 110:     mp = &message[mesg-1];
 111:     mp->m_flag |= MTOUCH;
 112:     if ((mp->m_flag & MREAD) == 0)
 113:         mp->m_flag |= MREAD|MSTATUS;
 114: }
 115: 
 116: /*
 117:  * Test to see if the passed file name is a directory.
 118:  * Return true if it is.
 119:  */
 120: 
 121: isdir(name)
 122:     char name[];
 123: {
 124:     struct stat sbuf;
 125: 
 126:     if (stat(name, &sbuf) < 0)
 127:         return(0);
 128:     return((sbuf.st_mode & S_IFMT) == S_IFDIR);
 129: }
 130: 
 131: /*
 132:  * Count the number of arguments in the given string raw list.
 133:  */
 134: 
 135: argcount(argv)
 136:     char **argv;
 137: {
 138:     register char **ap;
 139: 
 140:     for (ap = argv; *ap != NOSTR; ap++)
 141:         ;
 142:     return(ap-argv);
 143: }
 144: 
 145: /*
 146:  * Given a file address, determine the
 147:  * block number it represents.
 148:  */
 149: 
 150: blockof(off)
 151:     off_t off;
 152: {
 153:     off_t a;
 154: 
 155:     a = off >> 9;
 156:     a &= 077777;
 157:     return((int) a);
 158: }
 159: 
 160: /*
 161:  * Take a file address, and determine
 162:  * its offset in the current block.
 163:  */
 164: 
 165: offsetof(off)
 166:     off_t off;
 167: {
 168:     off_t a;
 169: 
 170:     a = off & 0777;
 171:     return((int) a);
 172: }
 173: 
 174: /*
 175:  * Return the desired header line from the passed message
 176:  * pointer (or NOSTR if the desired header field is not available).
 177:  */
 178: 
 179: char *
 180: hfield(field, mp)
 181:     char field[];
 182:     struct message *mp;
 183: {
 184:     register FILE *ibuf;
 185:     char linebuf[LINESIZE];
 186:     register int lc;
 187: 
 188:     ibuf = setinput(mp);
 189:     if ((lc = mp->m_lines) <= 0)
 190:         return(NOSTR);
 191:     if (readline(ibuf, linebuf) < 0)
 192:         return(NOSTR);
 193:     lc--;
 194:     do {
 195:         lc = gethfield(ibuf, linebuf, lc);
 196:         if (lc == -1)
 197:             return(NOSTR);
 198:         if (ishfield(linebuf, field))
 199:             return(savestr(hcontents(linebuf)));
 200:     } while (lc > 0);
 201:     return(NOSTR);
 202: }
 203: 
 204: /*
 205:  * Return the next header field found in the given message.
 206:  * Return > 0 if something found, <= 0 elsewise.
 207:  * Must deal with \ continuations & other such fraud.
 208:  */
 209: 
 210: gethfield(f, linebuf, rem)
 211:     register FILE *f;
 212:     char linebuf[];
 213:     register int rem;
 214: {
 215:     char line2[LINESIZE];
 216:     long loc;
 217:     register char *cp, *cp2;
 218:     register int c;
 219: 
 220: 
 221:     for (;;) {
 222:         if (rem <= 0)
 223:             return(-1);
 224:         if (readline(f, linebuf) < 0)
 225:             return(-1);
 226:         rem--;
 227:         if (strlen(linebuf) == 0)
 228:             return(-1);
 229:         if (isspace(linebuf[0]))
 230:             continue;
 231:         if (linebuf[0] == '>')
 232:             continue;
 233:         cp = index(linebuf, ':');
 234:         if (cp == NOSTR)
 235:             continue;
 236:         for (cp2 = linebuf; cp2 < cp; cp2++)
 237:             if (isdigit(*cp2))
 238:                 continue;
 239: 
 240:         /*
 241: 		 * I guess we got a headline.
 242: 		 * Handle wraparounding
 243: 		 */
 244: 
 245:         for (;;) {
 246:             if (rem <= 0)
 247:                 break;
 248: #ifdef CANTELL
 249:             loc = ftell(f);
 250:             if (readline(f, line2) < 0)
 251:                 break;
 252:             rem--;
 253:             if (!isspace(line2[0])) {
 254:                 fseek(f, loc, 0);
 255:                 rem++;
 256:                 break;
 257:             }
 258: #else
 259:             c = getc(f);
 260:             ungetc(c, f);
 261:             if (!isspace(c) || c == '\n')
 262:                 break;
 263:             if (readline(f, line2) < 0)
 264:                 break;
 265:             rem--;
 266: #endif
 267:             cp2 = line2;
 268:             for (cp2 = line2; *cp2 != 0 && isspace(*cp2); cp2++)
 269:                 ;
 270:             if (strlen(linebuf) + strlen(cp2) >= LINESIZE-2)
 271:                 break;
 272:             cp = &linebuf[strlen(linebuf)];
 273:             while (cp > linebuf &&
 274:                 (isspace(cp[-1]) || cp[-1] == '\\'))
 275:                 cp--;
 276:             *cp++ = ' ';
 277:             for (cp2 = line2; *cp2 != 0 && isspace(*cp2); cp2++)
 278:                 ;
 279:             strcpy(cp, cp2);
 280:         }
 281:         if ((c = strlen(linebuf)) > 0) {
 282:             cp = &linebuf[c-1];
 283:             while (cp > linebuf && isspace(*cp))
 284:                 cp--;
 285:             *++cp = 0;
 286:         }
 287:         return(rem);
 288:     }
 289:     /* NOTREACHED */
 290: }
 291: 
 292: /*
 293:  * Check whether the passed line is a header line of
 294:  * the desired breed.
 295:  */
 296: 
 297: ishfield(linebuf, field)
 298:     char linebuf[], field[];
 299: {
 300:     register char *cp;
 301:     register int c;
 302: 
 303:     if ((cp = index(linebuf, ':')) == NOSTR)
 304:         return(0);
 305:     if (cp == linebuf)
 306:         return(0);
 307:     cp--;
 308:     while (cp > linebuf && isspace(*cp))
 309:         cp--;
 310:     c = *++cp;
 311:     *cp = 0;
 312:     if (icequal(linebuf ,field)) {
 313:         *cp = c;
 314:         return(1);
 315:     }
 316:     *cp = c;
 317:     return(0);
 318: }
 319: 
 320: /*
 321:  * Extract the non label information from the given header field
 322:  * and return it.
 323:  */
 324: 
 325: char *
 326: hcontents(hfield)
 327:     char hfield[];
 328: {
 329:     register char *cp;
 330: 
 331:     if ((cp = index(hfield, ':')) == NOSTR)
 332:         return(NOSTR);
 333:     cp++;
 334:     while (*cp && isspace(*cp))
 335:         cp++;
 336:     return(cp);
 337: }
 338: 
 339: /*
 340:  * Compare two strings, ignoring case.
 341:  */
 342: 
 343: icequal(s1, s2)
 344:     register char *s1, *s2;
 345: {
 346: 
 347:     while (raise(*s1++) == raise(*s2))
 348:         if (*s2++ == 0)
 349:             return(1);
 350:     return(0);
 351: }
 352: 
 353: /*
 354:  * Copy a string, lowercasing it as we go.
 355:  */
 356: istrcpy(dest, src)
 357:     char *dest, *src;
 358: {
 359:     register char *cp, *cp2;
 360: 
 361:     cp2 = dest;
 362:     cp = src;
 363:     do {
 364:         *cp2++ = little(*cp);
 365:     } while (*cp++ != 0);
 366: }
 367: 
 368: /*
 369:  * The following code deals with input stacking to do source
 370:  * commands.  All but the current file pointer are saved on
 371:  * the stack.
 372:  */
 373: 
 374: static  int ssp = -1;       /* Top of file stack */
 375: struct sstack {
 376:     FILE    *s_file;        /* File we were in. */
 377:     int s_cond;         /* Saved state of conditionals */
 378:     int s_loading;      /* Loading .mailrc, etc. */
 379: } sstack[NOFILE];
 380: 
 381: /*
 382:  * Pushdown current input file and switch to a new one.
 383:  * Set the global flag "sourcing" so that others will realize
 384:  * that they are no longer reading from a tty (in all probability).
 385:  */
 386: 
 387: source(name)
 388:     char name[];
 389: {
 390:     register FILE *fi;
 391:     register char *cp;
 392: 
 393:     if ((cp = expand(name)) == NOSTR)
 394:         return(1);
 395:     if ((fi = fopen(cp, "r")) == NULL) {
 396:         perror(cp);
 397:         return(1);
 398:     }
 399:     if (ssp >= NOFILE - 2) {
 400:         printf("Too much \"sourcing\" going on.\n");
 401:         fclose(fi);
 402:         return(1);
 403:     }
 404:     sstack[++ssp].s_file = input;
 405:     sstack[ssp].s_cond = cond;
 406:     sstack[ssp].s_loading = loading;
 407:     loading = 0;
 408:     cond = CANY;
 409:     input = fi;
 410:     sourcing++;
 411:     return(0);
 412: }
 413: 
 414: /*
 415:  * Source a file, but do nothing if the file cannot be opened.
 416:  */
 417: 
 418: source1(name)
 419:     char name[];
 420: {
 421:     register int f;
 422: 
 423:     if ((f = open(name, 0)) < 0)
 424:         return(0);
 425:     close(f);
 426:     source(name);
 427: }
 428: 
 429: /*
 430:  * Pop the current input back to the previous level.
 431:  * Update the "sourcing" flag as appropriate.
 432:  */
 433: 
 434: unstack()
 435: {
 436:     if (ssp < 0) {
 437:         printf("\"Source\" stack over-pop.\n");
 438:         sourcing = 0;
 439:         return(1);
 440:     }
 441:     fclose(input);
 442:     if (cond != CANY)
 443:         printf("Unmatched \"if\"\n");
 444:     cond = sstack[ssp].s_cond;
 445:     loading = sstack[ssp].s_loading;
 446:     input = sstack[ssp--].s_file;
 447:     if (ssp < 0)
 448:         sourcing = loading;
 449:     return(0);
 450: }
 451: 
 452: /*
 453:  * Touch the indicated file.
 454:  * This is nifty for the shell.
 455:  * If we have the utime() system call, this is better served
 456:  * by using that, since it will work for empty files.
 457:  * On non-utime systems, we must sleep a second, then read.
 458:  */
 459: 
 460: alter(name)
 461:     char name[];
 462: {
 463: #ifdef UTIME
 464:     struct stat statb;
 465:     long time();
 466:     time_t time_p[2];
 467: #else
 468:     register int pid, f;
 469:     char w;
 470: #endif UTIME
 471: 
 472: #ifdef UTIME
 473:     if (stat(name, &statb) < 0)
 474:         return;
 475:     time_p[0] = time((long *) 0) + 1;
 476:     time_p[1] = statb.st_mtime;
 477:     utime(name, time_p);
 478: #else
 479:     sleep(1);
 480:     if ((f = open(name, 0)) < 0)
 481:         return;
 482:     read(f, &w, 1);
 483:     exit(0);
 484: #endif
 485: }
 486: 
 487: /*
 488:  * Examine the passed line buffer and
 489:  * return true if it is all blanks and tabs.
 490:  */
 491: 
 492: blankline(linebuf)
 493:     char linebuf[];
 494: {
 495:     register char *cp;
 496: 
 497:     for (cp = linebuf; *cp; cp++)
 498:         if (*cp != ' ' && *cp != '\t')
 499:             return(0);
 500:     return(1);
 501: }
 502: 
 503: /*
 504:  * Get sender's name from this message.  If the message has
 505:  * a bunch of arpanet stuff in it, we may have to skin the name
 506:  * before returning it.
 507:  */
 508: char *
 509: nameof(mp, reptype)
 510:     register struct message *mp;
 511: {
 512:     register char *cp, *cp2;
 513: 
 514:     cp = skin(name1(mp, reptype));
 515:     if (reptype != 0 || charcount(cp, '!') < 2)
 516:         return(cp);
 517:     cp2 = rindex(cp, '!');
 518:     cp2--;
 519:     while (cp2 > cp && *cp2 != '!')
 520:         cp2--;
 521:     if (*cp2 == '!')
 522:         return(cp2 + 1);
 523:     return(cp);
 524: }
 525: 
 526: /*
 527:  * Skin an arpa net address according to the RFC 822 interpretation
 528:  * of "host-phrase."
 529:  */
 530: char *
 531: skin(name)
 532:     char *name;
 533: {
 534:     register int c;
 535:     register char *cp, *cp2;
 536:     char *bufend;
 537:     int gotlt, lastsp;
 538:     char nbuf[BUFSIZ];
 539:     int nesting;
 540: 
 541:     if (name == NOSTR)
 542:         return(NOSTR);
 543:     if (index(name, '(') == NOSTR && index(name, '<') == NOSTR
 544:     && index(name, ' ') == NOSTR)
 545:         return(name);
 546:     gotlt = 0;
 547:     lastsp = 0;
 548:     bufend = nbuf;
 549:     for (cp = name, cp2 = bufend; c = *cp++; ) {
 550:         switch (c) {
 551:         case '(':
 552:             /*
 553: 			 * Start of a "comment".
 554: 			 * Ignore it.
 555: 			 */
 556:             nesting = 1;
 557:             while ((c = *cp) != 0) {
 558:                 cp++;
 559:                 switch (c) {
 560:                 case '\\':
 561:                     if (*cp == 0)
 562:                         goto outcm;
 563:                     cp++;
 564:                     break;
 565:                 case '(':
 566:                     nesting++;
 567:                     break;
 568: 
 569:                 case ')':
 570:                     --nesting;
 571:                     break;
 572:                 }
 573: 
 574:                 if (nesting <= 0)
 575:                     break;
 576:             }
 577:         outcm:
 578:             lastsp = 0;
 579:             break;
 580: 
 581:         case '"':
 582:             /*
 583: 			 * Start of a "quoted-string".
 584: 			 * Copy it in its entirety.
 585: 			 */
 586:             while ((c = *cp) != 0) {
 587:                 cp++;
 588:                 switch (c) {
 589:                 case '\\':
 590:                     if ((c = *cp) == 0)
 591:                         goto outqs;
 592:                     cp++;
 593:                     break;
 594:                 case '"':
 595:                     goto outqs;
 596:                 }
 597:                 *cp2++ = c;
 598:             }
 599:         outqs:
 600:             lastsp = 0;
 601:             break;
 602: 
 603:         case ' ':
 604:             if (cp[0] == 'a' && cp[1] == 't' && cp[2] == ' ')
 605:                 cp += 3, *cp2++ = '@';
 606:             else
 607:             if (cp[0] == '@' && cp[1] == ' ')
 608:                 cp += 2, *cp2++ = '@';
 609:             else
 610:                 lastsp = 1;
 611:             break;
 612: 
 613:         case '<':
 614:             cp2 = bufend;
 615:             gotlt++;
 616:             lastsp = 0;
 617:             break;
 618: 
 619:         case '>':
 620:             if (gotlt) {
 621:                 gotlt = 0;
 622:                 while (*cp != ',' && *cp != 0)
 623:                     cp++;
 624:                 if (*cp == 0 )
 625:                     goto done;
 626:                 *cp2++ = ',';
 627:                 *cp2++ = ' ';
 628:                 bufend = cp2;
 629:                 break;
 630:             }
 631: 
 632:             /* Fall into . . . */
 633: 
 634:         default:
 635:             if (lastsp) {
 636:                 lastsp = 0;
 637:                 *cp2++ = ' ';
 638:             }
 639:             *cp2++ = c;
 640:             break;
 641:         }
 642:     }
 643: done:
 644:     *cp2 = 0;
 645: 
 646:     return(savestr(nbuf));
 647: }
 648: 
 649: /*
 650:  * Fetch the sender's name from the passed message.
 651:  * Reptype can be
 652:  *	0 -- get sender's name for display purposes
 653:  *	1 -- get sender's name for reply
 654:  *	2 -- get sender's name for Reply
 655:  */
 656: 
 657: char *
 658: name1(mp, reptype)
 659:     register struct message *mp;
 660: {
 661:     char namebuf[LINESIZE];
 662:     char linebuf[LINESIZE];
 663:     register char *cp, *cp2;
 664:     register FILE *ibuf;
 665:     int first = 1;
 666: 
 667:     if ((cp = hfield("from", mp)) != NOSTR)
 668:         return(cp);
 669:     if (reptype == 0 && (cp = hfield("sender", mp)) != NOSTR)
 670:         return(cp);
 671:     ibuf = setinput(mp);
 672:     copy("", namebuf);
 673:     if (readline(ibuf, linebuf) <= 0)
 674:         return(savestr(namebuf));
 675: newname:
 676:     for (cp = linebuf; *cp != ' '; cp++)
 677:         ;
 678:     while (any(*cp, " \t"))
 679:         cp++;
 680:     for (cp2 = &namebuf[strlen(namebuf)]; *cp && !any(*cp, " \t") &&
 681:         cp2-namebuf < LINESIZE-1; *cp2++ = *cp++)
 682:         ;
 683:     *cp2 = '\0';
 684:     if (readline(ibuf, linebuf) <= 0)
 685:         return(savestr(namebuf));
 686:     if ((cp = index(linebuf, 'F')) == NULL)
 687:         return(savestr(namebuf));
 688:     if (strncmp(cp, "From", 4) != 0)
 689:         return(savestr(namebuf));
 690:     while ((cp = index(cp, 'r')) != NULL) {
 691:         if (strncmp(cp, "remote", 6) == 0) {
 692:             if ((cp = index(cp, 'f')) == NULL)
 693:                 break;
 694:             if (strncmp(cp, "from", 4) != 0)
 695:                 break;
 696:             if ((cp = index(cp, ' ')) == NULL)
 697:                 break;
 698:             cp++;
 699:             if (first) {
 700:                 copy(cp, namebuf);
 701:                 first = 0;
 702:             } else
 703:                 strcpy(rindex(namebuf, '!')+1, cp);
 704:             strcat(namebuf, "!");
 705:             goto newname;
 706:         }
 707:         cp++;
 708:     }
 709:     return(savestr(namebuf));
 710: }
 711: 
 712: /*
 713:  * Count the occurances of c in str
 714:  */
 715: charcount(str, c)
 716:     char *str;
 717: {
 718:     register char *cp;
 719:     register int i;
 720: 
 721:     for (i = 0, cp = str; *cp; cp++)
 722:         if (*cp == c)
 723:             i++;
 724:     return(i);
 725: }
 726: 
 727: /*
 728:  * Find the rightmost pointer to an instance of the
 729:  * character in the string and return it.
 730:  */
 731: char *
 732: rindex(str, c)
 733:     char str[];
 734:     register int c;
 735: {
 736:     register char *cp, *cp2;
 737: 
 738:     for (cp = str, cp2 = NOSTR; *cp; cp++)
 739:         if (c == *cp)
 740:             cp2 = cp;
 741:     return(cp2);
 742: }
 743: 
 744: /*
 745:  * See if the string is a number.
 746:  */
 747: 
 748: numeric(str)
 749:     char str[];
 750: {
 751:     register char *cp = str;
 752: 
 753:     while (*cp)
 754:         if (!isdigit(*cp++))
 755:             return(0);
 756:     return(1);
 757: }
 758: 
 759: /*
 760:  * Are any of the characters in the two strings the same?
 761:  */
 762: 
 763: anyof(s1, s2)
 764:     register char *s1, *s2;
 765: {
 766:     register int c;
 767: 
 768:     while (c = *s1++)
 769:         if (any(c, s2))
 770:             return(1);
 771:     return(0);
 772: }
 773: 
 774: /*
 775:  * Determine the leftmost index of the character
 776:  * in the string.
 777:  */
 778: 
 779: char *
 780: index(str, ch)
 781:     char *str;
 782: {
 783:     register char *cp;
 784:     register int c;
 785: 
 786:     for (c = ch, cp = str; *cp; cp++)
 787:         if (*cp == c)
 788:             return(cp);
 789:     return(NOSTR);
 790: }
 791: 
 792: /*
 793:  * See if the given header field is supposed to be ignored.
 794:  */
 795: isign(field)
 796:     char *field;
 797: {
 798:     char realfld[BUFSIZ];
 799: 
 800:     /*
 801: 	 * Lower-case the string, so that "Status" and "status"
 802: 	 * will hash to the same place.
 803: 	 */
 804:     istrcpy(realfld, field);
 805: 
 806:     if (nretained > 0)
 807:         return (!member(realfld, retain));
 808:     else
 809:         return (member(realfld, ignore));
 810: }
 811: 
 812: member(realfield, table)
 813:     register char *realfield;
 814:     register struct ignore **table;
 815: {
 816:     register struct ignore *igp;
 817: 
 818:     for (igp = table[hash(realfield)]; igp != 0; igp = igp->i_link)
 819:         if (equal(igp->i_field, realfield))
 820:             return (1);
 821: 
 822:     return (0);
 823: }

Defined functions

_error defined in line 75; never used
alter defined in line 460; used 2 times
anyof defined in line 763; used 1 times
argcount defined in line 135; used 7 times
blankline defined in line 492; used 3 times
blockof defined in line 150; used 3 times
charcount defined in line 715; used 1 times
copyname defined in line 47; never used
gethfield defined in line 210; used 1 times
hcontents defined in line 325; used 2 times
index defined in line 779; used 14 times
isdir defined in line 121; used 2 times
ishfield defined in line 297; used 1 times
isign defined in line 795; used 4 times
istrcpy defined in line 356; used 3 times
member defined in line 812; used 3 times
name1 defined in line 657; used 2 times
numeric defined in line 748; never used
offsetof defined in line 165; used 3 times
prs defined in line 88; used 6 times
rindex defined in line 731; used 9 times
savestr defined in line 25; used 7 times
skin defined in line 530; used 10 times
source defined in line 387; used 3 times
source1 defined in line 418; never used
touch defined in line 104; used 9 times
unstack defined in line 434; used 9 times

Defined variables

sccsid defined in line 8; never used
ssp defined in line 374; used 9 times
sstack defined in line 379; used 6 times

Defined struct's

sstack defined in line 375; never used
Last modified: 1986-02-01
Generated: 2016-12-26
Generated by src2html V0.67
page hit count: 2221
Valid CSS Valid XHTML 1.0 Strict