home *** CD-ROM | disk | FTP | other *** search
/ Unix System Administration Handbook 1997 October / usah_oct97.iso / news / nn.tar / nn-6.5.1 / contrib / nnsub / nnsub.c < prev    next >
C/C++ Source or Header  |  1995-04-29  |  28KB  |  935 lines

  1. /*
  2.     nnsub [-v] [newsgroups....]
  3.  
  4.     exit status ERR_EXIT indicates a failure
  5.  
  6.     option -v for verbosity
  7.     -v0    don't show output, no questions.
  8.         exit status shows number of newsgroups requested but
  9.         not subscribed (bogus).
  10.         Default if running without terminal control.
  11.     -v1    if interactive the program can ask questions and
  12.         a report of subscriptions is written.
  13.         Default if running with a terminal.
  14.  
  15.     groups to subscribe to can be supplied as program arguments
  16.     or on input. Partial groupnames can be given by starting or
  17.     ending the name with a dot, and the program will try to find
  18.     the groupname (as in the nn init file). If running without
  19.     terminal control or with verbosity 0 all the matches found
  20.     on such a partial name will be subscribed to.
  21.         With a terminal and verbosity unequal 0 all names are handled
  22.     as partial names and the options are to subscribe to all, or
  23.     to none or list the found matches and ask confirmation to
  24.     subscribe. Farther a questionmark (?) as groupname will result
  25.     in menu's with NLINES groupnames and choices can be made by entering
  26.         a string of the indicating letters (or only a NEWLINE if nothing
  27.         is selected). The capital R will resume the option of entering
  28.         (partial) newsgroup names, a Q will quit that phase entirily and
  29.         complete the program by updating the newsrc file.
  30.         
  31.     For testing purposes there are some higher verbosity levels
  32.         (if symbol DEBUG is defined).
  33.     -v2    report some file read or file search actions.
  34.     -v3    also the progress while reading or searching is made visible
  35.         -v4    yet more output about internal databases (debugging)
  36.     -v5    as -v4, but don't delete temporary files
  37.     These higher verbosity-levels also causes the .newsrc file to be
  38.     update in directory ~/tmp en not the home directory itself.
  39.  
  40.     Define symbols:
  41.     ACTIVE        pathname of file containing active newsgroups
  42.     DEFRC        pathname of default .newsrc (see nn INSTALL)
  43.     ERR_EXIT    value for error exit
  44.     NCOLS        number of colums on a terminal screen (80)
  45.     NEWSGROUPS    pathname of file with short descriptions
  46.     NEWSRC        file .newsrc
  47.     NLINES        number of lines in a screen menu (22)
  48.     NNTP        hostname of nntp server
  49.     OLDRC        filename to save old .newsrc
  50.     PORT        port number for nntp, see file /etc/services
  51.     SYSV        compile and run on System V UNIX
  52.     TEMPRC        temporary template to write new .newsrc
  53.     TIMEOUT        timeout value while waiting for server reply
  54.     iShwfac        interval for writing a dot if debugging
  55.     iSlen        length of string buffer
  56.     iMeuGrpart    max space to be used to write groupname in menu
  57.  
  58.     For NNTP-servers where the files "active" and "newsgroups"
  59.     are kept local, mostly in "/usr/lib/news" the symbols ACTIVE
  60.     and NEWSGROUPS must be set to the pathnames of these files
  61.     and the symbol NNTP must *NOT* be defined, PORT is not used.
  62.     For NNTP-clients the symbol NNTP must be set to the name of
  63.     the NNTP-server (as in the file nntp-server in nn's "cofig.h")
  64.     and the symbol PORT must be correct, ACTIVE and NEWSGROUPS are
  65.     not used.
  66.  
  67.     Strategy:
  68.     1.Build an avl-tree (balanced binary tree) from all the
  69.       newsgroups listed in the active file (nntp-clients can obtain
  70.       these data via nntp with the command 'list active').
  71.     2.If Verbosity > 0 store with each newsgroup the byte address
  72.       of the corresponding entry in the newsgroups file (nntp-clients
  73.       must first build this file with the nntp command 'list newsgroups').
  74.     3.Then build a second avl-tree with pointers to the requested
  75.       newsgroups.
  76.     4.Write a new .newsrc combining the data with the second avl-tree.
  77.  
  78.  
  79.     Copyright: Rudi van Houten <Rudi@cc.ruu.nl>
  80.            Acadmisch Computer Centrum Utrecht
  81.            Budapestlaan 6  3584 CD  Utrecht
  82. */
  83.  
  84.                 /* filename definitions */
  85.  
  86. #ifndef ACTIVE
  87. #define ACTIVE "/usr/lib/news/active"    /* file "active" (non NNTP) */
  88. #endif
  89. #ifndef NEWSGROUPS
  90. #define NEWSGROUPS "/usr/lib/news/newsgroups" /* idem "newsgroups" */
  91. #endif
  92.  
  93. #ifndef DEFRC
  94. #define DEFRC  "/usr/spool/news/.nn/db/.defaultnewsrc"
  95. #endif
  96.  
  97. #ifndef NEWSRC
  98. #define NEWSRC ".newsrc"
  99. #endif
  100. #ifndef OLDRC
  101. #define OLDRC  ".newsrc.old"
  102. #endif
  103. #ifndef TEMPRC
  104. #define TEMPRC "tmprcXXXXXX"
  105. #endif
  106. #ifndef PORT
  107. #define PORT 119            /* port for NNTP "/etc/services" */
  108. #endif
  109.  
  110.                 /* some constants */
  111. #ifndef iSlen
  112. #define iSlen 256
  113. #endif
  114. #ifndef NLINES
  115. #define NLINES 22
  116. #endif
  117. #ifndef NCOLS
  118. #define NCOLS 80
  119. #endif
  120. #ifndef iMenuGrpart
  121. #define iMenuGrpart 20        /* max. space for newsgroupname in menu */
  122. #endif
  123.  
  124. #ifndef TIMEOUT
  125. #define TIMEOUT 15
  126. #endif
  127. #define ERR_EXIT 9999
  128.  
  129.                 /* and more constants and includes */
  130.  
  131. #define iVreport 1
  132. #ifdef DEBUG
  133. #define iVactions 2
  134. #define iVprogress 3
  135. #define iVdebug 4
  136. #define iVleavetmp 5
  137. #define iShwfac 100
  138. #endif
  139.  
  140. #define iVerbdef iVreport
  141.  
  142. #ifdef NNTP
  143. #define CHARTERS "/tmp/ngrpXXXXXX"
  144. #else
  145. #define CHARTERS NEWSGROUPS
  146. #endif
  147.  
  148. #include <stdio.h>
  149. #include <string.h>
  150. #include <ctype.h>
  151. #include "avl.h"
  152. #include "version.h"
  153. #include "patchlevel.h"
  154. #ifdef NNTP
  155. #include <sys/types.h>
  156. #include <sys/socket.h>
  157. #include <netinet/in.h>
  158. #include <netdb.h>
  159. #endif
  160. #ifdef DEBUG
  161. #include <sys/file.h>
  162. #endif
  163. #ifdef SYSV
  164. #define index strchr
  165. #define bcopy(a,b,c) memcpy(b,a,c)
  166. #include <unistd.h>
  167. #endif
  168.  
  169. typedef struct
  170.     {
  171.         char *sName;
  172.         long iTitle_pos;
  173.     } s_Group;
  174. typedef s_Group * ps_Group;
  175.  
  176. typedef struct s_grlink
  177.     {
  178.         s_Group * pGroup;
  179.         struct s_grlink * pNext;
  180.     } s_Grlink;
  181.  
  182. static P_AVL_NODE paGr_tree= NULL, paGr_requests= NULL;
  183. static s_Grlink *pGr_list= NULL, *pGr_endlist= NULL;
  184. static short iVerbosity, bIntractive;
  185. static FILE *fGroups, *fCharters, *fNewgroups;
  186. static char *sNewgrplfn, *sCharterlfn;
  187. #ifdef NNTP
  188. static int sock, sock_wr;
  189. struct sockaddr_in sNntpServer;
  190. struct hostent *psNntpHost, *gethostbyname();
  191. #endif
  192.  
  193. static char * sRequest;
  194. static short bStart_is_free, bEnd_is_free; /* needed for fuzzy compare */
  195.  
  196. extern char * Findsub();
  197. extern char * mktemp();
  198. extern char * getenv();
  199. extern char * malloc();
  200.  
  201. int Grpcmp(g1,g2)
  202. s_Group *g1, *g2;
  203. {
  204.     return (strcmp(g1->sName,g2->sName));
  205. } /* Grpcmp */
  206.  
  207. Printcharter(pGr,iOffset)
  208. s_Group *pGr;
  209. int iOffset;
  210. {
  211.     char sLine[iSlen], sNewsgroup[iSlen], sDescription[iSlen];
  212.     int iNglen, iDesclen;      /* length of groupname and description */
  213.     int iNgwidth, iDescwidth;  /* space to use for printing */
  214.     int iAvailable;            /* available space on screnline */
  215.  
  216.     iAvailable= NCOLS - iOffset;
  217.     if (pGr->iTitle_pos < 0) { puts(pGr->sName); return; }
  218.     if (fseek(fCharters,pGr->iTitle_pos,0) < 0)
  219.         { perror(sCharterlfn); exit(ERR_EXIT); }
  220.     fgets(sLine,iSlen,fCharters);
  221.     sscanf(sLine,"%[^ \t]%*[ \t]%[^\n]",sNewsgroup,sDescription);
  222.     iNglen= strlen(sNewsgroup); iDesclen= strlen(sDescription);
  223.     if (iNglen + 1 + iDesclen > iAvailable)
  224.     { /* have to truncate dscription */
  225.     iNgwidth= iNglen + 1; /* one blank after group name */
  226.     iDescwidth= iAvailable - iNgwidth;   /* rest of line */
  227.     }
  228.     else
  229.     if (iNglen < iMenuGrpart)
  230.     {
  231.     if (iDesclen + iMenuGrpart <= iAvailable)
  232.     { /* use "iMenuGrpart" columns for groupname */
  233.         iNgwidth= iMenuGrpart;
  234.         iDescwidth= iDesclen;
  235.     }
  236.     else
  237.     { /* use less space for groupname
  238.          and right-justify description */
  239.         iNgwidth= iAvailable - iDesclen;
  240.         iDescwidth= iDesclen;
  241.     }
  242.     }
  243.     else /* if (iNglen < iMenuGrpart) */
  244.     {
  245.     if (iNglen + 5 + iDesclen <= iAvailable)
  246.     { /* leave 5 spaces between name and description */
  247.         iNgwidth= iNglen + 5;
  248.         iDescwidth= iDesclen;
  249.     }
  250.     else
  251.     { /* leave less blanks after groupname and right-justify description */
  252.         iNgwidth= iAvailable - iDesclen;
  253.         iDescwidth= iDesclen;
  254.     }
  255.     }
  256.     printf("%-*s%.*s\n",iNgwidth,sNewsgroup,iDescwidth,sDescription);
  257.  
  258. } /* Printcharter */
  259.  
  260. int Add_list_element(pGr)
  261. s_Group *pGr;
  262. {
  263.     register s_Grlink *pLocal;
  264.     pLocal= (s_Grlink*)malloc(sizeof(s_Grlink));
  265.     pLocal->pGroup= pGr;
  266.     pLocal->pNext= NULL;
  267.     if (pGr_list == NULL)
  268.         pGr_list= pGr_endlist= pLocal;
  269.     else
  270.         pGr_endlist= pGr_endlist->pNext= pLocal;
  271. #   ifdef DEBUG
  272.     if (iVerbosity > iVdebug) putchar('.');
  273. #   endif
  274.     return 0;
  275. } /* Add_list_element */
  276.  
  277. int Fuzzycheck(pGr)
  278. s_Group *pGr;
  279. /*
  280.     if a fuzzy compare of the string sRequest matches the
  281.     name of the supplied group that group is added in the
  282.     one-way-linked list for later consideration
  283. */
  284. {
  285.     register short bFound;
  286.     register char *pcC;
  287.     if (bStart_is_free)
  288.         { if ((pcC= Findsub(pGr->sName,sRequest)) == NULL) return(0); }
  289.     else pcC= pGr->sName;
  290.     if (!bEnd_is_free) bFound= strcmp(sRequest,pcC) == 0;
  291.     else bFound= strncmp(sRequest,pcC,strlen(sRequest)) == 0;
  292.     if (bFound) Add_list_element(pGr);
  293.     return(bFound);
  294. } /* int Fuzzycheck(pGr) */
  295.  
  296. int Checkrequest(sName)
  297. char * sName;
  298. {
  299.     register P_AVL_NODE pNode;
  300.     register s_Grlink *pLink;
  301.     s_Group s_Glocal, *pGr_dum;
  302.     char cC;
  303.     int iCount;
  304.     for (iCount=0;iCount<strlen(sName);iCount++)
  305.         if (isupper(sName[iCount])) sName[iCount]= tolower(sName[iCount]);
  306.     bStart_is_free= sName[0] == '.';
  307.     bEnd_is_free= (cC= sName[strlen(sName)-1]) == '.';
  308.     s_Glocal.sName= sName;
  309.     if (!bStart_is_free && !bEnd_is_free)
  310.     {
  311.         if (find_avlnode(&s_Glocal,paGr_requests,Grpcmp)) return(0);
  312.         if (pNode= find_avlnode(&s_Glocal,paGr_tree,Grpcmp))
  313.         {
  314.             insert_avl(pNode->content,&paGr_requests,Grpcmp,&pGr_dum);
  315. #           ifdef DEBUG
  316.             if (iVerbosity > iVprogress)
  317.                 printf("..Added in request tree: %s\n",sName);
  318. #           endif
  319.             return(1);
  320.         }
  321.         else /* if (pNode= find_avlnode(&s_Glocal,paGr_tree,Grpcmp)) */
  322.         if (iVerbosity && bIntractive)
  323.             bStart_is_free= bEnd_is_free= 1;
  324.         else /* if (iVerbosity && bIntractive) */
  325.         {
  326.             if (iVerbosity) printf("BOGUS newsgroup \"%s\"\n",sName);
  327.             return(0);
  328.         } /* if (iVerbosity && bIntractive) */
  329.         /* if (pNode= find_avlnode(&s_Glocal,paGr_tree,Grpcmp)) */
  330.     } /* if (!bStart_is_free && !bEnd_is_free) */
  331.     sRequest= sName;
  332.     unravel_avl(&paGr_tree,Fuzzycheck,0);
  333.     if (pGr_list == NULL)
  334.     {
  335.         if (iVerbosity) printf("\"%s\": no matching newsgroups found\n",sName);
  336.         return(0);
  337.     }
  338.     iCount= 0; /* and now count the found groups */
  339.     for (pGr_endlist= pGr_list;
  340.          pGr_endlist != NULL;
  341.          pGr_endlist= pGr_endlist->pNext) iCount++;
  342.     if (iVerbosity)
  343.     {
  344. #       ifdef DEBUG
  345.     if (iVerbosity >= iVdebug) putchar('\n');
  346. #       endif
  347.         if (bIntractive)
  348.             if (iCount == 1)
  349.             {
  350.         putchar('\n');
  351.                 Printcharter(pGr_list->pGroup,1);
  352.                 fputs("  OK? (y) ",stdout);
  353.                 cC= getchar();
  354.         if (cC != '\n') while (getchar() != '\n'); else cC= 'y';
  355.                 if (cC != 'y' && cC != 'Y') { free(pGr_list); pGr_list= NULL; }
  356.             }
  357.             else /* if iCount == 1) */
  358.             {
  359.                 printf("Partial name \"%s\": %d matching newsgroups found\n"
  360.                       ,sName,iCount);
  361.                 fputs("Subscribe to: a)ll, c)onfirmation, n)one? ",stdout);
  362.                 cC= getchar(); if (cC != '\n') while (getchar() != '\n');
  363.                 switch (cC)
  364.                 {
  365.                 default:
  366.                     break; /* default answer is 'a' */
  367.                 case 'n':
  368.                 case 'N':
  369.                     pGr_endlist= pGr_list; pGr_list= NULL;
  370.                     while (pGr_endlist)
  371.                     {
  372.                         pLink= pGr_endlist; pGr_endlist= pLink->pNext;
  373.                         free(pLink);
  374.                     }
  375.                     return(0);
  376.                 case 'c':
  377.                 case 'C':
  378.                     return(Menusubscribe());
  379.                 } /* switch(cC) */
  380.             } /* if (iCount == 1) */
  381.         /* if (bIntractive) */
  382.     } /* if (iVerbosity) */
  383.     if (pGr_list == NULL) return(0);
  384.     iCount= 0; pGr_endlist= pGr_list; pGr_list= NULL;
  385.     while (pGr_endlist != NULL)
  386.     {
  387.         if (find_avlnode(pGr_endlist->pGroup,paGr_requests,Grpcmp) == NULL)
  388.         {
  389.             insert_avl(pGr_endlist->pGroup,&paGr_requests,Grpcmp,&pGr_dum);
  390. #           ifdef DEBUG
  391.             if (iVerbosity > iVprogress)
  392.                 printf("..Added in request tree: %s\n"
  393.                       ,pGr_endlist->pGroup->sName);
  394. #           endif
  395.             iCount++;
  396.         }
  397.         pLink= pGr_endlist; pGr_endlist= pLink->pNext;
  398.         free(pLink);
  399.     } /* while (pGr_endlist != NULL) */
  400.     return(iCount);
  401. } /* int Checkrequest(sName) */
  402.  
  403. int Menusubscribe()
  404. /*
  405.     returnvalue: number of subscribed groups
  406. */
  407. {
  408.     ps_Group sgMline[NLINES], pGr_dum;
  409.     char sLine[iSlen], cTop;
  410.     register s_Grlink * pLink;
  411.     register int i;
  412.     short bStop;
  413.     int iReturnvalue;
  414.  
  415.     if (pGr_list == NULL)    /* first unravel the tree */
  416. #   ifdef DEBUG
  417.     {
  418.         if (iVerbosity >= iVdebug) puts("Unravel tree to one-way linked list");
  419. #       endif
  420.         unravel_avl(&paGr_tree,Add_list_element,0);
  421. #       ifdef DEBUG
  422.         if (iVerbosity >= iVdebug) puts("\n=Done ===========");
  423.     }
  424. #   endif
  425.     pGr_endlist= pGr_list; pGr_list= NULL;
  426.     i= iReturnvalue= 0; bStop= 0;
  427.     while (pGr_endlist != NULL && bStop == 0)
  428.     {
  429.     if (i == 0) puts("\n\n\n");
  430.         pLink= pGr_endlist; printf("%c: ",i+'a');
  431.     Printcharter(sgMline[i]= pLink->pGroup,4);
  432.         pGr_endlist= pLink->pNext; free(pLink);
  433.         if (i++, pGr_endlist == NULL || i == NLINES)
  434.         {
  435.             cTop= (char)((int)'a' + i - 1);
  436.             printf("\n>> Select with [a..%c], or Quit:",cTop);
  437.             if (gets(sLine) == NULL) bStop= 1;
  438.             else
  439.             for (i= 0; sLine[i] != '\0'; i++)
  440.                 if (index("QREBSX",sLine[i])) bStop= 1;
  441.                 else
  442.                 if (sLine[i] >= 'a' && sLine[i] <= cTop)
  443.         {
  444.                     insert_avl(sgMline[(int)sLine[i] - (int)'a']
  445.                               ,&paGr_requests,Grpcmp,&pGr_dum);
  446.                     if (pGr_dum == sgMline[(int)sLine[i] - (int)'a'])
  447.                     {
  448.                         iReturnvalue++;
  449. #                       ifdef DEBUG
  450.                         if (iVerbosity >= iVdebug)
  451.                 {
  452.                 puts("Added to request tree:");
  453.                             Printcharter(pGr_dum,1);
  454.                         }
  455. #                       endif
  456.                     }
  457.         }
  458.                 /* ignore garbage */
  459.             /* for (i= 0; sLine[i] != '\0'; i++) */
  460.             i= 0;
  461.          } /* if (pGr_endlist == NULL || ++i > NLINES) */
  462.     } /* while (pGr_endlist != NULL && bStop == 0) */
  463. #   ifdef DEBUG
  464.     if (iVerbosity >= iVdebug && pGr_endlist != NULL)
  465.         puts("Clean up the one-way linked list");
  466. #   endif
  467.     while (pGr_endlist != NULL)
  468.     {
  469.         pLink= pGr_endlist; pGr_endlist= pLink->pNext;
  470.         free(pLink);
  471. #       ifdef DEBUG
  472.         if (iVerbosity >= iVdebug)
  473.     {
  474.         putchar('.');
  475.             if (pGr_endlist == NULL) puts("\n>> Cleaning done\n");
  476.     }
  477. #       endif
  478.     } /* while (pGr_endlist != NULL) */
  479.     return(iReturnvalue);
  480. } /* int Menusubscribe() */
  481.  
  482. int Addsubscription(pGr)
  483. s_Group *pGr;
  484. {
  485.     if (islower(pGr->sName[0]))
  486.     {
  487.         fprintf(fNewgroups,"%s:\n",pGr->sName);
  488.         if (iVerbosity) { puts("New subscription:"); Printcharter(pGr,1); }
  489.     }
  490.     return(0);
  491. } /* Addsubscription */
  492.  
  493. #ifdef NNTP
  494. static FILE *fNntpOut;
  495.  
  496. static Nntpcommand(sCommand)
  497. char *sCommand;
  498. /*
  499.      send a command to the nntp server ad check the reply
  500.      in case of error give an exit, timeout exits on alarm.
  501. */
  502. {
  503.     char sLine[iSlen];
  504.     int iReply;
  505. #   ifdef DEBUG
  506.     if (iVerbosity >= iVactions) printf("NNTP: %s\n",sCommand);
  507. #   endif
  508.     fprintf(fNntpOut,"%s\r\n",sCommand);
  509.     if (fflush(fNntpOut) == EOF)
  510.     { perror(sCommand); exit(ERR_EXIT); }
  511.     alarm(TIMEOUT);
  512.     if (fgets(sLine,iSlen,fGroups) == NULL)
  513.     { fprintf(stderr,"NULL response on %s\n",sCommand); exit(ERR_EXIT); }
  514.     alarm(0);
  515.     if (sscanf(sLine,"%d",&iReply) != 1)
  516.     {
  517.     fprintf(stderr,"Illegal response on %s:\n%s",sCommand,sLine);
  518.         exit(ERR_EXIT);
  519.     }
  520.     if (iReply < 200 || iReply >= 400)
  521.     {
  522.     fprintf(stderr,"Error response on %s\n%s",sCommand,sLine);
  523.     exit(ERR_EXIT);
  524.     }
  525. #   ifdef DEBUG
  526.     if (iVerbosity >= iVactions) fputs(sLine,stdout);
  527. #   endif
  528. } /* Nntpcommand */
  529. #endif
  530.  
  531.  
  532. main(argc,argv,envp)
  533. int argc;
  534. char *argv[], *envp[];
  535. {
  536. char sLine[iSlen];
  537. register char *pcC;
  538. register s_Group *pG1;
  539. s_Group *pG2;
  540. register P_AVL_NODE p_AVL;
  541. int i, j, k, iGr, iGrfault;
  542.  
  543. if (bIntractive= isatty(0)) iVerbosity= iVerbdef; else iVerbosity= 0;
  544.  
  545. iGr= 0; /* counter for groupname arguments */
  546. if (argc > 1)
  547. for (i=1; i<argc; i++)
  548.     if (argv[i][0] == '-')
  549.         switch(argv[i][1])
  550.         {
  551.         default:
  552.             fprintf(stderr,"Unknown option \"%s\" ignored\n",argv[i]);
  553.             break;
  554.         case 'V':
  555.         case 'v':
  556.             if (argv[i][2] == '\0') iVerbosity= iVerbdef;
  557.             else iVerbosity= atoi(&argv[i][2]);
  558.             break;
  559.         } /* switch(argv[i][1]) */
  560.     else /* if (argv[i][0] == '-') */
  561.         iGr++; /* increment supplied groupname counter */
  562. /* for (i=1; i<argc; i++) */
  563.  
  564. if (iVerbosity)
  565.     printf("\nNetnews subscribe\nRelease %d.%d PL%d (Rudi van Houten 1991)\n\n"
  566.       ,VERSION,SUBNR,PATCHLEVEL);
  567.  
  568. #ifdef NNTP
  569.     /* create socket */
  570. if ((sock= socket(AF_INET,SOCK_STREAM,0)) < 0)
  571.     { perror("-- opening stream socket"); exit(ERR_EXIT); }
  572.  
  573.     /* fill socket structure .....*/
  574. sNntpServer.sin_family= AF_INET;
  575. if ((psNntpHost= gethostbyname(NNTP)) == 0)
  576. {
  577. #define cMessage "While trying to connect to "
  578.     char *sMessage;
  579.     sMessage= malloc(strlen(NNTP)+strlen(cMessage));
  580.     strcpy(sMessage,cMessage); strcat(sMessage,NNTP);
  581.     perror(sMessage);
  582.     exit(ERR_EXIT);
  583. }
  584. bcopy(psNntpHost->h_addr,&sNntpServer.sin_addr,psNntpHost->h_length);
  585. sNntpServer.sin_port= htons(PORT);
  586.  
  587.     /*........and connect */
  588. if (iVerbosity) printf("Connecting to nntp server %s\n",NNTP);
  589. if (connect(sock,&sNntpServer,sizeof(sNntpServer)) < 0)
  590.     { perror("connecting stream socket"); exit(ERR_EXIT); }
  591. if ((fGroups= fdopen(sock,"r")) == NULL)
  592.     { perror("assign socket to FILE fGroups"); exit(ERR_EXIT); }
  593. alarm(TIMEOUT);
  594. if (fgets(sLine,iSlen,fGroups) == NULL)
  595.     { fputs("No reply from server\n",stderr); exit(ERR_EXIT); }
  596. alarm(0);
  597. if (strncmp(sLine,"200 ",4)) { fputs(sLine,stderr); exit(ERR_EXIT); }
  598. #ifdef DEBUG
  599. if (iVerbosity >= iVactions) puts(sLine);
  600. #endif
  601. sock_wr= dup(sock);
  602. if ((fNntpOut= fdopen(sock_wr,"w")) == NULL)
  603.     { perror("assign outputfile to socket"); exit(ERR_EXIT); }
  604. Nntpcommand("LIST ACTIVE");
  605. #else
  606. if ((fGroups= fopen(ACTIVE,"r")) == NULL) { perror(ACTIVE); exit(ERR_EXIT); }
  607. #ifdef DEBUG
  608. if (iVerbosity >= iVactions)
  609.     printf("Reading active newsgroups from \"%s\"\n",ACTIVE);
  610. #endif
  611. #endif
  612.  
  613. i= 0; pG1= NULL;
  614. #ifdef NNTP
  615. #ifdef DEBUG
  616. if (iVerbosity >= iVdebug) puts("Get active groups from server");
  617. #endif
  618. while (alarm(TIMEOUT),fgets(sLine,iSlen,fGroups) != NULL)
  619. #else
  620. while (fgets(sLine,iSlen,fGroups) != NULL)
  621. #endif
  622. {
  623. #   ifdef NNTP
  624.     alarm(0);
  625. #   endif
  626.     k= strlen(sLine);
  627.     for (j=0; j<k; j++)
  628.         if (index(" \t\r\n",sLine[j])) { sLine[j]= '\0'; break; }
  629.         else if (isupper(sLine[j])) sLine[j]= tolower(sLine[j]);
  630.     if (strcmp(sLine,".") == 0) break;
  631.     if (pG1 == NULL) pG1= (s_Group*)malloc(sizeof(s_Group));
  632.     pG1->sName= strsave(sLine); pG1->iTitle_pos= -1;
  633.     insert_avl(pG1,&paGr_tree,Grpcmp,&pG2);
  634.     if (pG1 == pG2)
  635.     {
  636.         i++;
  637. #       ifdef DEBUG
  638.         if (iVerbosity >= iVprogress)
  639.             { if (i % iShwfac == 0) putchar('.'); }
  640. #       endif
  641.         pG1= NULL;
  642.     }
  643. } /* while (...fgets(sLine,iSlen,fGroups) != NULL) */
  644. if (iVerbosity) printf("\n %d active groups found\n",i);
  645. #ifndef NNTP
  646. fclose(fGroups);
  647. sCharterlfn= CHARTERS;
  648. #else
  649. sCharterlfn= strsave(CHARTERS);
  650. sCharterlfn= mktemp(sCharterlfn);
  651. if ((fCharters= fopen(sCharterlfn,"w")) == NULL)
  652.     { perror(sCharterlfn); exit(ERR_EXIT); }
  653. Nntpcommand("LIST NEWSGROUPS");
  654. #ifdef DEBUG
  655. if (iVerbosity >= iVactions)
  656. {
  657.     puts(sLine);
  658.     printf("Copying nntp <LIST NEWSGROUPS> to \"%s\"\n",sCharterlfn);
  659. }
  660. #endif
  661. for (j=0;;j++)
  662. {
  663. #   ifdef NNTP
  664.     alarm(TIMEOUT);
  665. #   endif
  666.     if (fgets(sLine,iSlen,fGroups) == NULL) break;
  667. #   ifdef NNTP
  668.     alarm(0);
  669. #   endif
  670.     i= strlen(sLine) - 1;
  671.     while (index(" \t\r\n",sLine[i])) sLine[i--]= '\0';
  672.     if (strcmp(sLine,".") == 0) break;
  673.     fputs(sLine,fCharters); putc('\n',fCharters);
  674. #   ifdef DEBUG
  675.     if (iVerbosity >= iVprogress) if (j % iShwfac == 0) putchar('.');
  676. #   endif
  677. }
  678. #ifdef DEBUG
  679. if (iVerbosity >= iVprogress) putchar('\n');
  680. #endif
  681. fclose(fCharters);
  682. Nntpcommand("QUIT");
  683. if (iVerbosity) printf("Connection with %s closed\n",NNTP);
  684. fclose(fGroups); fclose(fNntpOut);
  685. #endif
  686.  
  687. /* now read newsgroups with Charter/Title per group */
  688.  
  689. if ((fCharters= fopen(sCharterlfn,"r")) == NULL)
  690. {
  691.     if (iVerbosity)
  692.         printf("File %s not found, no descriptions can be given\n"
  693.               ,sCharterlfn);
  694. }
  695. else /* if ((fCharters= fopen(sCharterlfn,"r")) == NULL) */
  696. {
  697. #   ifdef DEBUG
  698.     if (iVerbosity >= iVactions)
  699.         printf("Reading short descriptions from \"%s\"\n",sCharterlfn);
  700. #   endif
  701.     if (pG1 == NULL) pG1= (s_Group*)malloc(sizeof(s_Group));
  702.     pG1->iTitle_pos= 0; i= 0;
  703.     while (fgets(sLine,iSlen,fCharters) != NULL)
  704.     {
  705.         pG1->sName= pcC= &sLine[0];
  706.     while (isgraph(*pcC)) 
  707.         { if (isupper(*pcC)) *pcC= tolower(*pcC); pcC++; };
  708.         *pcC++= '\0';
  709.     if (sLine[0] != '\0')
  710.             if ((p_AVL= find_avlnode(pG1,paGr_tree,Grpcmp)) != NULL)
  711.                 ((s_Group*)(p_AVL->content))->iTitle_pos= pG1->iTitle_pos;
  712. #       ifdef DEBUG
  713.             else
  714.             if (iVerbosity > iVprogress)
  715.                 printf("\nNot \"active\" group: %s\n\t%s",sLine,pcC);
  716. #       endif
  717.         pG1->iTitle_pos= ftell(fCharters);
  718. #       ifdef DEBUG
  719.         if (iVerbosity >= iVprogress)
  720.             if (++i % iShwfac == 0) putchar('.');
  721. #       endif
  722.     } /* while (fgets(sLine,iSlen,fCharters) != NULL) */
  723.     /* don't close file, read random when titles must be printed */
  724. #   ifdef DEBUG
  725.     if (iVerbosity >= iVprogress) putchar('\n');
  726. #   endif
  727. } /* if ((fCharter= fopen(sCharterlfn,"r")) == NULL) */
  728.  
  729. if (iGr) /* requests are supplied as program arguments */
  730. {
  731. #   ifdef DEBUG
  732.     if (iVerbosity >= iVdebug) puts("Process arguments");
  733. #   endif
  734.     iGrfault= iGr;
  735.     for (i=1;i<argc;i++)
  736.     {
  737.         if (argv[i][0] != '-')
  738.             if (strcmp(argv[i],"?") == 0)
  739.             {
  740.                 if (bIntractive && iVerbosity)
  741.                     if (Menusubscribe()) iGrfault--;
  742.             }
  743.             else
  744.             if (strcmp(argv[i],"."))
  745.                 if (Checkrequest(argv[i])) iGrfault--;
  746.     }
  747. }
  748.  
  749. if (iGr == 0) /* no groupnames supplied as arguments, read from stdin */
  750. {
  751.     if (bIntractive && iVerbosity) puts("\nEnter newsgroups to subscribe to:");
  752.     for (;;)
  753.     {
  754.         if (bIntractive && iVerbosity) fputs(" ?  ",stdout);
  755.         if (gets(sLine) == NULL) break;
  756.     if (sLine[0] == '\0') break;
  757.     if (strcmp(sLine,"Q") == 0) break;
  758.         if (strcmp(sLine,".") == 0) break;
  759.         if (strcmp(sLine,"?") == 0)
  760.         {
  761.             if (bIntractive && iVerbosity) if (Menusubscribe()) break;
  762.         }
  763.         else
  764.         if (Checkrequest(sLine) == 0) iGr++;
  765.     }
  766. }
  767.  
  768. if (paGr_requests == NULL)
  769. {
  770.     if (iVerbosity) puts("No legal subscribe requests");
  771. #   ifdef NNTP
  772. #   ifdef DEBUG
  773.     if (iVerbosity == iVdebug) printf("unlink temporary %s\n",sCharterlfn);
  774.     if (iVerbosity < iVleavetmp)
  775. #   endif
  776.     unlink(sCharterlfn);
  777. #   ifdef DEBUG
  778.     else printf("Temporary newsgroup descriptions on %s\n",sCharterlfn);
  779. #   endif
  780. #   endif
  781.     exit(0);
  782. }
  783.  
  784. /*
  785.     And now go looking into .newsrc
  786. */
  787.  
  788. if (chdir(getenv("HOME"))) { perror("dir to $HOME"); exit(ERR_EXIT); }
  789. #ifdef DEBUG
  790. if (iVerbosity > iVreport)
  791. {
  792.     if (access("tmp",F_OK))
  793.     if (mkdir("tmp",0750)) { perror("mkdir tmp"); exit(ERR_EXIT); }
  794.     if (chdir("tmp")) { perror("dir to ~/tmp"); exit(ERR_EXIT); }
  795. }
  796. if (iVerbosity >= iVdebug) printf(" cwd = %s\n",getcwd(sLine,iSlen));
  797. #endif
  798. sNewgrplfn= strsave(TEMPRC);
  799. sNewgrplfn= mktemp(sNewgrplfn);
  800.  
  801. if ((fNewgroups= fopen(sNewgrplfn,"w")) == NULL)
  802.     { perror("NEW newsrc"); exit(ERR_EXIT); }
  803. #ifdef DEBUG
  804. if (iVerbosity >= iVactions) printf("Opened file %s for writing\n"
  805.                                    ,sNewgrplfn);
  806. #endif
  807.  
  808. if ((fGroups= fopen(NEWSRC,"r")) == NULL)
  809. {
  810.     FILE *fDefnewsrc;
  811.     if ((fDefnewsrc= fopen(DEFRC,"r")) != NULL)
  812.     { /* add groups on .defaultnewsrc */
  813.         s_Group sGr_local, *pGr_dum;
  814.     register char *pCc;
  815.     register P_AVL_NODE pNode;
  816. #       ifdef DEBUG
  817.     if (iVerbosity >= iVactions)
  818.         printf("Read default groups from %s\n",DEFRC);
  819. #       endif
  820.     sGr_local.sName= sLine; /* and hold it during the loop */
  821.     while (fgets(sLine,iSlen,fDefnewsrc) != NULL)
  822.         if (strlen(sLine))
  823.         if (pCc= index(sLine,':'))
  824.             {
  825.             *pCc= '\0';
  826.             if (find_avlnode(&sGr_local,paGr_requests,Grpcmp) == NULL)
  827.             if (pNode= find_avlnode(&sGr_local,paGr_tree,Grpcmp))
  828.             {
  829.                             insert_avl(pNode->content,&paGr_requests
  830.                     ,Grpcmp,&pGr_dum);
  831. #                           ifdef DEBUG
  832.                     if (iVerbosity >= iVdebug)
  833.                     printf("Default group %s added\n",sLine);
  834. #                           endif
  835.             }
  836.                 }
  837.     fclose(fDefnewsrc);
  838.     }
  839. #   ifdef DEBUG
  840.     else
  841.     if (iVerbosity >= iVactions)
  842.     printf("Default newsrc (%s) non-existant or not readable\n",DEFRC);
  843. #   endif
  844. }
  845. else /* if ((fGroups= fopen(NEWSRC,"r")) == NULL) */
  846. {
  847.     s_Group sG_local;
  848.     register char cC;
  849. #   ifdef DEBUG
  850.     if (iVerbosity >= iVactions) printf("Opened file %s for reading\n",NEWSRC);
  851. #   endif
  852.     sG_local.sName= sLine; /* and hold it so during read of .newsrc */
  853.     for (;;)
  854.     {
  855.         if (fgets(sLine,iSlen,fGroups) == NULL)
  856.         {
  857. #           ifdef DEBUG
  858.             if (iVerbosity >= iVprogress) putchar('\n');
  859.             if (iVerbosity >= iVdebug)
  860.                 printf("End of %s reached\n",NEWSRC);
  861. #           endif
  862.             break;
  863.         }
  864.  
  865.             /* search for '!' or ':' or 'white space' */
  866.         pcC= sLine; while(index("!: \t\n",*pcC) == NULL) pcC++;
  867.         cC= *pcC; *pcC= '\0';
  868.         if (p_AVL= find_avlnode(&sG_local,paGr_requests,Grpcmp))
  869.         {
  870. #           ifdef DEBUG
  871.         if (iVerbosity >= iVprogress) putchar('\n');
  872. #           endif
  873.             if (iVerbosity)
  874.                 switch(cC)
  875.                 {
  876.                 default:
  877.                     puts("Strange line in \".newsrc\", run \"nntidy\"");
  878.                     *pcC= cC; puts(sLine); exit(ERR_EXIT);
  879.                 case ':':
  880.                     puts("Already subscribed:");
  881.                     Printcharter(p_AVL->content,1);
  882.                     break;
  883.                 case '!':
  884.                     puts("Renew cancelled subscription");
  885.                     Printcharter(p_AVL->content,1);
  886.                     break;
  887.                 } /* switch(cC) */
  888.             /* if (iVerbosity) */
  889.             *pcC= ':';
  890.         pcC= ((s_Group*)(p_AVL->content))->sName;
  891.             *pcC= toupper(*pcC); /* mark as already processed */
  892.         }
  893.         else /* if (p_AVL= find_avlnode(&sG_local,paGr_requests,Grpcmp)) */
  894.     {
  895.             *pcC= cC;
  896. #           ifdef DEBUG
  897.         if (iVerbosity >= iVprogress) putchar('.');
  898. #           endif
  899.     } /* if (p_AVL= find_avlnode(&sG_local,paGr_requests,Grpcmp)) */
  900.         fputs(sLine,fNewgroups);
  901.     } /* for (;;) */
  902.     fclose(fGroups);
  903. } /* if ((fGroups= fopen(NEWSRC,"r")) == NULL) */
  904.  
  905. unravel_avl(&paGr_requests,Addsubscription,0);
  906. fclose(fNewgroups);
  907. if (fCharters != NULL) fclose(fCharters);
  908. #ifdef DEBUG
  909. if (iVerbosity >= iVdebug) printf("\nunlink %s\n",OLDRC);
  910. #endif
  911. unlink(OLDRC);
  912. #ifdef DEBUG
  913. if (iVerbosity >= iVdebug) printf("rename %s to %s\n",NEWSRC,OLDRC);
  914. #endif
  915. rename(NEWSRC,OLDRC);
  916. #ifdef DEBUG
  917. if (iVerbosity >= iVdebug) printf("rename %s to %s\n",sNewgrplfn,NEWSRC);
  918. #endif
  919. rename(sNewgrplfn,NEWSRC);
  920. #ifdef NNTP
  921. #ifdef DEBUG
  922. if (iVerbosity == iVdebug) printf("unlink temporary %s\n",sCharterlfn);
  923. if (iVerbosity < iVleavetmp)
  924. #endif
  925.     unlink(sCharterlfn);
  926. #ifdef DEBUG
  927. else printf("Temporary newsgroup descriptions on %s\n",sCharterlfn);
  928. #endif
  929. #endif
  930.  
  931. if (iVerbosity == 0) exit(iGrfault);
  932. exit(0);
  933.  
  934. } /* main */
  935.