home *** CD-ROM | disk | FTP | other *** search
/ Source Code 1992 March / Source_Code_CD-ROM_Walnut_Creek_March_1992.iso / usenet / altsrcs / 3 / 3198 < prev    next >
Internet Message Format  |  1991-04-17  |  42KB

  1. From: skrenta@blekko.commodore.com (Rich Skrenta)
  2. Newsgroups: alt.sources
  3. Subject: Tass 3.2 newsreader part 2/3
  4. Message-ID: <160@blekko.commodore.com>
  5. Date: 17 Apr 91 17:01:20 GMT
  6.  
  7.  
  8. # This is a shell archive.  Remove anything before this line,
  9. # then unpack it by saving it in a file and typing "sh file".
  10. #
  11. # This archive contains:
  12. #    mail.c        main.c        misc.c        nntp.h        
  13. #    nntp_open.c    prompt.c    tass.h        time.c        
  14. #
  15.  
  16. echo x - mail.c
  17. cat >mail.c <<'@EOF'
  18.  
  19. #include    <stdio.h>
  20. #include    <sys/types.h>
  21. #include    <sys/stat.h>
  22.  
  23. #define        TRUE        1
  24. #define        FALSE        0
  25.  
  26.  
  27. char *mailbox_name = NULL;
  28. off_t mailbox_size;
  29.  
  30.  
  31. /*
  32.  *  Record size of mailbox so we can detect if new mail has arrived
  33.  */
  34.  
  35. mail_setup() {
  36.     struct stat buf;
  37.     extern char *getenv();
  38.  
  39.     if (mailbox_name == NULL)
  40.         mailbox_name = getenv("MAIL");
  41.  
  42.     if (mailbox_name == NULL)
  43.         mailbox_size = 0;
  44.     else {
  45.         if (stat(mailbox_name, &buf) >= 0)
  46.             mailbox_size = buf.st_size;
  47.         else
  48.             mailbox_size = 0;
  49.     }
  50. }
  51.  
  52.  
  53. /*
  54.  *  Return TRUE if new mail has arrived
  55.  */
  56.  
  57. mail_check() {
  58.     struct stat buf;
  59.  
  60.     if (mailbox_name != NULL
  61.     &&  stat(mailbox_name, &buf) >= 0
  62.     &&  mailbox_size < buf.st_size)
  63.         return TRUE;
  64.  
  65.     return FALSE;
  66. }
  67.  
  68. @EOF
  69.  
  70. chmod 640 mail.c
  71.  
  72. echo x - main.c
  73. cat >main.c <<'@EOF'
  74.  
  75. /*
  76.  *  Tass, a visual Usenet news reader
  77.  *  (c) Copyright 1990 by Rich Skrenta
  78.  *
  79.  *  Distribution agreement:
  80.  *
  81.  *    You may freely copy or redistribute this software, so long
  82.  *    as there is no profit made from its use, sale, trade or
  83.  *    reproduction.  You may not change this copyright notice,
  84.  *    and it must be included prominently in any copy made.
  85.  */
  86.  
  87. #include    <stdio.h>
  88. #include    <signal.h>
  89. #include    <termio.h>        /* for struct winsize */
  90. #ifdef SCO_UNIX
  91. #include    <sys/types.h>
  92. #include    <sys/stream.h>
  93. #include    <sys/ptem.h>
  94. #endif
  95. #include    "tass.h"
  96.  
  97.  
  98. int LINES, COLS;
  99.  
  100. int max_active;
  101. struct group_ent *active;        /* active file */
  102. int group_hash[TABLE_SIZE];        /* group name --> active[] */
  103. int *my_group;                /* .newsrc --> active[] */
  104. int *unread;                /* highest art read in group */
  105. int num_active;                /* one past top of active */
  106. int local_top;                /* one past top of my_group */
  107. int update = FALSE;            /* update index files only mode */
  108.  
  109. struct header *arts;
  110. long *base;
  111. int max_art;
  112. int top = 0;
  113. int top_base;
  114.  
  115. int tass_uid;
  116. int tass_gid;
  117. int real_uid;
  118. int real_gid;
  119.  
  120. int local_index;            /* do private indexing? */
  121.  
  122. char *cvers = "Tass 3.2  (c) Copyright 1991 by Rich Skrenta.  All rights reserved";
  123.  
  124.  
  125. #ifdef SIGTSTP
  126. void
  127. main_susp(i)
  128. int i;
  129. {
  130.  
  131.     Raw(FALSE);
  132.     putchar('\n');
  133.     signal(SIGTSTP, SIG_DFL);
  134.     kill(0, SIGTSTP);
  135.  
  136.     signal(SIGTSTP, main_susp);
  137.     mail_setup();
  138.     Raw(TRUE);
  139. }
  140. #endif
  141.  
  142.  
  143. main(argc, argv)
  144. int argc;
  145. char **argv;
  146. {
  147.     extern int optind, opterr;
  148.     extern char *optarg;
  149.     int errflag = 0;
  150.     int i;
  151.     int c;
  152.     extern char group_search_string[];
  153.     extern char author_search_string[];
  154.     extern char subject_search_string[];
  155.     extern char *is_remote();
  156.  
  157.     group_search_string[0] = '\0';
  158.     author_search_string[0] = '\0';
  159.     subject_search_string[0] = '\0';
  160.  
  161.     hash_init();
  162.     for (i = 0; i < TABLE_SIZE; i++)
  163.         group_hash[i] = -1;
  164.  
  165.     signal(SIGPIPE, SIG_IGN);
  166. #ifdef SIGTSTP
  167.     signal(SIGTSTP, main_susp);
  168. #endif
  169.  
  170.     tass_uid = geteuid();
  171.     tass_gid = getegid();
  172.     real_uid = getuid();
  173.     real_gid = getgid();
  174.  
  175.     init_selfinfo();    /* set up char *'s: homedir, newsrc, etc. */
  176.     init_alloc();        /* allocate initial array sizes */
  177.  
  178.     if (tass_uid == real_uid) {    /* run out of someone's account */
  179.         local_index = TRUE;    /* index in their home directory */
  180.         mkdir(indexdir, 0755);
  181.     } else            /* we're setuid, index in /usr/spool/news */
  182.         local_index = FALSE;
  183.  
  184.     while ((c = getopt(argc, argv, "f:u")) != -1) {
  185.         switch(c) {
  186.         case 'f':
  187.             strcpy(newsrc, optarg);
  188.             break;
  189.  
  190.         case 'u':
  191.             update = TRUE;
  192.             break;
  193.  
  194.         case '?':
  195.         default:
  196.             errflag++;
  197.         }
  198.     }
  199.  
  200.     if (errflag) {
  201.         fprintf(stderr, "usage: tass [options] [newsgroups]\n");
  202.         fprintf(stderr, "   -f file   use file instead of $HOME/.newsrc\n");
  203.         fprintf(stderr, "   -u        update index files only\n");
  204.         exit(1);
  205.     }
  206.  
  207.     if (!update)
  208.         printf("Tass 3.2%s\n", is_remote());
  209.  
  210.     nntp_startup();        /* connect to server if we're using nntp */
  211.     read_active();        /* load the active file into active[] */
  212.  
  213.     if (optind < argc) {
  214.         while (optind < argc) {
  215.             if (add_group(argv[optind], TRUE) < 0)
  216.                 fprintf(stderr,
  217.                     "group %s not found in active file\n",
  218.                                 argv[optind]);
  219.             optind++;
  220.         }
  221.     } else
  222.         read_newsrc(TRUE);
  223.  
  224.     if (update) {            /* index file updater only */
  225.         do_update();
  226.         exit(0);
  227.     }
  228.  
  229.     if (InitScreen() == FALSE) {
  230.         fprintf(stderr,"Screen initialization failed\n");
  231.         exit(1);
  232.     }
  233.  
  234.     ScreenSize(&LINES, &COLS);
  235.     Raw(TRUE);
  236.  
  237. #ifdef TIOCGWINSZ
  238.     {
  239.         struct winsize win;
  240.  
  241.         if (ioctl(0, TIOCGWINSZ, &win) == 0) {
  242.             if (win.ws_row != 0)
  243.                 LINES = win.ws_row - 1;
  244.             if (win.ws_col != 0)
  245.                 COLS = win.ws_col;
  246.         }
  247.     }
  248. #endif
  249.  
  250.     mail_setup();        /* record mailbox size for "you have mail" */
  251.     selection_index();
  252.  
  253.     tass_done(0);
  254. }
  255.  
  256.  
  257.  
  258. tass_done(ret)
  259. int ret;
  260. {
  261.  
  262.     nntp_finish();        /* close connection if we're using nntp */
  263.     MoveCursor(LINES, 0);
  264.     printf("\r\n");
  265.     Raw(FALSE);
  266.     exit(ret);
  267. }
  268.  
  269.  
  270. /*
  271.  *  Dynamic table management
  272.  *  These settings are memory conservative:  small initial allocations
  273.  *  and a 50% expansion on table overflow.  A fast vm system with
  274.  *  much memory might want to start with higher initial allocations
  275.  *  and a 100% expansion on overflow, especially for the arts[] array.
  276.  */
  277.  
  278. init_alloc() {
  279.  
  280.     max_active = 100;    /* initial alloc */
  281.  
  282.     active = (struct group_ent *) my_malloc(sizeof(*active) * max_active);
  283.     my_group = (int *) my_malloc(sizeof(int) * max_active);
  284.     unread = (int *) my_malloc(sizeof(int) * max_active);
  285.  
  286.     max_art = 300;        /* initial alloc */
  287.  
  288.     arts = (struct header *) my_malloc(sizeof(*arts) * max_art);
  289.     base = (long *) my_malloc(sizeof(long) * max_art);
  290. }
  291.  
  292.  
  293. expand_art() {
  294.  
  295.     max_art += max_art / 2;        /* increase by 50% */
  296.  
  297.     arts = (struct header *) my_realloc(arts, sizeof(*arts) * max_art);
  298.     base = (long *) my_realloc(base, sizeof(long) * max_art);
  299. }
  300.  
  301.  
  302. expand_active() {
  303.  
  304.     max_active += max_active / 2;        /* increase by 50% */
  305.  
  306.     active = (struct group_ent *) my_realloc(active,
  307.                         sizeof(*active) * max_active);
  308.     my_group = (int *) my_realloc(my_group, sizeof(int) * max_active);
  309.     unread = (int *) my_realloc(unread, sizeof(int) * max_active);
  310. }
  311.  
  312.  
  313. /*
  314.  *  Load the active file into active[]
  315.  */
  316.  
  317. read_active()
  318. {
  319.     FILE *fp;
  320.     char *p, *q;
  321.     char buf[200];
  322.     long h;
  323.     int i;
  324.     extern long hash_groupname();
  325.     FILE *open_active_fp();
  326.  
  327.     num_active = 0;
  328.  
  329.     fp = open_active_fp();
  330.     if (fp == NULL) {
  331.         fprintf(stderr, "can't get active file\n");
  332.         exit(1);
  333.     }
  334.  
  335.     while (fgets(buf, 200, fp) != NULL) {
  336.         for (p = buf; *p && *p != ' '; p++) ;
  337.         if (*p != ' ') {
  338.             fprintf(stderr, "active file corrupt\n");
  339.             continue;
  340.         }
  341.         *p++ = '\0';
  342.  
  343.         if (num_active >= max_active)
  344.             expand_active();
  345.  
  346.         h = hash_groupname(buf);
  347.  
  348.         if (group_hash[h] == -1)
  349.             group_hash[h] = num_active;
  350.         else {                /* hash linked list chaining */
  351.             for (i = group_hash[h]; active[i].next >= 0;
  352.                         i = active[i].next) {
  353.                 if (strcmp(active[i].name, buf) == 0)
  354.                     goto read_active_continue;
  355.                             /* kill dups */
  356.             }
  357.             if (strcmp(active[i].name, buf) == 0)
  358.                 goto read_active_continue;
  359.             active[i].next = num_active;
  360.         }
  361.  
  362.         for (q = p; *q && *q != ' '; q++) ;
  363.         if (*q != ' ') {
  364.             fprintf(stderr, "active file corrupt\n");
  365.             continue;
  366.         }
  367.  
  368.         active[num_active].name = str_save(buf);
  369.         active[num_active].max = atol(p);
  370.         active[num_active].min = atol(q);
  371.         active[num_active].next = -1;        /* hash chaining */
  372.         active[num_active].flag = NOTGOT;   /* not in my_group[] yet */
  373.  
  374.         num_active++;
  375.  
  376. read_active_continue:;
  377.  
  378.     }
  379.  
  380.     fclose(fp);
  381. }
  382.  
  383.  
  384.  
  385. /*
  386.  *  Read $HOME/.newsrc into my_group[].  my_group[] ints point to
  387.  *  active[] entries.  Sub_only determines whether we just read
  388.  *  subscribed groups or all of them.
  389.  */
  390.  
  391. read_newsrc(sub_only)
  392. int sub_only;        /* TRUE=subscribed groups only, FALSE=all groups */
  393. {
  394.     FILE *fp;
  395.     char *p;
  396.     char buf[8192];
  397.     char c;
  398.     int i;
  399.  
  400.     local_top = 0;
  401.  
  402.     fp = fopen(newsrc, "r");
  403.     if (fp == NULL) {        /* attempt to make a .newsrc */
  404.         for (i = 0; i < num_active; i++) {
  405.             if (local_top >= max_active)
  406.                 expand_active();
  407.             my_group[local_top] = i;
  408.             active[i].flag = 0;
  409.             unread[local_top] = -1;
  410.             local_top++;
  411.         }
  412.         write_newsrc();
  413.         return;
  414.     }
  415.  
  416.     while (fgets(buf, 8192, fp) != NULL) {
  417.         p = buf;
  418.         while (*p && *p != '\n' && *p != ' ' && *p != ':' && *p != '!')
  419.             p++;
  420.         c = *p;
  421.         *p++ = '\0';
  422.         if (c == '!' && sub_only)
  423.             continue;        /* unsubscribed */
  424.  
  425.         if ((i = add_group(buf, FALSE)) < 0) {
  426.             fprintf(stderr, "group %s not found in active file\n", buf);
  427.             continue;
  428.         }
  429.  
  430.         if (c != '!')        /* if we're subscribed to it */
  431.             active[my_group[i]].flag |= SUBS;
  432.  
  433.         unread[i] = parse_unread(p, my_group[i]);
  434.     }
  435.     fclose(fp);
  436. }
  437.  
  438.  
  439. /*
  440.  *  Write a new newsrc from my_group[] and active[]
  441.  *  Used to a create a new .newsrc if there isn't one already, or when
  442.  *  the newsrc is reset.
  443.  */
  444.  
  445. write_newsrc() {
  446.     FILE *fp;
  447.     int i;
  448.  
  449.     setuid(real_uid);    /* become the user to write in his */
  450.     setgid(real_gid);    /* home directory */
  451.  
  452.     fp = fopen(newsrc, "w");
  453.     if (fp == NULL)
  454.         goto write_newsrc_done;
  455.  
  456.     for (i = 0; i < num_active; i++)
  457.         fprintf(fp, "%s: \n", active[i].name);
  458.  
  459.     fclose(fp);
  460.  
  461. write_newsrc_done:
  462.     setuid(tass_uid);
  463.     setgid(tass_gid);
  464. }
  465.  
  466.  
  467. /*
  468.  *  Load the sequencer rang lists and mark arts[] according to the
  469.  *  .newsrc info for a particular group.  i.e.  rec.arts.comics: 1-94,97
  470.  */
  471.  
  472. read_newsrc_line(group)
  473. char *group;
  474. {
  475.     FILE *fp;
  476.     char buf[8192];
  477.     char *p;
  478.  
  479.     fp = fopen(newsrc, "r");
  480.     if (fp == NULL)
  481.         return;
  482.  
  483.     while (fgets(buf, 8192, fp) != NULL) {
  484.         p = buf;
  485.         while (*p && *p != '\n' && *p != ' ' && *p != ':' && *p != '!')
  486.             p++;
  487.         *p++ = '\0';
  488.         if (strcmp(buf, group) != 0)
  489.             continue;
  490.         parse_seq(p);
  491.         break;
  492.     }
  493.  
  494.     fclose(fp);
  495. }
  496.  
  497.  
  498. /*
  499.  *  For our current group, update the sequencer information in .newsrc
  500.  */
  501.  
  502. update_newsrc(group, groupnum)
  503. char *group;
  504. int groupnum;            /* index into active[] for this group */
  505. {
  506.     FILE *fp;
  507.     FILE *newfp;
  508.     char buf[8192];
  509.     char *p;
  510.     char c;
  511.     int gotit = FALSE;
  512.  
  513.     setuid(real_uid);
  514.     setgid(real_gid);
  515.  
  516.     fp = fopen(newsrc, "r");
  517.     newfp = fopen(newnewsrc, "w");
  518.     if (newfp == NULL)
  519.         goto update_done;
  520.  
  521.     if (fp != NULL) {
  522.         while (fgets(buf, 8192, fp) != NULL) {
  523.             for (p = buf; *p; p++)
  524.                 if (*p == '\n') {
  525.                     *p = '\0';
  526.                     break;
  527.                 }
  528.  
  529.             p = buf;
  530.             while (*p && *p != ' ' && *p != ':' && *p != '!')
  531.                     p++;
  532.             c = *p;
  533.             if (c != '\0')
  534.                 *p++ = '\0';
  535.  
  536.             if (c != '!')
  537.                 c = ':';
  538.  
  539.             if (strcmp(buf, group) == 0) {
  540.                 fprintf(newfp, "%s%c ", buf, c);
  541.                 gotit = TRUE;
  542.                 print_seq(newfp, groupnum);
  543.                 fprintf(newfp, "\n");
  544.             } else
  545.                 fprintf(newfp, "%s%c%s\n", buf, c, p);
  546.         }
  547.         fclose(fp);
  548.     }
  549.  
  550.     fclose(newfp);
  551.     unlink(newsrc);
  552.     link(newnewsrc, newsrc);
  553.     unlink(newnewsrc);
  554.  
  555. update_done:
  556.     setuid(tass_uid);
  557.     setgid(tass_gid);
  558. }
  559.  
  560.  
  561. /*
  562.  *  Subscribe/unsubscribe to a group in .newsrc.  ch should either be
  563.  *  '!' to unsubscribe or ':' to subscribe.  num is the group's index
  564.  *  in active[].
  565.  */
  566.  
  567. subscribe(group, ch, num, out_seq)
  568. char *group;
  569. char ch;
  570. int num;
  571. int out_seq;                /* output sequencer info? */
  572. {
  573.     FILE *fp;
  574.     FILE *newfp;
  575.     char buf[8192];
  576.     char *p;
  577.     char c;
  578.     int gotit = FALSE;
  579.  
  580.     if (ch == '!')
  581.         active[num].flag &= ~SUBS;
  582.     else
  583.         active[num].flag |= SUBS;
  584.  
  585.     setuid(real_uid);
  586.     setgid(real_gid);
  587.  
  588.     fp = fopen(newsrc, "r");
  589.     newfp = fopen(newnewsrc, "w");
  590.     if (newfp == NULL)
  591.         goto subscribe_done;
  592.  
  593.     if (fp != NULL) {
  594.         while (fgets(buf, 8192, fp) != NULL) {
  595.             for (p = buf; *p; p++)
  596.                 if (*p == '\n') {
  597.                     *p = '\0';
  598.                     break;
  599.                 }
  600.  
  601.             p = buf;
  602.             while (*p && *p != ' ' && *p != ':' && *p != '!')
  603.                     p++;
  604.             c = *p;
  605.             if (c != '\0')
  606.                 *p++ = '\0';
  607.  
  608.             if (c != '!')
  609.                 c = ':';
  610.  
  611.             if (strcmp(buf, group) == 0) {
  612.                 fprintf(newfp, "%s%c%s\n", buf, ch, p);
  613.                 gotit = TRUE;
  614.             } else
  615.                 fprintf(newfp, "%s%c%s\n", buf, c, p);
  616.         }
  617.         fclose(fp);
  618.     }
  619.  
  620.     if (!gotit) {
  621.         if (out_seq) {
  622.             fprintf(newfp, "%s%c ", group, ch);
  623.             print_seq(newfp, num);
  624.             fprintf(newfp, "\n");
  625.         } else
  626.             fprintf(newfp, "%s%c\n", group, ch);
  627.     }
  628.  
  629.     fclose(newfp);
  630.     unlink(newsrc);
  631.     link(newnewsrc, newsrc);
  632.     unlink(newnewsrc);
  633.  
  634. subscribe_done:
  635.     setuid(tass_uid);
  636.     setgid(tass_gid);
  637. }
  638.  
  639.  
  640. print_seq(fp, groupnum)
  641. FILE *fp;
  642. int groupnum;            /* index into active[] for this group */
  643. {
  644.     int i;
  645.     int flag = FALSE;
  646.  
  647.     if (top <= 0) {
  648.         if (active[groupnum].min > 1) {
  649.             fprintf(fp, "1-%ld", active[groupnum].min);
  650.             fflush(fp);
  651.         }
  652.         return;
  653.     }
  654.  
  655.     i = 0;
  656.     if (arts[0].artnum > 1) {
  657.         for (; i < top && !arts[i].unread; i++) ;
  658.         if (i > 0)
  659.             fprintf(fp, "1-%ld", arts[i-1].artnum);
  660.         else
  661.             fprintf(fp, "1-%ld", arts[0].artnum - 1);
  662.         flag = TRUE;
  663.     }
  664.  
  665.     for (; i < top; i++) {
  666.         if (!arts[i].unread) {
  667.             if (flag)
  668.                 fprintf(fp, ",");
  669.             else
  670.                 flag = TRUE;
  671.             fprintf(fp, "%ld", arts[i].artnum);
  672.             if (i+1 < top && !arts[i+1].unread) {
  673.                 while (i+1 < top && !arts[i+1].unread)
  674.                     i++;
  675.                 fprintf(fp, "-%ld", arts[i].artnum);
  676.             }
  677.         }
  678.     }
  679.  
  680.     if (!flag && active[groupnum].min > 1)
  681.         fprintf(fp, "1-%ld", active[groupnum].min);
  682.     fflush(fp);
  683. }
  684.  
  685.  
  686. parse_seq(s)
  687. char *s;
  688. {
  689.     long low, high;
  690.     int i;
  691.  
  692.     while (*s) {
  693.         while (*s && (*s < '0' || *s > '9'))
  694.             s++;
  695.  
  696.         if (*s && *s >= '0' && *s <= '9') {
  697.             low = atol(s);
  698.             while (*s && *s >= '0' && *s <= '9')
  699.                 s++;
  700.             if (*s == '-') {
  701.                 s++;
  702.                 high = atol(s);
  703.                 while (*s && *s >= '0' && *s <= '9')
  704.                     s++;
  705.             }  else
  706.                 high = low;
  707.  
  708.             for (i = 0; i < top; i++)
  709.                 if (arts[i].artnum >= low &&
  710.                     arts[i].artnum <= high)
  711.                     arts[i].unread = 0;
  712.         }
  713.     }
  714. }
  715.  
  716.  
  717. parse_unread(s, groupnum)
  718. char *s;
  719. int groupnum;            /* index for group in active[] */
  720. {
  721.     long low, high;
  722.     long last_high;
  723.     int i;
  724.     int sum = 0;
  725.     int gotone = FALSE;
  726.     int n;
  727.  
  728. /*
  729.  *  Read the first range from the .newsrc sequencer information.  If the
  730.  *  top of the first range is higher than what the active file claims is
  731.  *  the bottom, use it as the new bottom instead
  732.  */
  733.  
  734.     high = 0;
  735.     if (*s) {
  736.         while (*s && (*s < '0' || *s > '9'))
  737.             s++;
  738.  
  739.         if (*s && *s >= '0' && *s <= '9') {
  740.             low = atol(s);
  741.             while (*s && *s >= '0' && *s <= '9')
  742.                 s++;
  743.             if (*s == '-') {
  744.                 s++;
  745.                 high = atol(s);
  746.                 while (*s && *s >= '0' && *s <= '9')
  747.                     s++;
  748.             }  else
  749.                 high = low;
  750.             gotone = TRUE;
  751.         }
  752.     }
  753.  
  754.     if (high < active[groupnum].min)
  755.         high = active[groupnum].min;
  756.  
  757.     while (*s) {
  758.         last_high = high;
  759.  
  760.         while (*s && (*s < '0' || *s > '9'))
  761.             s++;
  762.  
  763.         if (*s && *s >= '0' && *s <= '9') {
  764.             low = atol(s);
  765.             while (*s && *s >= '0' && *s <= '9')
  766.                 s++;
  767.             if (*s == '-') {
  768.                 s++;
  769.                 high = atol(s);
  770.                 while (*s && *s >= '0' && *s <= '9')
  771.                     s++;
  772.             }  else
  773.                 high = low;
  774.  
  775.             if (low > last_high)    /* otherwise seq out of order */
  776.                 sum += (low - last_high) - 1;
  777.         }
  778.     }
  779.  
  780.     if (gotone) {
  781.         if (active[groupnum].max > high)
  782.             sum += active[groupnum].max - high;
  783.         return sum;
  784.     }
  785.  
  786.     n = (int) (active[groupnum].max - active[groupnum].min);
  787.     if (n < 2)
  788.         return 0;
  789.  
  790.     return -1;
  791. }
  792.  
  793.  
  794. get_line_unread(group, groupnum)
  795. char *group;
  796. int groupnum;                /* index for group in active[] */
  797. {
  798.     FILE *fp;
  799.     char buf[8192];
  800.     char *p;
  801.     int ret = -1;
  802.  
  803.     fp = fopen(newsrc, "r");
  804.     if (fp == NULL)
  805.         return -1;
  806.  
  807.     while (fgets(buf, 8192, fp) != NULL) {
  808.         p = buf;
  809.         while (*p && *p != '\n' && *p != ' ' && *p != ':' && *p != '!')
  810.             p++;
  811.         *p++ = '\0';
  812.         if (strcmp(buf, group) != 0)
  813.             continue;
  814.         ret = parse_unread(p, groupnum);
  815.         break;
  816.     }
  817.  
  818.     fclose(fp);
  819.     return ret;
  820. }
  821.  
  822.  
  823. reset_newsrc()
  824. {
  825.     FILE *fp;
  826.     FILE *newfp;
  827.     char buf[8192];
  828.     char *p;
  829.     char c;
  830.     int gotit = FALSE;
  831.     int i;
  832.  
  833.     setuid(real_uid);
  834.     setgid(real_gid);
  835.  
  836.     fp = fopen(newsrc, "r");
  837.     newfp = fopen(newnewsrc, "w");
  838.     if (newfp == NULL)
  839.         goto update_done;
  840.  
  841.     if (fp != NULL) {
  842.         while (fgets(buf, 8192, fp) != NULL) {
  843.             for (p = buf; *p && *p != '\n'; p++) ;
  844.             *p = '\0';
  845.  
  846.             p = buf;
  847.             while (*p && *p != ' ' && *p != ':' && *p != '!')
  848.                     p++;
  849.             c = *p;
  850.             if (c != '\0')
  851.                 *p++ = '\0';
  852.  
  853.             if (c != '!')
  854.                 c = ':';
  855.  
  856.             fprintf(newfp, "%s%c\n", buf, c);
  857.         }
  858.         fclose(fp);
  859.     }
  860.  
  861.     fclose(newfp);
  862.     unlink(newsrc);
  863.     link(newnewsrc, newsrc);
  864.     unlink(newnewsrc);
  865.  
  866. update_done:
  867.     setuid(tass_uid);
  868.     setgid(tass_gid);
  869.  
  870.     for (i = 0; i < local_top; i++)
  871.         unread[i] = -1;
  872. }
  873.  
  874.  
  875. delete_group(group)
  876. char *group;
  877. {
  878.     FILE *fp;
  879.     FILE *newfp;
  880.     char buf[8192];
  881.     char *p;
  882.     char c;
  883.     int gotit = FALSE;
  884.     FILE *del;
  885.  
  886.     setuid(real_uid);
  887.     setgid(real_gid);
  888.  
  889.     fp = fopen(newsrc, "r");
  890.     newfp = fopen(newnewsrc, "w");
  891.     if (newfp == NULL)
  892.         goto del_done;
  893.     del = fopen(delgroups, "a+");
  894.     if (del == NULL)
  895.         goto del_done;
  896.  
  897.     if (fp != NULL) {
  898.         while (fgets(buf, 8192, fp) != NULL) {
  899.             for (p = buf; *p && *p != '\n'; p++) ;
  900.             *p = '\0';
  901.  
  902.             p = buf;
  903.             while (*p && *p != ' ' && *p != ':' && *p != '!')
  904.                     p++;
  905.             c = *p;
  906.             if (c != '\0')
  907.                 *p++ = '\0';
  908.  
  909.             if (c != '!')
  910.                 c = ':';
  911.  
  912.             if (strcmp(buf, group) == 0) {
  913.                 fprintf(del, "%s%c%s\n", buf, c, p);
  914.                 gotit = TRUE;
  915.             } else
  916.                 fprintf(newfp, "%s%c%s\n", buf, c, p);
  917.         }
  918.         fclose(fp);
  919.     }
  920.  
  921.     fclose(newfp);
  922.  
  923.     if (!gotit)
  924.         fprintf(del, "%s! \n", group);
  925.  
  926.     fclose(del);
  927.     unlink(newsrc);
  928.     link(newnewsrc, newsrc);
  929.     unlink(newnewsrc);
  930.  
  931. del_done:
  932.     setuid(tass_uid);
  933.     setgid(tass_gid);
  934. }
  935.  
  936.  
  937. undel_group() {
  938.     FILE *del;
  939.     FILE *newfp;
  940.     FILE *fp;
  941.     char buf[2][8192];
  942.     char *p;
  943.     int which = 0;
  944.     long h;
  945.     extern int cur_groupnum;
  946.     int i, j;
  947.     char c;
  948.  
  949.     setuid(real_uid);
  950.     setgid(real_gid);
  951.  
  952.     del = fopen(delgroups, "r");
  953.     if (del == NULL) {
  954.         setuid(tass_uid);
  955.         setgid(tass_gid);
  956.         return FALSE;
  957.     }
  958.     unlink(delgroups);
  959.     newfp = fopen(delgroups, "w");
  960.     if (newfp == NULL) {
  961.         setuid(tass_uid);
  962.         setgid(tass_gid);
  963.         return FALSE;
  964.     }
  965.  
  966.     buf[0][0] = '\0';
  967.     buf[1][0] = '\0';
  968.  
  969.     while (fgets(buf[which], 8192, del) != NULL) {
  970.         which = !which;
  971.         if (*buf[which])
  972.             fputs(buf[which], newfp);
  973.     }
  974.  
  975.     fclose(del);
  976.     fclose(newfp);
  977.     which = !which;
  978.  
  979.     if (!*buf[which]) {
  980.         setuid(tass_uid);
  981.         setgid(tass_gid);
  982.         return FALSE;
  983.     }
  984.  
  985.     for (p = buf[which]; *p && *p != '\n'; p++) ;
  986.     *p = '\0';
  987.  
  988.     p = buf[which];
  989.     while (*p && *p != ' ' && *p != ':' && *p != '!')
  990.         p++;
  991.     c = *p;
  992.     if (c != '\0')
  993.         *p++ = '\0';
  994.  
  995.     if (c != '!')
  996.         c = ':';
  997.  
  998.     {            /* find the hash of the group name */
  999.         char *t = buf[which];
  1000.  
  1001.         h = *t++;
  1002.         while (*t)
  1003.             h = (h * 64 + *t++) % TABLE_SIZE;
  1004.     }
  1005.  
  1006.     for (i = group_hash[h]; i >= 0; i = active[i].next) {
  1007.         if (strcmp(buf[which], active[i].name) == 0) {
  1008.             for (j = 0; j < local_top; j++)
  1009.                 if (my_group[j] == i) {
  1010.                     setuid(tass_uid);
  1011.                     setgid(tass_gid);
  1012.                     return j;
  1013.                 }
  1014.  
  1015.             active[i].flag &= ~NOTGOT;   /* mark that we got it */
  1016.             if (c != '!')
  1017.                 active[i].flag |= SUBS;
  1018.  
  1019.             if (local_top >= max_active)
  1020.                 expand_active();
  1021.             local_top++;
  1022.             for (j = local_top; j > cur_groupnum; j--) {
  1023.                 my_group[j] = my_group[j-1];
  1024.                 unread[j] = unread[j-1];
  1025.             }
  1026.             my_group[cur_groupnum] = i;
  1027.             unread[cur_groupnum] = parse_unread(p, i);
  1028.  
  1029.             fp = fopen(newsrc, "r");
  1030.             if (fp == NULL) {
  1031.                 setuid(tass_uid);
  1032.                 setgid(tass_gid);
  1033.                 return FALSE;
  1034.             }
  1035.             newfp = fopen(newnewsrc, "w");
  1036.             if (newfp == NULL) {
  1037.                 fclose(fp);
  1038.                 setuid(tass_uid);
  1039.                 setgid(tass_gid);
  1040.                 return FALSE;
  1041.             }
  1042.             i = 0;
  1043.             while (fgets(buf[!which], 8192, fp) != NULL) {
  1044.                 for (p = buf[!which]; *p && *p != '\n'; p++) ;
  1045.                 *p = '\0';
  1046.  
  1047.                 p = buf[!which];
  1048.                 while (*p && *p!=' ' && *p != ':' && *p != '!')
  1049.                     p++;
  1050.                 c = *p;
  1051.                 if (c != '\0')
  1052.                     *p++ = '\0';
  1053.  
  1054.                 if (c != '!')
  1055.                     c = ':';
  1056.  
  1057.                 while (i < cur_groupnum) {
  1058.                     if (strcmp(buf[!which],
  1059.                       active[my_group[i]].name) == 0) {
  1060.                         fprintf(newfp, "%s%c%s\n",
  1061.                             buf[!which], c, p);
  1062.                         goto foo_cont;
  1063.                     }
  1064.                     i++;
  1065.                 }
  1066.                 fprintf(newfp, "%s%c%s\n", buf[which], c, p);
  1067.                 fprintf(newfp, "%s%c%s\n", buf[!which], c, p);
  1068.                 break;
  1069. foo_cont:;
  1070.             }
  1071.  
  1072.             while (fgets(buf[!which], 8192, fp) != NULL)
  1073.                 fputs(buf[!which], newfp);
  1074.  
  1075.             fclose(newfp);
  1076.             fclose(fp);
  1077.             unlink(newsrc);
  1078.             link(newnewsrc, newsrc);
  1079.             unlink(newnewsrc);
  1080.             setuid(tass_uid);
  1081.             setgid(tass_gid);
  1082.             return TRUE;
  1083.         }
  1084.     }
  1085.  
  1086.     setuid(tass_uid);
  1087.     setgid(tass_gid);
  1088.  
  1089.     return FALSE;
  1090. }
  1091.  
  1092.  
  1093. mark_group_read(group, groupnum)
  1094. char *group;
  1095. int groupnum;            /* index into active[] for this group */
  1096. {
  1097.     FILE *fp;
  1098.     FILE *newfp;
  1099.     char buf[8192];
  1100.     char *p;
  1101.     char c;
  1102.     int gotit = FALSE;
  1103.  
  1104.     if (active[groupnum].max < 2)
  1105.         return;
  1106.  
  1107.     setuid(real_uid);
  1108.     setgid(real_gid);
  1109.  
  1110.     fp = fopen(newsrc, "r");
  1111.     newfp = fopen(newnewsrc, "w");
  1112.     if (newfp == NULL)
  1113.         goto mark_group_read_done;
  1114.  
  1115.     if (fp != NULL) {
  1116.         while (fgets(buf, 8192, fp) != NULL) {
  1117.             for (p = buf; *p; p++)
  1118.                 if (*p == '\n') {
  1119.                     *p = '\0';
  1120.                     break;
  1121.                 }
  1122.  
  1123.             p = buf;
  1124.             while (*p && *p != ' ' && *p != ':' && *p != '!')
  1125.                     p++;
  1126.             c = *p;
  1127.             if (c != '\0')
  1128.                 *p++ = '\0';
  1129.  
  1130.             if (c != '!')
  1131.                 c = ':';
  1132.  
  1133.             if (strcmp(buf, group) == 0) {
  1134.                 fprintf(newfp, "%s%c 1-%ld\n", buf, c,
  1135.                         active[groupnum].max);
  1136.                 gotit = TRUE;
  1137.             } else
  1138.                 fprintf(newfp, "%s%c%s\n", buf, c, p);
  1139.         }
  1140.         fclose(fp);
  1141.     }
  1142.  
  1143.     fclose(newfp);
  1144.     unlink(newsrc);
  1145.     link(newnewsrc, newsrc);
  1146.     unlink(newnewsrc);
  1147.  
  1148. mark_group_read_done:
  1149.     setuid(tass_uid);
  1150.     setgid(tass_gid);
  1151. }
  1152.  
  1153.  
  1154. long
  1155. hash_groupname(buf)        /* hash group name for fast lookup later */
  1156. char *buf;
  1157. {
  1158.     char *t = buf;
  1159.     unsigned long h;
  1160.  
  1161.     h = *t++;
  1162.     while (*t)
  1163.         h = ((h << 1) ^ *t++) % TABLE_SIZE;
  1164. /*        h = (h * 64 + *t++) % TABLE_SIZE;    */
  1165.  
  1166.     return h;
  1167. }
  1168.  
  1169.  
  1170. #ifdef M_XENIX
  1171. mkdir(path, mode)
  1172. char *path;
  1173. int mode;
  1174. {
  1175.     char buf[200];
  1176.  
  1177.     sprintf(buf, "mkdir %s", path);
  1178.     system(buf);
  1179.     chmod(path, mode);
  1180. }
  1181. #endif
  1182.  
  1183. @EOF
  1184.  
  1185. chmod 644 main.c
  1186.  
  1187. echo x - misc.c
  1188. cat >misc.c <<'@EOF'
  1189.  
  1190. #include    <stdio.h>
  1191. #include    <ctype.h>
  1192. #include    <signal.h>
  1193. #include    <pwd.h>
  1194. #include    <sys/types.h>
  1195. #include    <sys/stat.h>
  1196. #include    "tass.h"
  1197.  
  1198.  
  1199. char active_file[LEN];
  1200. char homedir[LEN];
  1201. char userid[LEN];
  1202. char delgroups[LEN];
  1203. char newsrc[LEN];
  1204. char newnewsrc[LEN];
  1205. char indexdir[LEN];
  1206. char my_org[LEN];        /* organization */
  1207. char sig[LEN];
  1208. char signature[LEN];
  1209.  
  1210.  
  1211. /*
  1212.  *  Which base note (an index into base[]) does a respnum
  1213.  *  (an index into arts[]) corresponsd to?
  1214.  *
  1215.  *  In other words, base[] points to an entry in arts[] which is
  1216.  *  the head of a thread, linked with arts[].thread.  For any q: arts[q],
  1217.  *  find i such that base[i]->arts[n]->arts[o]->...->arts[q]
  1218.  */
  1219.  
  1220. which_base(n)
  1221. int n;
  1222. {
  1223.     int i, j;
  1224.  
  1225.     for (i = 0; i < top_base; i++)
  1226.         for (j = base[i]; j >= 0; j = arts[j].thread)
  1227.             if (j == n)
  1228.                 return i;
  1229.  
  1230.     fprintf(stderr, "can't find base article\n");
  1231.     return 0;
  1232. }
  1233.  
  1234.  
  1235. /*
  1236.  *  Find how deep in a thread a response is.  Start counting at zero
  1237.  */
  1238.  
  1239. which_resp(n)
  1240. int n;
  1241. {
  1242.     int i, j;
  1243.     int num = 0;
  1244.  
  1245.     i = which_base(n);
  1246.  
  1247.     for (j = base[i]; j != -1; j = arts[j].thread)
  1248.         if (j == n)
  1249.             break;
  1250.         else
  1251.             num++;
  1252.  
  1253.     return num;
  1254. }
  1255.  
  1256.  
  1257. /*
  1258.  *  Given an index into base[], find the number of responses for
  1259.  *  that basenote
  1260.  */
  1261.  
  1262. nresp(n)
  1263. int n;
  1264. {
  1265.     int i;
  1266.     int oldi = -3;
  1267.     int sum = 0;
  1268.  
  1269.     assert(n < top_base);
  1270.  
  1271.     for (i = base[n]; i != -1; i = arts[i].thread) {
  1272.         assert(i != -2);
  1273.         assert(i != oldi);
  1274.         oldi = i;
  1275.         sum++;
  1276.     }
  1277.  
  1278.     return sum - 1;
  1279. }
  1280.  
  1281.  
  1282. asfail(file, line, cond)
  1283. char    *file;
  1284. int    line;
  1285. char    *cond;
  1286. {
  1287.     fprintf(stderr, "tass: assertion failure: %s (%d): %s\n",
  1288.                             file, line, cond);
  1289.     exit(1);
  1290. }
  1291.  
  1292.  
  1293. /*
  1294.  * init_selfinfo
  1295.  *   Deterimines users home directory, userid, and a path
  1296.  *   for an rc file in the home directory
  1297.  */
  1298.  
  1299. init_selfinfo()
  1300. {
  1301.     struct passwd *myentry;
  1302.     extern struct passwd *getpwuid();
  1303.     struct stat sb;
  1304.     char nam[LEN];
  1305.     char *p;
  1306.     extern char *getenv();
  1307.     FILE *fp;
  1308.  
  1309.     myentry = getpwuid(getuid());
  1310.     strcpy(userid, myentry->pw_name);
  1311.     strcpy(homedir, myentry->pw_dir);
  1312.  
  1313.     sprintf(signature, "%s/.signature", homedir);
  1314.     sprintf(sig, "%s/.Sig", homedir);
  1315.     sprintf(newsrc, "%s/.newsrc", homedir);
  1316.     sprintf(newnewsrc, "%s/.newnewsrc", homedir);
  1317.     sprintf(delgroups, "%s/.delgroups", homedir);
  1318.     sprintf(indexdir, "%s/.tindx", homedir);
  1319.     sprintf(active_file, "%s/active", LIBDIR);
  1320.     if (stat(active_file, &sb) >= 0)
  1321.         goto got_active;
  1322.  
  1323. /*
  1324.  *  I hate forgetting to define LIBDIR correctly.  Guess a
  1325.  *  couple of likely places if it's not where LIBDIR says it is.
  1326.  */
  1327.  
  1328.     strcpy(active_file, "/usr/lib/news/active");
  1329.     if (stat(active_file, &sb) >= 0)
  1330.         goto got_active;
  1331.  
  1332.     strcpy(active_file, "/usr/local/lib/news/active");
  1333.     if (stat(active_file, &sb) >= 0)
  1334.         goto got_active;
  1335.  
  1336.     strcpy(active_file, "/usr/public/lib/news/active");
  1337.     if (stat(active_file, &sb) >= 0)
  1338.         goto got_active;
  1339.  
  1340. /*
  1341.  *  Oh well.  Revert to what LIBDIR says it is to produce a
  1342.  *  useful error message when read_active() fails later.
  1343.  */
  1344.  
  1345.     sprintf(active_file, "%s/active", LIBDIR);
  1346.  
  1347. got_active:
  1348.  
  1349.     *my_org = '\0';
  1350.     p = getenv("ORGANIZATION");
  1351.     if (p != NULL) {
  1352.         strcpy(my_org, p);
  1353.         goto got_org;
  1354.     }
  1355.  
  1356.     sprintf(nam, "%s/organization", LIBDIR);
  1357.     fp = fopen(nam, "r");
  1358.  
  1359.     if (fp == NULL) {
  1360.         sprintf(nam, "/usr/lib/news/organization");
  1361.         fp = fopen(nam, "r");
  1362.     }
  1363.  
  1364.     if (fp == NULL) {
  1365.         sprintf(nam, "/usr/local/lib/news/organization");
  1366.         fp = fopen(nam, "r");
  1367.     }
  1368.  
  1369.     if (fp == NULL) {
  1370.         sprintf(nam, "/usr/public/lib/news/organization");
  1371.         fp = fopen(nam, "r");
  1372.     }
  1373.  
  1374.     if (fp == NULL) {
  1375.         sprintf(nam, "/etc/organization");
  1376.         fp = fopen(nam, "r");
  1377.     }
  1378.  
  1379.     if (fp != NULL) {
  1380.         if (fgets(my_org, LEN, fp) != NULL) {
  1381.             for (p = my_org; *p && *p != '\n'; p++) ;
  1382.             *p = '\0';
  1383.         }
  1384.         fclose(fp);
  1385.     }
  1386.  
  1387. got_org:;
  1388.  
  1389. }
  1390.  
  1391.  
  1392. char *
  1393. my_malloc(size)
  1394. unsigned size;
  1395. {
  1396.     char *p;
  1397.     extern char *malloc();
  1398.  
  1399.     p = malloc(size);
  1400.     if (p == NULL) {
  1401.         fprintf(stderr, "tass: out of memory\n");
  1402.         exit(1);
  1403.     }
  1404.     return p;
  1405. }
  1406.  
  1407.  
  1408. char *
  1409. my_realloc(p, size)
  1410. char *p;
  1411. unsigned size;
  1412. {
  1413.     extern char *malloc();
  1414.     extern char *realloc();
  1415.  
  1416.     if (p == NULL)
  1417.         p = malloc(size);
  1418.     else
  1419.         p = realloc(p, size);
  1420.  
  1421.     if (p == NULL) {
  1422.         fprintf(stderr, "tass: out of memory\n");
  1423.         exit(1);
  1424.     }
  1425.     return p;
  1426. }
  1427.  
  1428.  
  1429. char *
  1430. str_save(s)
  1431. char *s;
  1432. {
  1433. char *p;
  1434.  
  1435.     assert(s != NULL);
  1436.     
  1437.     p = my_malloc(strlen(s) + 1);
  1438.     strcpy(p, s);
  1439.  
  1440.     return(p);
  1441. }
  1442.  
  1443.  
  1444. copy_fp(a, b, prefix)
  1445. FILE *a;
  1446. FILE *b;
  1447. char *prefix;
  1448. {
  1449.     char buf[8192];
  1450.  
  1451.     while (fgets(buf, 8192, a) != NULL)
  1452.         fprintf(b, "%s%s", prefix, buf);
  1453. }
  1454.  
  1455.  
  1456. char *
  1457. get_val(env, def)
  1458. char *env;        /* Environment variable we're looking for    */
  1459. char *def;        /* Default value if no environ value found    */
  1460. {
  1461.     extern char *getenv();
  1462.     char *ptr;
  1463.  
  1464.     if ((ptr = getenv(env)) != NULL)
  1465.         return(ptr);
  1466.     else
  1467.         return(def);
  1468. }
  1469.  
  1470.  
  1471. invoke_editor(nam)
  1472. char *nam;
  1473. {
  1474.     char buf[200];
  1475.     static int first = TRUE;
  1476.     static char editor[200];
  1477.     int ret;
  1478.  
  1479.     if (first) {
  1480.         strcpy(editor, get_val("EDITOR", DEF_EDITOR));
  1481.         first = FALSE;
  1482.     }
  1483.  
  1484.     sprintf(buf, "%s %s", editor, nam);
  1485.     printf("\r%s\n", buf);
  1486.     ret = invoke_cmd(buf);
  1487.     setuid(real_uid);
  1488.     setgid(real_gid);
  1489.  
  1490.     return ret;
  1491. }
  1492.  
  1493.  
  1494. invoke_cmd(nam)
  1495. char *nam;
  1496. {
  1497.     int ret;
  1498. #ifdef SIGTSTP
  1499.     void (*susp)();
  1500. #endif
  1501.  
  1502.     Raw(FALSE);
  1503.     setuid(real_uid);
  1504.     setgid(real_gid);
  1505.  
  1506. #ifdef SIGTSTP
  1507.     susp = signal(SIGTSTP, SIG_DFL);
  1508. #endif
  1509.  
  1510.     ret = system(nam);
  1511.  
  1512. #ifdef SIGTSTP
  1513.     signal(SIGTSTP, susp);
  1514. #endif
  1515.  
  1516.     setuid(tass_uid);
  1517.     setgid(tass_gid);
  1518.     Raw(TRUE);
  1519.  
  1520.     return ret == 0;
  1521. }
  1522.  
  1523.  
  1524. shell_escape() {
  1525.     char shell[LEN];
  1526.     char *p;
  1527. #ifdef SIGTSTP
  1528.     void (*susp)();
  1529. #endif
  1530.  
  1531.     if (!parse_string("!", shell))
  1532.         strcpy(shell, get_val("SHELL", "/bin/sh"));
  1533.  
  1534.     for (p = shell; *p && (*p == ' ' || *p == '\t'); p++) ;
  1535.  
  1536.     if (!*p)
  1537.         strcpy(shell, get_val("SHELL", "/bin/sh"));
  1538.     
  1539.     Raw(FALSE);
  1540.  
  1541.     setuid(real_uid);
  1542.     setgid(real_gid);
  1543.  
  1544.     fputs("\r\n", stdout);
  1545.  
  1546. #ifdef SIGTSTP
  1547.     susp = signal(SIGTSTP, SIG_DFL);
  1548. #endif
  1549.  
  1550.     system(p);
  1551.  
  1552. #ifdef SIGTSTP
  1553.     signal(SIGTSTP, susp);
  1554. #endif
  1555.  
  1556.     setuid(tass_uid);
  1557.     setgid(tass_gid);
  1558.  
  1559.     Raw(TRUE);
  1560.  
  1561.     continue_prompt();
  1562.     mail_setup();
  1563. }
  1564.  
  1565.  
  1566. /*
  1567.  *  Find the previous response.  Go to the last response in the previous
  1568.  *  thread if we go past the beginning of this thread.
  1569.  */
  1570.  
  1571. prev_response(n)
  1572. int n;
  1573. {
  1574.     int resp;
  1575.     int i;
  1576.  
  1577.     resp = which_resp(n);
  1578.  
  1579.     if (resp > 0)
  1580.         return choose_resp( which_base(n), resp-1 );
  1581.  
  1582.     i = which_base(n) - 1;
  1583.  
  1584.     if (i < 0)
  1585.         return -1;
  1586.  
  1587.     return choose_resp( i, nresp(i) );
  1588. }
  1589.  
  1590.  
  1591. /*
  1592.  *  Find the next response.  Go to the next basenote if there
  1593.  *  are no more responses in this thread
  1594.  */
  1595.  
  1596. next_response(n)
  1597. int n;
  1598. {
  1599.     int i;
  1600.  
  1601.     if (arts[n].thread >= 0)
  1602.         return arts[n].thread;
  1603.  
  1604.     i = which_base(n) + 1;
  1605.  
  1606.     if (i >= top_base)
  1607.         return -1;
  1608.  
  1609.     return base[i];
  1610. }
  1611.  
  1612.  
  1613. /*
  1614.  *  Given a respnum (index into arts[]), find the respnum of the
  1615.  *  next basenote
  1616.  */
  1617.  
  1618. next_basenote(n)
  1619. int n;
  1620. {
  1621.     int i;
  1622.  
  1623.     i = which_base(n) + 1;
  1624.     if (i >= top_base)
  1625.         return -1;
  1626.  
  1627.     return base[i];
  1628. }
  1629.  
  1630.  
  1631.  
  1632. /*
  1633.  *  Find the next unread response in this group 
  1634.  */
  1635.  
  1636. next_unread(n)
  1637. int n;
  1638. {
  1639.  
  1640.     while (n >= 0) {
  1641.         if (arts[n].unread == 1)
  1642.             return n;
  1643.         n = next_response(n);
  1644.     }
  1645.  
  1646.     return -1;
  1647. }
  1648.  
  1649.  
  1650. /*
  1651.  *  Find the previous unread response in this thread
  1652.  */
  1653.  
  1654. prev_unread(n)
  1655. int n;
  1656. {
  1657.  
  1658.     while (n >= 0) {
  1659.         if (arts[n].unread == 1)
  1660.             return n;
  1661.         n = prev_response(n);
  1662.     }
  1663.  
  1664.     return -1;
  1665. }
  1666.  
  1667.  
  1668. add_signature(fp, flag)
  1669. FILE *fp;
  1670. int flag;
  1671. {
  1672.     FILE *sigf;
  1673.  
  1674.     sigf = fopen(signature, "r");
  1675.     if (sigf != NULL) {
  1676.         if (flag) {
  1677.             fprintf(fp, "\n--\n");
  1678.             copy_fp(sigf, fp, "");
  1679.         }
  1680.         fclose(sigf);
  1681.         return;
  1682.     }
  1683.  
  1684.     sigf = fopen(sig, "r");
  1685.     if (sigf != NULL) {
  1686.         fprintf(fp, "\n--\n");
  1687.         copy_fp(sigf, fp, "");
  1688.         fclose(sigf);
  1689.     }
  1690. }
  1691.  
  1692.  
  1693. make_lower(s, t)
  1694. char *s;
  1695. char *t;
  1696. {
  1697.  
  1698.     while (*s) {
  1699.         if (isupper(*s))
  1700.             *t = tolower(*s);
  1701.         else
  1702.             *t = *s;
  1703.         s++;
  1704.         t++;
  1705.     }
  1706.     *t = 0;
  1707. }
  1708.  
  1709.  
  1710. match(s, t, n)
  1711. char *s;
  1712. char *t;
  1713. int n;
  1714. {
  1715.  
  1716.     while (*t) {
  1717.         if (*s == *t && strncmp(s, t, n) == 0)
  1718.             return TRUE;
  1719.         t++;
  1720.     }
  1721.  
  1722.     return FALSE;
  1723. }
  1724.  
  1725. @EOF
  1726.  
  1727. chmod 644 misc.c
  1728.  
  1729. echo x - nntp.h
  1730. cat >nntp.h <<'@EOF'
  1731. /* nntp.h -- nntp support for tass */
  1732.  
  1733. /* Changed a bit so nntp knows about Tass */
  1734.  
  1735. /*
  1736.  *  This file is originally from the nntp 1.5 source,
  1737.  *  but modified a bit
  1738.  */
  1739.  
  1740. #define        NNTP_SERVER    "/etc/nntpserver"
  1741.  
  1742. /*
  1743.  *  External routine declarations
  1744.  */
  1745.  
  1746. extern char *getserverbyfile();
  1747. extern int server_init();
  1748. extern int get_tcp_socket();
  1749. extern int handle_server_response();
  1750. extern void put_server();
  1751. extern int get_server();
  1752. extern void close_server();
  1753.  
  1754. /*
  1755.  *  External file descriptors for the server connection
  1756.  */
  1757.  
  1758. extern FILE *ser_wr_fp;
  1759. extern FILE *ser_wr_fp;
  1760.  
  1761.  
  1762. /*
  1763.  * Response codes for NNTP server
  1764.  *
  1765.  * @(#)nntp.h    1.7    (Berkeley) 1/11/88
  1766.  *
  1767.  * First digit:
  1768.  *
  1769.  *    1xx    Informative message
  1770.  *    2xx    Command ok
  1771.  *    3xx    Command ok so far, continue
  1772.  *    4xx    Command was correct, but couldn't be performed
  1773.  *        for some specified reason.
  1774.  *    5xx    Command unimplemented, incorrect, or a
  1775.  *        program error has occured.
  1776.  *
  1777.  * Second digit:
  1778.  *
  1779.  *    x0x    Connection, setup, miscellaneous
  1780.  *    x1x    Newsgroup selection
  1781.  *    x2x    Article selection
  1782.  *    x3x    Distribution
  1783.  *    x4x    Posting
  1784.  */
  1785.  
  1786. #define    CHAR_INF    '1'
  1787. #define    CHAR_OK        '2'
  1788. #define    CHAR_CONT    '3'
  1789. #define    CHAR_ERR    '4'
  1790. #define    CHAR_FATAL    '5'
  1791.  
  1792. #define    INF_HELP    100    /* Help text on way */
  1793. #define    INF_DEBUG    199    /* Debug output */
  1794.  
  1795. #define    OK_CANPOST    200    /* Hello; you can post */
  1796. #define    OK_NOPOST    201    /* Hello; you can't post */
  1797. #define    OK_SLAVE    202    /* Slave status noted */
  1798. #define    OK_GOODBYE    205    /* Closing connection */
  1799. #define    OK_GROUP    211    /* Group selected */
  1800. #define    OK_GROUPS    215    /* Newsgroups follow */
  1801.  
  1802. #define OK_TASSINDEX    218    /* Tass index follows */
  1803.  
  1804. #define    OK_ARTICLE    220    /* Article (head & body) follows */
  1805. #define    OK_HEAD        221    /* Head follows */
  1806. #define    OK_BODY        222    /* Body follows */
  1807. #define    OK_NOTEXT    223    /* No text sent -- stat, next, last */
  1808. #define    OK_NEWNEWS    230    /* New articles by message-id follow */
  1809. #define    OK_NEWGROUPS    231    /* New newsgroups follow */
  1810. #define    OK_XFERED    235    /* Article transferred successfully */
  1811. #define    OK_POSTED    240    /* Article posted successfully */
  1812.  
  1813. #define CONT_XFER    335    /* Continue to send article */
  1814. #define    CONT_POST    340    /* Continue to post article */
  1815.  
  1816. #define    ERR_GOODBYE    400    /* Have to hang up for some reason */
  1817. #define    ERR_NOGROUP    411    /* No such newsgroup */
  1818. #define    ERR_NCING    412    /* Not currently in newsgroup */
  1819.  
  1820. #define ERR_NOTASS    418    /* No tass index for this group */
  1821.  
  1822. #define    ERR_NOCRNT    420    /* No current article selected */
  1823. #define    ERR_NONEXT    421    /* No next article in this group */
  1824. #define    ERR_NOPREV    422    /* No previous article in this group */
  1825. #define    ERR_NOARTIG    423    /* No such article in this group */
  1826. #define ERR_NOART    430    /* No such article at all */
  1827. #define ERR_GOTIT    435    /* Already got that article, don't send */
  1828. #define ERR_XFERFAIL    436    /* Transfer failed */
  1829. #define    ERR_XFERRJCT    437    /* Article rejected, don't resend */
  1830. #define    ERR_NOPOST    440    /* Posting not allowed */
  1831. #define    ERR_POSTFAIL    441    /* Posting failed */
  1832.  
  1833. #define    ERR_COMMAND    500    /* Command not recognized */
  1834. #define    ERR_CMDSYN    501    /* Command syntax error */
  1835. #define    ERR_ACCESS    502    /* Access to server denied */
  1836. #define ERR_FAULT    503    /* Program fault, command not performed */
  1837.  
  1838. /* RFC 977 defines this; don't change it. */
  1839.  
  1840. #define    NNTP_STRLEN    512
  1841. @EOF
  1842.  
  1843. chmod 644 nntp.h
  1844.  
  1845. echo x - nntp_open.c
  1846. cat >nntp_open.c <<'@EOF'
  1847.  
  1848.  
  1849. #include    <stdio.h>
  1850. #include    <sys/types.h>
  1851. #include    <sys/stat.h>
  1852. #include    "nntp.h"
  1853. #include    "tass.h"
  1854.  
  1855.  
  1856. char *
  1857. is_remote() {
  1858.  
  1859.     return " (remote)";
  1860. }
  1861.  
  1862.  
  1863. nntp_startup() {
  1864.     char *server_name;
  1865.     int ret;
  1866.     extern char *getenv();
  1867.  
  1868.     server_name = getserverbyfile(NNTP_SERVER);
  1869.     if (server_name == NULL) {
  1870.         fprintf(stderr, "Can't get nntp server name\n");
  1871.         fprintf(stderr, "Either put the name in the file %s, or put\n",
  1872.                             NNTP_SERVER);
  1873.         fprintf(stderr, "it in the environment variable NNTPSERVER\n");
  1874.         exit(1);
  1875.     }
  1876.  
  1877.     ret = server_init(server_name);
  1878.  
  1879.     switch (ret) {
  1880.     case OK_CANPOST:
  1881.     case OK_NOPOST:
  1882.         break;
  1883.  
  1884.     case -1:
  1885.         fprintf(stderr, "failed to connect to server\n");
  1886.         exit(1);
  1887.  
  1888.     default:
  1889.         fprintf(stderr, "rejected by server, nntp error %d\n", ret);
  1890.         exit(1);
  1891.     }
  1892. }
  1893.  
  1894.  
  1895. nntp_finish() {
  1896.     close_server();
  1897. }
  1898.  
  1899.  
  1900. /*
  1901.  *  get_respcode
  1902.  *  get a response code from the server and return it to the caller
  1903.  */
  1904.  
  1905. int get_respcode() {
  1906.     char line[NNTP_STRLEN];
  1907.  
  1908.     if (get_server(line, NNTP_STRLEN) == -1) {
  1909.         fprintf(stderr, "connection to server broken\n");
  1910.         tass_done(1);
  1911.     }
  1912.  
  1913.     return atoi(line);
  1914. }
  1915.  
  1916.  
  1917.  
  1918. stuff_nntp(fnam)
  1919. char *fnam;
  1920. {
  1921.     FILE *fp;
  1922.     char line[NNTP_STRLEN];
  1923.     extern char *mktemp();
  1924.     struct stat sb;
  1925.     extern long note_size;
  1926.  
  1927.     strcpy(fnam, "/tmp/tass_nntpXXXXXX");
  1928.     mktemp(fnam);
  1929.  
  1930.     fp = fopen(fnam, "w");
  1931.     if (fp == NULL) {
  1932.         fprintf(stderr, "stuff_nntp: can't open %s: ", fnam);
  1933.         perror("");
  1934.         return FALSE;
  1935.     }
  1936.  
  1937.     while (1) {
  1938.         if (get_server(line, NNTP_STRLEN) == -1) {
  1939.             fprintf(stderr, "connection to server broken\n");
  1940.             tass_done(1);
  1941.         }
  1942.         if (strcmp(line, ".") == 0)
  1943.             break;            /* end of text */
  1944.         strcat(line, "\n");
  1945.         if (line[0] == '.')        /* reduce leading .'s */
  1946.             fputs(&line[1], fp);
  1947.         else
  1948.             fputs(line, fp);
  1949.     }
  1950.     fclose(fp);
  1951.  
  1952.     if (stat(fnam, &sb) < 0)
  1953.         note_size = 0;
  1954.     else
  1955.         note_size = sb.st_size;
  1956.  
  1957.     return TRUE;
  1958. }
  1959.  
  1960.  
  1961. FILE *
  1962. nntp_to_fp() {
  1963.     char fnam[LEN];
  1964.     FILE *fp;
  1965.  
  1966.     if (!stuff_nntp(fnam))
  1967.         return NULL;
  1968.  
  1969.     fp = fopen(fnam, "r");
  1970.     if (fp == NULL) {
  1971.         fprintf(stderr, "nntp_to_fp: can't reopen %s: ", fnam);
  1972.         perror("");
  1973.         return NULL;
  1974.     }
  1975.     unlink(fnam);
  1976.     return fp;
  1977. }
  1978.  
  1979.  
  1980. nntp_to_fd() {
  1981.     char fnam[LEN];
  1982.     int fd;
  1983.  
  1984.     if (!stuff_nntp(fnam))
  1985.         return NULL;
  1986.  
  1987.     fd = open(fnam, 0);
  1988.     if (fd == NULL) {
  1989.         fprintf(stderr, "nntp_to_fd: can't reopen %s: ", fnam);
  1990.         perror("");
  1991.         return -1;
  1992.     }
  1993.     unlink(fnam);
  1994.     return fd;
  1995. }
  1996.  
  1997.  
  1998.  
  1999. FILE *
  2000. open_active_fp() {
  2001.  
  2002.     put_server("list");
  2003.     if (get_respcode() != OK_GROUPS)
  2004.         return NULL;
  2005.  
  2006.     return nntp_to_fp();
  2007. }
  2008.  
  2009.  
  2010. FILE *
  2011. open_art_fp(group_path, art)
  2012. char *group_path;
  2013. long art;
  2014. {
  2015.     char buf[LEN];
  2016.  
  2017.     sprintf(buf, "article %ld", art);
  2018.  
  2019.     put_server(buf);
  2020.     if (get_respcode() != OK_ARTICLE)
  2021.         return NULL;
  2022.  
  2023.     return nntp_to_fp();
  2024. }
  2025.  
  2026.  
  2027. open_header_fd(group_path, art)
  2028. char *group_path;
  2029. long art;
  2030. {
  2031.     char buf[LEN];
  2032.  
  2033.     sprintf(buf, "head %ld", art);
  2034.     put_server(buf);
  2035.     if (get_respcode() != OK_HEAD)
  2036.         return -1;
  2037.  
  2038.     return nntp_to_fd();
  2039. }
  2040.  
  2041.  
  2042. setup_base(group, group_path)
  2043. char *group;
  2044. char *group_path;
  2045. {
  2046.     char buf[LEN];
  2047.     char line[NNTP_STRLEN];
  2048.     long start, last, dummy, count;
  2049.  
  2050.     top_base = 0;
  2051.  
  2052.     sprintf(buf, "group %s", group);
  2053.     put_server(buf);
  2054.  
  2055.     if (get_server(line, NNTP_STRLEN) == -1) {
  2056.         fprintf(stderr, "connection to server broken\n");
  2057.         tass_done(1);
  2058.     }
  2059.  
  2060.     if (atoi(line) != OK_GROUP)
  2061.         return;
  2062.  
  2063.     sscanf(line,"%ld %ld %ld %ld", &dummy, &count, &start, &last);
  2064.     if (last - count > start)
  2065.         start = last - count;
  2066.  
  2067.     while (start <= last) {
  2068.         if (top_base >= max_art)
  2069.             expand_art();
  2070.         base[top_base++] = start++;
  2071.     }
  2072. }
  2073.  
  2074.  
  2075. @EOF
  2076.  
  2077. chmod 644 nntp_open.c
  2078.  
  2079. echo x - prompt.c
  2080. cat >prompt.c <<'@EOF'
  2081.  
  2082. #include    <stdio.h>
  2083. #include    "tass.h"
  2084.  
  2085.  
  2086. /*
  2087.  *  parse_num
  2088.  *  get a number from the user
  2089.  *  Return -1 if missing or bad number typed
  2090.  */
  2091.  
  2092. parse_num(ch, prompt)
  2093. char ch;
  2094. char *prompt;
  2095. {
  2096.     char buf[40];
  2097.     int len;
  2098.     int i;
  2099.     int num;
  2100.  
  2101.     MoveCursor(LINES,0);
  2102.     printf("%s %c",prompt,ch);
  2103.     fflush(stdout);
  2104.     buf[0] = ch;
  2105.     buf[1] = '\0';
  2106.     len = 1;
  2107.     ch = ReadCh();
  2108.     while (ch != '\n'&& ch != '\r') {
  2109.         if (ch >= '0' && ch <= '9' && len < 4) {
  2110.             buf[len++] = ch;
  2111.             buf[len] = '\0';
  2112.             putchar(ch);
  2113.         } else if (ch == 8 || ch == 127) {
  2114.             if (len) {
  2115.                 len--;
  2116.                 buf[len] = '\0';
  2117.                 putchar('\b');
  2118.                 putchar(' ');
  2119.                 putchar('\b');
  2120.             } else {
  2121.                 MoveCursor(LINES, 0);
  2122.                 CleartoEOLN();
  2123.                 return(-1);
  2124.             }
  2125.         } else if (ch == 21) {    /* control-U    */
  2126.             for (i = len;i>0;i--) {
  2127.                 putchar('\b');
  2128.                 putchar(' ');
  2129.                 putchar('\b');
  2130.             }
  2131.             buf[0] = '\0';
  2132.             len = 0;
  2133.         } else
  2134.             putchar(7);
  2135.         fflush(stdout);
  2136.         ch = ReadCh();
  2137.     }
  2138.  
  2139.     MoveCursor(LINES, 0);
  2140.     CleartoEOLN();
  2141.  
  2142.     if (len) {
  2143.         num = atoi(buf);
  2144.         return(num);
  2145.     } else
  2146.         return(-1);
  2147. }
  2148.  
  2149.  
  2150. /*
  2151.  *  parse_string
  2152.  *  get a string from the user
  2153.  *  Return TRUE if a valid string was typed, FALSE otherwise
  2154.  */
  2155.  
  2156. parse_string(prompt, buf)
  2157. char *prompt;
  2158. char *buf;
  2159. {
  2160. int len;
  2161. int i;
  2162. char ch;
  2163.  
  2164.     clear_message();
  2165.     MoveCursor(LINES,0);
  2166.     printf("%s", prompt);
  2167.     fflush(stdout);
  2168.     buf[0] = '\0';
  2169.     len = 0;
  2170.     ch = ReadCh();
  2171.     while (ch != '\n' && ch != '\r') {
  2172.         if (ch >= ' ' && len < 60) {
  2173.             buf[len++] = ch;
  2174.             buf[len] = '\0';
  2175.             putchar(ch);
  2176.         } else if (ch == 8 || ch == 127) {
  2177.             if (len) {
  2178.                 len--;
  2179.                 buf[len] = '\0';
  2180.                 putchar('\b');
  2181.                 putchar(' ');
  2182.                 putchar('\b');
  2183.             } else {
  2184.                 MoveCursor(LINES, 0);
  2185.                 CleartoEOLN();
  2186.                 return(FALSE);
  2187.             }
  2188.         } else if (ch == 21) {    /* control-U    */
  2189.             for (i = len;i>0;i--) {
  2190.                 putchar('\b');
  2191.                 putchar(' ');
  2192.                 putchar('\b');
  2193.             }
  2194.             buf[0] = '\0';
  2195.             len = 0;
  2196.         } else
  2197.             putchar(7);
  2198.         fflush(stdout);
  2199.         ch = ReadCh();
  2200.     }
  2201.     MoveCursor(LINES,0);
  2202.     CleartoEOLN();
  2203.  
  2204.     return TRUE;
  2205. }
  2206.  
  2207.  
  2208. prompt_yn(prompt)
  2209. char *prompt;
  2210. {
  2211.     char ch;
  2212.  
  2213.     clear_message();
  2214.     MoveCursor(LINES,0);
  2215.     printf("%s", prompt);
  2216.     fflush(stdout);
  2217.  
  2218.     ch = ReadCh();
  2219.     clear_message();
  2220.  
  2221.     if (ch == 'y' || ch == 'Y')
  2222.         return TRUE;
  2223.  
  2224.     return FALSE;
  2225. }
  2226.  
  2227.  
  2228. continue_prompt() {
  2229.  
  2230.     printf("-Hit return to continue-");
  2231.     fflush(stdout);
  2232.     ReadCh();
  2233. }
  2234.  
  2235.  
  2236. @EOF
  2237.  
  2238. chmod 644 prompt.c
  2239.  
  2240. echo x - tass.h
  2241. cat >tass.h <<'@EOF'
  2242.  
  2243. #define        LIBDIR        "/usr/lib/news"
  2244. #define        SPOOLDIR    "/usr/spool/news"
  2245. #define        MAILER        "/bin/rmail"
  2246. #define        DEF_EDITOR    "/usr/bin/vi"
  2247.  
  2248. #define        TRUE        1
  2249. #define        FALSE        0
  2250.  
  2251. #define        LEN        200
  2252.  
  2253. #define        INDEX_TOP        4
  2254. #define        NOTESLINES        (LINES - INDEX_TOP - 2)
  2255. #define        RIGHT_POS        (COLS - 16)
  2256. #define        MORE_POS        (COLS - 20)
  2257.  
  2258. #define        MAX_FROM    25
  2259. #define        MAX_SUBJ    38
  2260. #define        TABLE_SIZE    1409        /* should be prime */
  2261.  
  2262. /* #define        MAX_SUBJ    (COLS - 42)    */
  2263.  
  2264.  
  2265. struct header {
  2266.     long artnum;
  2267.     char *subject;
  2268.     char *from;
  2269.     int thread;
  2270.     int inthread;
  2271.     int unread;        /* has this article been read? */
  2272.                 /* 0 = read, 1 = unread, 2 = will return */
  2273. };
  2274.  
  2275. /*
  2276.  *  header.artnum:
  2277.  *    article number in spool directory for group
  2278.  *
  2279.  *  header.thread:
  2280.  *    initially -1
  2281.  *    points to another arts[] (struct header): zero and up
  2282.  *    -2 means article has expired (wasn't found in file search
  2283.  *    of spool directory for the group)
  2284.  *
  2285.  *  header.inthread:
  2286.  *    FALSE for the first article in a thread, TRUE for all
  2287.  *    following articles in thread
  2288.  *
  2289.  *  header.read:
  2290.  *    boolean, has this article been read or not
  2291.  */
  2292.  
  2293. struct group_ent {
  2294.     char *name;
  2295.     long max;
  2296.     long min;
  2297.     int next;        /* next active entry in hash chain */
  2298.     int flag;
  2299. };
  2300.  
  2301. #define        NOTGOT        0x01    /* haven't put in my_group yet */
  2302. #define        SUBS        0x02    /* subscribed to */
  2303.  
  2304.  
  2305. extern int top;
  2306. extern struct header *arts;
  2307. extern long *base;
  2308. extern int max_art;
  2309.  
  2310. extern char sig[LEN];
  2311. extern char signature[LEN];
  2312. extern char userid[LEN];
  2313. extern char homedir[LEN];
  2314. extern char indexdir[LEN];
  2315. extern char my_org[LEN];
  2316. extern char active_file[LEN];
  2317. extern char newsrc[LEN];
  2318. extern char newnewsrc[LEN];
  2319. extern char delgroups[LEN];
  2320. extern int top_base;
  2321. extern int LINES, COLS;
  2322. extern char *str_save();
  2323. extern char *my_malloc();
  2324. extern char *my_realloc();
  2325. extern int group_hash[TABLE_SIZE];
  2326.  
  2327. extern int num_active;
  2328. extern struct group_ent *active;
  2329. extern int *my_group;
  2330. extern int *unread;
  2331. extern int max_active;
  2332.  
  2333. extern int local_top;
  2334. extern char *eat_re();
  2335. extern char *nice_time();
  2336. extern int update;
  2337. extern int inverse_okay;
  2338.  
  2339. extern int tass_uid;
  2340. extern int tass_gid;
  2341. extern int real_uid;
  2342. extern int real_gid;
  2343. extern int local_index;
  2344.  
  2345. extern char *strcpy();
  2346. extern char *strncat();
  2347. extern char *strncpy();
  2348. extern long atol();
  2349.  
  2350.  
  2351. #define        ctrl(c)            ((c) & 0x1F)
  2352.  
  2353. /*
  2354.  *  Assertion verifier
  2355.  */
  2356.  
  2357. #ifdef __STDC__
  2358. #define    assert(p)    if(! (p)) asfail(__FILE__, __LINE__, #p); else
  2359. #else
  2360. #define    assert(p)    if(! (p)) asfail(__FILE__, __LINE__, "p"); else
  2361. #endif
  2362.  
  2363. #define        TASS_HEADER    "Tass 3.2"
  2364.  
  2365. @EOF
  2366.  
  2367. chmod 644 tass.h
  2368.  
  2369. echo x - time.c
  2370. cat >time.c <<'@EOF'
  2371.  
  2372. #include    <sys/types.h>
  2373. #include    <time.h>
  2374.  
  2375.  
  2376. nicedate(timestr, newstr)
  2377. char *timestr, *newstr;
  2378. {
  2379.     int i;
  2380.  
  2381.     for (i = 0; i <= 7; i++)
  2382.         *newstr++ = timestr[i];
  2383.     if (timestr[8] != ' ')
  2384.         *newstr++ = timestr[8];
  2385.     *newstr++ = timestr[9];
  2386.     *newstr++ = ',';
  2387.     *newstr++ = ' ';
  2388.     for (i = 20;i <= 23; i++)
  2389.         *newstr++ = timestr[i];
  2390.     *newstr++ = '\0';
  2391. }
  2392.  
  2393. nicetime(timestr, newstr)
  2394. char *timestr, *newstr;
  2395. {
  2396.     int hours;
  2397.     char dayornite[3];
  2398.  
  2399.     if (timestr[11] == ' ')
  2400.         hours = timestr[12] - '0';
  2401.     else
  2402.         hours = (timestr[11]-'0')*10 + (timestr[12]-'0');
  2403.     if (hours < 12)
  2404.         strcpy(dayornite, "am");
  2405.     else
  2406.         strcpy(dayornite, "pm");
  2407.     if (hours >= 13)
  2408.         hours -= 12;
  2409.     if (!hours)
  2410.         hours = 12;
  2411.     sprintf(newstr, "%d:%c%c%s", hours, timestr[14],
  2412.                     timestr[15], dayornite);
  2413. }
  2414.  
  2415. char *nice_time() {
  2416.     char *timestr;
  2417.     char the_date[17];
  2418.     char the_time[8];
  2419.     extern char *ctime();
  2420.     long time_now;
  2421.     static char buf[25];
  2422.  
  2423.     time(&time_now);
  2424.     timestr = ctime(&time_now);
  2425.     nicedate(timestr, the_date);
  2426.     nicetime(timestr, the_time);
  2427.     sprintf(buf,"%s  %s", the_date, the_time);
  2428.     return(buf);
  2429. }
  2430.  
  2431. @EOF
  2432.  
  2433. chmod 644 time.c
  2434.  
  2435. exit 0
  2436.