home *** CD-ROM | disk | FTP | other *** search
/ Usenet 1994 October / usenetsourcesnewsgroupsinfomagicoctober1994disk2.iso / unix / volume19 / cnews2 / part18 < prev    next >
Text File  |  1989-06-29  |  46KB  |  2,050 lines

  1. Subject:  v19i095:  Cnews production release, Part18/19
  2. Newsgroups: comp.sources.unix
  3. Sender: sources
  4. Approved: rsalz@uunet.UU.NET
  5.  
  6. Submitted-by: utzoo!henry
  7. Posting-number: Volume 19, Issue 95
  8. Archive-name: cnews2/part18
  9.  
  10. : ---CUT HERE---
  11. echo 'rna/header.c':
  12. sed 's/^X//' >'rna/header.c' <<'!'
  13. X/*
  14. X * extract/output headers
  15. X */
  16. X
  17. X#include "defs.h"
  18. X
  19. X#if AUSAM
  20. Xextern struct pwent pe;
  21. X#else
  22. Xextern struct passwd *pp;
  23. X#endif
  24. Xextern char systemid[];
  25. Xextern long now;
  26. X
  27. Xchar tzone[]         = TIMEZONE;
  28. Xchar hform[]         = "%s: %s\n";
  29. X
  30. X/* Mandatory Headers */
  31. Xchar t_relayversion[]     = "Relay-Version";
  32. Xchar t_postversion[]     = "Posting-Version";
  33. Xchar t_from[]         = "From";
  34. Xchar t_date[]         = "Date";
  35. Xchar t_newsgroups[]     = "Newsgroups";
  36. Xchar t_subject[]     = "Subject";
  37. Xchar t_messageid[]     = "Message-ID";
  38. Xchar t_path[]         = "Path";
  39. X
  40. X/* Optional Headers */
  41. Xchar t_replyto[]     = "Reply-To";
  42. Xchar t_sender[]         = "Sender";
  43. Xchar t_followupto[]     = "Followup-To";
  44. Xchar t_datereceived[]     = "Date-Received";
  45. Xchar t_expires[]     = "Expires";
  46. Xchar t_references[]     = "References";
  47. Xchar t_control[]     = "Control";
  48. Xchar t_distribution[]     = "Distribution";
  49. Xchar t_organization[]     = "Organization";
  50. Xchar t_lines[]         = "Lines";
  51. X
  52. Xtypedef enum ft
  53. X{
  54. X    f_control, f_date, f_datereceived, f_distribution,
  55. X    f_expires, f_followupto, f_from, f_lines, f_messageid,
  56. X    f_newsgroups, f_organization, f_path, f_postversion,
  57. X    f_references, f_relayversion, f_replyto, f_sender,
  58. X    f_subject
  59. X}
  60. X
  61. X
  62. Xftype;
  63. X
  64. Xtypedef struct field {
  65. X    char *f_name;
  66. X    ftype    f_type;
  67. X} field;
  68. X
  69. Xstatic field fields[] = 
  70. X{
  71. X    { t_control,     f_control     },
  72. X    { t_date,         f_date         },
  73. X    { t_datereceived,     f_datereceived     },
  74. X    { t_distribution,     f_distribution     },
  75. X    { t_expires,     f_expires     },
  76. X    { t_followupto,     f_followupto     },
  77. X    { t_from,         f_from         },
  78. X    { t_lines,     f_lines         },
  79. X    { t_messageid,     f_messageid     },
  80. X    { t_newsgroups,     f_newsgroups     },
  81. X    { t_organization,     f_organization     },
  82. X    { t_path,         f_path         },
  83. X    { t_postversion,     f_postversion     },
  84. X    { t_references,     f_references     },
  85. X    { t_relayversion,     f_relayversion     },
  86. X    { t_replyto,     f_replyto     },
  87. X    { t_sender,     f_sender     },
  88. X    { t_subject,     f_subject     }
  89. X};
  90. X
  91. X
  92. Xchar *weekdays[7] = 
  93. X{
  94. X    "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"
  95. X};
  96. X
  97. X
  98. Xchar *months[12] = 
  99. X{
  100. X    "Jan", "Feb", "Mar", "Apr", "May", "Jun",
  101. X    "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"
  102. X};
  103. X
  104. X
  105. Xstatic
  106. Xfieldcmp(a, b)
  107. Xfield *a, *b;
  108. X{
  109. X    return CMP(a->f_name, b->f_name);
  110. X}
  111. X
  112. X
  113. X/*
  114. X * extract headers from file,
  115. X * position file to start of body
  116. X */
  117. Xgethead(f, hp)
  118. XFILE *f;
  119. Xheader *hp;
  120. X{
  121. X    register char *colon, *space, *s;
  122. X    register field    *fp;
  123. X    field        af;
  124. X    char buf[BUFLEN*2];
  125. X
  126. X    char *hfgets();
  127. X
  128. X    memset((char *) hp, 0, sizeof(header));
  129. X    while (hfgets(buf, sizeof(buf), f)) {
  130. X        if (buf[0] == '\n')
  131. X            return;
  132. X        if (isupper(buf[0]) && (colon = strchr(buf, ':')) && (space =
  133. X            strchr(buf, ' ')) && (colon + 1 == space)) {
  134. X            *colon = '\0';
  135. X            af.f_name = buf;
  136. X            fp = (field * ) bsearch((char *) & af, (char *) fields,
  137. X                                 sizeof(fields) / sizeof(fields[0]), sizeof(fields[0]),
  138. X                 fieldcmp);
  139. X            *colon = ':';
  140. X        } else
  141. X            fp = NIL(field);
  142. X        if (!fp)
  143. X            if (hp->h_others)
  144. X                hp->h_others = catstr(hp->h_others, buf);
  145. X            else
  146. X                hp->h_others = newstr(buf);
  147. X        else
  148. X         {
  149. X            if (colon = strchr(space + 1, '\n'))
  150. X                *colon = '\0';
  151. X            s = newstr(space + 1);
  152. X            switch (fp->f_type) {
  153. X            case f_control:        
  154. X                hp->h_control = s;    
  155. X                break;
  156. X            case f_date:        
  157. X                hp->h_date = s;        
  158. X                break;
  159. X            case f_datereceived:    
  160. X                hp->h_datereceived = s;    
  161. X                break;
  162. X            case f_distribution:    
  163. X                hp->h_distribution = s;    
  164. X                break;
  165. X            case f_expires:        
  166. X                hp->h_expires = s;    
  167. X                break;
  168. X            case f_followupto:    
  169. X                hp->h_followupto = s;    
  170. X                break;
  171. X            case f_from:        
  172. X                hp->h_from = s;        
  173. X                break;
  174. X            case f_lines:        
  175. X                hp->h_lines = s;    
  176. X                break;
  177. X            case f_messageid:    
  178. X                hp->h_messageid = s;    
  179. X                break;
  180. X            case f_newsgroups:    
  181. X                hp->h_newsgroups = s;    
  182. X                break;
  183. X            case f_organization:    
  184. X                hp->h_organisation = s;    
  185. X                break;
  186. X            case f_path:        
  187. X                hp->h_path = s;        
  188. X                break;
  189. X            case f_postversion:    
  190. X                hp->h_postversion = s;    
  191. X                break;
  192. X            case f_references:    
  193. X                hp->h_references = s;    
  194. X                break;
  195. X            case f_relayversion:    
  196. X                hp->h_relayversion = s;    
  197. X                break;
  198. X            case f_replyto:        
  199. X                hp->h_replyto = s;    
  200. X                break;
  201. X            case f_sender:        
  202. X                hp->h_sender = s;    
  203. X                break;
  204. X            case f_subject:        
  205. X                hp->h_subject = s;    
  206. X                break;
  207. X            }
  208. X        }
  209. X    }
  210. X}
  211. X
  212. X
  213. X/*
  214. X * put headers to file
  215. X */
  216. Xputhead(hp, f, com)
  217. Xheader *hp;
  218. XFILE *f;
  219. Xpheadcom com;
  220. X{
  221. X    register char *s;
  222. X    char *getunique();
  223. X    extern char *getenv();
  224. X
  225. X    if (hp->h_relayversion && com == printing)
  226. X        (void) fprintf(f, hform, t_relayversion, hp->h_relayversion);
  227. X    else if (com != printing)
  228. X        (void) fprintf(f, "%s: version %s; site %s.%s\n", t_relayversion, NEWSVERSION,
  229. X             systemid, MYDOMAIN);
  230. X
  231. X    if (hp->h_postversion)
  232. X        (void) fprintf(f, hform, t_postversion, hp->h_postversion);
  233. X    else if (com == making)
  234. X        (void) fprintf(f, "%s: version %s; site %s.%s\n", t_postversion, NEWSVERSION,
  235. X             systemid, MYDOMAIN);
  236. X
  237. X
  238. X    if (hp->h_from)
  239. X        (void) fprintf(f, hform, t_from, hp->h_from);
  240. X    else if(com == making) {
  241. X        if(s = getenv("NAME"))
  242. X            (void) fprintf(f, "%s: %s@%s.%s (%s)\n", t_from,
  243. X#if AUSAM
  244. X                pe.pw_strings[LNAME],
  245. X#else
  246. X                pp->pw_name,
  247. X#endif
  248. X                systemid, MYDOMAIN, s);
  249. X        else
  250. X            (void) fprintf(f,
  251. X#if AUSAM
  252. X                "%s: %s@%s.%s (%s %s)\n",
  253. X#else
  254. X                "%s: %s@%s.%s\n",
  255. X#endif
  256. X                t_from,
  257. X#if AUSAM
  258. X                pe.pw_strings[LNAME],
  259. X#else
  260. X                pp->pw_name,
  261. X#endif
  262. X                systemid, MYDOMAIN
  263. X#if AUSAM
  264. X                ,
  265. X                pe.pw_strings[FIRSTNAME],
  266. X                pe.pw_strings[LASTNAME]
  267. X#endif
  268. X            );
  269. X    }
  270. X
  271. X    if (hp->h_date)
  272. X        (void) fprintf(f, hform, t_date, hp->h_date);
  273. X    else if (com == making)
  274. X        (void) fprintf(f, hform, t_date, ttoa(now));
  275. X
  276. X    if (hp->h_newsgroups)
  277. X        (void) fprintf(f, hform, t_newsgroups, hp->h_newsgroups);
  278. X    else if (com == making)
  279. X        (void) fprintf(f, hform, t_newsgroups, DFLTGRP);
  280. X
  281. X    if (hp->h_subject)
  282. X        (void) fprintf(f, hform, t_subject, hp->h_subject);
  283. X    else if (com == making)
  284. X        error("No subject field.");
  285. X
  286. X    if (hp->h_messageid)
  287. X        (void) fprintf(f, hform, t_messageid, hp->h_messageid);
  288. X    else if (com == making)
  289. X        error("No messageid.");
  290. X
  291. X    if (hp->h_path && com == passing)
  292. X        (void) fprintf(f, "%s: %s!%s\n", t_path, systemid, hp->h_path);
  293. X    else if (hp->h_path)
  294. X        (void) fprintf(f, hform, t_path, hp->h_path);
  295. X    else if(com == making)
  296. X        (void) fprintf(f, "%s: %s!%s\n", t_path, systemid,
  297. X#if AUSAM
  298. X            pe.pw_strings[LNAME]
  299. X#else
  300. X            pp->pw_name
  301. X#endif
  302. X        );
  303. X
  304. X    /* optional */
  305. X
  306. X    if (hp->h_replyto)
  307. X        (void) fprintf(f, hform, t_replyto, hp->h_replyto);
  308. X
  309. X    if (hp->h_sender)
  310. X        (void) fprintf(f, hform, t_sender, hp->h_sender);
  311. X
  312. X    if (hp->h_followupto)
  313. X        (void) fprintf(f, hform, t_followupto, hp->h_followupto);
  314. X
  315. X    if (hp->h_datereceived && com == printing)
  316. X        (void) fprintf(f, hform, t_datereceived, hp->h_datereceived);
  317. X    else if (com != printing)
  318. X        (void) fprintf(f, hform, t_datereceived, ttoa(now));
  319. X
  320. X    if (hp->h_expires)
  321. X        (void) fprintf(f, hform, t_expires, hp->h_expires);
  322. X
  323. X    if (hp->h_references)
  324. X        (void) fprintf(f, hform, t_references, hp->h_references);
  325. X
  326. X    if (hp->h_control)
  327. X        (void) fprintf(f, hform, t_control, hp->h_control);
  328. X
  329. X    if (hp->h_distribution)
  330. X        (void) fprintf(f, hform, t_distribution, hp->h_distribution);
  331. X
  332. X    if (hp->h_organisation)
  333. X        (void) fprintf(f, hform, t_organization, hp->h_organisation);
  334. X    else if (com == making)
  335. X        (void) fprintf(f, hform, t_organization, (s = getenv("ORGANIZATION")) ?
  336. X            s : MYORG);
  337. X
  338. X    if (hp->h_lines)
  339. X        (void) fprintf(f, hform, t_lines, hp->h_lines);
  340. X
  341. X    if (hp->h_others)
  342. X        fputs(hp->h_others, f);
  343. X}
  344. X
  345. X
  346. X/*
  347. X * free all strings allocated to header
  348. X */
  349. Xfreehead(hp)
  350. Xregister header *hp;
  351. X{
  352. X    if (hp->h_relayversion)    
  353. X        free(hp->h_relayversion);
  354. X    if (hp->h_postversion)    
  355. X        free(hp->h_postversion);
  356. X    if (hp->h_from)        
  357. X        free(hp->h_from);
  358. X    if (hp->h_date)        
  359. X        free(hp->h_date);
  360. X    if (hp->h_newsgroups)    
  361. X        free(hp->h_newsgroups);
  362. X    if (hp->h_subject)    
  363. X        free(hp->h_subject);
  364. X    if (hp->h_messageid)    
  365. X        free(hp->h_messageid);
  366. X    if (hp->h_path)        
  367. X        free(hp->h_path);
  368. X    if (hp->h_replyto)    
  369. X        free(hp->h_replyto);
  370. X    if (hp->h_sender)    
  371. X        free(hp->h_sender);
  372. X    if (hp->h_followupto)    
  373. X        free(hp->h_followupto);
  374. X    if (hp->h_datereceived)    
  375. X        free(hp->h_datereceived);
  376. X    if (hp->h_expires)    
  377. X        free(hp->h_expires);
  378. X    if (hp->h_references)    
  379. X        free(hp->h_references);
  380. X    if (hp->h_control)    
  381. X        free(hp->h_control);
  382. X    if (hp->h_distribution)    
  383. X        free(hp->h_distribution);
  384. X    if (hp->h_organisation)    
  385. X        free(hp->h_organisation);
  386. X    if (hp->h_lines)        
  387. X        free(hp->h_lines);
  388. X    if (hp->h_others)    
  389. X        free(hp->h_others);
  390. X}
  391. X
  392. X
  393. X/*
  394. X * hfgets is like fgets, but deals with continuation lines.
  395. X * It also ensures that even if a line that is too long is
  396. X * received, the remainder of the line is thrown away
  397. X * instead of treated like a second line.
  398. X */
  399. Xchar *
  400. Xhfgets(buf, len, fp)
  401. Xchar *buf;
  402. Xint len;
  403. XFILE *fp;
  404. X{
  405. X    register int c;
  406. X    register char *cp, *tp;
  407. X
  408. X    if ((cp = fgets(buf, len, fp)) == NIL(char))
  409. X        return NIL(char);
  410. X
  411. X    if (*cp == '\n')
  412. X        return cp;
  413. X
  414. X    tp = cp + strlen(cp);
  415. X    if (tp[-1] != '\n') {
  416. X        /* Line too long - part read didn't fit into a newline */
  417. X        while ((c = getc(fp)) != '\n' && c != EOF)
  418. X            ;
  419. X    } else
  420. X        *--tp = '\0';    /* clobber newline */
  421. X
  422. X    while ((c = getc(fp)) == ' ' || c == '\t') {
  423. X        /* Continuation line. */
  424. X        while ((c = getc(fp)) == ' ' || c == '\t')
  425. X            ;
  426. X        if (tp - cp < len) {
  427. X            *tp++ = ' ';
  428. X            *tp++ = c;
  429. X        }
  430. X        while ((c = getc(fp)) != '\n' && c != EOF)
  431. X            if (tp - cp < len)
  432. X                *tp++ = c;
  433. X    }
  434. X    *tp++ = '\n';
  435. X    *tp++ = '\0';
  436. X    if (c != EOF)
  437. X        ungetc(c, fp);    /* push back first char of next header */
  438. X    return cp;
  439. X}
  440. X
  441. X
  442. X/*
  443. X * time to ascii
  444. X *    leave time in static var
  445. X */
  446. Xchar *
  447. Xttoa(t)
  448. Xlong t;
  449. X{
  450. X    static char buf[40];
  451. X    struct tm *tp;
  452. X    extern struct tm *localtime();
  453. X
  454. X    tp = localtime(&t);
  455. X    sprintf(buf, "%s, %d %s %d %02d:%02d:%02d %s", weekdays[tp->tm_wday],
  456. X         tp->tm_mday, months[tp->tm_mon], tp->tm_year, tp->tm_hour, tp->tm_min,
  457. X         tp->tm_sec, tzone);
  458. X    return buf;
  459. X
  460. X}
  461. X
  462. X
  463. X/*
  464. X * ascii to time
  465. X * return 0L on error
  466. X */
  467. Xlong
  468. Xatot(s)
  469. Xchar *s;
  470. X{
  471. X    char *argv[4];
  472. X    int day, year, hour, min, sec;
  473. X    char month[10], sday[10], stime[10], syear[10];
  474. X    extern long maketime();
  475. X
  476. X    if (sscanf(s, "%*s %d %*[ -] %9[^ -] %*[ -] %d %2d:%2d:%2d", &day, month,
  477. X         &year, &hour, &min, &sec) != 6)
  478. X        return 0L;
  479. X    sprintf(sday, "%d", day);
  480. X    sprintf(stime, "%d:%d:%d", hour, min, sec);
  481. X    sprintf(syear, "%d", 1900 + year);
  482. X    argv[0] = sday;
  483. X    argv[1] = month;
  484. X    argv[2] = stime;
  485. X    argv[3] = syear;
  486. X    return maketime(4, argv, STIMES);
  487. X}
  488. X
  489. X
  490. !
  491. echo 'rna/history.c':
  492. sed 's/^X//' >'rna/history.c' <<'!'
  493. X/*
  494. X * History file
  495. X * each line contains a message-id, install or expire time
  496. X * names of linked files
  497. X */
  498. X
  499. X#include "defs.h"
  500. X
  501. Xstatic char histname[]     = HISTORY;
  502. Xstatic char *histid;            /* messageid to save */
  503. Xstatic char *histline;            /* list of linked files */
  504. Xstatic long etime;            /* expire time */
  505. X
  506. Xtypedef enum stypes { 
  507. X    chk, delete } stype;
  508. X
  509. X/*
  510. X * do things with history file
  511. X * chk - see if id present
  512. X * delete - delete article with id
  513. X */
  514. Xstatic bool
  515. Xsearchhist(id, type)
  516. Xchar *id;
  517. Xstype type;
  518. X{
  519. X    register FILE    *f;
  520. X    register char *s, *name;
  521. X    register bool    found;
  522. X    char buf[BUFSIZ * 2];
  523. X
  524. X    extern char *newsdir;
  525. X
  526. X    f = fopenl(histname);
  527. X
  528. X    found = false;
  529. X    while (fgets(buf, sizeof(buf), f)) {
  530. X        if (s = strchr(buf, ' '))
  531. X            *s = '\0';
  532. X        else
  533. X            error("Bad format: %s", histname);
  534. X        if (CMP(buf, id) == 0) {
  535. X            found = true;
  536. X            break;
  537. X        }
  538. X    }
  539. X    if (found && type == delete) {
  540. X        if ((name = strchr(s + 1, ' ')) == NIL(char))
  541. X            error("Bad format: %s", histname);
  542. X        name++;
  543. X        while (name && (s = strpbrk(name, " \n"))) {
  544. X            *s = '\0';
  545. X            name = newstr3(newsdir, "/", name);
  546. X            remove(name);
  547. X            free(name);
  548. X            name = s + 1;
  549. X        }
  550. X    }
  551. X    fclose(f);
  552. X#if !AUSAM
  553. X    unlock(histname);
  554. X#endif
  555. X    return found;
  556. X}
  557. X
  558. X
  559. X/*
  560. X * delete files given id
  561. X */
  562. Xbool
  563. Xcancel(id)
  564. Xchar *id;
  565. X{
  566. X    bool searchhist();
  567. X
  568. X    return searchhist(id, delete);
  569. X}
  570. X
  571. X
  572. X/*
  573. X * check if article has been recieved
  574. X */
  575. Xbool
  576. Xchkhist(id)
  577. Xchar *id;
  578. X{
  579. X    bool searchhist();
  580. X
  581. X    return searchhist(id, chk);
  582. X}
  583. X
  584. X
  585. X/*
  586. X * scan history, clearing uflag list entry if id not seen
  587. X */
  588. Xscanhist(ulist, usize)
  589. Xchar **ulist;
  590. Xint usize;
  591. X{
  592. X    register FILE    *f;
  593. X    register char *s, **found;
  594. X    register int i;
  595. X    char *key[1];
  596. X    char buf[BUFSIZ * 2];
  597. X    bool         * seen;
  598. X
  599. X    extern char *newsdir;
  600. X
  601. X    seen = (bool * ) myalloc((int) sizeof(bool) * usize);
  602. X    memset((char *)seen, 0, (int) sizeof(bool) * usize);
  603. X
  604. X    f = fopenf(histname, "r");
  605. X    while (fgets(buf, sizeof(buf), f)) {
  606. X        if (s = strchr(buf, ' '))
  607. X            *s = '\0';
  608. X        else
  609. X            error("Bad format: %s", histname);
  610. X        key[0] = buf;
  611. X        found = (char **) bsearch((char *) key, (char *) ulist, (unsigned) usize,
  612. X             sizeof(char *), strpcmp);
  613. X        if (found)
  614. X            seen[found - ulist] = true;
  615. X    }
  616. X    fclose(f);
  617. X
  618. X    for (i = 0; i < usize; i++)
  619. X        if (!seen[i]) {
  620. X            free(ulist[i]);
  621. X            ulist[i] = NIL(char);
  622. X        }
  623. X    free((char *)seen);
  624. X}
  625. X
  626. X
  627. X/*
  628. X * open hist file, write id and time
  629. X */
  630. Xopenhist(hp)
  631. Xheader *hp;
  632. X{
  633. X
  634. X    histid = newstr(hp->h_messageid);
  635. X    if (hp->h_expires)
  636. X        etime = atot(hp->h_expires);
  637. X    else
  638. X        etime = 0L;
  639. X    histline = NIL(char);
  640. X}
  641. X
  642. X
  643. X/*
  644. X * write name of file article resides into history file
  645. X */
  646. Xwritehist(fname)
  647. Xchar *fname;
  648. X{
  649. X    histline = (histline ? catstr2(histline, " ", fname) : newstr(fname));
  650. X}
  651. X
  652. X
  653. X/*
  654. X * close history file
  655. X */
  656. Xclosehist()
  657. X{
  658. X    register FILE    *f;
  659. X    extern long now;
  660. X
  661. X    f = fopenl(histname);
  662. X    fseek(f, 0L, 2);
  663. X    (void) fprintf(f, "%s %s%ld %s\n", histid, etime ? "E" : "", etime ? etime :
  664. X        now, histline);
  665. X    fclose(f);
  666. X#if !AUSAM
  667. X    unlock(histname);
  668. X#endif
  669. X    free(histid); 
  670. X    free(histline);
  671. X}
  672. X
  673. X
  674. X/*
  675. X * remove a news item
  676. X * check owner first
  677. X */
  678. Xstatic
  679. Xremove(fname)
  680. Xchar *fname;
  681. X{
  682. X    header            h;
  683. X    FILE             * f;
  684. X    register char *s, *mname;
  685. X
  686. X#if AUSAM
  687. X    extern struct pwent pe;
  688. X#else
  689. X    extern struct passwd *pp;
  690. X#endif
  691. X    extern char systemid[];
  692. X    extern bool        su;
  693. X    extern bool        pflag;
  694. X
  695. X    if (!su && !pflag) {
  696. X        f = fopenf(fname, "r");
  697. X        gethead(f, &h);
  698. X        fclose(f);
  699. X        if (s = strchr(h.h_from, ' '))
  700. X            *s = '\0';
  701. X        mname = newstr5(
  702. X#if AUSAM
  703. X                pe.pw_strings[LNAME],
  704. X#else
  705. X                pp->pw_name,
  706. X#endif
  707. X            "@", systemid, ".", MYDOMAIN);
  708. X        if (CMP(mname, h.h_from) != 0)
  709. X            error("Can't cancel articles you didn't write.");
  710. X        free(mname);
  711. X    }
  712. X    if (unlink(fname) != 0)
  713. X        error("Couldn't unlink %s", fname);
  714. X
  715. X}
  716. !
  717. echo 'rna/lib/bsearch.c':
  718. sed 's/^X//' >'rna/lib/bsearch.c' <<'!'
  719. X/*LINTLIBRARY*/
  720. X/*
  721. X * Binary search algorithm, generalized from Knuth (6.2.1) Algorithm B.
  722. X *
  723. X * Written by J. S. Rugaber; rewritten by L. Rosler, Dept. 45175, August, 1981.
  724. X */
  725. X
  726. Xtypedef char *POINTER;
  727. X
  728. XPOINTER
  729. Xbsearch(key, base, nel, width, compar)
  730. XPOINTER    key;            /* Key to be located */
  731. XPOINTER    base;            /* Beginning of table */
  732. Xunsigned nel;            /* Number of elements in the table */
  733. Xunsigned width;            /* Width of an element (bytes) */
  734. Xint    (*compar)();        /* Comparison function */
  735. X{
  736. X    int two_width = width + width;
  737. X    POINTER last = base + width * (nel - 1); /* Last element in table */
  738. X
  739. X    while (last >= base) {
  740. X
  741. X        register POINTER p = base + width * ((last - base)/two_width);
  742. X        register int res = (*compar)(key, p);
  743. X
  744. X        if (res == 0)
  745. X            return (p);    /* Key found */
  746. X        if (res < 0)
  747. X            last = p - width;
  748. X        else
  749. X            base = p + width;
  750. X    }
  751. X    return ((POINTER) 0);        /* Key not found */
  752. X}
  753. !
  754. echo 'rna/lib/itoa.c':
  755. sed 's/^X//' >'rna/lib/itoa.c' <<'!'
  756. Xchar *
  757. Xitoa(i)
  758. Xint i;
  759. X{
  760. X    static char buf[30];
  761. X
  762. X    sprintf(buf, "%d", i);
  763. X    return buf;
  764. X}
  765. !
  766. echo 'rna/lib/makefile':
  767. sed 's/^X//' >'rna/lib/makefile' <<'!'
  768. X# makefile for Australian readnews library
  769. XRANLIB=ranlib
  770. X# workaround for System V make bug
  771. XSHELL = /bin/sh
  772. X
  773. Xlib.a: bsearch.o itoa.o memset.o strpbrk.o
  774. X    ar rv $@ bsearch.o itoa.o memset.o strpbrk.o
  775. X    $(RANLIB) $@
  776. Xclean:
  777. X    rm -f *.o *.a
  778. !
  779. echo 'rna/lib/memset.c':
  780. sed 's/^X//' >'rna/lib/memset.c' <<'!'
  781. X/*LINTLIBRARY*/
  782. X/*
  783. X * Set an array of n chars starting at sp to the character c.
  784. X * Return sp.
  785. X */
  786. Xchar *
  787. Xmemset(sp, c, n)
  788. Xregister char *sp, c;
  789. Xregister int n;
  790. X{
  791. X    register char *sp0 = sp;
  792. X
  793. X    while (--n >= 0)
  794. X        *sp++ = c;
  795. X    return (sp0);
  796. X}
  797. !
  798. echo 'rna/lib/strpbrk.c':
  799. sed 's/^X//' >'rna/lib/strpbrk.c' <<'!'
  800. X/*
  801. X * Return ptr to first occurance of any character from `brkset'
  802. X * in the character string `string'; NULL if none exists.
  803. X */
  804. X
  805. X#define    NULL    (char *) 0
  806. X
  807. Xchar *
  808. Xstrpbrk(string, brkset)
  809. Xregister char *string, *brkset;
  810. X{
  811. X    register char *p;
  812. X
  813. X    do {
  814. X        for(p=brkset; *p != '\0' && *p != *string; ++p)
  815. X            ;
  816. X        if(*p != '\0')
  817. X            return(string);
  818. X    }
  819. X    while(*string++);
  820. X    return(NULL);
  821. X}
  822. !
  823. echo 'rna/COVER.pd':
  824. sed 's/^X//' >'rna/COVER.pd' <<'!'
  825. XFrom decvax!mulga!michaelr:elecvax Mon Jul 16 04:25:19 1984
  826. XFrom: decvax!mulga!michaelr:elecvax
  827. XReceived: by decvax.UUCP (4.12/1.0)
  828. X    id AA25742; Mon, 16 Jul 84 03:56:35 edt
  829. XReceived: by mulga.OZ (4.3)
  830. X    id AA06943; Mon, 16 Jul 84 14:19:46 EST
  831. XTo: decvax!utcsstat!geoff:mulga
  832. XSubject: Re: your news re-write
  833. X
  834. XYes, my news re-write is in the public domain.
  835. XI couldn't sell it if I wanted to, since it was developed under an
  836. Xeducational license.
  837. X
  838. XPlease distribute it freely as you wish.
  839. X
  840. XMichael Rourke
  841. XUniversity of New South Wales, Kensington, N.S.W. 2033 AUSTRALIA
  842. XPhone:    +61 2 662 2781    Netaddr: {decvax,vax135,sfjec}!mulga!michaelr:elecvax
  843. X
  844. X
  845. !
  846. echo 'rna/makefile':
  847. sed 's/^X//' >'rna/makefile' <<'!'
  848. X# australian readnews makefile
  849. X# =()<NEWSARTS = @<NEWSARTS>@>()=
  850. XNEWSARTS = /usr/spool/news
  851. X# =()<NEWSBIN = @<NEWSBIN>@>()=
  852. XNEWSBIN = /usr/lib/newsbin
  853. X# =()<NEWSCTL = @<NEWSCTL>@>()=
  854. XNEWSCTL = /usr/lib/news
  855. X# workaround for System V make bug
  856. XSHELL = /bin/sh
  857. X
  858. XDEFINES=
  859. XCOPTS=-O
  860. XCFLAGS=$(COPTS) $(DEFINES)
  861. XINSTALL=:# install bigpdp
  862. XNFLAG =
  863. XLINT = lint -ha $(DEFINES)
  864. XI=/usr/include
  865. XC=/lib/libc.a
  866. XLIBS=lib/lib.a ../libcnews.a
  867. XBIN=/bin
  868. XBINDIR=$(BIN)
  869. X
  870. XCOMMANDS = postnews uurec readnews uusend expire postgroup
  871. XFILES = help
  872. XLCOMMANDS = postnews.lint uurec.lint readnews.lint uusend.lint expire.lint
  873. XPFILES = header.c postnews.c funcs.c active.c history.c maketime.c mtempnam.c
  874. XRFILES = header.c readnews.c funcs.c active.c newsrc.c history.c maketime.c
  875. XROFILES= header.o readnews.o funcs.o active.o newsrc.o history.o maketime.o
  876. XEFILES = expire.c funcs.c active.c
  877. X
  878. X.c.lint:
  879. X    $(LINT) $< > $@
  880. X
  881. X.c:
  882. X    $(CC) $(CFLAGS) $*.c $(NFLAG) -o $(@F)
  883. X    $(INSTALL) $* bin 711 $@
  884. X
  885. X.sh:
  886. X    $(INSTALL) -c $*.sh bin 755 $@
  887. X
  888. Xall: readnews checknews
  889. X    : warning, defs.h may not be right for your system
  890. X
  891. Xreadnews: $(ROFILES) defs.h $(LIBS)
  892. X    $(CC) $(CFLAGS) $(ROFILES) $(LIBS) -o $@
  893. X
  894. Xnewsinstall:
  895. X    : "install all control files; don't overwrite any!"
  896. X    -if test ! -r $(NEWSCTL)/news.help; then cp news.help $(NEWSCTL); fi
  897. X
  898. X# bininstall: make directories, install programs
  899. Xbininstall: install
  900. Xinstall: $(BINDIR)/readnews $(BINDIR)/checknews
  901. X$(BINDIR)/readnews: readnews
  902. X    cp readnews $(BINDIR)
  903. X    : "$(INSTALL) - readnews bin 711 $(BINDIR)/readnews $(BINDIR)/news"
  904. X$(BINDIR)/checknews:    checknews
  905. X    chmod +x checknews
  906. X    cp checknews $(BINDIR)
  907. X
  908. Xdefs.h:    at.h # $C
  909. X    touch defs.h
  910. X
  911. Xlint: $(LCOMMANDS)
  912. X
  913. Xhelp: $(NEWSCTL)/help
  914. X$(NEWSCTL)/help: news.help
  915. X    $(INSTALL) -c news.help news 644 $(NEWSCTL)/help
  916. X
  917. Xlib/lib.a:
  918. X    : if you do not have ranlib, you will need to edit lib/makefile
  919. X    (cd lib; make)
  920. X
  921. X$(ROFILES): defs.h
  922. X
  923. Xpostnews: $(BINDIR)/postnews
  924. X$(BINDIR)/postnews: $(PFILES) defs.h
  925. X    $(CC) $(CFLAGS) $(PFILES) -o postnews
  926. X    $(INSTALL) - postnews news 6711 $(BINDIR)/postnews
  927. X
  928. Xpostgroup: $(BINDIR)/postgroup
  929. X$(BINDIR)/postgroup: postgroup.sh
  930. X
  931. Xuurec: $(NEWSCTL)/uurec
  932. X$(NEWSCTL)/uurec: uurec.c defs.h
  933. X
  934. Xuusend: $(NEWSCTL)/uusend
  935. X$(NEWSCTL)/uusend: defs.h uusend.c
  936. X
  937. Xexpire: $(NEWSCTL)/expire
  938. X$(NEWSCTL)/expire: $(EFILES) defs.h
  939. X    $(CC) $(CFLAGS) $(EFILES) $(NFLAG) -o expire
  940. X    $(INSTALL) expire news 700 $(NEWSCTL)/expire
  941. X
  942. Xpostnews.lint: $(PFILES)
  943. X    $(LINT) $(PFILES) > postnews.lint
  944. X
  945. Xreadnews.lint: $(RFILES)
  946. X    $(LINT) $(RFILES) > readnews.lint
  947. X
  948. Xexpire.lint: $(EFILES)
  949. X    $(LINT) $(EFILES) > expire.lint
  950. X
  951. Xuurec.lint: uurec.c defs.h
  952. Xuusend.lint: uusend.c defs.h
  953. X
  954. X.FINISH:
  955. X    rm -s *.o
  956. Xclean:
  957. X    rm -f *.o core a.out readnews
  958. X    (cd lib ; make clean)
  959. !
  960. echo 'rna/maketime.c':
  961. sed 's/^X//' >'rna/maketime.c' <<'!'
  962. X/*
  963. X * long
  964. X * maketime(argc, argv, type)
  965. X *
  966. X * A standard routine to convert a future time (in English) to seconds.
  967. X * Arguments are order-independent (except for suffixes), and words
  968. X * may be shortened to a non-ambiguous abbreviation.
  969. X * As the time must be in the future, unspecified years, months and days default
  970. X * to the "next" year, month or day if necessary; otherwise the current
  971. X * month, day and hour are used.
  972. X *
  973. X * type is either TIMES in which days, times are recognised, or just DAYS.
  974. X *
  975. X * Tries hard to give meaningful messages, and make sure the user
  976. X * gets the time she/he wanted!
  977. X *
  978. X * Return is in seconds or 0 if error.
  979. X * Error messages to stderr.
  980. X *
  981. X * Michael Rourke (UNSW) Christmas 1982
  982. X *
  983. X * Syntax:
  984. X *
  985. X *    timespec ::= { time | day | month | year } .
  986. X *    
  987. X *    time ::= [ hour [ ":" min [ ":" second ] ] ] [ timemodifier ] .
  988. X *    
  989. X *    timemodifier ::= "am" | "pm" | "noon" | "midday" | "midnight" | "now" .
  990. X *    
  991. X *    day ::= ( dayofweek [ "week" ] ) | number .
  992. X *    
  993. X *    dayofweek ::= "sunday" | "monday" | "tuesday" | "wednesday" |
  994. X *              "thursday" | "friday" | "saturday" | "tomorrow" |
  995. X *              "today" .
  996. X *    
  997. X *    month ::= "january" | "february" | "march" | "april" | "may" | "june" |
  998. X *          "july" | "august" | "september" | "october" | "november" |
  999. X *          "december" .
  1000. X *    
  1001. X *    year ::= "19" number .
  1002. X *
  1003. X */
  1004. X
  1005. X#include "defs.h"
  1006. X
  1007. X#define    NOW     -1
  1008. X
  1009. Xstatic timemod(), noonmid(), daymod(), weekday(), smonth();
  1010. X
  1011. Xstatic struct slist {
  1012. X    char *s_name;
  1013. X    int (*s_action)();
  1014. X    char s_val;
  1015. X    char s_type;
  1016. X} slist[] = 
  1017. X{
  1018. X    { "am",         timemod,     0,     TIMES,      },
  1019. X    { "pm",         timemod,     12,     TIMES,      },
  1020. X    { "noon",         noonmid,     12,     TIMES,      },
  1021. X    { "midday",     noonmid,     12,     TIMES,      },
  1022. X    { "midnight",     noonmid,     0,     TIMES,      },
  1023. X    { "now",         noonmid,     NOW,     TIMES,      },
  1024. X    { "week",         daymod,         0,     DAYS,      },
  1025. X    { "sunday",     weekday,     0,     DAYS,      },
  1026. X    { "monday",     weekday,     1,     DAYS,      },
  1027. X    { "tuesday",     weekday,     2,     DAYS,      },
  1028. X    { "wednesday",     weekday,     3,     DAYS,      },
  1029. X    { "thursday",     weekday,     4,     DAYS,      },
  1030. X    { "friday",     weekday,     5,     DAYS,      },
  1031. X    { "saturday",     weekday,     6,     DAYS,      },
  1032. X    { "tomorrow",     weekday,     7,     DAYS,      },
  1033. X    { "today",     weekday,     8,     DAYS,      },
  1034. X    { "january",     smonth,         0,     DAYS,      },
  1035. X    { "february",     smonth,         1,     DAYS,      },
  1036. X    { "march",     smonth,         2,     DAYS,      },
  1037. X    { "april",     smonth,         3,     DAYS,      },
  1038. X    { "may",         smonth,         4,     DAYS,      },
  1039. X    { "june",         smonth,         5,     DAYS,      },
  1040. X    { "july",         smonth,         6,     DAYS,      },
  1041. X    { "august",     smonth,         7,     DAYS,      },
  1042. X    { "september",     smonth,         8,     DAYS,      },
  1043. X    { "october",     smonth,         9,     DAYS,      },
  1044. X    { "november",     smonth,         10,     DAYS,      },
  1045. X    { "december",     smonth,         11,     DAYS,      },
  1046. X    { "",         0,         0,     0,      }
  1047. X};
  1048. X
  1049. X
  1050. Xstatic char daysinmonth[12] = 
  1051. X{
  1052. X    31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31
  1053. X};
  1054. X
  1055. X
  1056. Xstatic int hour, minute, second, day, year, dayofweek, month;
  1057. Xstatic int settime, setday, setyear, setdayofweek, setmonth;
  1058. Xstatic int setweek, err, setmod;
  1059. Xstatic char *curarg;
  1060. Xstatic struct tm *tim;
  1061. Xstatic int gtype;        /* global `type' arg */
  1062. Xstatic short silent;
  1063. X
  1064. Xlong
  1065. Xmaketime(argc, argv, type)
  1066. Xint argc;
  1067. Xchar **argv;
  1068. Xint type;
  1069. X{
  1070. X    struct tm *localtime();
  1071. X    long time(), construct(), now, then;
  1072. X
  1073. X    if (type == STIMES)
  1074. X        type = TIMES, silent = 1;
  1075. X    else
  1076. X        silent = 0;
  1077. X    gtype = type;
  1078. X    now = time((long *) 0);
  1079. X    tim = localtime(&now);
  1080. X
  1081. X    /*
  1082. X     * set defaults 
  1083. X     */
  1084. X    hour = tim->tm_hour;
  1085. X    minute = tim->tm_min;
  1086. X    second = tim->tm_sec;
  1087. X    day = tim->tm_mday;
  1088. X    year = tim->tm_year + 1900;
  1089. X    dayofweek = tim->tm_wday;
  1090. X    month = tim->tm_mon;
  1091. X
  1092. X    settime = setday = setyear = setdayofweek = setmonth = 0;
  1093. X    setweek = err = setmod = 0;
  1094. X
  1095. X    while (argc--)
  1096. X        timearg(curarg = *argv++);
  1097. X    if (err)
  1098. X        return (long) 0;
  1099. X
  1100. X    checktime();
  1101. X    if (err)
  1102. X        return (long) 0;
  1103. X
  1104. X    then = construct();
  1105. X    /*
  1106. X    if(now > then)
  1107. X    {
  1108. X        error("Time specified has passed.");
  1109. X        return (long) 0;
  1110. X    }
  1111. X*/
  1112. X    return then;
  1113. X}
  1114. X
  1115. X
  1116. Xstatic
  1117. Xtimearg(s)
  1118. Xchar *s;
  1119. X{
  1120. X    lower(s);
  1121. X    if (isdigit(*s))
  1122. X        numbers(s);
  1123. X    else
  1124. X        words(s);
  1125. X}
  1126. X
  1127. X
  1128. Xstatic
  1129. Xlower(s)
  1130. Xregister char *s;
  1131. X{
  1132. X    while (*s) {
  1133. X        *s = tolower(*s);
  1134. X        s++;
  1135. X    }
  1136. X}
  1137. X
  1138. X
  1139. Xstatic
  1140. Xnumbers(s)
  1141. Xregister char *s;
  1142. X{
  1143. X    register int val;
  1144. X
  1145. X    val = 0;
  1146. X    while (isdigit(*s))
  1147. X        val = val * 10 + *s++ - '0';
  1148. X    if (val > 1900)
  1149. X        if (setyear++)
  1150. X            reperror("year");
  1151. X        else
  1152. X            year = val;
  1153. X    else if (*s == '\0')
  1154. X        if (setday++)
  1155. X            reperror("day");
  1156. X        else
  1157. X            day = val;
  1158. X    else if (settime++)
  1159. X        reperror("time");
  1160. X    else
  1161. X     {
  1162. X        hour = val;
  1163. X        if (*s == ':') {
  1164. X            s++;
  1165. X            val = 0;
  1166. X            while (isdigit(*s))
  1167. X                val = val * 10 + *s++ - '0';
  1168. X            minute = val;
  1169. X            if (*s == ':') {
  1170. X                s++;
  1171. X                val = 0;
  1172. X                while (isdigit(*s))
  1173. X                    val = val * 10 + *s++ - '0';
  1174. X                second = val;
  1175. X            } else
  1176. X                second = 0;
  1177. X        } else
  1178. X            minute = second = 0;
  1179. X    }
  1180. X    if (*s)
  1181. X        words(curarg = s);
  1182. X}
  1183. X
  1184. X
  1185. Xstatic
  1186. Xreperror(s)
  1187. Xchar *s;
  1188. X{
  1189. X    error("Repeated %s argument: \"%s\"", s, curarg);
  1190. X}
  1191. X
  1192. X
  1193. X/* VARARGS1 */
  1194. Xstatic
  1195. Xerror(s, a1, a2, a3, a4)
  1196. Xchar *s;
  1197. Xint a1, a2, a3, a4;
  1198. X{
  1199. X    err++;
  1200. X    if (silent)
  1201. X        return;
  1202. X    (void) fprintf(stderr, "Error in time specification: ");
  1203. X    (void) fprintf(stderr, s, a1, a2, a3, a4);
  1204. X    (void) fprintf(stderr, "\n");
  1205. X}
  1206. X
  1207. X
  1208. Xstatic
  1209. Xwords(s)
  1210. Xchar *s;
  1211. X{
  1212. X    register struct slist *sp, *found;
  1213. X    register int size;
  1214. X    register char *wstart;
  1215. X
  1216. X    sp = slist;
  1217. X    wstart = s;
  1218. X    size = 0;
  1219. X    while (*s && !isdigit(*s))
  1220. X        size++, s++;
  1221. X    found = (struct slist *) 0;
  1222. X    while (*(sp->s_name)) {
  1223. X        if (sp->s_type <= gtype && CMPN(sp->s_name, wstart, size) ==
  1224. X            0)
  1225. X            if (!found) {
  1226. X                found = sp;
  1227. X                if (strlen(sp->s_name) == size)
  1228. X                    break;    /* otherwise an abbreviation */
  1229. X            }
  1230. X            else
  1231. X             {
  1232. X                error("Ambiguous abbreviation: \"%.*s\"", size,
  1233. X                     wstart);
  1234. X                return;
  1235. X            }
  1236. X        sp++;
  1237. X    }
  1238. X    if (found)
  1239. X        (*(found->s_action))(found->s_val);
  1240. X    else
  1241. X        error("Unknown word: \"%.*s\"", size, wstart);
  1242. X    if (*s)
  1243. X        numbers(curarg = s);
  1244. X}
  1245. X
  1246. X
  1247. Xstatic
  1248. Xtimemod(val)
  1249. Xint val;
  1250. X{
  1251. X    if (!settime)
  1252. X        error("Can only use \"am\" or \"pm\" after a time.");
  1253. X    else if (setmod++)
  1254. X        reperror("time modifier");
  1255. X    else if (hour < 12)
  1256. X        hour += val;
  1257. X    else if (hour > 12)
  1258. X        error("Can't use \"am\" or \"pm\" with 24 hour clock.");
  1259. X    else if (val == 0) /* am && hour == 12 */
  1260. X        hour = 0;    /* 12am correction */
  1261. X}
  1262. X
  1263. X
  1264. Xstatic
  1265. Xnoonmid(val)
  1266. Xint val;
  1267. X{
  1268. X    if (val < 0)    /* NOW */ {
  1269. X        if (settime++)
  1270. X            reperror("time");
  1271. X        /* let defaults work */
  1272. X    } else if (setmod++) /* noon, midnight */
  1273. X        reperror("time modifier");
  1274. X    else
  1275. X     {
  1276. X        if (!settime)
  1277. X            settime++;
  1278. X        else if (hour != 12 || minute != 0 || second != 0)
  1279. X            error("Illegal time: %02d:%02d:%02d %s", hour, minute,
  1280. X                 second, curarg);
  1281. X        hour = val;
  1282. X        minute = second = 0;
  1283. X    }
  1284. X}
  1285. X
  1286. X
  1287. Xstatic
  1288. Xdaymod()
  1289. X{
  1290. X    if (setweek++)
  1291. X        reperror("\b");
  1292. X    else if (!setdayofweek)
  1293. X        error("Can only use \"week\" after a weekday name.");
  1294. X    else
  1295. X        dayofweek += 7;
  1296. X}
  1297. X
  1298. X
  1299. Xstatic
  1300. Xweekday(val)
  1301. Xint val;
  1302. X{
  1303. X    if (setday++)
  1304. X        reperror("day");
  1305. X    else
  1306. X     {
  1307. X        setdayofweek++;
  1308. X        if (val < 7) {
  1309. X            dayofweek = val - dayofweek;    /* now a displacement */
  1310. X            if (dayofweek <= 0)
  1311. X                dayofweek += 7;
  1312. X        } else if (val == 7) /* tomorrow */
  1313. X            dayofweek = 1;
  1314. X        else    /* today */
  1315. X            dayofweek = 0;
  1316. X    }
  1317. X}
  1318. X
  1319. X
  1320. Xstatic
  1321. Xsmonth(val)
  1322. Xint val;
  1323. X{
  1324. X    if (setmonth++)
  1325. X        reperror("day of month");
  1326. X    else
  1327. X        month = val;
  1328. X}
  1329. X
  1330. X
  1331. Xstatic
  1332. Xchecktime()
  1333. X{
  1334. X    register int dim;
  1335. X
  1336. X    if (gtype == DAYS && settime)
  1337. X        error("Times are not accepted.");
  1338. X    if (year < 1983 || year > 2038)
  1339. X        error("Year out of range.");
  1340. X    if (hour > 23 || minute > 59 || second > 59)
  1341. X        error("Illegal time: %02d:%02d:%02d", hour, minute, second);
  1342. X    if (!setdayofweek) {
  1343. X        dim = daysinmonth[month] + (month == 1 ? leapyear(year) : 0);
  1344. X        if (day > dim)
  1345. X            error("Month day out of range. (> %d)", dim);
  1346. X    }
  1347. X    if (setdayofweek && (setmonth || setyear))
  1348. X        error("Can't specify a weekday as well as a month or year.");
  1349. X}
  1350. X
  1351. X
  1352. Xstatic
  1353. Xleapyear(y)
  1354. Xint y;
  1355. X{
  1356. X    return ((y % 4) == 0 && (y % 100) != 0) || (y % 400 == 0);
  1357. X}
  1358. X
  1359. X
  1360. Xstatic long 
  1361. Xconstruct()
  1362. X{
  1363. X    register int i, days;
  1364. X
  1365. X    adjust();
  1366. X    days = DAYSTO1983;
  1367. X    for (i = 1983; i < year; i++)
  1368. X        days += 365 + leapyear(i);
  1369. X    for (i = 0; i < month; i++)
  1370. X        days += daysinmonth[i] + (i == 1 ? leapyear(year) : 0);
  1371. X    days += day - 1;    /* days since 1 Jan 1970 */
  1372. X    if (setdayofweek)
  1373. X        days += dayofweek;
  1374. X    return days * SECINDAY + hour * SECINHOUR + minute * SECINMIN + second;
  1375. X}
  1376. X
  1377. X
  1378. Xstatic
  1379. Xadjust()
  1380. X{
  1381. X    register int dim;
  1382. X
  1383. X    /*
  1384. X     * make sure time defaults to the future
  1385. X     */
  1386. X    if (setdayofweek || setyear || month > tim->tm_mon)
  1387. X        return;
  1388. X    if (month < tim->tm_mon) {
  1389. X        year++;
  1390. X        return;
  1391. X    }
  1392. X    /*
  1393. X     * month == tim->tm_mon
  1394. X     */
  1395. X    if (day > tim->tm_mday)
  1396. X        return;
  1397. X    if (day < tim->tm_mday) {
  1398. X        if (setmonth || ++month / 12)
  1399. X            year++, month %= 12;
  1400. X        return;
  1401. X    }
  1402. X    /*
  1403. X     * month == tim->tm_mon && day == tim->tm_mday
  1404. X     */
  1405. X    if ((long)(hour*SECINHOUR + minute*SECINMIN + second) <
  1406. X        (long)(tim->tm_hour*SECINHOUR + tim->tm_min*SECINMIN + tim->tm_sec)) {
  1407. X        dim = daysinmonth[month] + (month == 1? leapyear(month): 0);
  1408. X        if (setday || ++day / dim) {
  1409. X            if (setmonth || ++month / 12)
  1410. X                year++, month %= 12;
  1411. X            day %= dim;
  1412. X        }
  1413. X        return;
  1414. X    }
  1415. X}
  1416. X
  1417. X
  1418. !
  1419. echo 'rna/news.help':
  1420. sed 's/^X//' >'rna/news.help' <<'!'
  1421. XCommands to read news:
  1422. X
  1423. XRETURN        either print current article or go to next article and print header
  1424. X
  1425. X.        print current article    + or n      go to next article
  1426. X-         go to previous article    s [file]  save current article
  1427. Xh        print current header    H      print current header in full
  1428. Xu        unsubscribe from followups    U      unsubscribe from current group
  1429. Xq or EOT    quit            x      exit (as if no articles seen)
  1430. X<number>    go to article <number>    !cmd      shell escape
  1431. XN [newsgrp] go to next newsgroup    DEL      break to command level
  1432. X
  1433. XCommands to post/reply/cancel news:
  1434. X
  1435. Xc        cancel article         r      reply to sender (by mail)
  1436. Xf        post a followup article    p      post new article
  1437. Xm person    mail to person
  1438. X
  1439. XCommands when posting/replying (p, r, f and m commands):
  1440. X
  1441. X. or EOT    terminate article        .e      edit article
  1442. X.!cmd        shell escape        .i      interpolate current item
  1443. XDEL        abort posting/mailing
  1444. !
  1445. echo 'rna/newsrc.c':
  1446. sed 's/^X//' >'rna/newsrc.c' <<'!'
  1447. X/*
  1448. X * newsrc file handling
  1449. X */
  1450. X
  1451. X#include "defs.h"
  1452. X
  1453. Xstatic char nrcname[]     = NEWSRC;
  1454. X
  1455. Xstatic char *rcname;        /* full pathname of .newsrc */
  1456. Xnewsrc *rc;            /* internal .newsrc */
  1457. Xchar *rcgrps;            /* subscription from .newsrc */
  1458. Xstatic newsrc *lastrc;        /* last newsrc struct in list */
  1459. Xstatic int rclineno;        /* current lineno in .newsrc */
  1460. Xstatic bool sortrc;        /* if we should sort on output */
  1461. X
  1462. Xstatic newsrc *findnewsrc();
  1463. X
  1464. Xreadnewsrc()
  1465. X{
  1466. X    register FILE *f;
  1467. X    static char option[] = "options";
  1468. X    char word[BUFSIZ], rest[BUFSIZ];
  1469. X    extern char *getenv();
  1470. X
  1471. X    if ((rcname = getenv("HOME")) == NULL)
  1472. X        error("No $HOME in environment.");
  1473. X    rcname = newstr3(rcname, "/", nrcname);
  1474. X    if ((f = fopen(rcname, "r")) == NULL)
  1475. X        return;
  1476. X
  1477. X    rclineno = 0;
  1478. X    while (getline(f, word, rest))
  1479. X        if (CMP(word, option) == 0)
  1480. X            dooptions(rest);
  1481. X        else
  1482. X            dorcline(word, rest);
  1483. X    (void) fclose(f);
  1484. X}
  1485. X
  1486. X/*
  1487. X * Read a line from f, put first word into w and the rest into r.
  1488. X * Discard trailing newline instead of storing it.
  1489. X * This is a poor design, as w & r are unchecked for overrun.
  1490. X */
  1491. Xstatic
  1492. Xgetline(f, w, r)
  1493. Xregister FILE *f;
  1494. Xchar *w, *r;
  1495. X{
  1496. X    register int c;
  1497. X    register char *s;
  1498. X
  1499. X    rclineno++;
  1500. X    s = w;
  1501. X    while ((c = getc(f)) != EOF && c != ' ' && c != '\t')
  1502. X        *s++ = c;            /* stash first word */
  1503. X    *s = '\0';
  1504. X
  1505. X    if (c != EOF) {
  1506. X        s = r;
  1507. X        while ((c = getc(f)) != EOF && c != '\n')
  1508. X            *s++ = c;        /* stash the rest */
  1509. X        *s = '\0';
  1510. X    }
  1511. X
  1512. X    if (c != '\n' && c != EOF)
  1513. X        error("Bad format: %s line %d: %s", rcname, rclineno, w);
  1514. X
  1515. X    return c != EOF;
  1516. X}
  1517. X
  1518. X/*
  1519. X * Parse s into words and simulate command line arguments with them.
  1520. X */
  1521. Xstatic
  1522. Xdooptions(s)
  1523. Xchar *s;
  1524. X{
  1525. X    register char *cp;
  1526. X    register int argc;
  1527. X    register char **argv;
  1528. X
  1529. X    cp = s;
  1530. X    while (isspace(*cp))
  1531. X        cp++;
  1532. X    if (!*cp)
  1533. X        return;
  1534. X
  1535. X    argc = 1;
  1536. X    argv = (char **) myalloc(sizeof(char *));
  1537. X    argv[argc - 1] = cp;
  1538. X    while (*cp && (cp = strpbrk(cp, " \t")) != NULL) {
  1539. X        while (*cp == ' ' || *cp == '\t')
  1540. X            *cp++ = '\0';
  1541. X        if (*cp) {
  1542. X            argc++;
  1543. X            argv = (char **) myrealloc((char *) argv,
  1544. X                argc * (int)sizeof(char *));
  1545. X            argv[argc - 1] = cp;
  1546. X        }
  1547. X    }
  1548. X    if (options(argc, argv, false))
  1549. X        error("Bad options: %s line %d: %s", rcname, rclineno, s);
  1550. X    free((char *) argv);
  1551. X}
  1552. X
  1553. X/*
  1554. X * Parse w & r together as a .newsrc newsgroup line.
  1555. X */
  1556. Xstatic
  1557. Xdorcline(w, r)
  1558. Xchar *w, *r;
  1559. X{
  1560. X    register char lastw;
  1561. X    register int len;
  1562. X    register newsrc    *np;
  1563. X
  1564. X    len = strlen(w);
  1565. X    lastw = w[len - 1];            /* save presumed colon or bang */
  1566. X    w[len - 1] = '\0';            /* nuke presumed colon */
  1567. X    while (*r == ' ' || *r == '\t')
  1568. X        r++;                /* skip extra whitespace */
  1569. X
  1570. X    /* kludges, hacks, etc. for compatibility with other readers */
  1571. X    if (strncmp(r, "1-", sizeof "1-"-1) == 0)
  1572. X        r += sizeof "1-"-1;        /* skip usual `1-' */
  1573. X    if (*r == '\0')                /* rn's: `news.trash: ' */
  1574. X        r = "0";            /* fake a zero */
  1575. X
  1576. X    if (lastw != ':' && lastw != NEGCHAR || !isdigit(*r))
  1577. X        error("Bad line: %s line %d: %s", rcname, rclineno, w);
  1578. X
  1579. X    np = NEW(newsrc);
  1580. X    np->n_subscribe = (bool) (lastw == ':');    /* colon or bang? */
  1581. X    np->n_next = NIL(newsrc);
  1582. X    np->n_last = atoi(r);            /* stash first number only */
  1583. X    np->n_name = newstr(w);            /* stash n.g. name */
  1584. X
  1585. X    if (rc == 0)
  1586. X        rc = np;
  1587. X    else
  1588. X        lastrc->n_next = np;
  1589. X    lastrc = np;
  1590. X}
  1591. X
  1592. X/*
  1593. X * for every group in active list, which belongs to the specified subscription
  1594. X * list, and has messages to be read, call func
  1595. X * if no mention in newsrc file, make new entry
  1596. X */
  1597. Xapply(alist, group, func, dolast)
  1598. Xactive *alist;
  1599. Xchar *group;
  1600. Xapplycom (*func)();
  1601. Xbool dolast;
  1602. X{
  1603. X    register active *ap;
  1604. X    register newsrc *np;
  1605. X    register applycom act;
  1606. X    register bool donesome;
  1607. X
  1608. X    donesome = false;
  1609. X    do {
  1610. X        act = stop;
  1611. X        for (ap = alist; ap; ap = ap->a_next) {
  1612. X            if (ap->a_seq == 0 || ap->a_low > ap->a_seq)
  1613. X                continue;    /* empty group */
  1614. X            if (!ngmatch(ap->a_name, group))
  1615. X                continue;
  1616. X            if ((np = findnewsrc(ap->a_name)) == NIL(newsrc)) {
  1617. X                np = NEW(newsrc);
  1618. X                np->n_name = newstr(ap->a_name);
  1619. X                np->n_next = NIL(newsrc);
  1620. X                np->n_last = 0;
  1621. X                np->n_subscribe = true;
  1622. X                if (!rc)
  1623. X                    rc = np;
  1624. X                else
  1625. X                    lastrc->n_next = np;
  1626. X                lastrc = np;
  1627. X            }
  1628. X            if (!np->n_subscribe)
  1629. X                continue;
  1630. X            /*
  1631. X             * if we haven't read any news for a while (or at all),
  1632. X             * or somehow seq got smaller (active corrupted?),
  1633. X             * set last read to oldest available article
  1634. X             */
  1635. X            if (ap->a_low - 1 > np->n_last || ap->a_seq < np->n_last)
  1636. X                np->n_last = ap->a_low - 1;
  1637. X            while (np->n_last < ap->a_seq) {
  1638. X                donesome = true;
  1639. X                switch (act = (*func)(ap, np, false, false)) {
  1640. X                case stop:        
  1641. X                    return;
  1642. X                case next:        
  1643. X                    continue;
  1644. X                case nextgroup:        
  1645. X                    break;
  1646. X                case searchgroup:    
  1647. X                    break;
  1648. X                }
  1649. X                break;
  1650. X            }                /* while */
  1651. X            if (act == searchgroup)
  1652. X                break;
  1653. X        }                    /* for */
  1654. X        if (act != searchgroup && dolast && donesome)
  1655. X            act = (*func)(NIL(active), NIL(newsrc), true, false);
  1656. X    } while (act == searchgroup);
  1657. X}
  1658. X
  1659. X/*
  1660. X * find if a newrc entry exists,
  1661. X * taking advantange of the fact that requests should be
  1662. X * in the same order
  1663. X *
  1664. X * detect when the newsrc gets out of order
  1665. X * so it can be sorted at the end of the session
  1666. X */
  1667. Xstatic newsrc *
  1668. Xfindnewsrc(name)
  1669. Xregister char *name;
  1670. X{
  1671. X    register newsrc *np, *start;
  1672. X    register bool found;
  1673. X    static newsrc *nextp;
  1674. X
  1675. X    if (!rc)
  1676. X        return NIL(newsrc);
  1677. X
  1678. X    found = false;
  1679. X    np = nextp ? nextp : rc;
  1680. X    nextp = start = np;
  1681. X    do {
  1682. X        if (CMP(np->n_name, name) == 0) {
  1683. X            found = true;
  1684. X            break;
  1685. X        }
  1686. X        np = np->n_next;
  1687. X        if (!np)
  1688. X            np = rc;
  1689. X    } while (np != nextp);
  1690. X
  1691. X    if (!found)
  1692. X        return NIL(newsrc);
  1693. X    nextp = np->n_next;
  1694. X    if (np != start)
  1695. X        sortrc = true;
  1696. X    return np;
  1697. X}
  1698. X
  1699. X/*
  1700. X * rewrite the newsrc file
  1701. X */
  1702. Xwritenewsrc(alist)
  1703. Xactive *alist;
  1704. X{
  1705. X    register FILE *f;
  1706. X    register active    *ap;
  1707. X    register newsrc    *np;
  1708. X    register int i;
  1709. X    extern char **uflag;
  1710. X    extern int usize;
  1711. X
  1712. X    if (!rc && !uflag && (!rcgrps || !*rcgrps))
  1713. X        return;
  1714. X
  1715. X    signal(SIGINT, SIG_IGN);
  1716. X    signal(SIGQUIT, SIG_IGN);
  1717. X
  1718. X    f = fopenf(rcname, "w");
  1719. X    if (rcgrps && *rcgrps)
  1720. X        (void) fprintf(f, "options -n %s\n", rcgrps);
  1721. X    if (uflag) {
  1722. X        scanhist(uflag, usize);        /* forget id's not in history */
  1723. X        for (i = 0; i < usize; i++)    /* print whats left */
  1724. X            if (uflag[i])
  1725. X                (void) fprintf(f, "options -u %s\n", uflag[i]);
  1726. X    }
  1727. X    if (sortrc) {
  1728. X        /*
  1729. X         * sort newsrc so next time we use it,
  1730. X         * history/newsrc comparisons will be faster
  1731. X         */
  1732. X        for (ap = alist; ap; ap = ap->a_next)
  1733. X            if (np = findnewsrc(ap->a_name))
  1734. X                writengline(f, np);
  1735. X    } else
  1736. X        for (np = rc; np; np = np->n_next)
  1737. X            writengline(f, np);
  1738. X    (void) fclose(f);
  1739. X}
  1740. X
  1741. Xstatic
  1742. Xwritengline(f, np)        /* write .newsrc n.g. line in normal form on f */
  1743. Xregister newsrc *np;
  1744. X{
  1745. X    (void) fprintf(f, "%s%c 1-%d\n", np->n_name,
  1746. X        (np->n_subscribe? ':': NEGCHAR), np->n_last);
  1747. X}
  1748. !
  1749. echo 'rna/readnews.1':
  1750. sed 's/^X//' >'rna/readnews.1' <<'!'
  1751. X.TH READNEWS 1
  1752. X.SH NAME
  1753. Xnews, readnews \- read news articles
  1754. X.SH SYNOPSIS
  1755. X.B readnews
  1756. X.RB [ -n
  1757. Xnewsgroups]
  1758. X.RB [ -i ]
  1759. X.RB [ -clpC ]
  1760. X.RB [ -s [ -+?
  1761. X.RI [ group ]]]
  1762. X.RB [ -u
  1763. Xmessageid]
  1764. X.SH DESCRIPTION
  1765. X.I Readnews
  1766. Xwithout arguments enters command mode,
  1767. Xwhich allows printing of unread articles.
  1768. XThis is the normal way of using
  1769. X.IR readnews .
  1770. X.P
  1771. X.I Readnews
  1772. Xmaintains a
  1773. X.I .newsrc
  1774. Xfile in the user's home directory that specifies
  1775. Xall news articles already read.
  1776. XIt is updated at the end of each reading session.
  1777. X.P
  1778. XSome useful functions are available which don't use command mode.
  1779. XThe flags for these are:
  1780. X.TP
  1781. X.B -c
  1782. XCheck if there is news, and if so print `You have news.'.
  1783. XA line `readnews -c' is usually placed in the system
  1784. X.I .profile
  1785. X.RB (  /etc/profile ).
  1786. X.TP
  1787. X.B -C
  1788. XCheck if there is news, and print the groups and number of
  1789. Xarticles in each group to be read.
  1790. X.TP
  1791. X.B -l
  1792. XList the titles of available news articles.
  1793. X.TP
  1794. X.B -p
  1795. XPrint all articles on standard output,
  1796. Xand update
  1797. X.IR newsrc .
  1798. X.TP
  1799. X.B -s
  1800. XPrint the newsgroup subscription list.
  1801. X.TP
  1802. X.BI -s+ " group"
  1803. XAdd
  1804. X.I group
  1805. Xto the subscription list.
  1806. X.TP
  1807. X.BI -s- " group"
  1808. XSubtract
  1809. X.I group
  1810. Xfrom the subscription list.
  1811. X.TP
  1812. X.B -s?
  1813. XList currently active newsgroups.
  1814. X.P
  1815. XThe remaining flags determine article selection,
  1816. Xand may also appear in the
  1817. X.I .newsrc
  1818. Xfile.
  1819. XOptions may be specified in the
  1820. X.I .newsrc
  1821. Xfile by entering lines prefixed with the word `options',
  1822. Xfollowed by the options arguments.
  1823. XThis is most useful with the
  1824. X.B -n
  1825. Xflag, specifying the usual groups one wishes to subscribe to.
  1826. X.TP
  1827. X\fB-n \fInewsgroups\fR
  1828. XSelect all articles belonging to
  1829. X.IR newsgroups .
  1830. X.I newsgroups
  1831. Xis a comma separated list of newsgroup names.
  1832. XThe character `!' may be used to exclude certain groups,
  1833. Xand the word `all' can be used to match any group.
  1834. Xe.g. `-n all,!net.jokes'
  1835. X.TP
  1836. X.B -i
  1837. XIgnore
  1838. X.I .newsrc
  1839. Xfile. It is not read or updated.
  1840. XThis allows selection of articles that have already been read.
  1841. X.TP
  1842. X\fB-u \fImessageid\fR
  1843. XUnsubscribe to followup articles referring to
  1844. X.IR messageid .
  1845. X(This flag is usually only placed in the
  1846. X.I .newsrc
  1847. Xfile as a result of the `u' command.)
  1848. X.SH COMMANDS
  1849. XThis section details the commands available when
  1850. X.I readnews
  1851. Xis in command mode (no
  1852. X.B -clpsC
  1853. Xarguments).
  1854. XThe simplest way of using this mode, is to enter RETURN after every
  1855. Xprompt.
  1856. XThis will present to the user, a short heading for an article, then a prompt.
  1857. XTyping RETURN again will print the article body.
  1858. XTyping RETURN yet again will print the next heading, and so on.
  1859. XIf having read the heading, you don't wish to read the article, you may
  1860. Xtype `n' (or `+' or ';') which will take you directly to the next heading.
  1861. X.P
  1862. X.P
  1863. XAn article is treated as having been read, if either you have seen
  1864. Xthe article body, or typed `n' to skip over it.
  1865. X.P
  1866. XA number of commands operate on the `current' article.
  1867. XThis is defined as the article whose header you have most recently seen.
  1868. X.P
  1869. XThe commands to read news are:
  1870. X.TP
  1871. X.B RETURN
  1872. XEither print the current article,
  1873. Xor go to the next article and print its header.
  1874. X.TP
  1875. X\fBn\fR or \fB+\fR or \fB;\fR
  1876. XGo to the next article and print its header.
  1877. X.TP
  1878. X.B .
  1879. XPrint the current article.
  1880. X.TP
  1881. X.B -
  1882. XGo back to the previous article. This is a toggle, typing it
  1883. Xtwice returns you to the original article.
  1884. X.TP
  1885. X.I number
  1886. XGo to the article
  1887. X.I number
  1888. Xin the current newsgroup.
  1889. XLike the `-' command,
  1890. Xyou always return to the original article
  1891. Xafter reading the selected article.
  1892. X.TP
  1893. X\fBs \fR[\fIfile\fR]
  1894. XSave the current article, either in the specified file, or
  1895. Xin
  1896. X.BR $HOME/articles .
  1897. X.TP
  1898. X.B h
  1899. XPrint the current header (slightly more verbose than normal header).
  1900. X.TP
  1901. X.B H
  1902. XPrint the current header in full (very verbose).
  1903. X.TP
  1904. X\fBN \fR[\fInewsgroup\fR]
  1905. XGo to the next newsgroup, or to the specified newsgroup.
  1906. X.TP
  1907. X.B u
  1908. XUnsubscribe from all further followup articles on this topic.
  1909. X.TP
  1910. X.B U
  1911. XUnsubscribe from this newsgroup, and go to the next newsgroup.
  1912. X.TP
  1913. X\fB!\fIcommand\fB
  1914. XShell escape.
  1915. X.I Command
  1916. Xis executed.
  1917. XIf 
  1918. X.I command
  1919. Xis `!'
  1920. Xthe last escape command is executed.
  1921. X.TP
  1922. X\fBq\fR or \fBEOT\fR
  1923. XQuit.
  1924. XThe
  1925. X.I .newsrc
  1926. Xfile will be updated provided the flag
  1927. X.B -i
  1928. Xwas not specified.
  1929. X.TP
  1930. X.B x
  1931. XExit.
  1932. X.I .newsrc
  1933. Xis left unchanged (as if no articles had been read).
  1934. X.TP
  1935. X.B DEL
  1936. XAn interrupt will cause
  1937. X.I readnews
  1938. Xto terminate its current activity and return to command mode.
  1939. XAn interrupt in command mode will cause `Interrupt' to be printed,
  1940. Xand a subsequent interrupt will cause immediate exit (as in the `x' command).
  1941. X.P
  1942. XSome commands are available to send/reply or cancel news articles:
  1943. X.TP
  1944. X.B c
  1945. XCancel article. Only the author, or news administrator can do this.
  1946. X.IR postnews (1)
  1947. Xis called to do the actual cancelling.
  1948. X.TP
  1949. X.B r
  1950. XReply to sender of the current article by mail.
  1951. X.I Readnews
  1952. Xsets up the appropriate headers, and then calls
  1953. X.IR mail (1)
  1954. Xto send a reply to the sender.
  1955. X.TP
  1956. X.B f
  1957. XPost a followup to the current article.
  1958. X.I Readnews
  1959. Xsets up the appropriate headers, and then calls
  1960. X.IR postnews (1)
  1961. Xto post the followup article.
  1962. X.TP
  1963. X.B p
  1964. XPost an article on a new topic.
  1965. X.IR postnews (1)
  1966. Xis called to post the new article.
  1967. X.TP
  1968. X\fBm \fIperson\fB
  1969. XMail to
  1970. X.IR person .
  1971. X.P
  1972. XWhen replying by mail, or posting an article, the user
  1973. Xis prompted for certain headers, and then the text of the article or mail
  1974. Xitem is entered until a `.' or EOT is entered alone on a line.
  1975. XThen the article/mail is posted/mailed.
  1976. XOther commands are available:
  1977. X.TP
  1978. X\&\fB.e\fR
  1979. XEdit the message/article collected so far (see
  1980. X.IR ed (1)).
  1981. XThe 'To:' or 'cc:' fields may be changed if mailing.
  1982. XAfter editing further lines may be appended to the message.
  1983. X.TP
  1984. X\&\fB.i\fR
  1985. XInterpolate
  1986. Xthe current news article onto the end of the message.
  1987. XThe interpolated item
  1988. Xis indented by four spaces.
  1989. X.TP
  1990. X\&\fB.!\fIcmd\fR or \fB!\fIcmd\fR
  1991. XShell escape.
  1992. X.IR Cmd
  1993. Xis executed.
  1994. X.TP
  1995. X.B DEL
  1996. XCauses posting/mailing to be aborted, and the article entered so far
  1997. Xis saved in
  1998. X.B $HOME/dead.article
  1999. Xor
  2000. X.BR $HOME/dead.letter .
  2001. X.P
  2002. XIn order to permanently resubscribe to a newsgroup denied by `U',
  2003. Xor a series of followups denied by `u' it is necessary to understand
  2004. Xthe format of the
  2005. X.I .newsrc
  2006. Xfile.
  2007. XThe
  2008. X.I .newsrc
  2009. Xfile consists of two types of lines:
  2010. X.TP
  2011. Xoption lines
  2012. XThese start with the word `option' and contain the same arguments
  2013. Xas the
  2014. X.I readnews
  2015. Xcommand on the command line.
  2016. XFollowups are denied with `option -u <messageid>'.
  2017. XTo resubscribe to further followups, the correct options line must be deleted.
  2018. X.TP
  2019. Xread newsgroup lines
  2020. XThese have the format <newsgroup>`:' <number>, where
  2021. X<number> represents the last item number seen in that particular newsgroup.
  2022. XIf the newsgroup has been unsubscribed, the `:' is replaced by a `!'.
  2023. XTo resubscribe the `!' must be changed back to a `:'.
  2024. X.SH FILES
  2025. X.ta 24
  2026. X.nf
  2027. X$HOME/.newsrc    options and list of previously read articles
  2028. X%news    where the articles are kept
  2029. X/usr/lib/news/active    current newsgroups
  2030. X/usr/lib/news/help    help file
  2031. X.fi
  2032. X.SH SEE ALSO
  2033. Xpostnews(1), mail(1), ed(1), uusend(8), uurec(8).
  2034. X.SH BUGS
  2035. X.I Readnews
  2036. Xwith the
  2037. X.B -c
  2038. Xflag may say "You have news.", when the available article is a unsubscribed
  2039. Xfollowup article.
  2040. X.P
  2041. XYou may see followups, even if you have used the `u' command.
  2042. XThis is because many sites have faulty news programs, which do
  2043. Xnot follow the correct protocol, or the sender did not use the `r' command.
  2044. X.SH AUTHOR
  2045. XMichael Rourke, University of N.S.W (decvax!mulga!michaelr:elecvax)
  2046. !
  2047. echo done
  2048.  
  2049.  
  2050.