home *** CD-ROM | disk | FTP | other *** search
/ Source Code 1992 March / Source_Code_CD-ROM_Walnut_Creek_March_1992.iso / usenet / altsrcs / 2 / 2764 < prev    next >
Internet Message Format  |  1991-02-14  |  61KB

  1. From: skrenta@blekko.commodore.com (Rich Skrenta)
  2. Newsgroups: alt.sources
  3. Subject: Tass newsreader (part 1 of 2)
  4. Message-ID: <145@blekko.commodore.com>
  5. Date: 14 Feb 91 19:33:07 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. #    COPYRIGHT    Makefile    Todo        art.c        
  13. #    curses.c    group.c        mail.c        main.c        
  14. #    misc.c        
  15. #
  16.  
  17. echo x - COPYRIGHT
  18. cat >COPYRIGHT <<'@EOF'
  19. /*
  20.  *  Tass, a visual Usenet news reader
  21.  *  (c) Copyright 1990 by Rich Skrenta
  22.  *
  23.  *  Distribution agreement:
  24.  *
  25.  *    You may freely copy or redistribute this software, so long
  26.  *    as there is no profit made from its use, sale, trade or
  27.  *    reproduction.  You may not change this copyright notice,
  28.  *    and it must be included prominently in any copy made.
  29.  */
  30. @EOF
  31.  
  32. chmod 600 COPYRIGHT
  33.  
  34. echo x - Makefile
  35. cat >Makefile <<'@EOF'
  36.  
  37. # Edit the defines in tass.h to point tass at the correct news libdir
  38.  
  39. #
  40. # For Berkeley systems:
  41. #
  42. # CFLAGS= -DBSD
  43. # LIBS= -lcurses -ltermcap
  44.  
  45. #  art.c needs to know whether readdir returns struct dirent or
  46. #  struct direct.  You don't need to change anything if:
  47. #    you're bsd and have BSD defined
  48. #    you're Xenix 286
  49. #    you're SCO Unix and have -UM_XENIX defined
  50. #    you use struct dirent
  51.  
  52. #
  53. # For System V and Xenix:
  54. #
  55. CFLAGS=-g
  56. LIBS= -lcurses -lgen
  57.  
  58.  
  59. OBJECTS    =    curses.o art.o group.o mail.o main.o misc.o page.o \
  60.         prompt.o screen.o select.o time.o
  61.  
  62. tass: $(OBJECTS)
  63.     cc $(CFLAGS) -o tass $(OBJECTS) $(LIBS)
  64.  
  65. shar:
  66.     -mv -f ../tass.shar ../tass.shar-
  67.     shar -v [A-Z]* *.[ch] > ../tass.shar
  68.  
  69.  
  70. art.o:        art.c tass.h
  71. curses.o:    curses.c
  72. group.o:    group.c tass.h
  73. mail.o:        mail.c
  74. main.o:        main.c tass.h
  75. misc.o:        misc.c tass.h
  76. page.o:        page.c tass.h
  77. prompt.o:    prompt.c tass.h
  78. screen.o:    screen.c tass.h
  79. select.o:    select.c tass.h
  80. time.o:        time.c
  81. @EOF
  82.  
  83. chmod 644 Makefile
  84.  
  85. echo x - Todo
  86. cat >Todo <<'@EOF'
  87. make initial .newsrc just be created but  contain nothing--must
  88. subscribe
  89.  
  90. find_new_to doesnn't seem to work in mail_to_someone()
  91. @EOF
  92.  
  93. chmod 644 Todo
  94.  
  95. echo x - art.c
  96. cat >art.c <<'@EOF'
  97.  
  98.  
  99. #include    <stdio.h>
  100. #include    <signal.h>
  101. #include    "tass.h"
  102.  
  103.  
  104. /* Hopefully one of these is right for you. */
  105.  
  106. #ifdef BSD
  107. #    include <sys/types.h>
  108. #    include <sys/dir.h>
  109. #    define        DIR_BUF        struct direct
  110. #    define        D_LENGTH    d_namlen
  111. #endif
  112. #ifdef M_XENIX
  113. #    include <sys/ndir.h>
  114. #    define        DIR_BUF        struct direct
  115. #    define        D_LENGTH    d_namlen
  116. #endif
  117. #ifndef DIR_BUF
  118. #    include    <sys/types.h>
  119. #    include    <dirent.h>
  120. #    define        DIR_BUF        struct dirent
  121. #    define        D_LENGTH    d_reclen
  122. #endif
  123.  
  124.  
  125. char index_file[LEN+1];
  126. char *glob_art_group;
  127.  
  128.  
  129. #ifdef SIGTSTP
  130. void
  131. art_susp(i)
  132. int i;
  133. {
  134.  
  135.     Raw(FALSE);
  136.     putchar('\n');
  137.     signal(SIGTSTP, SIG_DFL);
  138.     kill(0, SIGTSTP);
  139.  
  140.     signal(SIGTSTP, art_susp);
  141.     Raw(TRUE);
  142.  
  143.     mail_setup();
  144.     ClearScreen();
  145.     MoveCursor(LINES, 0);
  146.     printf("Group %s...    ", glob_art_group);
  147.     fflush(stdout);
  148. }
  149. #endif
  150.  
  151.  
  152. /*
  153.  *  Convert a string to a long, only look at first n characters
  154.  */
  155.  
  156. my_atol(s, n)
  157. char *s;
  158. int n;
  159. {
  160.     long ret = 0;
  161.  
  162.     while (*s && n--) {
  163.         if (*s >= '0' && *s <= '9')
  164.             ret = ret * 10 + (*s - '0');
  165.         else
  166.             return -1;
  167.         s++;
  168.     }
  169.  
  170.     return ret;
  171. }
  172.  
  173.  
  174. /*
  175.  *  Construct the pointers to the basenotes of each thread
  176.  *  arts[] contains every article in the group.  inthread is
  177.  *  set on each article that is after the first article in the
  178.  *  thread.  Articles which have been expired have their thread
  179.  *  set to -2.
  180.  */
  181.  
  182. find_base() {
  183.     int i;
  184.  
  185.     top_base = 0;
  186.  
  187.     for (i = 0; i < top; i++)
  188.         if (!arts[i].inthread && arts[i].thread != -2) {
  189.             if (top_base >= max_art)
  190.                 expand_art();
  191.             base[top_base++] = i;
  192.         }
  193. }
  194.  
  195.  
  196. /* 
  197.  *  Count the number of non-expired articles in arts[]
  198.  */
  199.  
  200. num_arts() {
  201.     int sum = 0;
  202.  
  203.     int i;
  204.  
  205.     for (i = 0; i < top; i++)
  206.         if (arts[i].thread != -2)
  207.             sum++;
  208.  
  209.     return sum;
  210. }
  211.  
  212.  
  213. /*
  214.  *  Do we have an entry for article art?
  215.  */
  216.  
  217. valid_artnum(art)
  218. long art;
  219. {
  220.     int i;
  221.  
  222.     for (i = 0; i < top; i++)
  223.         if (arts[i].artnum == art)
  224.             return i;
  225.  
  226.     return -1;
  227. }
  228.  
  229.  
  230. /*
  231.  *  Return TRUE if arts[] contains any expired articles
  232.  *  (articles we have an entry for which don't have a corresponding
  233.  *   article file in the spool directory)
  234.  */
  235.  
  236. purge_needed() {
  237.     int i;
  238.  
  239.     for (i = 0; i < top; i++)
  240.         if (arts[i].thread == -2)
  241.             return TRUE;
  242.  
  243.     return FALSE;
  244. }
  245.  
  246.  
  247. /*
  248.  *  Main group indexing routine.  Group should be the name of the
  249.  *  newsgroup, i.e. "comp.unix.amiga".  group_path should be the
  250.  *  same but with the .'s turned into /'s: "comp/unix/amiga"
  251.  *
  252.  *  Will read any existing index, create or incrementally update
  253.  *  the index by looking at the articles in the spool directory,
  254.  *  and attempt to write a new index if necessary.
  255.  */
  256.  
  257. index_group(group, group_path)
  258. char *group;
  259. char *group_path;
  260. {
  261.     int modified;
  262.  
  263.     glob_art_group = group;
  264.  
  265. #ifdef SIGTSTP
  266.     signal(SIGTSTP, art_susp);
  267. #endif
  268.  
  269.     if (!update) {
  270.         clear_message();
  271.         MoveCursor(LINES, 0);
  272.         printf("Group %s...    ", group);
  273.         fflush(stdout);
  274.     }
  275.  
  276.     if (local_index)
  277.         find_local_index(group);
  278.     else
  279.         sprintf(index_file, "%s/%s/.tindex", SPOOLDIR, group_path);
  280.  
  281.     load_index();
  282.     modified = read_group(group_path);
  283.     make_threads();
  284.     if (modified || purge_needed()) {
  285.         if (local_index) {    /* writing index in home directory */
  286.             setuid(real_uid);    /* so become them */
  287.             setgid(real_gid);
  288.         }
  289.         dump_index(group);
  290.         if (local_index) {
  291.             setuid(tass_uid);
  292.             setgid(tass_gid);
  293.         }
  294.     }
  295.     find_base();
  296.  
  297.     if (modified && !update)
  298.         clear_message();
  299. }
  300.  
  301.  
  302. /*
  303.  *  Longword comparison routine for the qsort()
  304.  */
  305.  
  306. base_comp(a, b)
  307. long *a;
  308. long *b;
  309. {
  310.  
  311.     if (*a < *b)
  312.         return -1;
  313.     if (*a > *b)
  314.         return 1;
  315.     return 0;
  316. }
  317.  
  318.  
  319. /*
  320.  *  Read the article numbers existing in a group's spool directory
  321.  *  into base[] and sort them.  base_top is one past top.
  322.  */
  323.  
  324. scan_dir(group)
  325. char *group;
  326. {
  327.     DIR *d;
  328.     DIR_BUF *e;
  329.     long art;
  330.     char buf[200];
  331.  
  332.     top_base = 0;
  333.  
  334.     sprintf(buf, "%s/%s", SPOOLDIR, group);
  335.  
  336.     d = opendir(buf);
  337.     if (d != NULL) {
  338.         while ((e = readdir(d)) != NULL) {
  339.             art = my_atol(e->d_name, e->D_LENGTH);
  340.             if (art >= 0) {
  341.                 if (top_base >= max_art)
  342.                     expand_art();
  343.                 base[top_base++] = art;
  344.             }
  345.         }
  346.         closedir(d);
  347.     }
  348.  
  349.     qsort(base, top_base, sizeof(long), base_comp);
  350. }
  351.  
  352.  
  353. /*
  354.  *  Index a group.  Assumes any existing index has already been
  355.  *  loaded.
  356.  */
  357.  
  358. read_group(group)
  359. char *group;
  360. {
  361.     char buf[200];
  362.     int fd;
  363.     long art;
  364.     int count;
  365.     int modified = FALSE;
  366.     int respnum;
  367.     int i;
  368.  
  369.     scan_dir(group);    /* load article numbers into base[] */
  370.  
  371.     count = 0;
  372.  
  373.     for (i = 0; i < top_base; i++) {    /* for each article # */
  374.         art = base[i];
  375.  
  376. /*
  377.  *  Do we already have this article in our index?  Change thread from
  378.  *  -2 to -1 if so and skip the header eating.
  379.  */
  380.  
  381.         if ((respnum = valid_artnum(art)) >= 0) {
  382.             arts[respnum].thread = -1;
  383.             arts[respnum].unread = 1;
  384.             continue;
  385.         }
  386.  
  387.         if (!modified) {
  388.             modified = TRUE;   /* we've modified the index */
  389.                        /* it will need to be re-written */
  390. #if 0
  391.             if (!update) {
  392.                 MoveCursor(LINES, 0);
  393.                 fputs("Indexing...    ", stdout);
  394.                 fflush(stdout);
  395.             }
  396. #endif
  397.         }
  398.  
  399. /*
  400.  *  Add article to arts[]
  401.  */
  402.         if (top >= max_art)
  403.             expand_art();
  404.  
  405.         arts[top].artnum = art;
  406.         arts[top].thread = -1;
  407.         arts[top].inthread = FALSE;
  408.         arts[top].unread = 1;
  409.  
  410.         sprintf(buf, "%s/%s/%ld", SPOOLDIR, group, art);
  411.         fd = open(buf, 0);
  412.         if (fd < 0) {
  413.             fprintf(stderr, "can't open article %s: ", buf);
  414.             perror("");
  415.             continue;
  416.         }
  417.  
  418.         if (!parse_headers(fd, &arts[top]))
  419.             continue;
  420.         top++;
  421.         close(fd);
  422.  
  423.         if (++count % 10 == 0 && !update) {
  424.             printf("\b\b\b\b%4d", count);
  425.             fflush(stdout);
  426.         }
  427.     }
  428.  
  429.     return modified;
  430. }
  431.  
  432.  
  433. /*
  434.  *  Go through the articles in arts[] and use .thread to snake threads
  435.  *  through them.  Use the subject line to construct threads.  The
  436.  *  first article in a thread should have .inthread set to FALSE, the
  437.  *  rest TRUE.  Only do unexprired articles we haven't visited yet
  438.  *  (arts[].thread == -1).
  439.  */
  440.  
  441. make_threads() {
  442.     int i;
  443.     int j;
  444.  
  445.     for (i = 0; i < top; i++) {
  446.         if (arts[i].thread == -1)
  447.             for (j = i+1; j < top; j++)
  448.             if (arts[i].hash == arts[j].hash
  449.             &&  arts[j].thread != -2
  450.             &&  strncmp(arts[i].nore, arts[j].nore, 10) == 0) {
  451.                 arts[i].thread = j;
  452.                 arts[j].inthread = TRUE;
  453.                 break;
  454.             }
  455.     }
  456. }
  457.  
  458.  
  459. /*
  460.  *  Return a pointer into s eliminating any leading Re:'s.  Example:
  461.  *
  462.  *      Re: Reorganization of misc.jobs
  463.  *      ^   ^
  464.  */
  465.  
  466. char *
  467. eat_re(s)
  468. char *s;
  469. {
  470.  
  471. #if 1
  472.     while (*s == 'r' || *s == 'R') {
  473.         if ((*(s+1) == 'e' || *(s+1) == 'E') && *(s+2) == ':')
  474.             s += 3;
  475.         else
  476.             break;
  477.         if (*s == ' ')
  478.             s++;
  479.     }
  480. #else
  481.     while (*s == 'R') {
  482.         if (strncmp(s, "Re: ", 4) == 0)
  483.             s += 4;
  484.         else if (strncmp(s, "Re:", 3) == 0)
  485.             s += 3;
  486.         else if (strncmp(s, "Re^2: ", 6) == 0)
  487.             s += 6;
  488.         else
  489.             break;
  490.     }
  491. #endif
  492.  
  493.     return s;
  494. }
  495.  
  496.  
  497. /*
  498.  *  Hash the subjects (after eating the Re's off) for a quicker
  499.  *  thread search later.  We store the hashes for subjects in the
  500.  *  index file for speed.
  501.  */
  502.  
  503. long
  504. hash_s(s)
  505. char *s;
  506. {
  507.     long h = 0;
  508.  
  509.     while (*s)
  510.         h = h * 64 + *s++;
  511.  
  512.     return h;
  513. }
  514.  
  515.  
  516. parse_headers(fd, h)
  517. int fd;
  518. struct header *h;
  519. {
  520.     char buf[1024];
  521.     char *p, *q;
  522.     char flag;
  523.  
  524.     if (read(fd, buf, 1024) <= 0)
  525.         return FALSE;
  526.  
  527.     buf[1023] = '\0';
  528.  
  529.     p = buf;
  530.  
  531.     h->from[0] = '\0';
  532.     h->subject[0] = '\0';
  533.     h->nore = h->subject;
  534.     h->hash = 0;
  535.  
  536.     while (1) {
  537.         q = p;
  538.         while (*p && *p != '\n') {
  539.             if (*p & 0x7F < 32)
  540.                 *p = ' ';
  541.             p++;
  542.         }
  543.         flag = *p;
  544.         *p++ = '\0';
  545.  
  546.         if (strncmp(q, "From: ", 6) == 0) {
  547.             strncpy(h->from, &q[6], MAX_FROM);
  548.             h->from[MAX_FROM-1] = '\0';
  549.         } else if (strncmp(q, "Subject: ", 9) == 0) {
  550.             h->hash = hash_s(eat_re(&q[9]));
  551.             strncpy(h->subject, &q[9], MAX_SUBJ);
  552.             h->subject[MAX_SUBJ-1] = '\0';
  553.             h->nore = eat_re(h->subject);
  554.         }
  555.  
  556.         if (!flag)
  557.             break;
  558.     }
  559.  
  560.     return TRUE;
  561. }
  562.  
  563.  
  564. /* 
  565.  *  Write out a .tindex file.  Write the group name first so if
  566.  *  local indexing is done we can disambiguate between group name
  567.  *  hash collisions by looking at the index file.
  568.  */
  569.  
  570. dump_index(group)
  571. char *group;
  572. {
  573.     int i;
  574.     char buf[200];
  575.     FILE *fp;
  576.     char *p, *q;
  577.     long l;
  578.  
  579.     fp = fopen(index_file, "w");
  580.     if (fp == NULL)
  581.         return;
  582.  
  583.     fprintf(fp, "%s\n", group);
  584.     fprintf(fp, "%d\n", num_arts());
  585.     for (i = 0; i < top; i++)
  586.         if (arts[i].thread != -2) {
  587.         p = arts[i].nore;
  588.         q = arts[i].subject;
  589.         l = p - q;
  590.         fprintf(fp, "%ld\n%s\n%s\n%ld\n%ld\n",
  591.                 arts[i].artnum,
  592.                 arts[i].subject,
  593.                 arts[i].from,
  594.                 arts[i].hash,
  595. #if 0
  596.                 (long) arts[i].nore - (long) arts[i].subject);
  597. #else
  598.                 l);
  599. #endif
  600.     }
  601.  
  602.     fclose(fp);
  603.     chmod(index_file, 0644);
  604. }
  605.  
  606.  
  607. /*
  608.  *  strncpy that stops at a newline and null terminates
  609.  */
  610.  
  611. my_strncpy(p, q, n)
  612. char *p;
  613. char *q;
  614. int n;
  615. {
  616.  
  617.     while (n--) {
  618.         if (!*q || *q == '\n')
  619.             break;
  620.         *p++ = *q++;
  621.     }
  622.     *p = '\0';
  623. }
  624.  
  625.  
  626. /*
  627.  *  Read in a .tindex file.
  628.  */
  629.  
  630. load_index()
  631. {
  632.     int i;
  633.     long j;
  634.     char buf[200];
  635.     FILE *fp;
  636.     int first = TRUE;
  637.  
  638.     top = 0;
  639.  
  640.     fp = fopen(index_file, "r");
  641.     if (fp == NULL)
  642.         return;
  643.  
  644.     if (fgets(buf, 200, fp) == NULL
  645.     ||  fgets(buf, 200, fp) == NULL) {
  646.         fprintf(stderr, "one\n");
  647.         goto corrupt_index;
  648.     }
  649.  
  650.     i = atol(buf);
  651.     while (top < i) {
  652.         if (top >= max_art)
  653.             expand_art();
  654.  
  655.         arts[top].thread = -2;
  656.         arts[top].inthread = FALSE;
  657.  
  658.         if (fgets(buf, 200, fp) == NULL) {
  659.             fprintf(stderr, "two\n");
  660.             goto corrupt_index;
  661.         }
  662.         arts[top].artnum = atol(buf);
  663.  
  664.         if (fgets(buf, 200, fp) == NULL) {
  665.             fprintf(stderr, "three\n");
  666.             goto corrupt_index;
  667.         }
  668.  
  669.         my_strncpy(arts[top].subject, buf, MAX_SUBJ-1);
  670.             
  671.         if (fgets(buf, 200, fp) == NULL) {
  672.             fprintf(stderr, "four\n");
  673.             goto corrupt_index;
  674.         }
  675.         my_strncpy(arts[top].from, buf, MAX_FROM-1);
  676.  
  677.         if (fgets(buf, 200, fp) == NULL) {
  678.             fprintf(stderr, "five\n");
  679.             goto corrupt_index;
  680.         }
  681.         arts[top].hash = atol(buf);
  682.  
  683.         if (fgets(buf, 200, fp) == NULL) {
  684.             fprintf(stderr, "six\n");
  685.             goto corrupt_index;
  686.         }
  687.  
  688.         j = atol(buf);
  689. #if 0
  690.         if (j < 0 || j > 100) {
  691. #if 0
  692.             goto corrupt_index;
  693. #else
  694.             arts[top].nore = eat_re(arts[top].subject);
  695. #endif
  696.         } else
  697.             arts[top].nore = arts[top].subject + j;
  698. #else
  699.         arts[top].nore = eat_re(arts[top].subject);
  700. #endif
  701.  
  702.         top++;
  703.     }
  704.  
  705.     fclose(fp);
  706.     return;
  707.  
  708. corrupt_index:
  709.     fprintf(stderr, "index file %s corrupt\n", index_file);
  710.     fprintf(stderr, "top = %d\n", top);
  711.     exit(1);
  712.     unlink(index_file);
  713.     top = 0;
  714. }
  715.  
  716.  
  717. /*
  718.  *  Look in the local $HOME/.tindex (or wherever) directory for the
  719.  *  index file for the given group.  Hashing the group name gets
  720.  *  a number.  See if that #.1 file exists; if so, read first line.
  721.  *  Group we want?  If no, try #.2.  Repeat until no such file or
  722.  *  we find an existing file that matches our group.
  723.  */
  724.  
  725. find_local_index(group)
  726. char *group;
  727. {
  728.     unsigned long h;
  729.     static char buf[200];
  730.     int i;
  731.     char *p;
  732.     FILE *fp;
  733.  
  734.     {
  735.         char *t = group;
  736.  
  737.         h = *t++;
  738.         while (*t)
  739.             h = h * 64 + *t++;
  740.     }
  741.  
  742.     i = 1;
  743.     while (1) {
  744.         sprintf(index_file, "%s/%lu.%d", indexdir, h, i);
  745.         fp = fopen(index_file, "r");
  746.         if (fp == NULL)
  747.             return;
  748.  
  749.         if (fgets(buf, 200, fp) == NULL) {
  750.             fclose(fp);
  751.             return;
  752.         }
  753.         fclose(fp);
  754.  
  755.         for (p = buf; *p && *p != '\n'; p++) ;
  756.         *p = '\0';
  757.  
  758.         if (strcmp(buf, group) == 0)
  759.             return;
  760.  
  761.         i++;
  762.     }
  763. }
  764.  
  765.  
  766. /*
  767.  *  Run the index file updater only for the groups we've loaded.
  768.  */
  769.  
  770. do_update() {
  771.     int i;
  772.     char group_path[200];
  773.     char *p;
  774.  
  775.     for (i = 0; i < local_top; i++) {
  776.         strcpy(group_path, active[my_group[i]].name);
  777.         for (p = group_path; *p; p++)
  778.             if (*p == '.')
  779.                 *p = '/';
  780.  
  781.         index_group(active[my_group[i]].name, group_path);
  782.     }
  783. }
  784.  
  785. @EOF
  786.  
  787. chmod 644 art.c
  788.  
  789. echo x - curses.c
  790. cat >curses.c <<'@EOF'
  791.  
  792. /*
  793.  *  This is a screen management library borrowed with permission from the
  794.  *  Elm mail system (a great mailer--I highly recommend it!).
  795.  *
  796.  *  I've hacked this library to only provide what Tass needs.
  797.  *
  798.  *  Original copyright follows:
  799.  */
  800.  
  801. /*******************************************************************************
  802.  *  The Elm Mail System  -  $Revision: 2.1 $   $State: Exp $
  803.  *
  804.  *             Copyright (c) 1986 Dave Taylor
  805.  ******************************************************************************/
  806.  
  807. #include <stdio.h>
  808. #include <curses.h>
  809.  
  810. #define        TRUE        1
  811. #define        FALSE        0
  812.  
  813. #define        BACKSPACE    '\b'
  814. #define        VERY_LONG_STRING    2500
  815.  
  816. int LINES=23;
  817. int COLS=80;
  818.  
  819. int inverse_okay = TRUE;
  820.  
  821. /*
  822. #ifdef BSD
  823. #  ifndef BSD4_1
  824. #    include <sgtty.h>
  825. #  else
  826. #    include <termio.h>
  827. #  endif
  828. # else
  829. #  include <termio.h>
  830. #endif
  831. */
  832.  
  833. #include <ctype.h>
  834.  
  835. /*
  836. #ifdef BSD
  837. #undef tolower
  838. #endif
  839. */
  840.  
  841. #define TTYIN    0
  842.  
  843. #ifdef SHORTNAMES
  844. # define _clearinverse    _clrinv
  845. # define _cleartoeoln    _clrtoeoln
  846. # define _cleartoeos    _clr2eos
  847. #endif
  848.  
  849. #ifndef BSD
  850. struct termio _raw_tty, 
  851.               _original_tty;
  852. #else
  853. #define TCGETA    TIOCGETP
  854. #define TCSETAW    TIOCSETP
  855.  
  856. struct sgttyb _raw_tty,
  857.           _original_tty;
  858. #endif
  859.  
  860. static int _inraw = 0;                  /* are we IN rawmode?    */
  861.  
  862. #define DEFAULT_LINES_ON_TERMINAL    24
  863. #define DEFAULT_COLUMNS_ON_TERMINAL    80
  864.  
  865. static int _memory_locked = 0;        /* are we IN memlock??   */
  866.  
  867. static int _intransmit;            /* are we transmitting keys? */
  868.  
  869. static
  870. char *_clearscreen, *_moveto, *_cleartoeoln, *_cleartoeos,
  871.     *_setinverse, *_clearinverse, *_setunderline, *_clearunderline;
  872.  
  873. static
  874. int _lines,_columns;
  875.  
  876. static char _terminal[1024];              /* Storage for terminal entry */
  877. static char _capabilities[1024];           /* String for cursor motion */
  878.  
  879. static char *ptr = _capabilities;    /* for buffering         */
  880.  
  881. int    outchar();            /* char output for tputs */
  882. char  *tgetstr(),                    /* Get termcap capability */
  883.       *tgoto();                /* and the goto stuff    */
  884.  
  885. InitScreen()
  886. {
  887. int  tgetent(),      /* get termcap entry */
  888.      err;
  889. char termname[40];
  890. char *strcpy(), *getenv();
  891.     
  892.     if (getenv("TERM") == NULL) {
  893.         fprintf(stderr,
  894.           "TERM variable not set; Tass requires screen capabilities\n");
  895.         return(FALSE);
  896.     }
  897.     if (strcpy(termname, getenv("TERM")) == NULL) {
  898.         fprintf(stderr,"Can't get TERM variable\n");
  899.         return(FALSE);
  900.     }
  901.     if ((err = tgetent(_terminal, termname)) != 1) {
  902.         fprintf(stderr,"Can't get entry for TERM\n");
  903.         return(FALSE);
  904.     }
  905.  
  906.     /* load in all those pesky values */
  907.     _clearscreen       = tgetstr("cl", &ptr);
  908.     _moveto            = tgetstr("cm", &ptr);
  909.     _cleartoeoln       = tgetstr("ce", &ptr);
  910.     _cleartoeos        = tgetstr("cd", &ptr);
  911.     _lines                 = tgetnum("li");
  912.     _columns       = tgetnum("co");
  913.     _setinverse        = tgetstr("so", &ptr);
  914.     _clearinverse      = tgetstr("se", &ptr);
  915.     _setunderline      = tgetstr("us", &ptr);
  916.     _clearunderline    = tgetstr("ue", &ptr);
  917.  
  918.     if (!_clearscreen) {
  919.         fprintf(stderr,
  920.             "Terminal must have clearscreen (cl) capability\n");
  921.         return(FALSE);
  922.     }
  923.     if (!_moveto) {
  924.         fprintf(stderr,
  925.             "Terminal must have cursor motion (cm)\n");
  926.         return(FALSE);
  927.     }
  928.     if (!_cleartoeoln) {
  929.         fprintf(stderr,
  930.             "Terminal must have clear to end-of-line (ce)\n");
  931.         return(FALSE);
  932.     }
  933.     if (!_cleartoeos) {
  934.         fprintf(stderr,
  935.             "Terminal must have clear to end-of-screen (cd)\n");
  936.         return(FALSE);
  937.     }
  938.     if (_lines == -1)
  939.         _lines = DEFAULT_LINES_ON_TERMINAL;
  940.     if (_columns == -1)
  941.         _columns = DEFAULT_COLUMNS_ON_TERMINAL;
  942.     return(TRUE);
  943. }
  944.  
  945. ScreenSize(lines, columns)
  946. int *lines, *columns;
  947. {
  948.     /** returns the number of lines and columns on the display. **/
  949.  
  950.     if (_lines == 0) _lines = DEFAULT_LINES_ON_TERMINAL;
  951.     if (_columns == 0) _columns = DEFAULT_COLUMNS_ON_TERMINAL;
  952.  
  953.     *lines = _lines - 1;        /* assume index from zero*/
  954.     *columns = _columns;        /* assume index from one */
  955. }
  956.  
  957. ClearScreen()
  958. {
  959.     /* clear the screen: returns -1 if not capable */
  960.  
  961.     tputs(_clearscreen, 1, outchar);
  962.     fflush(stdout);      /* clear the output buffer */
  963. }
  964.  
  965. MoveCursor(row, col)
  966. int row, col;
  967. {
  968.     /** move cursor to the specified row column on the screen.
  969.             0,0 is the top left! **/
  970.  
  971.     char *stuff, *tgoto();
  972.  
  973.     stuff = tgoto(_moveto, col, row);
  974.     tputs(stuff, 1, outchar);
  975.     fflush(stdout);
  976. }
  977.  
  978. CleartoEOLN()
  979. {
  980.     /** clear to end of line **/
  981.  
  982.     tputs(_cleartoeoln, 1, outchar);
  983.     fflush(stdout);  /* clear the output buffer */
  984. }
  985.  
  986. CleartoEOS()
  987. {
  988.     /** clear to end of screen **/
  989.  
  990.     tputs(_cleartoeos, 1, outchar);
  991.     fflush(stdout);  /* clear the output buffer */
  992. }
  993.  
  994. StartInverse()
  995. {
  996.     /** set inverse video mode **/
  997.  
  998.     if (_setinverse && inverse_okay)
  999.         tputs(_setinverse, 1, outchar);
  1000. /*    fflush(stdout);    */
  1001. }
  1002.  
  1003.  
  1004. EndInverse()
  1005. {
  1006.     /** compliment of startinverse **/
  1007.  
  1008.     if (_clearinverse && inverse_okay)
  1009.         tputs(_clearinverse, 1, outchar);
  1010. /*    fflush(stdout);    */
  1011. }
  1012.  
  1013. #if 0
  1014. StartUnderline()
  1015. {
  1016.     /** start underline mode **/
  1017.  
  1018.     if (!_setunderline)
  1019.         return(-1);
  1020.  
  1021.     tputs(_setunderline, 1, outchar);
  1022.     fflush(stdout);
  1023.     return(0);
  1024. }
  1025.  
  1026.  
  1027. EndUnderline()
  1028. {
  1029.     /** the compliment of start underline mode **/
  1030.  
  1031.     if (!_clearunderline)
  1032.         return(-1);
  1033.  
  1034.     tputs(_clearunderline, 1, outchar);
  1035.     fflush(stdout);
  1036.     return(0);
  1037. }
  1038. #endif
  1039.  
  1040. RawState()
  1041. {
  1042.     /** returns either 1 or 0, for ON or OFF **/
  1043.  
  1044.     return( _inraw );
  1045. }
  1046.  
  1047. Raw(state)
  1048. int state;
  1049. {
  1050.     /** state is either TRUE or FALSE, as indicated by call **/
  1051.  
  1052.     if (state == FALSE && _inraw) {
  1053.       (void) ioctl(TTYIN, TCSETAW, &_original_tty);
  1054.       _inraw = 0;
  1055.     }
  1056.     else if (state == TRUE && ! _inraw) {
  1057.  
  1058.       (void) ioctl(TTYIN, TCGETA, &_original_tty);    /** current setting **/
  1059.  
  1060.       (void) ioctl(TTYIN, TCGETA, &_raw_tty);    /** again! **/
  1061. #ifdef BSD
  1062.       _raw_tty.sg_flags &= ~(ECHO | CRMOD);    /* echo off */
  1063.       _raw_tty.sg_flags |= CBREAK;    /* raw on    */
  1064. #else
  1065.       _raw_tty.c_lflag &= ~(ICANON | ECHO);    /* noecho raw mode        */
  1066.  
  1067.       _raw_tty.c_cc[VMIN] = '\01';    /* minimum # of chars to queue    */
  1068.       _raw_tty.c_cc[VTIME] = '\0';    /* minimum time to wait for input */
  1069.  
  1070. #endif
  1071.       (void) ioctl(TTYIN, TCSETAW, &_raw_tty);
  1072.  
  1073.       _inraw = 1;
  1074.     }
  1075. }
  1076.  
  1077. int
  1078. ReadCh()
  1079. {
  1080.     /** read a character with Raw mode set! **/
  1081.  
  1082.     register int result;
  1083.     char ch;
  1084.     result = read(0, &ch, 1);
  1085.         return((result <= 0 ) ? EOF : ch & 0x7F);
  1086. }
  1087.  
  1088.  
  1089. outchar(c)
  1090. char c;
  1091. {
  1092.     /** output the given character.  From tputs... **/
  1093.     /** Note: this CANNOT be a macro!              **/
  1094.  
  1095.     putc(c, stdout);
  1096. }
  1097.  
  1098. @EOF
  1099.  
  1100. chmod 644 curses.c
  1101.  
  1102. echo x - group.c
  1103. cat >group.c <<'@EOF'
  1104.  
  1105.  
  1106. #include    <stdio.h>
  1107. #include    <signal.h>
  1108. #include    "tass.h"
  1109.  
  1110.  
  1111. int index_point;
  1112. int first_subj_on_screen;
  1113. int last_subj_on_screen;
  1114. char subject_search_string[LEN+1];    /* last search pattern */
  1115. extern int cur_groupnum;
  1116. extern int last_resp;        /* page.c */
  1117. extern int this_resp;        /* page.c */
  1118. extern int space_mode;        /* select.c */
  1119. extern char *cvers;
  1120.  
  1121. char *glob_group;
  1122.  
  1123.  
  1124. #ifdef SIGTSTP
  1125. void
  1126. group_susp(i)
  1127. int i;
  1128. {
  1129.  
  1130.     Raw(FALSE);
  1131.     putchar('\n');
  1132.     signal(SIGTSTP, SIG_DFL);
  1133.     kill(0, SIGTSTP);
  1134.  
  1135.     signal(SIGTSTP, group_susp);
  1136.     Raw(TRUE);
  1137.     mail_setup();
  1138.     show_group_page(glob_group);
  1139. }
  1140. #endif
  1141.  
  1142.  
  1143. group_page(group)
  1144. char *group;
  1145. {
  1146.     char ch;
  1147.     int i, n;
  1148.     char group_path[200];
  1149.     char *p;
  1150.     char buf[200];
  1151.  
  1152.     glob_group = group;
  1153.  
  1154.     strcpy(group_path, group);        /* turn comp.unix.amiga into */
  1155.     for (p = group_path; *p; p++)        /* comp/unix/amiga */
  1156.         if (*p == '.')
  1157.             *p = '/';
  1158.  
  1159.     last_resp = -1;
  1160.     this_resp = -1;
  1161.     index_group(group, group_path);        /* update index file */
  1162.     read_newsrc_line(group);        /* get sequencer information */
  1163.  
  1164.     if (space_mode) {
  1165.         for (i = 0; i < top_base; i++)
  1166.             if (new_responses(i))
  1167.                 break;
  1168.         if (i < top_base)
  1169.             index_point = i;
  1170.         else
  1171.             index_point = top_base - 1;
  1172.     } else
  1173.         index_point = top_base - 1;
  1174.  
  1175.     show_group_page(group);
  1176.  
  1177.     while (1) {
  1178.         ch = ReadCh();
  1179.  
  1180.         if (ch > '0' && ch <= '9') {    /* 0 goes to basenote */
  1181.             prompt_subject_num(ch, group);
  1182.         } else switch (ch) {
  1183.             case 'I':    /* toggle inverse video */
  1184.                 inverse_okay = !inverse_okay;
  1185.                 if (inverse_okay)
  1186.                     info_message("Inverse video enabled");
  1187.                 else
  1188.                     info_message("Inverse video disabled");
  1189.                 break;
  1190.  
  1191.             case 's':    /* subscribe to this group */
  1192.                 subscribe(group, ':', my_group[cur_groupnum],
  1193.                                     TRUE);
  1194.                 sprintf(buf, "subscribed to %s", group);
  1195.                 info_message(buf);
  1196.                 break;
  1197.  
  1198.             case 'u':    /* unsubscribe to this group */
  1199.                 subscribe(group, '!', my_group[cur_groupnum],
  1200.                                     TRUE);
  1201.                 sprintf(buf, "unsubscribed to %s", group);
  1202.                 info_message(buf);
  1203.                 break;
  1204.  
  1205.             case 'g':    /* choose a new group by name */
  1206.                 n = choose_new_group();
  1207.                 if (n >= 0 && n != cur_groupnum) {
  1208.                     fix_new_highest(cur_groupnum);
  1209.                     cur_groupnum = n;
  1210.                     index_point = -3;
  1211.                     goto group_done;
  1212.                 }
  1213.                 break;
  1214.  
  1215.             case 'c':    /* catchup--mark all articles as read */
  1216.                 if (prompt_yn("Mark everything as read? (y/n): ")) {
  1217.                 for (n = 0; n < top; n++)
  1218.                     arts[n].unread = 0;
  1219.                 show_group_page(group);
  1220.                 info_message("All articles marked as read");
  1221.                 }
  1222.                 break;
  1223.  
  1224.             case 27:    /* common arrow keys */
  1225.                 ch = ReadCh();
  1226.                 if (ch == '[' || ch == 'O')
  1227.                     ch = ReadCh();
  1228.                 switch (ch) {
  1229.                 case 'A':
  1230.                 case 'D':
  1231.                 case 'i':
  1232.                     goto group_up;
  1233.  
  1234.                 case 'B':
  1235.                 case 'I':
  1236.                 case 'C':
  1237.                     goto group_down;
  1238.                 }
  1239.                 break;
  1240.  
  1241.             case 'n':    /* next group */
  1242.                 clear_message();
  1243.                 if (cur_groupnum + 1 >= local_top)
  1244.                     info_message("No more groups");
  1245.                 else {
  1246.                     fix_new_highest(cur_groupnum);
  1247.                     cur_groupnum++;
  1248.                     index_point = -3;
  1249.                     space_mode = FALSE;
  1250.                     goto group_done;
  1251.                 }
  1252.                 break;
  1253.  
  1254.             case 'p':    /* previous group */
  1255.                 clear_message();
  1256.                 if (cur_groupnum <= 0)
  1257.                     info_message("No previous group");
  1258.                 else {
  1259.                     fix_new_highest(cur_groupnum);
  1260.                     cur_groupnum--;
  1261.                     index_point = -3;
  1262.                     space_mode = FALSE;
  1263.                     goto group_done;
  1264.                 }
  1265.                 break;
  1266.  
  1267.             case ' ':
  1268.                 if (top_base <= 0)
  1269.                     info_message("*** No Articles ***");
  1270.                 else if (last_subj_on_screen == top_base)
  1271.                     info_message("*** End of Articles ***");
  1272.                 else
  1273.                     clear_message();
  1274.                 break;
  1275.  
  1276.             case '\t':
  1277.                 fix_new_highest(cur_groupnum);
  1278.                 space_mode = TRUE;
  1279.  
  1280.                 if (index_point < 0
  1281.                 || (n=next_unread((int) base[index_point]))<0) {
  1282.                     for (i = cur_groupnum+1;
  1283.                             i < local_top; i++)
  1284.                         if (unread[i] > 0)
  1285.                             break;
  1286.                     if (i >= local_top)
  1287.                         goto group_done;
  1288.  
  1289.                     cur_groupnum = i;
  1290.                     index_point = -3;
  1291.                     goto group_done;
  1292.                 }
  1293.                 index_point = show_page(n, group, group_path);
  1294.                 if (index_point < 0)
  1295.                     goto group_done;
  1296.                 show_group_page(group);
  1297.                 break;
  1298.     
  1299.  
  1300.             case 'N':    /* go to next unread article */
  1301.                 if (index_point < 0) {
  1302.                     info_message("No next unread article");
  1303.                     break;
  1304.                 }
  1305.  
  1306.                 n = next_unread( (int) base[index_point]);
  1307.                 if (n == -1)
  1308.                     info_message("No next unread article");
  1309.                 else {
  1310.                     index_point =
  1311.                         show_page(n, group, group_path);
  1312.                     if (index_point < 0) {
  1313.                         fix_new_highest(cur_groupnum);
  1314.                         space_mode = FALSE;
  1315.                         goto group_done;
  1316.                     }
  1317.                     show_group_page(group);
  1318.                 }
  1319.                 break;
  1320.  
  1321.             case 'P':    /* go to previous unread article */
  1322.                 if (index_point < 0) {
  1323.                     info_message("No previous unread article");
  1324.                     break;
  1325.                 }
  1326.  
  1327.                 n = prev_response( (int) base[index_point]);
  1328.                 n = prev_unread(n);
  1329.                 if (n == -1)
  1330.                     info_message("No previous unread article");
  1331.                 else {
  1332.                     index_point =
  1333.                         show_page(n, group, group_path);
  1334.                     if (index_point < 0) {
  1335.                         fix_new_highest(cur_groupnum);
  1336.                         space_mode = FALSE;
  1337.                         goto group_done;
  1338.                     }
  1339.                     show_group_page(group);
  1340.                 }
  1341.                 break;
  1342.  
  1343.             case 'w':    /* post a basenote */
  1344.                 post_base(group);
  1345.                 update_newsrc(group, my_group[cur_groupnum]);
  1346.                 index_group(group, group_path);
  1347.                 read_newsrc_line(group);
  1348.                 index_point = top_base - 1;
  1349.                 show_group_page(group);
  1350.                 break;
  1351.  
  1352.             case 't':    /* return to group selection page */
  1353.                 fix_new_highest(cur_groupnum);
  1354.                 goto group_done;
  1355.  
  1356.             case '\r':
  1357.             case '\n':    /* read current basenote */
  1358.                 if (index_point < 0) {
  1359.                     info_message("*** No Articles ***");
  1360.                     break;
  1361.                 }
  1362.                 index_point = show_page((int) base[index_point],
  1363.                             group, group_path);
  1364.                 if (index_point < 0) {
  1365.                     fix_new_highest(cur_groupnum);
  1366.                     space_mode = FALSE;
  1367.                     goto group_done;
  1368.                 }
  1369.                 show_group_page(group);
  1370.                 break;
  1371.  
  1372.             case ctrl('D'):        /* page down */
  1373.                 if (!top_base || index_point == top_base - 1)
  1374.                     break;
  1375.  
  1376.                 erase_subject_arrow();
  1377.                 index_point += NOTESLINES / 2;
  1378.                 if (index_point >= top_base)
  1379.                     index_point = top_base - 1;
  1380.  
  1381.                 if (index_point < first_subj_on_screen
  1382.                 || index_point >= last_subj_on_screen)
  1383.                     show_group_page(group);
  1384.                 else
  1385.                     draw_subject_arrow();
  1386.                 break;
  1387.  
  1388.             case '-':    /* go to last viewed article */
  1389.                 if (this_resp < 0) {
  1390.                     info_message("No last message");
  1391.                     break;
  1392.                 }
  1393.                 index_point = show_page(this_resp,
  1394.                             group, group_path);
  1395.                 if (index_point < 0) {
  1396.                     fix_new_highest(cur_groupnum);
  1397.                     space_mode = FALSE;
  1398.                     goto group_done;
  1399.                 }
  1400.                 show_group_page(group);
  1401.                 break;
  1402.  
  1403.             case ctrl('U'):        /* page up */
  1404.                 if (!top_base)
  1405.                     break;
  1406.  
  1407.                 erase_subject_arrow();
  1408.                 index_point -= NOTESLINES / 2;
  1409.                 if (index_point < 0)
  1410.                     index_point = 0;
  1411.                 if (index_point < first_subj_on_screen
  1412.                 || index_point >= last_subj_on_screen)
  1413.                     show_group_page(group);
  1414.                 else
  1415.                     draw_subject_arrow();
  1416.                 break;
  1417.  
  1418.             case 'v':
  1419.                 info_message(cvers);
  1420.                 break;
  1421.  
  1422.             case '!':
  1423.                 shell_escape();
  1424.                 show_group_page(group);
  1425.                 break;
  1426.  
  1427.             case ctrl('N'):
  1428.             case 'j':        /* line down */
  1429. group_down:
  1430.                 if (!top_base || index_point + 1 >= top_base)
  1431.                     break;
  1432.  
  1433.                 if (index_point + 1 >= last_subj_on_screen) {
  1434.                     index_point++;
  1435.                     show_group_page(group);
  1436.                 } else {
  1437.                     erase_subject_arrow();
  1438.                     index_point++;
  1439.                     draw_subject_arrow();
  1440.                 }
  1441.                 break;
  1442.  
  1443.             case ctrl('P'):
  1444.             case 'k':        /* line up */
  1445. group_up:
  1446.                 if (!top_base || !index_point)
  1447.                     break;
  1448.  
  1449.                 if (index_point <= first_subj_on_screen) {
  1450.                     index_point--;
  1451.                     show_group_page(group);
  1452.                 } else {
  1453.                     erase_subject_arrow();
  1454.                     index_point--;
  1455.                     draw_subject_arrow();
  1456.                 }
  1457.                 break;
  1458.  
  1459.             case ctrl('R'):
  1460.             case ctrl('L'):
  1461.             case ctrl('W'):
  1462.             case 'i':        /* return to index */
  1463.                     show_group_page(group);
  1464.                     break;
  1465.  
  1466.             case '/':        /* forward search */
  1467.                     search_subject(TRUE, group);
  1468.                     break;
  1469.  
  1470.             case '?':        /* backward search */
  1471.                     search_subject(FALSE, group);
  1472.                     break;
  1473.  
  1474.             case 'q':        /* quit */
  1475.                     index_point = -2;
  1476.                     fix_new_highest(cur_groupnum);
  1477.                     space_mode = FALSE;
  1478.                     goto group_done;
  1479.  
  1480.             case 'h':
  1481.                 tass_group_help();
  1482.                 show_group_page(group);
  1483.                 break;
  1484.  
  1485.             default:
  1486.                 info_message("Bad command.  Type 'h' for help.");
  1487.         }
  1488.     }
  1489.  
  1490. group_done:
  1491.     update_newsrc(group, my_group[cur_groupnum]);
  1492.  
  1493.     if (index_point == -2)
  1494.         tass_done(0);
  1495. }
  1496.  
  1497.  
  1498. /*
  1499.  *  Correct highest[] for the group selection page display since
  1500.  *  new articles may have been read or marked unread
  1501.  */
  1502.  
  1503. fix_new_highest(groupnum)
  1504. int groupnum;
  1505. {
  1506.     int i;
  1507.     int sum = 0;
  1508.  
  1509.     for (i = 0; i < top; i++)
  1510.         if (arts[i].unread)
  1511.             sum++;
  1512.  
  1513.     unread[groupnum] = sum;
  1514. }
  1515.  
  1516.  
  1517. show_group_page(group)
  1518. char *group;
  1519. {
  1520.     int i;
  1521.     int n;
  1522.     char resps[10];
  1523.     char new_resps;
  1524.     int respnum;
  1525.  
  1526. #ifdef SIGTSTP
  1527.     signal(SIGTSTP, group_susp);
  1528. #endif
  1529.  
  1530.     ClearScreen();
  1531.     printf("%s\r\n", nice_time());    /* time in upper left */
  1532.     center_line(1, group);
  1533.  
  1534.     if (mail_check()) {            /* you have mail message in */
  1535.         MoveCursor(0, 66);        /* upper right */
  1536.         printf("you have mail\n");
  1537.     }
  1538.  
  1539.     MoveCursor(INDEX_TOP, 0);
  1540.  
  1541.     first_subj_on_screen = (index_point / NOTESLINES) * NOTESLINES;
  1542.     if (first_subj_on_screen < 0)
  1543.         first_subj_on_screen = 0;
  1544.  
  1545.     last_subj_on_screen = first_subj_on_screen + NOTESLINES;
  1546.     if (last_subj_on_screen >= top_base) {
  1547.         last_subj_on_screen = top_base;
  1548.         first_subj_on_screen = top_base - NOTESLINES;
  1549.  
  1550.         if (first_subj_on_screen < 0)
  1551.             first_subj_on_screen = 0;
  1552.     }
  1553.  
  1554.     for (i = first_subj_on_screen; i < last_subj_on_screen; i++) {
  1555.         if (new_responses(i))
  1556.             new_resps = '+';
  1557.         else
  1558.             new_resps = ' ';
  1559.  
  1560.         n = nresp(i);
  1561.         if (n)
  1562.             sprintf(resps, "%4d", n);
  1563.         else
  1564.             strcpy(resps, "    ");
  1565.  
  1566.         respnum = base[i];
  1567.  
  1568.         printf("  %4d  %-*s %s %-*s %c\r\n",
  1569.                 i + 1,
  1570.                 MAX_SUBJ,
  1571.                 arts[respnum].subject,
  1572.                 resps,
  1573.                 MAX_FROM,
  1574.                 arts[respnum].from,
  1575.                 new_resps);
  1576.     }
  1577.  
  1578.     if (top_base <= 0)
  1579.         info_message("*** No Articles ***");
  1580.     else if (last_subj_on_screen == top_base)
  1581.         info_message("*** End of Articles ***");
  1582.  
  1583.     if (top_base > 0)
  1584.         draw_subject_arrow();
  1585. }
  1586.  
  1587. draw_subject_arrow() {
  1588.  
  1589.     draw_arrow(INDEX_TOP + (index_point-first_subj_on_screen) );
  1590. }
  1591.  
  1592. erase_subject_arrow() {
  1593.  
  1594.     erase_arrow(INDEX_TOP + (index_point-first_subj_on_screen) );
  1595. }
  1596.  
  1597.  
  1598. prompt_subject_num(ch, group)
  1599. char ch;
  1600. char *group;
  1601. {
  1602. int num;
  1603.  
  1604.  
  1605.     clear_message();
  1606.  
  1607.     if ((num = parse_num(ch, "Read article> ")) == -1) {
  1608.         clear_message();
  1609.         return FALSE;
  1610.     }
  1611.     num--;        /* index from 0 (internal) vs. 1 (user) */
  1612.  
  1613.     if (num >= top_base)
  1614.         num = top_base - 1;
  1615.  
  1616.     if (num >= first_subj_on_screen
  1617.     &&  num < last_subj_on_screen) {
  1618.         erase_subject_arrow();
  1619.         index_point = num;
  1620.         draw_subject_arrow();
  1621.     } else {
  1622.         index_point = num;
  1623.         show_group_page(group);
  1624.     }
  1625. }
  1626.  
  1627.  
  1628. search_subject(forward, group)
  1629. int forward;
  1630. char *group;
  1631. {
  1632.     char buf[LEN+1];
  1633.     int i;
  1634.     extern char *regcmp();
  1635.     extern char *regex();
  1636.     char *re;
  1637.     char *prompt;
  1638.  
  1639.     clear_message();
  1640.  
  1641.     if (forward)
  1642.         prompt = "/";
  1643.     else
  1644.         prompt = "?";
  1645.  
  1646.     if (!parse_string(prompt, buf))
  1647.         return;
  1648.  
  1649.     if (strlen(buf))
  1650.         strcpy(subject_search_string, buf);
  1651.     else if (!strlen(subject_search_string)) {
  1652.         info_message("No search pattern");
  1653.         return;
  1654.     }
  1655.  
  1656.     i = index_point;
  1657.  
  1658.     glob_name(subject_search_string, buf);
  1659.  
  1660.     if ((re = regcmp(buf, NULL)) == NULL) {
  1661.         info_message("Bad search pattern");
  1662.         return;
  1663.     }
  1664.  
  1665.     do {
  1666.         if (forward)
  1667.             i++;
  1668.         else
  1669.             i--;
  1670.  
  1671.         if (i >= top_base)
  1672.             i = 0;
  1673.         if (i < 0)
  1674.             i = top_base - 1;
  1675.  
  1676.         if (regex(re, arts[ base[i] ].subject) != NULL) {
  1677.             if (i >= first_subj_on_screen
  1678.             &&  i < last_subj_on_screen) {
  1679.                 erase_subject_arrow();
  1680.                 index_point = i;
  1681.                 draw_subject_arrow();
  1682.             } else {
  1683.                 index_point = i;
  1684.                 show_group_page(group);
  1685.             }
  1686.             return;
  1687.         }
  1688.     } while (i != index_point);
  1689.  
  1690.     info_message("No match");
  1691. }
  1692.  
  1693.  
  1694. /*
  1695.  *  Post an original article (not a followup)
  1696.  */
  1697.  
  1698. post_base(group)
  1699. char *group;
  1700. {
  1701.     FILE *fp;
  1702.     char nam[100];
  1703.     char ch;
  1704.     char subj[LEN+1];
  1705.     char buf[200];
  1706.  
  1707.     if (!parse_string("Subject: ", subj))
  1708.         return;
  1709.     if (subj[0] == '\0')
  1710.         return;
  1711.  
  1712.     setuid(real_uid);
  1713.     setgid(real_gid);
  1714.  
  1715.     sprintf(nam, "%s/.article", homedir);
  1716.     if ((fp = fopen(nam, "w")) == NULL) {
  1717.         fprintf(stderr, "can't open %s: ", nam);
  1718.         perror("");
  1719.         return(FALSE);
  1720.     }
  1721.     chmod(nam, 0600);
  1722.  
  1723.     fprintf(fp, "Subject: %s\n", subj);
  1724.     fprintf(fp, "Newsgroups: %s\n", group);
  1725.     fprintf(fp, "Distribution: \n");
  1726.     if (*my_org)
  1727.         fprintf(fp, "Organization: %s\n", my_org);
  1728.     fprintf(fp, "\n");
  1729.  
  1730.     fclose(fp);
  1731.  
  1732.     ch = 'e';
  1733.     while (1) {
  1734.         switch (ch) {
  1735.         case 'e':
  1736.             invoke_editor(nam);
  1737.             break;
  1738.  
  1739.         case 'a':
  1740.             return FALSE;
  1741.  
  1742.         case 'p':
  1743.             printf("\nPosting...  ");
  1744.             fflush(stdout);
  1745.             sprintf(buf, "%s/inews -h < %s", LIBDIR, nam);
  1746.             if (invoke_cmd(buf)) {
  1747.                 printf("article posted\n");
  1748.                 fflush(stdout);
  1749.                 goto post_base_done;
  1750.             } else {
  1751.                 printf("article rejected\n");
  1752.                 fflush(stdout);
  1753.                 break;
  1754.             }
  1755.         }
  1756.  
  1757.         do {
  1758.             MoveCursor(LINES, 0);
  1759.             fputs("abort, edit, post: ", stdout);
  1760.             fflush(stdout);
  1761.             ch = ReadCh();
  1762.         } while (ch != 'a' && ch != 'e' && ch != 'p');
  1763.     }
  1764.  
  1765. post_base_done:
  1766.     setuid(tass_uid);
  1767.     setgid(tass_gid);
  1768.  
  1769.     continue_prompt();
  1770.  
  1771.     return(TRUE);
  1772. }
  1773.  
  1774.  
  1775. /*
  1776.  *  Return the number of unread articles there are within a thread
  1777.  */
  1778.  
  1779. new_responses(thread)
  1780. int thread;
  1781. {
  1782.     int i;
  1783.     int sum = 0;
  1784.  
  1785.     for (i = base[thread]; i >= 0; i = arts[i].thread)
  1786.         if (arts[i].unread)
  1787.             sum++;
  1788.  
  1789.     return sum;
  1790. }
  1791.  
  1792.  
  1793. tass_group_help() {
  1794.     char ch;
  1795.  
  1796. group_help_start:
  1797.  
  1798.     ClearScreen();
  1799.     center_line(0, TASS_HEADER);
  1800.     center_line(1, "Index Page Commands (page 1 of 2)");
  1801.  
  1802.     MoveCursor(3, 0);
  1803.  
  1804.     printf("4        Select article 4\r\n");
  1805.     printf("^D       Page down\r\n");
  1806.     printf("^U       Page up\r\n");
  1807.     printf("<CR>     Read current article\r\n");
  1808.     printf("<TAB>    View next unread article or group\r\n");
  1809.     printf("c        Mark all articles as read\r\n");
  1810.     printf("g        Choose a new group by name\r\n");
  1811.     printf("j        Down a line\r\n");
  1812.     printf("k        Up a line\r\n");
  1813.     printf("n        Go to next group\n");
  1814.     printf("N        Go to next unread article\n");
  1815.     printf("p        Go to previous group\n");
  1816.     printf("P        Go to previous unread article\n");
  1817.     printf("q        Quit\r\n");
  1818.  
  1819.     center_line(LINES, "-- hit space for more commands --");
  1820.     ch = ReadCh();
  1821.     if (ch != ' ')
  1822.         return;
  1823.  
  1824.     ClearScreen();
  1825.     center_line(0, TASS_HEADER);
  1826.     center_line(1, "Index Page Commands (page 2 of 2)");
  1827.  
  1828.     MoveCursor(3, 0);
  1829.  
  1830.     printf("s        Subscribe to this group\r\n");
  1831.     printf("t        Return to group selection index\r\n");
  1832.     printf("u        Unsubscribe to this group\r\n");
  1833.     printf("w        Post an article\r\n");
  1834.     printf("/        Search forward for subject\r\n");
  1835.     printf("?        Search backward for subject\r\n");
  1836.     printf("-        Show last message\r\n");
  1837.  
  1838.     center_line(LINES, "-- hit any key --");
  1839.     ch = ReadCh();
  1840.     if (ch == 'b')
  1841.         goto group_help_start;
  1842. }
  1843.  
  1844. @EOF
  1845.  
  1846. chmod 644 group.c
  1847.  
  1848. echo x - mail.c
  1849. cat >mail.c <<'@EOF'
  1850.  
  1851. #include    <stdio.h>
  1852. #include    <sys/types.h>
  1853. #include    <sys/stat.h>
  1854.  
  1855. #define        TRUE        1
  1856. #define        FALSE        0
  1857.  
  1858.  
  1859. char *mailbox_name = NULL;
  1860. off_t mailbox_size;
  1861.  
  1862.  
  1863. /*
  1864.  *  Record size of mailbox so we can detect if new mail has arrived
  1865.  */
  1866.  
  1867. mail_setup() {
  1868.     struct stat buf;
  1869.     extern char *getenv();
  1870.  
  1871.     if (mailbox_name == NULL)
  1872.         mailbox_name = getenv("MAIL");
  1873.  
  1874.     if (mailbox_name == NULL)
  1875.         mailbox_size = 0;
  1876.     else {
  1877.         if (stat(mailbox_name, &buf) >= 0)
  1878.             mailbox_size = buf.st_size;
  1879.         else
  1880.             mailbox_size = 0;
  1881.     }
  1882. }
  1883.  
  1884.  
  1885. /*
  1886.  *  Return TRUE if new mail has arrived
  1887.  */
  1888.  
  1889. mail_check() {
  1890.     struct stat buf;
  1891.  
  1892.     if (mailbox_name != NULL
  1893.     &&  stat(mailbox_name, &buf) >= 0
  1894.     &&  mailbox_size < buf.st_size)
  1895.         return TRUE;
  1896.  
  1897.     return FALSE;
  1898. }
  1899.  
  1900. @EOF
  1901.  
  1902. chmod 640 mail.c
  1903.  
  1904. echo x - main.c
  1905. cat >main.c <<'@EOF'
  1906.  
  1907. /*
  1908.  *  Tass, a visual Usenet news reader
  1909.  *  (c) Copyright 1990 by Rich Skrenta
  1910.  *
  1911.  *  Distribution agreement:
  1912.  *
  1913.  *    You may freely copy or redistribute this software, so long
  1914.  *    as there is no profit made from its use, sale, trade or
  1915.  *    reproduction.  You may not change this copyright notice,
  1916.  *    and it must be included prominently in any copy made.
  1917.  */
  1918.  
  1919. #include    <stdio.h>
  1920. #include    <signal.h>
  1921. #include    "tass.h"
  1922.  
  1923.  
  1924. int LINES, COLS;
  1925.  
  1926. int max_active;
  1927. struct group_ent *active;        /* active file */
  1928. int group_hash[TABLE_SIZE];        /* group name --> active[] */
  1929. int *my_group;                /* .newsrc --> active[] */
  1930. int *unread;                /* highest art read in group */
  1931. int num_active;                /* one past top of active */
  1932. int local_top;                /* one past top of my_group */
  1933. int update = FALSE;            /* update index files only mode */
  1934.  
  1935. struct header *arts;
  1936. long *base;
  1937. int max_art;
  1938. int top = 0;
  1939. int top_base;
  1940.  
  1941. int tass_uid;
  1942. int tass_gid;
  1943. int real_uid;
  1944. int real_gid;
  1945.  
  1946. int local_index;            /* do private indexing? */
  1947.  
  1948. char *cvers = "Tass 3.0  (c) Copyright 1991 by Rich Skrenta.  All rights reserved";
  1949.  
  1950.  
  1951. #ifdef SIGTSTP
  1952. void
  1953. main_susp(i)
  1954. int i;
  1955. {
  1956.  
  1957.     Raw(FALSE);
  1958.     putchar('\n');
  1959.     signal(SIGTSTP, SIG_DFL);
  1960.     kill(0, SIGTSTP);
  1961.  
  1962.     signal(SIGTSTP, main_susp);
  1963.     mail_setup();
  1964.     Raw(TRUE);
  1965. }
  1966. #endif
  1967.  
  1968.  
  1969. main(argc, argv)
  1970. int argc;
  1971. char **argv;
  1972. {
  1973.     extern int optind, opterr;
  1974.     extern char *optarg;
  1975.     int errflag = 0;
  1976.     int i;
  1977.     int c;
  1978.  
  1979.     for (i = 0; i < TABLE_SIZE; i++)
  1980.         group_hash[i] = -1;
  1981.  
  1982.     signal(SIGPIPE, SIG_IGN);
  1983. #ifdef SIGTSTP
  1984.     signal(SIGTSTP, main_susp);
  1985. #endif
  1986.  
  1987.     tass_uid = geteuid();
  1988.     tass_gid = getegid();
  1989.     real_uid = getuid();
  1990.     real_gid = getgid();
  1991.  
  1992.     init_selfinfo();    /* set up char *'s: homedir, newsrc, etc. */
  1993.     init_alloc();        /* allocate initial array sizes */
  1994.  
  1995.     if (tass_uid == real_uid) {    /* run out of someone's account */
  1996.         local_index = TRUE;    /* index in their home directory */
  1997.         mkdir(indexdir, 0755);
  1998.     } else            /* we're setuid, index in /usr/spool/news */
  1999.         local_index = FALSE;
  2000.  
  2001.     read_active();        /* load the active file into active[] */
  2002.  
  2003.     while ((c = getopt(argc, argv, "f:u")) != -1) {
  2004.         switch(c) {
  2005.         case 'f':
  2006.             strcpy(newsrc, optarg);
  2007.             break;
  2008.  
  2009.         case 'u':
  2010.             update = TRUE;
  2011.             break;
  2012.  
  2013.         case '?':
  2014.         default:
  2015.             errflag++;
  2016.         }
  2017.     }
  2018.  
  2019.     if (errflag) {
  2020.         fprintf(stderr, "usage: tass [options] [newsgroups]\n");
  2021.         fprintf(stderr, "   -f file   use file instead of $HOME/.newsrc\n");
  2022.         fprintf(stderr, "   -u        update index files only\n");
  2023.         exit(1);
  2024.     }
  2025.  
  2026.     if (!update)
  2027.         printf("Tass 3.0\n");
  2028.  
  2029.     if (optind < argc) {
  2030.         while (optind < argc) {
  2031.             if (add_group(argv[optind], TRUE) < 0)
  2032.                 fprintf(stderr,
  2033.                     "group %s not found in active file\n",
  2034.                                 argv[optind]);
  2035.             optind++;
  2036.         }
  2037.     } else
  2038.         read_newsrc(TRUE);
  2039.  
  2040.     if (update) {            /* index file updater only */
  2041.         do_update();
  2042.         exit(0);
  2043.     }
  2044.  
  2045.     if (InitScreen() == FALSE) {
  2046.         fprintf(stderr,"Screen initialization failed\n");
  2047.         exit(1);
  2048.     }
  2049.  
  2050.     ScreenSize(&LINES, &COLS);
  2051.     Raw(TRUE);
  2052.  
  2053.     mail_setup();        /* record mailbox size for "you have mail" */
  2054.     selection_index();
  2055.  
  2056.     tass_done(0);
  2057. }
  2058.  
  2059.  
  2060.  
  2061. tass_done(ret)
  2062. int ret;
  2063. {
  2064.  
  2065.     MoveCursor(LINES, 0);
  2066.     printf("\r\n");
  2067.     Raw(FALSE);
  2068.     exit(ret);
  2069. }
  2070.  
  2071.  
  2072. /*
  2073.  *  Dynamic table management
  2074.  *  These settings are memory conservative:  small initial allocations
  2075.  *  and a 50% expansion on table overflow.  A fast vm system with
  2076.  *  much memory might want to start with higher initial allocations
  2077.  *  and a 100% expansion on overflow, especially for the arts[] array.
  2078.  */
  2079.  
  2080. init_alloc() {
  2081.  
  2082.     max_active = 100;    /* initial alloc */
  2083.  
  2084.     active = (struct group_ent *) my_malloc(sizeof(*active) * max_active);
  2085.     my_group = (int *) my_malloc(sizeof(int) * max_active);
  2086.     unread = (int *) my_malloc(sizeof(int) * max_active);
  2087.  
  2088.     max_art = 300;        /* initial alloc */
  2089.  
  2090.     arts = (struct header *) my_malloc(sizeof(*arts) * max_art);
  2091.     base = (long *) my_malloc(sizeof(long) * max_art);
  2092. }
  2093.  
  2094.  
  2095. expand_art() {
  2096.  
  2097.     max_art += max_art / 2;        /* increase by 50% */
  2098.  
  2099.     arts = (struct header *) my_realloc(arts, sizeof(*arts) * max_art);
  2100.     base = (long *) my_realloc(base, sizeof(long) * max_art);
  2101. }
  2102.  
  2103.  
  2104. expand_active() {
  2105.  
  2106.     max_active += max_active / 2;        /* increase by 50% */
  2107.  
  2108.     active = (struct group_ent *) my_realloc(active,
  2109.                         sizeof(*active) * max_active);
  2110.     my_group = (int *) my_realloc(my_group, sizeof(int) * max_active);
  2111.     unread = (int *) my_realloc(unread, sizeof(int) * max_active);
  2112. }
  2113.  
  2114.  
  2115. /*
  2116.  *  Load the active file into active[]
  2117.  */
  2118.  
  2119. read_active()
  2120. {
  2121.     FILE *fp;
  2122.     char *p, *q;
  2123.     char buf[200];
  2124.     long h;
  2125.     int i;
  2126.  
  2127.     num_active = 0;
  2128.  
  2129.     if ((fp = fopen(active_file, "r")) == NULL) {
  2130.         fprintf(stderr, "can't open %s: ", active_file);
  2131.         perror("");
  2132.         exit(1);
  2133.     }
  2134.  
  2135.     while (fgets(buf, 200, fp) != NULL) {
  2136.         for (p = buf; *p && *p != ' '; p++) ;
  2137.         if (*p != ' ') {
  2138.             fprintf(stderr, "active file corrupt\n");
  2139.             continue;
  2140.         }
  2141.         *p++ = '\0';
  2142.  
  2143.         if (num_active >= max_active)
  2144.             expand_active();
  2145.  
  2146.         {        /* hash group name for fast lookup later */
  2147.             char *t = buf;
  2148.  
  2149.             h = *t++;
  2150.             while (*t)
  2151.                 h = (h * 64 + *t++) % TABLE_SIZE;
  2152.         }
  2153.  
  2154.         if (group_hash[h] == -1)
  2155.             group_hash[h] = num_active;
  2156.         else {                /* hash linked list chaining */
  2157.             for (i = group_hash[h]; active[i].next >= 0;
  2158.                         i = active[i].next) {
  2159.                 if (strcmp(active[i].name, buf) == 0)
  2160.                     goto read_active_continue;
  2161.                             /* kill dups */
  2162.             }
  2163.             if (strcmp(active[i].name, buf) == 0)
  2164.                 goto read_active_continue;
  2165.             active[i].next = num_active;
  2166.         }
  2167.  
  2168.         for (q = p; *q && *q != ' '; q++) ;
  2169.         if (*q != ' ') {
  2170.             fprintf(stderr, "active file corrupt\n");
  2171.             continue;
  2172.         }
  2173.  
  2174.         active[num_active].name = str_save(buf);
  2175.         active[num_active].max = atol(p);
  2176.         active[num_active].min = atol(q);
  2177.         active[num_active].next = -1;        /* hash chaining */
  2178.         active[num_active].flag = NOTGOT;   /* not in my_group[] yet */
  2179.  
  2180.         num_active++;
  2181.  
  2182. read_active_continue:;
  2183.  
  2184.     }
  2185.  
  2186.     fclose(fp);
  2187. }
  2188.  
  2189.  
  2190.  
  2191. /*
  2192.  *  Read $HOME/.newsrc into my_group[].  my_group[] ints point to
  2193.  *  active[] entries.  Sub_only determines whether we just read
  2194.  *  subscribed groups or all of them.
  2195.  */
  2196.  
  2197. read_newsrc(sub_only)
  2198. int sub_only;        /* TRUE=subscribed groups only, FALSE=all groups */
  2199. {
  2200.     FILE *fp;
  2201.     char *p;
  2202.     char buf[8192];
  2203.     char c;
  2204.     int i;
  2205.  
  2206.     local_top = 0;
  2207.  
  2208.     fp = fopen(newsrc, "r");
  2209.     if (fp == NULL) {        /* attempt to make a .newsrc */
  2210.         for (i = 0; i < num_active; i++) {
  2211.             if (local_top >= max_active)
  2212.                 expand_active();
  2213.             my_group[local_top] = i;
  2214.             active[i].flag = 0;
  2215. #if 0
  2216.             unread[local_top] = active[i].max - active[i].min;
  2217. #else
  2218.             unread[local_top] = -1;
  2219. #endif
  2220.             local_top++;
  2221.         }
  2222.         write_newsrc();
  2223.         return;
  2224.     }
  2225.  
  2226.     while (fgets(buf, 8192, fp) != NULL) {
  2227.         p = buf;
  2228.         while (*p && *p != '\n' && *p != ' ' && *p != ':' && *p != '!')
  2229.             p++;
  2230.         c = *p;
  2231.         *p++ = '\0';
  2232.         if (c == '!' && sub_only)
  2233.             continue;        /* unsubscribed */
  2234.  
  2235.         if ((i = add_group(buf, FALSE)) < 0) {
  2236.             fprintf(stderr, "group %s not found in active file\n", buf);
  2237.             continue;
  2238.         }
  2239.  
  2240.         if (c != '!')        /* if we're subscribed to it */
  2241.             active[my_group[i]].flag |= SUBS;
  2242.  
  2243.         unread[i] = parse_unread(p, my_group[i]);
  2244.     }
  2245.     fclose(fp);
  2246. }
  2247.  
  2248.  
  2249. /*
  2250.  *  Write a new newsrc from my_group[] and active[]
  2251.  *  Used to a create a new .newsrc if there isn't one already, or when
  2252.  *  the newsrc is reset.
  2253.  */
  2254.  
  2255. write_newsrc() {
  2256.     FILE *fp;
  2257.     int i;
  2258.  
  2259.     setuid(real_uid);    /* become the user to write in his */
  2260.     setgid(real_gid);    /* home directory */
  2261.  
  2262.     fp = fopen(newsrc, "w");
  2263.     if (fp == NULL)
  2264.         goto write_newsrc_done;
  2265.  
  2266.     for (i = 0; i < num_active; i++)
  2267.         fprintf(fp, "%s: \n", active[i].name);
  2268.  
  2269.     fclose(fp);
  2270.  
  2271. write_newsrc_done:
  2272.     setuid(tass_uid);
  2273.     setgid(tass_gid);
  2274. }
  2275.  
  2276.  
  2277. /*
  2278.  *  Load the sequencer rang lists and mark arts[] according to the
  2279.  *  .newsrc info for a particular group.  i.e.  rec.arts.comics: 1-94,97
  2280.  */
  2281.  
  2282. read_newsrc_line(group)
  2283. char *group;
  2284. {
  2285.     FILE *fp;
  2286.     char buf[8192];
  2287.     char *p;
  2288.  
  2289.     fp = fopen(newsrc, "r");
  2290.     if (fp == NULL)
  2291.         return;
  2292.  
  2293.     while (fgets(buf, 8192, fp) != NULL) {
  2294.         p = buf;
  2295.         while (*p && *p != '\n' && *p != ' ' && *p != ':' && *p != '!')
  2296.             p++;
  2297.         *p++ = '\0';
  2298.         if (strcmp(buf, group) != 0)
  2299.             continue;
  2300.         parse_seq(p);
  2301.         break;
  2302.     }
  2303.  
  2304.     fclose(fp);
  2305. }
  2306.  
  2307.  
  2308. /*
  2309.  *  For our current group, update the sequencer information in .newsrc
  2310.  */
  2311.  
  2312. update_newsrc(group, groupnum)
  2313. char *group;
  2314. int groupnum;            /* index into active[] for this group */
  2315. {
  2316.     FILE *fp;
  2317.     FILE *newfp;
  2318.     char buf[8192];
  2319.     char *p;
  2320.     char c;
  2321.     int gotit = FALSE;
  2322.  
  2323.     setuid(real_uid);
  2324.     setgid(real_gid);
  2325.  
  2326.     fp = fopen(newsrc, "r");
  2327.     newfp = fopen(newnewsrc, "w");
  2328.     if (newfp == NULL)
  2329.         goto update_done;
  2330.  
  2331.     if (fp != NULL) {
  2332.         while (fgets(buf, 8192, fp) != NULL) {
  2333.             for (p = buf; *p; p++)
  2334.                 if (*p == '\n') {
  2335.                     *p = '\0';
  2336.                     break;
  2337.                 }
  2338.  
  2339.             p = buf;
  2340.             while (*p && *p != ' ' && *p != ':' && *p != '!')
  2341.                     p++;
  2342.             c = *p;
  2343.             if (c != '\0')
  2344.                 *p++ = '\0';
  2345.  
  2346.             if (c != '!')
  2347.                 c = ':';
  2348.  
  2349.             if (strcmp(buf, group) == 0) {
  2350.                 fprintf(newfp, "%s%c ", buf, c);
  2351.                 gotit = TRUE;
  2352.                 print_seq(newfp, groupnum);
  2353.                 fprintf(newfp, "\n");
  2354.             } else
  2355.                 fprintf(newfp, "%s%c%s\n", buf, c, p);
  2356.         }
  2357.         fclose(fp);
  2358.     }
  2359.  
  2360.     fclose(newfp);
  2361.     unlink(newsrc);
  2362.     link(newnewsrc, newsrc);
  2363.     unlink(newnewsrc);
  2364.  
  2365. update_done:
  2366.     setuid(tass_uid);
  2367.     setgid(tass_gid);
  2368. }
  2369.  
  2370.  
  2371. /*
  2372.  *  Subscribe/unsubscribe to a group in .newsrc.  ch should either be
  2373.  *  '!' to unsubscribe or ':' to subscribe.  num is the group's index
  2374.  *  in active[].
  2375.  */
  2376.  
  2377. subscribe(group, ch, num, out_seq)
  2378. char *group;
  2379. char ch;
  2380. int num;
  2381. int out_seq;                /* output sequencer info? */
  2382. {
  2383.     FILE *fp;
  2384.     FILE *newfp;
  2385.     char buf[8192];
  2386.     char *p;
  2387.     char c;
  2388.     int gotit = FALSE;
  2389.  
  2390.     if (ch == '!')
  2391.         active[num].flag &= ~SUBS;
  2392.     else
  2393.         active[num].flag |= SUBS;
  2394.  
  2395.     setuid(real_uid);
  2396.     setgid(real_gid);
  2397.  
  2398.     fp = fopen(newsrc, "r");
  2399.     newfp = fopen(newnewsrc, "w");
  2400.     if (newfp == NULL)
  2401.         goto update_done;
  2402.  
  2403.     if (fp != NULL) {
  2404.         while (fgets(buf, 8192, fp) != NULL) {
  2405.             for (p = buf; *p; p++)
  2406.                 if (*p == '\n') {
  2407.                     *p = '\0';
  2408.                     break;
  2409.                 }
  2410.  
  2411.             p = buf;
  2412.             while (*p && *p != ' ' && *p != ':' && *p != '!')
  2413.                     p++;
  2414.             c = *p;
  2415.             if (c != '\0')
  2416.                 *p++ = '\0';
  2417.  
  2418.             if (c != '!')
  2419.                 c = ':';
  2420.  
  2421.             if (strcmp(buf, group) == 0) {
  2422.                 fprintf(newfp, "%s%c%s\n", buf, ch, p);
  2423.                 gotit = TRUE;
  2424.             } else
  2425.                 fprintf(newfp, "%s%c%s\n", buf, c, p);
  2426.         }
  2427.         fclose(fp);
  2428.     }
  2429.  
  2430.     if (!gotit) {
  2431.         if (out_seq) {
  2432.             fprintf(newfp, "%s%c ", group, ch);
  2433.             print_seq(newfp, num);
  2434.             fprintf(newfp, "\n");
  2435.         } else
  2436.             fprintf(newfp, "%s%c\n", group, ch);
  2437.     }
  2438.  
  2439.     fclose(newfp);
  2440.     unlink(newsrc);
  2441.     link(newnewsrc, newsrc);
  2442.     unlink(newnewsrc);
  2443.  
  2444. update_done:
  2445.     setuid(tass_uid);
  2446.     setgid(tass_gid);
  2447. }
  2448.  
  2449.  
  2450. print_seq(fp, groupnum)
  2451. FILE *fp;
  2452. int groupnum;            /* index into active[] for this group */
  2453. {
  2454.     int i;
  2455.     int flag = FALSE;
  2456.  
  2457.     if (top <= 0) {
  2458.         if (active[groupnum].min > 1) {
  2459.             fprintf(fp, "1-%ld", active[groupnum].min);
  2460.             fflush(fp);
  2461.         }
  2462.         return;
  2463.     }
  2464.  
  2465.     i = 0;
  2466.     if (arts[0].artnum > 1) {
  2467.         for (; i < top && !arts[i].unread; i++) ;
  2468.         if (i > 0)
  2469.             fprintf(fp, "1-%ld", arts[i-1].artnum);
  2470.         else
  2471.             fprintf(fp, "1-%ld", arts[0].artnum - 1);
  2472.         flag = TRUE;
  2473.     }
  2474.  
  2475.     for (; i < top; i++) {
  2476.         if (!arts[i].unread) {
  2477.             if (flag)
  2478.                 fprintf(fp, ",");
  2479.             else
  2480.                 flag = TRUE;
  2481.             fprintf(fp, "%ld", arts[i].artnum);
  2482.             if (i+1 < top && !arts[i+1].unread) {
  2483.                 while (i+1 < top && !arts[i+1].unread)
  2484.                     i++;
  2485.                 fprintf(fp, "-%ld", arts[i].artnum);
  2486.             }
  2487.         }
  2488.     }
  2489.  
  2490.     if (!flag && active[groupnum].min > 1)
  2491.         fprintf(fp, "1-%ld", active[groupnum].min);
  2492.     fflush(fp);
  2493. }
  2494.  
  2495.  
  2496. parse_seq(s)
  2497. char *s;
  2498. {
  2499.     long low, high;
  2500.     int i;
  2501.  
  2502.     while (*s) {
  2503.         while (*s && (*s < '0' || *s > '9'))
  2504.             s++;
  2505.  
  2506.         if (*s && *s >= '0' && *s <= '9') {
  2507.             low = atol(s);
  2508.             while (*s && *s >= '0' && *s <= '9')
  2509.                 s++;
  2510.             if (*s == '-') {
  2511.                 s++;
  2512.                 high = atol(s);
  2513.                 while (*s && *s >= '0' && *s <= '9')
  2514.                     s++;
  2515.             }  else
  2516.                 high = low;
  2517.  
  2518.             for (i = 0; i < top; i++)
  2519.                 if (arts[i].artnum >= low &&
  2520.                     arts[i].artnum <= high)
  2521.                     arts[i].unread = 0;
  2522.         }
  2523.     }
  2524. }
  2525.  
  2526.  
  2527. parse_unread(s, groupnum)
  2528. char *s;
  2529. int groupnum;            /* index for group in active[] */
  2530. {
  2531.     long low, high;
  2532.     long last_high;
  2533.     int i;
  2534.     int sum = 0;
  2535.     int gotone = FALSE;
  2536.     int n;
  2537.  
  2538. /*
  2539.  *  Read the first range from the .newsrc sequencer information.  If the
  2540.  *  top of the first range is higher than what the active file claims is
  2541.  *  the bottom, use it as the new bottom instead
  2542.  */
  2543.  
  2544.     high = 0;
  2545.     if (*s) {
  2546.         while (*s && (*s < '0' || *s > '9'))
  2547.             s++;
  2548.  
  2549.         if (*s && *s >= '0' && *s <= '9') {
  2550.             low = atol(s);
  2551.             while (*s && *s >= '0' && *s <= '9')
  2552.                 s++;
  2553.             if (*s == '-') {
  2554.                 s++;
  2555.                 high = atol(s);
  2556.                 while (*s && *s >= '0' && *s <= '9')
  2557.                     s++;
  2558.             }  else
  2559.                 high = low;
  2560.             gotone = TRUE;
  2561.         }
  2562.     }
  2563.  
  2564.     if (high < active[groupnum].min)
  2565.         high = active[groupnum].min;
  2566.  
  2567.     while (*s) {
  2568.         last_high = high;
  2569.  
  2570.         while (*s && (*s < '0' || *s > '9'))
  2571.             s++;
  2572.  
  2573.         if (*s && *s >= '0' && *s <= '9') {
  2574.             low = atol(s);
  2575.             while (*s && *s >= '0' && *s <= '9')
  2576.                 s++;
  2577.             if (*s == '-') {
  2578.                 s++;
  2579.                 high = atol(s);
  2580.                 while (*s && *s >= '0' && *s <= '9')
  2581.                     s++;
  2582.             }  else
  2583.                 high = low;
  2584.  
  2585.             if (low > last_high)    /* otherwise seq out of order */
  2586.                 sum += (low - last_high) - 1;
  2587.         }
  2588.     }
  2589.  
  2590.     if (gotone) {
  2591.         if (active[groupnum].max > high)
  2592.             sum += active[groupnum].max - high;
  2593.         return sum;
  2594.     }
  2595.  
  2596.     n = (int) (active[groupnum].max - active[groupnum].min);
  2597.     if (n < 2)
  2598.         return 0;
  2599.  
  2600.     return -1;
  2601. }
  2602.  
  2603.  
  2604. get_line_unread(group, groupnum)
  2605. char *group;
  2606. int groupnum;                /* index for group in active[] */
  2607. {
  2608.     FILE *fp;
  2609.     char buf[8192];
  2610.     char *p;
  2611.     int ret = -1;
  2612.  
  2613.     fp = fopen(newsrc, "r");
  2614.     if (fp == NULL)
  2615.         return -1;
  2616.  
  2617.     while (fgets(buf, 8192, fp) != NULL) {
  2618.         p = buf;
  2619.         while (*p && *p != '\n' && *p != ' ' && *p != ':' && *p != '!')
  2620.             p++;
  2621.         *p++ = '\0';
  2622.         if (strcmp(buf, group) != 0)
  2623.             continue;
  2624.         ret = parse_unread(p, groupnum);
  2625.         break;
  2626.     }
  2627.  
  2628.     fclose(fp);
  2629.     return ret;
  2630. }
  2631.  
  2632.  
  2633. reset_newsrc()
  2634. {
  2635.     FILE *fp;
  2636.     FILE *newfp;
  2637.     char buf[8192];
  2638.     char *p;
  2639.     char c;
  2640.     int gotit = FALSE;
  2641.     int i;
  2642.  
  2643.     setuid(real_uid);
  2644.     setgid(real_gid);
  2645.  
  2646.     fp = fopen(newsrc, "r");
  2647.     newfp = fopen(newnewsrc, "w");
  2648.     if (newfp == NULL)
  2649.         goto update_done;
  2650.  
  2651.     if (fp != NULL) {
  2652.         while (fgets(buf, 8192, fp) != NULL) {
  2653.             for (p = buf; *p && *p != '\n'; p++) ;
  2654.             *p = '\0';
  2655.  
  2656.             p = buf;
  2657.             while (*p && *p != ' ' && *p != ':' && *p != '!')
  2658.                     p++;
  2659.             c = *p;
  2660.             if (c != '\0')
  2661.                 *p++ = '\0';
  2662.  
  2663.             if (c != '!')
  2664.                 c = ':';
  2665.  
  2666.             fprintf(newfp, "%s%c\n", buf, c);
  2667.         }
  2668.         fclose(fp);
  2669.     }
  2670.  
  2671.     fclose(newfp);
  2672.     unlink(newsrc);
  2673.     link(newnewsrc, newsrc);
  2674.     unlink(newnewsrc);
  2675.  
  2676. update_done:
  2677.     setuid(tass_uid);
  2678.     setgid(tass_gid);
  2679.  
  2680.     for (i = 0; i < local_top; i++)
  2681.         unread[i] = -1;
  2682. }
  2683.  
  2684.  
  2685. delete_group(group)
  2686. char *group;
  2687. {
  2688.     FILE *fp;
  2689.     FILE *newfp;
  2690.     char buf[8192];
  2691.     char *p;
  2692.     char c;
  2693.     int gotit = FALSE;
  2694.     FILE *del;
  2695.  
  2696.     setuid(real_uid);
  2697.     setgid(real_gid);
  2698.  
  2699.     fp = fopen(newsrc, "r");
  2700.     newfp = fopen(newnewsrc, "w");
  2701.     if (newfp == NULL)
  2702.         goto del_done;
  2703.     del = fopen(delgroups, "a+");
  2704.     if (del == NULL)
  2705.         goto del_done;
  2706.  
  2707.     if (fp != NULL) {
  2708.         while (fgets(buf, 8192, fp) != NULL) {
  2709.             for (p = buf; *p && *p != '\n'; p++) ;
  2710.             *p = '\0';
  2711.  
  2712.             p = buf;
  2713.             while (*p && *p != ' ' && *p != ':' && *p != '!')
  2714.                     p++;
  2715.             c = *p;
  2716.             if (c != '\0')
  2717.                 *p++ = '\0';
  2718.  
  2719.             if (c != '!')
  2720.                 c = ':';
  2721.  
  2722.             if (strcmp(buf, group) == 0) {
  2723.                 fprintf(del, "%s%c%s\n", buf, c, p);
  2724.                 gotit = TRUE;
  2725.             } else
  2726.                 fprintf(newfp, "%s%c%s\n", buf, c, p);
  2727.         }
  2728.         fclose(fp);
  2729.     }
  2730.  
  2731.     fclose(newfp);
  2732.  
  2733.     if (!gotit)
  2734.         fprintf(del, "%s! \n", group);
  2735.  
  2736.     fclose(del);
  2737.     unlink(newsrc);
  2738.     link(newnewsrc, newsrc);
  2739.     unlink(newnewsrc);
  2740.  
  2741. del_done:
  2742.     setuid(tass_uid);
  2743.     setgid(tass_gid);
  2744. }
  2745.  
  2746.  
  2747. undel_group() {
  2748.     FILE *del;
  2749.     FILE *newfp;
  2750.     FILE *fp;
  2751.     char buf[2][8192];
  2752.     char *p;
  2753.     int which = 0;
  2754.     long h;
  2755.     extern int cur_groupnum;
  2756.     int i, j;
  2757.     char c;
  2758.  
  2759.     setuid(real_uid);
  2760.     setgid(real_gid);
  2761.  
  2762.     del = fopen(delgroups, "r");
  2763.     if (del == NULL) {
  2764.         setuid(tass_uid);
  2765.         setgid(tass_gid);
  2766.         return FALSE;
  2767.     }
  2768.     unlink(delgroups);
  2769.     newfp = fopen(delgroups, "w");
  2770.     if (newfp == NULL) {
  2771.         setuid(tass_uid);
  2772.         setgid(tass_gid);
  2773.         return FALSE;
  2774.     }
  2775.  
  2776.     buf[0][0] = '\0';
  2777.     buf[1][0] = '\0';
  2778.  
  2779.     while (fgets(buf[which], 8192, del) != NULL) {
  2780.         which = !which;
  2781.         if (*buf[which])
  2782.             fputs(buf[which], newfp);
  2783.     }
  2784.  
  2785.     fclose(del);
  2786.     fclose(newfp);
  2787.     which = !which;
  2788.  
  2789.     if (!*buf[which]) {
  2790.         setuid(tass_uid);
  2791.         setgid(tass_gid);
  2792.         return FALSE;
  2793.     }
  2794.  
  2795.     for (p = buf[which]; *p && *p != '\n'; p++) ;
  2796.     *p = '\0';
  2797.  
  2798.     p = buf[which];
  2799.     while (*p && *p != ' ' && *p != ':' && *p != '!')
  2800.         p++;
  2801.     c = *p;
  2802.     if (c != '\0')
  2803.         *p++ = '\0';
  2804.  
  2805.     if (c != '!')
  2806.         c = ':';
  2807.  
  2808.     {            /* find the hash of the group name */
  2809.         char *t = buf[which];
  2810.  
  2811.         h = *t++;
  2812.         while (*t)
  2813.             h = (h * 64 + *t++) % TABLE_SIZE;
  2814.     }
  2815.  
  2816.     for (i = group_hash[h]; i >= 0; i = active[i].next) {
  2817.         if (strcmp(buf[which], active[i].name) == 0) {
  2818.             for (j = 0; j < local_top; j++)
  2819.                 if (my_group[j] == i) {
  2820.                     setuid(tass_uid);
  2821.                     setgid(tass_gid);
  2822.                     return j;
  2823.                 }
  2824.  
  2825.             active[i].flag &= ~NOTGOT;   /* mark that we got it */
  2826.             if (c != '!')
  2827.                 active[i].flag |= SUBS;
  2828.  
  2829.             if (local_top >= max_active)
  2830.                 expand_active();
  2831.             local_top++;
  2832.             for (j = local_top; j > cur_groupnum; j--) {
  2833.                 my_group[j] = my_group[j-1];
  2834.                 unread[j] = unread[j-1];
  2835.             }
  2836.             my_group[cur_groupnum] = i;
  2837.             unread[cur_groupnum] = parse_unread(p, i);
  2838.  
  2839.             fp = fopen(newsrc, "r");
  2840.             if (fp == NULL) {
  2841.                 setuid(tass_uid);
  2842.                 setgid(tass_gid);
  2843.                 return FALSE;
  2844.             }
  2845.             newfp = fopen(newnewsrc, "w");
  2846.             if (newfp == NULL) {
  2847.                 fclose(fp);
  2848.                 setuid(tass_uid);
  2849.                 setgid(tass_gid);
  2850.                 return FALSE;
  2851.             }
  2852.             i = 0;
  2853.             while (fgets(buf[!which], 8192, fp) != NULL) {
  2854.                 for (p = buf[!which]; *p && *p != '\n'; p++) ;
  2855.                 *p = '\0';
  2856.  
  2857.                 p = buf[!which];
  2858.                 while (*p && *p!=' ' && *p != ':' && *p != '!')
  2859.                     p++;
  2860.                 c = *p;
  2861.                 if (c != '\0')
  2862.                     *p++ = '\0';
  2863.  
  2864.                 if (c != '!')
  2865.                     c = ':';
  2866.  
  2867.                 while (i < cur_groupnum) {
  2868.                     if (strcmp(buf[!which],
  2869.                       active[my_group[i]].name) == 0) {
  2870.                         fprintf(newfp, "%s%c%s\n",
  2871.                             buf[!which], c, p);
  2872.                         goto foo_cont;
  2873.                     }
  2874.                     i++;
  2875.                 }
  2876.                 fprintf(newfp, "%s%c%s\n", buf[which], c, p);
  2877.                 fprintf(newfp, "%s%c%s\n", buf[!which], c, p);
  2878.                 break;
  2879. foo_cont:;
  2880.             }
  2881.  
  2882.             while (fgets(buf[!which], 8192, fp) != NULL)
  2883.                 fputs(buf[!which], newfp);
  2884.  
  2885.             fclose(newfp);
  2886.             fclose(fp);
  2887.             unlink(newsrc);
  2888.             link(newnewsrc, newsrc);
  2889.             unlink(newnewsrc);
  2890.             setuid(tass_uid);
  2891.             setgid(tass_gid);
  2892.             return TRUE;
  2893.         }
  2894.     }
  2895.  
  2896.     setuid(tass_uid);
  2897.     setgid(tass_gid);
  2898.  
  2899.     return FALSE;
  2900. }
  2901.  
  2902.  
  2903. mark_group_read(group, groupnum)
  2904. char *group;
  2905. int groupnum;            /* index into active[] for this group */
  2906. {
  2907.     FILE *fp;
  2908.     FILE *newfp;
  2909.     char buf[8192];
  2910.     char *p;
  2911.     char c;
  2912.     int gotit = FALSE;
  2913.  
  2914.     if (active[groupnum].max < 2)
  2915.         return;
  2916.  
  2917.     setuid(real_uid);
  2918.     setgid(real_gid);
  2919.  
  2920.     fp = fopen(newsrc, "r");
  2921.     newfp = fopen(newnewsrc, "w");
  2922.     if (newfp == NULL)
  2923.         goto mark_group_read_done;
  2924.  
  2925.     if (fp != NULL) {
  2926.         while (fgets(buf, 8192, fp) != NULL) {
  2927.             for (p = buf; *p; p++)
  2928.                 if (*p == '\n') {
  2929.                     *p = '\0';
  2930.                     break;
  2931.                 }
  2932.  
  2933.             p = buf;
  2934.             while (*p && *p != ' ' && *p != ':' && *p != '!')
  2935.                     p++;
  2936.             c = *p;
  2937.             if (c != '\0')
  2938.                 *p++ = '\0';
  2939.  
  2940.             if (c != '!')
  2941.                 c = ':';
  2942.  
  2943.             if (strcmp(buf, group) == 0) {
  2944.                 fprintf(newfp, "%s%c 1-%ld\n", buf, c,
  2945.                         active[groupnum].max);
  2946.                 gotit = TRUE;
  2947.             } else
  2948.                 fprintf(newfp, "%s%c%s\n", buf, c, p);
  2949.         }
  2950.         fclose(fp);
  2951.     }
  2952.  
  2953.     fclose(newfp);
  2954.     unlink(newsrc);
  2955.     link(newnewsrc, newsrc);
  2956.     unlink(newnewsrc);
  2957.  
  2958. mark_group_read_done:
  2959.     setuid(tass_uid);
  2960.     setgid(tass_gid);
  2961. }
  2962.  
  2963. @EOF
  2964.  
  2965. chmod 644 main.c
  2966.  
  2967. echo x - misc.c
  2968. cat >misc.c <<'@EOF'
  2969.  
  2970. #include    <stdio.h>
  2971. #include    <signal.h>
  2972. #include    <pwd.h>
  2973. #include    <sys/types.h>
  2974. #include    <sys/stat.h>
  2975. #include    "tass.h"
  2976.  
  2977.  
  2978. char active_file[LEN];
  2979. char homedir[LEN];
  2980. char userid[LEN];
  2981. char delgroups[LEN];
  2982. char newsrc[LEN];
  2983. char newnewsrc[LEN];
  2984. char indexdir[LEN];
  2985. char my_org[LEN];        /* organization */
  2986.  
  2987.  
  2988. /*
  2989.  *  Which base note (an index into base[]) does a respnum
  2990.  *  (an index into arts[]) corresponsd to?
  2991.  *
  2992.  *  In other words, base[] points to an entry in arts[] which is
  2993.  *  the head of a thread, linked with arts[].thread.  For any q: arts[q],
  2994.  *  find i such that base[i]->arts[n]->arts[o]->...->arts[q]
  2995.  */
  2996.  
  2997. which_base(n)
  2998. int n;
  2999. {
  3000.     int i, j;
  3001.  
  3002.     for (i = 0; i < top_base; i++)
  3003.         for (j = base[i]; j >= 0; j = arts[j].thread)
  3004.             if (j == n)
  3005.                 return i;
  3006.  
  3007.     fprintf(stderr, "can't find base article\n");
  3008.     return 0;
  3009. }
  3010.  
  3011.  
  3012. /*
  3013.  *  Find how deep in a thread a response is.  Start counting at zero
  3014.  */
  3015.  
  3016. which_resp(n)
  3017. int n;
  3018. {
  3019.     int i, j;
  3020.     int num = 0;
  3021.  
  3022.     i = which_base(n);
  3023.  
  3024.     for (j = base[i]; j != -1; j = arts[j].thread)
  3025.         if (j == n)
  3026.             break;
  3027.         else
  3028.             num++;
  3029.  
  3030.     return num;
  3031. }
  3032.  
  3033.  
  3034. /*
  3035.  *  Given an index into base[], find the number of responses for
  3036.  *  that basenote
  3037.  */
  3038.  
  3039. nresp(n)
  3040. int n;
  3041. {
  3042.     int i;
  3043.     int oldi = -3;
  3044.     int sum = 0;
  3045.  
  3046.     assert(n < top_base);
  3047.  
  3048.     for (i = base[n]; i != -1; i = arts[i].thread) {
  3049.         assert(i != -2);
  3050.         assert(i != oldi);
  3051.         oldi = i;
  3052.         sum++;
  3053.     }
  3054.  
  3055.     return sum - 1;
  3056. }
  3057.  
  3058.  
  3059. asfail(file, line, cond)
  3060. char    *file;
  3061. int    line;
  3062. char    *cond;
  3063. {
  3064.     fprintf(stderr, "tass: assertion failure: %s (%d): %s\n",
  3065.                             file, line, cond);
  3066.     exit(1);
  3067. }
  3068.  
  3069.  
  3070. /*
  3071.  *  Make regular expressions pleasant for the masses:  glob them
  3072.  */
  3073.  
  3074. glob_name(group, grp)
  3075. char *group;
  3076. char *grp;
  3077. {
  3078. char *p, *q;
  3079.  
  3080. /*
  3081.  *  Prefix the .'s in the group name so they won't be interpreted
  3082.  *  as regular expression commands.  Change 'all' into '*'
  3083.  */
  3084.  
  3085.     p = group;
  3086.     q = grp;
  3087.  
  3088.     if (strncmp(p, "all", 3) == 0 && (p[3] == '.' || p[3] == '\0')) {
  3089.         *q++ = '.';
  3090.         *q++ = '*';
  3091.         p = &p[3];
  3092.     }
  3093.     while (*p != '\0') {
  3094.         if (*p == '.') {
  3095.             *q++ = '\\';
  3096.             *q++ = '.';
  3097.             p++;
  3098.  
  3099.             if (strncmp(p, "all", 3) == 0 &&
  3100.                 (p[3] == '.' || p[3] == '\0')) {
  3101.                     *q++ = '.';
  3102.                     *q++ = '*';
  3103.                     p = &p[3];
  3104.                 }
  3105.         } else if (*p == '*') {
  3106.             *q++ = '.';
  3107.             *q++ = '*';
  3108.             p++;
  3109.         } else
  3110.             *q++ = *p++;
  3111.     }
  3112.     *q = '\0';
  3113. }
  3114.  
  3115.  
  3116. /*
  3117.  * init_selfinfo
  3118.  *   Deterimines users home directory, userid, and a path
  3119.  *   for an rc file in the home directory
  3120.  */
  3121.  
  3122. init_selfinfo()
  3123. {
  3124.     struct passwd *myentry;
  3125.     extern struct passwd *getpwuid();
  3126.     struct stat sb;
  3127.     char nam[LEN];
  3128.     char *p;
  3129.     extern char *getenv();
  3130.     FILE *fp;
  3131.  
  3132.     myentry = getpwuid(getuid());
  3133.     strcpy(userid, myentry->pw_name);
  3134.     strcpy(homedir, myentry->pw_dir);
  3135.  
  3136.     sprintf(newsrc, "%s/.newsrc", homedir);
  3137.     sprintf(newnewsrc, "%s/.newnewsrc", homedir);
  3138.     sprintf(delgroups, "%s/.delgroups", homedir);
  3139.     sprintf(indexdir, "%s/.tindex", homedir);
  3140.     sprintf(active_file, "%s/active", LIBDIR);
  3141.     if (stat(active_file, &sb) >= 0)
  3142.         goto got_active;
  3143.  
  3144. /*
  3145.  *  I hate forgetting to define LIBDIR correctly.  Guess a
  3146.  *  couple of likely places if it's not where LIBDIR says it is.
  3147.  */
  3148.  
  3149.     strcpy(active_file, "/usr/lib/news/active");
  3150.     if (stat(active_file, &sb) >= 0)
  3151.         goto got_active;
  3152.  
  3153.     strcpy(active_file, "/usr/local/lib/news/active");
  3154.     if (stat(active_file, &sb) >= 0)
  3155.         goto got_active;
  3156.  
  3157.     strcpy(active_file, "/usr/public/lib/news/active");
  3158.     if (stat(active_file, &sb) >= 0)
  3159.         goto got_active;
  3160.  
  3161. /*
  3162.  *  Oh well.  Revert to what LIBDIR says it is to produce a
  3163.  *  useful error message when read_active() fails later.
  3164.  */
  3165.  
  3166.     sprintf(active_file, "%s/active", LIBDIR);
  3167.  
  3168. got_active:
  3169.  
  3170.     *my_org = '\0';
  3171.     p = getenv("ORGANIZATION");
  3172.     if (p != NULL) {
  3173.         strcpy(my_org, p);
  3174.         goto got_org;
  3175.     }
  3176.  
  3177.     sprintf(nam, "%s/organization", LIBDIR);
  3178.     fp = fopen(nam, "r");
  3179.  
  3180.     if (fp == NULL) {
  3181.         sprintf(nam, "/usr/lib/news/organization");
  3182.         fp = fopen(nam, "r");
  3183.     }
  3184.  
  3185.     if (fp == NULL) {
  3186.         sprintf(nam, "/usr/local/lib/news/organization");
  3187.         fp = fopen(nam, "r");
  3188.     }
  3189.  
  3190.     if (fp == NULL) {
  3191.         sprintf(nam, "/usr/public/lib/news/organization");
  3192.         fp = fopen(nam, "r");
  3193.     }
  3194.  
  3195.     if (fp == NULL) {
  3196.         sprintf(nam, "/etc/organization");
  3197.         fp = fopen(nam, "r");
  3198.     }
  3199.  
  3200.     if (fp != NULL) {
  3201.         if (fgets(my_org, LEN, fp) != NULL) {
  3202.             for (p = my_org; *p && *p != '\n'; p++) ;
  3203.             *p = '\0';
  3204.         }
  3205.         fclose(fp);
  3206.     }
  3207.  
  3208. got_org:;
  3209.  
  3210. }
  3211.  
  3212.  
  3213. char *
  3214. my_malloc(size)
  3215. unsigned size;
  3216. {
  3217.     char *p;
  3218.     extern char *malloc();
  3219.  
  3220.     p = malloc(size);
  3221.     if (p == NULL) {
  3222.         fprintf(stderr, "tass: out of memory\n");
  3223.         exit(1);
  3224.     }
  3225.     return p;
  3226. }
  3227.  
  3228.  
  3229. char *
  3230. my_realloc(p, size)
  3231. char *p;
  3232. unsigned size;
  3233. {
  3234.     extern char *malloc();
  3235.     extern char *realloc();
  3236.  
  3237.     if (p == NULL)
  3238.         p = malloc(size);
  3239.     else
  3240.         p = realloc(p, size);
  3241.  
  3242.     if (p == NULL) {
  3243.         fprintf(stderr, "tass: out of memory\n");
  3244.         exit(1);
  3245.     }
  3246.     return p;
  3247. }
  3248.  
  3249.  
  3250. char *
  3251. str_save(s)
  3252. char *s;
  3253. {
  3254. char *p;
  3255.  
  3256.     assert(s != NULL);
  3257.     
  3258.     p = my_malloc(strlen(s) + 1);
  3259.     strcpy(p, s);
  3260.  
  3261.     return(p);
  3262. }
  3263.  
  3264.  
  3265. copy_fp(a, b, prefix)
  3266. FILE *a;
  3267. FILE *b;
  3268. char *prefix;
  3269. {
  3270.     char buf[8192];
  3271.  
  3272.     while (fgets(buf, 8192, a) != NULL)
  3273.         fprintf(b, "%s%s", prefix, buf);
  3274. }
  3275.  
  3276.  
  3277. char *
  3278. get_val(env, def)
  3279. char *env;        /* Environment variable we're looking for    */
  3280. char *def;        /* Default value if no environ value found    */
  3281. {
  3282.     extern char *getenv();
  3283.     char *ptr;
  3284.  
  3285.     if ((ptr = getenv(env)) != NULL)
  3286.         return(ptr);
  3287.     else
  3288.         return(def);
  3289. }
  3290.  
  3291.  
  3292. invoke_editor(nam)
  3293. char *nam;
  3294. {
  3295.     char buf[200];
  3296.     static int first = TRUE;
  3297.     static char editor[200];
  3298.  
  3299.     if (first) {
  3300.         strcpy(editor, get_val("EDITOR", "/usr/bin/vi"));
  3301.         first = FALSE;
  3302.     }
  3303.  
  3304.     sprintf(buf, "%s %s", editor, nam);
  3305.     printf("%s\n", buf);
  3306.     return invoke_cmd(buf);
  3307. }
  3308.  
  3309.  
  3310. invoke_cmd(nam)
  3311. char *nam;
  3312. {
  3313.     int ret;
  3314. #ifdef SIGTSTP
  3315.     void (*susp)();
  3316. #endif
  3317.  
  3318.     Raw(FALSE);
  3319.     setuid(real_uid);
  3320.     setgid(real_gid);
  3321.  
  3322. #ifdef SIGTSTP
  3323.     susp = signal(SIGTSTP, SIG_DFL);
  3324. #endif
  3325.  
  3326.     ret = system(nam);
  3327.  
  3328. #ifdef SIGTSTP
  3329.     signal(SIGTSTP, susp);
  3330. #endif
  3331.  
  3332.     setuid(tass_uid);
  3333.     setgid(tass_gid);
  3334.     Raw(TRUE);
  3335.  
  3336.     return ret == 0;
  3337. }
  3338.  
  3339.  
  3340. shell_escape() {
  3341.     char shell[LEN];
  3342.     char *p;
  3343. #ifdef SIGTSTP
  3344.     void (*susp)();
  3345. #endif
  3346.  
  3347.     if (!parse_string("!", shell))
  3348.         strcpy(shell, get_val("SHELL", "/bin/sh"));
  3349.  
  3350.     for (p = shell; *p && (*p == ' ' || *p == '\t'); p++) ;
  3351.  
  3352.     if (!*p)
  3353.         strcpy(shell, get_val("SHELL", "/bin/sh"));
  3354.     
  3355.     Raw(FALSE);
  3356.  
  3357.     setuid(real_uid);
  3358.     setgid(real_gid);
  3359.  
  3360.     fputs("\r\n", stdout);
  3361.  
  3362. #ifdef SIGTSTP
  3363.     susp = signal(SIGTSTP, SIG_DFL);
  3364. #endif
  3365.  
  3366.     system(p);
  3367.  
  3368. #ifdef SIGTSTP
  3369.     signal(SIGTSTP, susp);
  3370. #endif
  3371.  
  3372.     setuid(tass_uid);
  3373.     setgid(tass_gid);
  3374.  
  3375.     Raw(TRUE);
  3376.  
  3377.     continue_prompt();
  3378.     mail_setup();
  3379. }
  3380.  
  3381.  
  3382. /*
  3383.  *  Find the next unread response in this group 
  3384.  */
  3385.  
  3386. next_unread(n)
  3387. int n;
  3388. {
  3389.  
  3390.     while (n >= 0) {
  3391.         if (arts[n].unread == 1)
  3392.             return n;
  3393.         n = next_response(n);
  3394.     }
  3395.  
  3396.     return -1;
  3397. }
  3398.  
  3399.  
  3400. /*
  3401.  *  Find the previous unread response in this thread
  3402.  */
  3403.  
  3404. prev_unread(n)
  3405. int n;
  3406. {
  3407.  
  3408.     while (n >= 0) {
  3409.         if (arts[n].unread == 1)
  3410.             return n;
  3411.         n = prev_response(n);
  3412.     }
  3413.  
  3414.     return -1;
  3415. }
  3416.  
  3417. @EOF
  3418.  
  3419. chmod 644 misc.c
  3420.  
  3421. exit 0
  3422. -- 
  3423. skrenta@blekko.commodore.com
  3424.