home *** CD-ROM | disk | FTP | other *** search
/ Usenet 1994 January / usenetsourcesnewsgroupsinfomagicjanuary1994.iso / sources / unix / volume19 / nn / part11 < prev    next >
Encoding:
Internet Message Format  |  1989-06-22  |  49.6 KB

  1. Subject:  v19i072:  NN, a Usenet news reader, Part11/15
  2. Newsgroups: comp.sources.unix
  3. Sender: sources
  4. Approved: rsalz@uunet.UU.NET
  5.  
  6. Submitted-by: storm@texas.dk (Kim F. Storm)
  7. Posting-number: Volume 19, Issue 72
  8. Archive-name: nn/part11
  9.  
  10. #!/bin/sh
  11. # this is part 11 of a multipart archive
  12. # do not concatenate these parts, unpack them in order with /bin/sh
  13. # file nngoback.1 continued
  14. #
  15. CurArch=11
  16. if test ! -r s2_seq_.tmp
  17. then echo "Please unpack part 1 first!"
  18.      exit 1; fi
  19. ( read Scheck
  20.   if test "$Scheck" != $CurArch
  21.   then echo "Please unpack part $Scheck next!"
  22.        exit 1;
  23.   else exit 0; fi
  24. ) < s2_seq_.tmp || exit 1
  25. echo "x - Continuing file nngoback.1"
  26. sed 's/^X//' << 'NO_NEWS_IS_GOOD_NEWS' >> nngoback.1
  27. X.SH SYNOPSIS
  28. X.B nngoback 
  29. X[ \-\fBh\fP ]
  30. X.I days
  31. X.SH DESCRIPTION
  32. X.I nngoback
  33. Xwill rewind the record file of \fBnn\fP(1) one or more days, i.e. mark
  34. Xthe news articles which have arrived on the system during the last
  35. X.I days
  36. Xdays unread.
  37. X.LP
  38. XIf you are not up-to-date with your news reading, you can also use
  39. X\fBnngoback\fP to catch up to only have the last few days of news
  40. Xwaiting to be read in the following way:
  41. X.br
  42. X    nn \-a0
  43. X.br
  44. X    nngoback 3
  45. X.br
  46. XThe \fBnn\fP command will mark all articles in all groups as read (answer
  47. X.I all
  48. Xto the catch-up question.)  The following \fBnngoback\fP will then make
  49. Xthe last three days of news unread again.
  50. X.LP
  51. XExamples:
  52. X.TP
  53. Xnngoback 0
  54. XMark the articles which have arrived today as unread.
  55. X.TP
  56. Xnngoback 1
  57. XMark the articles which have arrived yesterday and today as unread.
  58. X.TP
  59. Xnngoback 6
  60. XMark the articles which have arrived during the last week as unread.
  61. X.LP
  62. X\fBnngoback\fP have two methods to rewind the rc file:
  63. X.br
  64. X\- scan the history file for the articles (option \-\fBh\fP), or
  65. X.br
  66. X\- use a copy of the active file with the `right' age (default).
  67. X.LP
  68. XNormally, \fInngoback\fP will run \fInntidy\fP on your rc file using
  69. Xa copy of the active file saved on the requested day.  Using this
  70. Xmethod, you cannot go more than 14 days back.
  71. X.LP
  72. XIf the \-\fBh\fP option is specified, the history file will be scanned
  73. Xto locate articles which have been received after the specified day
  74. Xand mark those as unread in the rc file.  When this method is used,
  75. X\fBnngoback\fP can be quite slow depending on the size of the history
  76. Xfile in your news system; it uses \fBegrep\fP(1) to scan the file, so
  77. Xit must be read the entire file although the interesting data are
  78. Xfound towards the end of the file.  This method does not normally work
  79. Xwith NNTP or in a network environment.
  80. X.LP
  81. XIt is a prerequisite that the script \fBback_act\fP is executed at an
  82. Xappropriate time once (and only once) every day.  Preferably this is
  83. Xdone by \fBcron\fP right before the bacth of news for `today' is received.
  84. X\fBback_act\fP will maintain copies of the active file for the last 14
  85. Xdays, which can be used directly to rewind the rc file.
  86. X.SH FILES
  87. X.DT
  88. X.ta \w'$lib/date_regexp'u+3m
  89. X~/.nn/rc    The record of read articles.
  90. X.br
  91. X~/.nn/rc.bak1    The original rc file before goback.
  92. X.br
  93. X$db/active.\fIN\fP    The \fIN\fP days `old' active file.
  94. X.br
  95. X$lib/back_act    Script run by cron to keep the `old' active files.
  96. X.br
  97. X$lib/date_regexp    Program to build an egrep(1) pattern.
  98. X.br
  99. X$newslib/history    The list of all articles in the system.
  100. X.DT
  101. X.LP
  102. XThe \fIdate_regexp\fP program builds an egrep pattern to extract the
  103. Xhistory file entries for a specified number of days.
  104. X.LP
  105. XOn some systems the history file is split into smaller files in a
  106. Xsubdirectory called history.d; \fBnngoback\fP tries to locate the
  107. Xright history file to use.
  108. X.SH SEE ALSO
  109. Xnn(1), nncheck(1), nngrep(1), nntidy(1)
  110. X.br
  111. Xnnadmin(1M), nnquery(1M), nnusage(1M), nnmaster(8)
  112. X.SH NOTES
  113. XThe `old' active files method also works with NNTP and with a shared
  114. Xdatabase (that is why the files are stored in the database directory). 
  115. X.LP
  116. X\fBnngoback\fP does not check the age of the `old' active files; it
  117. Xwill blindly believe that active.0 was created today, and that
  118. Xactive.7 is really seven days old!  Therefore, the \fIback_act\fP
  119. Xscript should be run once and only once every day for \fInngoback\fP
  120. Xto work properly.
  121. X.LP
  122. XWhen the history file is used, days are known to split at 12 midnight.
  123. XWhen old active files are used, days are counted relative to the time
  124. Xthe files were saved.
  125. X.SH AUTHOR
  126. XKim F. Storm, Texas Instruments A/S, Denmark
  127. X.br
  128. XE-mail: storm@texas.dk
  129. NO_NEWS_IS_GOOD_NEWS
  130. echo "File nngoback.1 is complete"
  131. chmod 0644 nngoback.1 || echo "restore of nngoback.1 fails"
  132. set `wc -c nngoback.1`;Sum=$1
  133. if test "$Sum" != "3723"
  134. then echo original size 3723, current size $Sum;fi
  135. echo "x - extracting nngoback.sh (Text)"
  136. sed 's/^X//' << 'NO_NEWS_IS_GOOD_NEWS' > nngoback.sh &&
  137. X# prefix is inserted above by make
  138. X
  139. X# go $1 days back in the news feed and adjust .nn/rc
  140. X# accordingly.
  141. X
  142. XUSE_HIST=false
  143. Xif [ "$1" = "-h" ]
  144. Xthen
  145. X    USE_HIST=true
  146. X    shift
  147. Xfi
  148. Xif [ $# -ne 1 ]
  149. Xthen
  150. X    echo "Usage: $0 [-h] days"
  151. X    exit 1
  152. Xfi
  153. X
  154. Xif [ $USE_HIST = false ]
  155. Xthen
  156. X    if [ -s $DB/active.$1 ]
  157. X    then
  158. X      nntidy - $DB/active.$1
  159. X    else
  160. X      echo $0: No suitable copy of the active file exists -- no update
  161. X    fi
  162. X    exit 0
  163. Xfi
  164. X
  165. XHISTORY="`expr $ACTIVE : '\(/.*/\)'`"history
  166. X
  167. Xif [ -s "${HISTORY}" ]
  168. Xthen
  169. X    :
  170. Xelif [ -s "${HISTORY}.d/0" ]
  171. Xthen
  172. X    HISTORY="${HISTORY}.d/*"
  173. Xelse
  174. X    if $NNTP
  175. X    then
  176. X        echo "$0 is not setup to run with NNTP"
  177. X        exit 2
  178. X    fi
  179. X    echo "Cannot find the history file -- no update"
  180. X    exit 1
  181. Xfi
  182. X
  183. Xecho Working on your nn record file ... do not run nn until completed
  184. X
  185. Xecho "NOTICE:  this operation is SLOOOOOOOOWWWWWWWW....."
  186. X
  187. Xcd
  188. Xcd .nn
  189. Xrm -f rc.bak1
  190. X
  191. Xtrap "mv rc.bak1 rc ; echo No changes ; exit 0" 1 2 3 13 14 15
  192. X
  193. Xmv rc rc.bak1
  194. X
  195. X{
  196. Xecho PHASE_1
  197. X# news.group last.art.no first.art.no mod
  198. Xcat $ACTIVE
  199. X
  200. Xecho PHASE_2
  201. X# + last.read.art.no news.group
  202. Xcat rc.bak1
  203. X
  204. Xecho PHASE_3
  205. X# from date time news.group/art.no ...
  206. Xcat  $HISTORY | egrep "`$LIB/date_regexp $1`"
  207. X} |
  208. Xawk '
  209. XBEGIN{
  210. X    p=0
  211. X}
  212. X/^PHASE_/ {
  213. X    p++
  214. X    next
  215. X}
  216. Xp == 1 {
  217. X    last[$1]=$2+1
  218. X    next
  219. X}
  220. Xp == 2 {
  221. X    if (last[$3] > 0) {
  222. X        subscr[$3] = $1
  223. X        if ($1 == "!") last[$3] = $2 + 1;
  224. X    }
  225. X    next
  226. X}
  227. Xp == 3 {
  228. X    for (i = 4; i <= NF; i++) {
  229. X        if (split($i, x, "/") != 2) continue;
  230. X        g=x[1]; n=x[2]
  231. X        if (subscr[g] == "+" && n < last[g]) last[g] = n
  232. X    }
  233. X}
  234. XEND {
  235. X    for (g in subscr) printf("%s %06d %s\n", subscr[g], last[g]-1, g)
  236. X}' | sort +2 > rc
  237. X
  238. Xecho "nngoback $1 finished"
  239. NO_NEWS_IS_GOOD_NEWS
  240. chmod 0644 nngoback.sh || echo "restore of nngoback.sh fails"
  241. set `wc -c nngoback.sh`;Sum=$1
  242. if test "$Sum" != "1591"
  243. then echo original size 1591, current size $Sum;fi
  244. echo "x - extracting nngrep.1 (Text)"
  245. sed 's/^X//' << 'NO_NEWS_IS_GOOD_NEWS' > nngrep.1 &&
  246. X.TH NNGREP 1 "Release 6.3"
  247. X.UC 4
  248. X.SH NAME
  249. Xnngrep \- grep for news group names
  250. X.SH SYNOPSIS
  251. X.B nngrep
  252. X[ \-\fBa\fP ]
  253. X.I pattern
  254. X.SH DESCRIPTION
  255. X.B nngrep
  256. Xscan \fInn\fP's rc file for news group names that matches the
  257. X.I pattern 
  258. Xand print their names on the standard output.
  259. X.LP
  260. XFor example, to get the names of all "source" groups, you can use the
  261. Xcommand 
  262. X.br
  263. X    nngrep source
  264. X.LP
  265. XNormally, \fInngrep\fP will only print names of groups to which you subscribe.
  266. X.LP
  267. XIf you specify the \-a option, it will print all matching groups in
  268. Xthe rc file.
  269. X.LP
  270. XYou can use this to read a specific subset of news groups with
  271. X\fInn\fP; for example
  272. X.br
  273. X    nn -x -snn `nngrep source`
  274. X.SH FILES
  275. X.DT
  276. X.ta \w'~/.nn/rc.bak'u+3m
  277. X~/.nn/rc    The record of read articles
  278. X.DT
  279. X.SH SEE ALSO
  280. Xnn(1), nncheck(1), nngoback(1), nngrep(1)
  281. X.br
  282. Xnnadmin(1M), nnquery(1M), nnusage(1M), nnmaster(8)
  283. X.SH BUGS
  284. XThe rc file only contains entries for the groups you have actually
  285. Xread, i.e. new groups which you have not yet read are not printed.
  286. XTherefore, groups in which no articles have ever been posted will not
  287. Xshow up either.
  288. X.LP
  289. XThis is actually a feature of \fInn\fP:  it will
  290. Xdelay the entry of new groups into the rc file, so it can tell you
  291. Xthat it is a new group the first time you read it!
  292. X.SH AUTHOR
  293. XKim F. Storm, Texas Instruments A/S, Denmark
  294. X.br
  295. XE-mail: storm@texas.dk
  296. X
  297. NO_NEWS_IS_GOOD_NEWS
  298. chmod 0644 nngrep.1 || echo "restore of nngrep.1 fails"
  299. set `wc -c nngrep.1`;Sum=$1
  300. if test "$Sum" != "1337"
  301. then echo original size 1337, current size $Sum;fi
  302. echo "x - extracting nngrep.sh (Text)"
  303. sed 's/^X//' << 'NO_NEWS_IS_GOOD_NEWS' > nngrep.sh &&
  304. Xcd
  305. X
  306. Xif [ $# -eq 0 ] ; then
  307. X    echo "usage: nngrep -a pattern"
  308. X    exit 1
  309. Xfi
  310. X
  311. Xif [ "$1" = "-a" ] ; then
  312. X    grep "$2" .nn/rc
  313. Xelse
  314. X    grep "^+ .* .*$1" .nn/rc
  315. Xfi |
  316. Xawk '{print $3}'
  317. NO_NEWS_IS_GOOD_NEWS
  318. chmod 0644 nngrep.sh || echo "restore of nngrep.sh fails"
  319. set `wc -c nngrep.sh`;Sum=$1
  320. if test "$Sum" != "169"
  321. then echo original size 169, current size $Sum;fi
  322. echo "x - extracting nnmail.c (Text)"
  323. sed 's/^X//' << 'NO_NEWS_IS_GOOD_NEWS' > nnmail.c &&
  324. X/*
  325. X *     nnmail - a mailer that understands @ addressing
  326. X *         when you don't have sendmail or smail
  327. X */
  328. X
  329. X#include "config.h"
  330. X
  331. X#include "options.h"
  332. X
  333. Xchar * MAILER = MAILX;
  334. Xstatic int print_vers, test_mode;
  335. X
  336. XOption_Description( mail_options ) {
  337. X
  338. X    'v', Bool_Option( print_vers ),
  339. X    'm', String_Option( MAILER ),
  340. X    't', Bool_Option( test_mode ),
  341. X
  342. X    '\0',
  343. X};
  344. X
  345. X
  346. Xmain(argc, argv)
  347. Xint argc;
  348. Xchar **argv;
  349. X{
  350. X    int i, n;
  351. X    char route[512];
  352. X    char *getenv(), *envmail;
  353. X    extern char **environ;
  354. X
  355. X    if (envmail = getenv("NNMAILER"))
  356. X    MAILER = envmail;
  357. X
  358. X    n = parse_options(argc, argv, (char *)NULL, 
  359. X              mail_options, (char **)NULL, (int (*)())NULL);
  360. X    
  361. X    if (print_vers) {
  362. X    print_version("Release %R.%V.%P #%U\n");
  363. X    nn_exit(0);
  364. X    }
  365. X
  366. X#ifndef HAVE_ROUTING    
  367. X    if (test_mode) {
  368. X    extern FILE *route_trace;
  369. X    route_trace = stdout;
  370. X    }
  371. X#endif    
  372. X    
  373. X    argv[0] = MAILER;
  374. X
  375. X#ifndef HAVE_ROUTING    
  376. X    for (i = 1; i <= n; i++)
  377. X    if (reroute(route, argv[i])) {
  378. X        if (test_mode) {
  379. X        printf("%s \t-->  %s\n", argv[i], route);
  380. X        continue;
  381. X        }
  382. X        argv[i] = malloc(strlen(route)+1);
  383. X        strcpy(argv[i], route);
  384. X    } else
  385. X        if (test_mode)
  386. X        printf("%s \t***  no route found\n", argv[i]);
  387. X#endif
  388. X
  389. X    if (test_mode) nn_exit(0);
  390. X    
  391. X    execve(MAILER, argv, environ);
  392. X    fprintf(stderr, "Mailer '%s' not found\n", MAILER);
  393. X    nn_exit(7);
  394. X}
  395. X
  396. X
  397. X/*VARARGS*/
  398. Xuser_error()
  399. X{
  400. X}
  401. X
  402. Xnn_exit(n)
  403. X{
  404. X    exit(n);
  405. X}
  406. NO_NEWS_IS_GOOD_NEWS
  407. chmod 0644 nnmail.c || echo "restore of nnmail.c fails"
  408. set `wc -c nnmail.c`;Sum=$1
  409. if test "$Sum" != "1417"
  410. then echo original size 1417, current size $Sum;fi
  411. echo "x - extracting nnmaster.1m (Text)"
  412. sed 's/^X//' << 'NO_NEWS_IS_GOOD_NEWS' > nnmaster.1m &&
  413. X.TH NNMASTER 1M "Release 6.3"
  414. X.\" (c) Copyright 1988, Kim F. Storm
  415. X.UC 4
  416. X.SH NAME
  417. Xnnmaster \- nn database manager
  418. X.SH SYNOPSIS
  419. X.B nnmaster
  420. X[ \-\fBI\fP ]
  421. X.br
  422. X.B nnmaster
  423. X[ \-\fBw\fP ]
  424. X.br
  425. X.B nnmaster
  426. X[ \-\fBv\fP ]
  427. X.br
  428. X.B nnmaster
  429. X[ \-\fBr [ \fP\fIN\fP ] ]
  430. X[ \-\fBe\fP\fIN\fP ] 
  431. X[ \-\fBC\fP ]
  432. X[ \-\fBE\fP ]
  433. X[ \-\fBu\fP ]
  434. X[ \-\fBt\fP ]
  435. X.SH DESCRIPTION
  436. X.I nnmaster
  437. Xis the daemon which is responsible for building and maintaining the
  438. Xdatabase used by the \fInn\fP(1) news reader.
  439. X.LP
  440. XNormally, 
  441. X.I nnmaster
  442. Xis started when the system enters multi-user mode, and runs until 
  443. Xsystem shutdown.  To facilitate this, you should place the following
  444. Xcall in /etc/rc (or similar) to invoke the
  445. X.I nnmaster
  446. Xdaemon:
  447. X.sp 0.5v
  448. X    $lib/nnmaster -r -C
  449. X.sp 0.5v
  450. Xwhere $lib is the LIB_DIRECTORY defined during configuration of \fInn\fP.
  451. X.LP
  452. XWhen
  453. X.I nnmaster
  454. Xis started as specified above, it will first perform a thorough consistency
  455. Xcheck on the database (option \-C).
  456. X.LP
  457. XThen, every 10 minutes (option \-r), it will look at the time-stamp
  458. Xof the news active file to see whether new articles have arrived on
  459. Xthe system (or whether articles have been expired).
  460. X.LP
  461. XIf the active file has been modified,
  462. X.I nnmaster
  463. Xwill collect the header information from the new articles and enter
  464. Xthem into the database (or remove the headers of the expired articles
  465. Xfrom the database).
  466. X.LP
  467. XIf it detects that some articles have been expired, it will
  468. Xautomatically remove the header information of the expired articles
  469. Xfrom the database.
  470. X.SH OPTIONS
  471. XThe following options control the behaviour of the \fInnmaster\fP.
  472. X.TP
  473. X\-\fBr\fP [ \fImin\fP ]
  474. X.br
  475. XDaemon mode.  The \fInnmaster\fP will put itself in the background,
  476. Xand will checks for arrival of new articles and expired articles every
  477. X.I min
  478. Xminutes (and update the database accordingly).  If 
  479. X.I min
  480. Xis omitted, the default is to check every 10 minutes.
  481. X.sp 0.5v
  482. XWithout the \-r option, the \fInnmaster\fP will just perform a single
  483. Xcollection of new articles (if any) and then exit.  This can be used
  484. Xto have the \fInnmaster\fP started by
  485. X.IR cron (8)
  486. Xat regular intervals instead of having it as a daemon which sleeps
  487. Xbetween checking for new articles.  Since the \fInnmaster\fP is a bit
  488. Xexpensive to start up (it has to read a few files), it is up to you to
  489. Xdeside which mode is best on your system.  (I have also heard that it
  490. Xworks to call \fInnmaster\fP without \-r from 
  491. X.IR inews (1).
  492. XI cannot recommend this unless you receive batched news; invoking
  493. X\fInnmaster\fP for every received article sounds too expensive to me.)
  494. X.TP
  495. X\-\fBe\fP \fIart\fP
  496. X.br
  497. XRun internal
  498. X.I expire
  499. Xon a group in the database when
  500. X.I art
  501. Xarticles has been expired by
  502. X.IR expire (1M).
  503. XThe default is to expire a group whenever a single article in the
  504. Xgroup has been expired.  Using this option, you can delay the expiring
  505. Xof the header information from the database until the specified number
  506. Xof articles are gone.  
  507. X.sp 0.5v
  508. XUsing a 
  509. X.I art
  510. Xvalue of 0 disables the automatic expire completely.
  511. XThis may be used to keep old article
  512. X.I headers
  513. Xonline after the articles have been removed (useful if you backup
  514. Xexpired articles on tape.)  You can then use the Expire command in
  515. X\fInnadmin\fP(1M) to instruct the \fInnmaster\fP to expire individual
  516. Xor all groups.
  517. X.TP
  518. X.B \-C
  519. XPerform a consistency check on the database on start-up, and rebuild
  520. Xcorrupted database files.  This operation can be quite time-consuming
  521. Xsince it reads through all the database files.
  522. X.TP
  523. X.B \-E
  524. XExpire groups by recollecting all available articles.  
  525. X.sp 0.5v
  526. XThe default method to expire a group is to simply rewrite the database
  527. Xfiles for that group eliminating the entries for the non-existing
  528. Xarticles.  The problem with the default approach is that it will only
  529. Xremove the entries for the articles with article number less than the
  530. Xlowest existing article number (found in the active file).  This may
  531. Xcause problems if there is a single old article with a "distant"
  532. Xexpire date.
  533. X.sp 0.5v
  534. XSince recollecting all articles in a group may be expensive if you
  535. Xkeep news for a long time, you should not use the \-E option
  536. Xwithout a little thought. 
  537. X.TP
  538. X.B \-I
  539. XInitialize database.  This option will erase an existing database, and
  540. Xcreate an empty database containing entries for the currently known
  541. Xgroups.  Since groups are sometimes identified by a number across
  542. Xinvokations of \fInn\fP (saved article selections), \fInnmaster\fP
  543. Xwill offer you to use the same numbering of the news groups as in the
  544. Xold database (it will ask whether you want to use the old group file).
  545. X.TP
  546. X.B \-t
  547. XTrace the collection process.  This will place a lot of information
  548. Xinto the log file (T: entries).  Otherwise, the log file will only
  549. Xcontain entries reporting the total number of articles collected or
  550. Xexpired during the collection process.
  551. X.TP
  552. X.B \-v
  553. XPrint the release and version identification for \fInnmaster\fP, and exit.
  554. X.TP
  555. X.B \-u
  556. XNormally, \fInnmaster\fP will just compare the time-stamp on the
  557. Xactive file with a time-stamp saved in the database to see if new
  558. Xarticles have arrived.  The \-u option forces the \fInnmaster\fP to 
  559. X.I read
  560. Xthe active file on start-up to see if new articles have arrived; the
  561. Xfollowing checks (in daemon mode) will still be based on the
  562. Xtime-stamp of the active file.
  563. X.TP
  564. X.B \-w
  565. XWakeup the real \fInnmaster\fP.  Send a signal to the \fInnmaster\fP
  566. Xdaemon to have it check for new articles immediately.
  567. X.SH FILES
  568. XThe $db, $lib, and $news used below are synonyms for the DB_DIRECTORY,
  569. XLIB_DIRECTORY, and the news system's lib directories respectively.
  570. X.LP
  571. X.DT
  572. X.ta \w'$db/DATA/\fInnn\fP.[dx]'u+3m
  573. X$db/MASTER    Database master index
  574. X.br
  575. X$db/GROUPS    News group names in MASTER file order
  576. X.br
  577. X$db/DATA/\fInnn\fP.[dx]    Database files for group number \fInnn\fP
  578. X.br
  579. X$lib/GATE    Message channel from \fInnadmin\fP to \fInnmaster\fP
  580. X.br
  581. X$lib/MPID    The process id of the \fInnmaster\fP daemon.
  582. X.br
  583. X$lib/Log    The log file
  584. X.br
  585. X$news/active    Existing articles and groups
  586. X.DT
  587. X.LP
  588. XThe MASTER file contains a record for each news group, occurring in
  589. Xthe same sequence as the group names in the GROUPS file.  The sequence
  590. Xalso defines the group numbers used to identify the files in the
  591. Xdatabase and in a few other places.
  592. X.LP
  593. XThe GATE file will be created by \fInnadmin\fP when needed, and
  594. Xremoved by \fInnmaster\fP when it has read it.  Therefore, to send a
  595. Xmessage to the \fInnmaster\fP requires that you are allowed to write
  596. Xin the $lib directory.
  597. X.LP
  598. XThe contents of the Log file are described in the \fInnadmin\fP manual.
  599. X.SH SEE ALSO
  600. Xnn(1), nncheck(1), nngrep(1), nntidy(1)
  601. X.br
  602. Xnnadmin(1M), nnquery(1M), nnusage(1M), nnmaster(8)
  603. X.SH NOTES
  604. XThe functionality of the \-e options should be to specify how many
  605. Xexpired articles should remain in the database.  With the current
  606. Xfunctionality, all "old" headers will be removed from the database
  607. Xwhen \fInnmaster\fP performs the expire.
  608. X.LP
  609. XThe message "Article .... not found" is displayed by \fInn\fP if the
  610. Xuser has selected an article which is still in the database but has
  611. Xbeen expired in the news system.
  612. X.LP
  613. X\fInnmaster\fP uses the "lowest article number" field in the active
  614. Xfile to detect that 
  615. X.I expire 
  616. Xhas been run on the group.  This "quick check" will not trigger an
  617. Xinternal expire if the "lowest numbered article" was not removed by
  618. Xexpire.  To expire such a group, you must explicitly request it using
  619. Xthe Group-Expire command in \fInnadmin\fP(1M).
  620. X.SH AUTHOR
  621. XKim F. Storm, Texas Instruments A/S, Denmark
  622. X.br
  623. XE-mail: storm@texas.dk
  624. NO_NEWS_IS_GOOD_NEWS
  625. chmod 0644 nnmaster.1m || echo "restore of nnmaster.1m fails"
  626. set `wc -c nnmaster.1m`;Sum=$1
  627. if test "$Sum" != "7469"
  628. then echo original size 7469, current size $Sum;fi
  629. echo "x - extracting nnquery.sh (Text)"
  630. sed 's/^X//' << 'NO_NEWS_IS_GOOD_NEWS' > nnquery.sh &&
  631. X# CONFIG file is insert above this line by make
  632. X
  633. Xif [ -f ${ACTIVE} ] ; then
  634. X    echo "Cannot locate active file ${ACTIVE}"
  635. X    exit 3
  636. Xfi
  637. X
  638. XOUTAGED=30
  639. XREADERS=/tmp/nnq$$q
  640. XDIRS=/tmp/nnq$$d
  641. Xecho > $DIRS
  642. X
  643. X{
  644. Xsort ${ACTIVE}
  645. X
  646. Xawk -F: '{print "%s %s\n", $1, $6}' /etc/passwd |
  647. Xwhile read user homedir
  648. Xdo
  649. X  if [ -f $homedir/.nn/rc ] ; then
  650. X    if [ -n "`grep '^'$homedir'$' $DIRS`" ] ; then
  651. X      :
  652. X    else
  653. X      echo $homedir >> $DIRS
  654. X      if [ -n "`find $homedir/.nn -name rc -mtime -$OUTAGED -print`" ] ; then
  655. X        echo $user >> $READERS
  656. X        echo USER $user
  657. X        cat $homedir/.nn/rc
  658. X      fi
  659. X    fi
  660. X  fi
  661. Xdone
  662. X} |
  663. X
  664. Xawk '
  665. XBEGIN {
  666. X    a=1
  667. X    g=0
  668. X}
  669. X
  670. X$1 == "USER" {
  671. X    a = 0
  672. X    user = " " $2
  673. X    next
  674. X}
  675. X
  676. Xa == 1 {
  677. X    g++
  678. X    group[$1] = g
  679. X    name[g] = $1
  680. X#    if ($2 == $3) last[g] = 0; else last[g] = $2+0
  681. X    sub[g] = 0
  682. X    users[g] = ""
  683. X    next
  684. X}        
  685. X
  686. X$1 == "!" {
  687. X    next
  688. X}
  689. X
  690. X$1 == "+" {
  691. X    n = group[$3]
  692. X#    if ( last[n] == 0 ) next
  693. X#    if ( ($2+200) < last[n] ) next
  694. X    sub[n]++;
  695. X    users[n] = users[n] user
  696. X    next
  697. X}
  698. X
  699. X{
  700. X    next
  701. X}
  702. X
  703. XEND {
  704. X    for (n = 1; n <= g; n++) {
  705. X        printf "%5d %s\t%s\n", sub[n], name[n], users[n]
  706. X    }
  707. X}' |
  708. X
  709. Xif [ "$1" = "-s" ] ; then
  710. X    grep -v "^[     ]*0"
  711. Xelif [ "$1" = "-u" ] ; then
  712. X    grep "^[     ]*0"
  713. Xelse
  714. X    cat
  715. Xfi
  716. X
  717. Xrm -f $READERS $DIRS
  718. NO_NEWS_IS_GOOD_NEWS
  719. chmod 0644 nnquery.sh || echo "restore of nnquery.sh fails"
  720. set `wc -c nnquery.sh`;Sum=$1
  721. if test "$Sum" != "1199"
  722. then echo original size 1199, current size $Sum;fi
  723. echo "x - extracting nntidy.1 (Text)"
  724. sed 's/^X//' << 'NO_NEWS_IS_GOOD_NEWS' > nntidy.1 &&
  725. X.TH NNTIDY 1 "Release 6.3"
  726. X.UC 4
  727. X.SH NAME
  728. Xnntidy \- tidy your personal \fInn\fP(1) rc file
  729. X.SH SYNOPSIS
  730. X.B nntidy
  731. X.SH DESCRIPTION
  732. X.B nntidy
  733. Xwill clean out non-existing groups, adjust obviously wrong article
  734. Xnumbers, and remove badly formed lines from your rc file.
  735. X.LP
  736. XIt will also sort the lines in the rc file in alphabetical order.
  737. X.LP
  738. XYou should run 
  739. X.B nntidy
  740. Xif your rc file has been corrupted for some reason.
  741. X.SH FILES
  742. X.DT
  743. X.ta \w'~/.nn/rc.bak'u+3m
  744. X~/.nn/rc    The record of read articles
  745. X.br
  746. X~/.nn/rc.bak    The original rc file before tidy
  747. X.DT
  748. X.SH SEE ALSO
  749. Xnn(1), nncheck(1), nngoback(1), nngrep(1)
  750. X.br
  751. Xnnadmin(1M), nnquery(1M), nnusage(1M), nnmaster(8)
  752. X.SH AUTHOR
  753. XKim F. Storm, Texas Instruments A/S, Denmark
  754. X.br
  755. XE-mail: storm@texas.dk
  756. X
  757. X
  758. NO_NEWS_IS_GOOD_NEWS
  759. chmod 0644 nntidy.1 || echo "restore of nntidy.1 fails"
  760. set `wc -c nntidy.1`;Sum=$1
  761. if test "$Sum" != "742"
  762. then echo original size 742, current size $Sum;fi
  763. echo "x - extracting nntidy.sh (Text)"
  764. sed 's/^X//' << 'NO_NEWS_IS_GOOD_NEWS' > nntidy.sh &&
  765. X# CONFIG file is inserted above during installation
  766. X
  767. X# clean out non-existing groups, badly formed lines etc.
  768. X
  769. Xif [ ! -s ${2-$ACTIVE} ] ; then
  770. X    echo "$0: Cannot locate active file ${2-$ACTIVE}"
  771. X    exit 3
  772. Xfi
  773. X
  774. Xecho Working on your nn record file ... do not run nn until completed
  775. X
  776. Xcd
  777. Xcd .nn
  778. Xrm -f rc.bak1
  779. X
  780. Xtrap 'mv rc.bak1 rc ; echo "No changes" ; exit 0' 1 2 3 13 14 15
  781. X
  782. Xmv rc rc.bak1
  783. X
  784. X{
  785. X    cat ${ACTIVE}
  786. X    echo EOA
  787. X    cat rc.bak1
  788. X
  789. X} |
  790. Xawk '
  791. XBEGIN{
  792. X    act=1
  793. X}
  794. X$1 == "EOA" {
  795. X    act=0
  796. X    next
  797. X}
  798. XNF==4 {
  799. X    if (act) { 
  800. X        X[$1] = 1
  801. X        L[$1] = $2+0 
  802. X        if (L[$1] == 0) F[$1] = 0; else F[$1] = $3+0
  803. X    }
  804. X    next
  805. X}
  806. X$1 ~ /^#/ {
  807. X    print $0
  808. X    next
  809. X}
  810. XNF == 3 && ($1 == "!" || $1 == "+") {
  811. X    if (X[$3] != 1) next
  812. X    S[$3] = $1
  813. X    if (L[$3] >= $2+0)
  814. X        N[$3] = $2+0
  815. X    else
  816. X        N[$3] = F[$3]
  817. X    next
  818. X}
  819. XEND {
  820. X    for (g in N) printf("%s %06d %s\n", S[g], N[g], g)
  821. X}
  822. X' |
  823. Xsort +2 > rc
  824. X
  825. Xexit 0
  826. NO_NEWS_IS_GOOD_NEWS
  827. chmod 0644 nntidy.sh || echo "restore of nntidy.sh fails"
  828. set `wc -c nntidy.sh`;Sum=$1
  829. if test "$Sum" != "838"
  830. then echo original size 838, current size $Sum;fi
  831. echo "x - extracting nntp.c (Text)"
  832. sed 's/^X//' << 'NO_NEWS_IS_GOOD_NEWS' > nntp.c &&
  833. X/* 
  834. X * nntp module for nn.
  835. X *
  836. X * The original taken from the nntp 1.5 clientlib.c
  837. X * Modified heavily for nn.
  838. X *
  839. X * Rene' Seindal (seindal@diku.dk) Thu Dec  1 18:41:23 1988
  840. X *
  841. X * Last change: Tue May 23 18:38:49 1989
  842. X */
  843. X
  844. X
  845. X#include "config.h"
  846. X
  847. X/* 
  848. X *     nn maintains a cache of recently used articles to improve efficiency.
  849. X *     To change the size of the cache, define NNTPCACHE in config.h to be
  850. X *    the new size of this cache.
  851. X */
  852. X
  853. X#ifndef NNTPCACHE
  854. X#define NNTPCACHE    10
  855. X#endif
  856. X
  857. X#undef DEBUG
  858. X
  859. X#ifdef NNTP
  860. X#include <stdio.h>
  861. X#include "nntp.h"
  862. X#include <sys/types.h>
  863. X#include <sys/socket.h>
  864. X#include <netdb.h>
  865. X
  866. X/* This is necessary due to the definitions in m-XXX.h */
  867. X#ifdef NETWORK_BYTE_ORDER
  868. X#include <netinet/in.h>
  869. X#endif
  870. X
  871. Xexport char nntp_server[256];    /* name of nntp server */
  872. Xexport int use_nntp = 0;    /* bool: t iff we use nntp */
  873. Xexport int nntp_failed = 0;    /* bool: t iff connection is broken */
  874. X                /*  in nntp_get_article() or nntp_get_active() */
  875. Ximport int silent;
  876. Ximport char news_active[];
  877. X
  878. Xextern int errno, sys_nerr;
  879. Xextern char *sys_errlist[];
  880. X
  881. X#define syserr() (errno >= 0 && errno < sys_nerr ? \
  882. X          sys_errlist[errno] : "Illegal error.")
  883. X
  884. Xextern char *mktemp();
  885. X
  886. Xstatic FILE *nntp_in = NULL;        /* fp for reading from server */
  887. Xstatic FILE *nntp_out = NULL;        /* fp for writing to server */
  888. Xstatic int is_connected = 0;        /* bool: t iff we are connected */
  889. Xstatic group_header *group_hd;        /* ptr to servers current group */
  890. Xstatic int is_set = 0;            /* bool: t iff group_hd is set */
  891. X
  892. X#define ERROR_LEVEL    400        /* All codes below are not errors */
  893. X
  894. X
  895. X/* 
  896. X * nntp_find_server: Find out which host to use as NNTP server.
  897. X *
  898. X *     This is done by consulting the file NNTP_SERVER (defined in config.h).
  899. X *     Set nntp_server[] to the host's name.
  900. X */
  901. X
  902. Xvoid nntp_find_server()
  903. X{
  904. X    char *cp, *name, *getenv();
  905. X    char buf[BUFSIZ];
  906. X    FILE *fp;
  907. X    
  908. X    /* 
  909. X     * This feature cannot normally be enabled, because the data base
  910. X     * and the users rc file contains references to articles by number,
  911. X     * which is not unique across NNTP servers.
  912. X     */
  913. X#ifdef DEBUG 
  914. X    if ((cp = getenv("NNTPSERVER")) != NULL) {
  915. X    strncpy(nntp_server, cp, sizeof nntp_server);
  916. X    return;
  917. X    }
  918. X#endif /* DEBUG */
  919. X
  920. X    name = NNTP_SERVER;
  921. X    if (*name != '/')
  922. X    name = relative(lib_directory, name);
  923. X
  924. X    if ((fp = open_file(name, OPEN_READ)) != NULL) {
  925. X    while (fgets(buf, sizeof buf, fp) != 0) {
  926. X        if (*buf == '#' || *buf == '\n')
  927. X        continue;
  928. X        if ((cp = strchr(buf, '\n')) != 0)
  929. X        *cp = '\0';
  930. X        strncpy(nntp_server, buf, sizeof nntp_server);
  931. X        fclose(fp);
  932. X        return;
  933. X    }
  934. X    fclose(fp);
  935. X    }
  936. X    
  937. X    if (!is_master)
  938. X    printf("\nCannot find name of NNTP server.\nCheck %s\n", name);
  939. X
  940. X    sys_error("Failed to find name of NNTP server!");
  941. X}
  942. X
  943. X/* 
  944. X * nntp_check: Find out whether we need to use NNTP.
  945. X *
  946. X *     This is done by comparing the NNTP servers name with whatever
  947. X *     gethostname() returns.  
  948. X *    use_nntp and news_active[] are initialised as a side effect.
  949. X */
  950. X
  951. Xvoid nntp_check()
  952. X{
  953. X    char host[128];
  954. X    char *cp;
  955. X
  956. X    nntp_find_server();
  957. X    gethostname(host, sizeof host);
  958. X    use_nntp = strcmp(host, nntp_server) != 0; /* too simplistic ??? */
  959. X
  960. X    if (use_nntp) 
  961. X    strcpy(news_active, relative(db_directory, "ACTIVE"));
  962. X    else 
  963. X    strcpy(news_active, NEWS_ACTIVE);
  964. X}
  965. X
  966. X/* 
  967. X * nntp_server_init: initialise a connection to the nntp server.
  968. X *
  969. X *     It expects nntp_server[] to be set previously, by a call to
  970. X *     nntp_check.  It is called from nntp_get_article() and 
  971. X *    nntp_get_active() if there is no established connection.
  972. X */
  973. X
  974. Xnntp_server_init()
  975. X{
  976. X    int sockt_rd, sockt_wr;
  977. X    int response;
  978. X    char line[NNTP_STRLEN];
  979. X
  980. X    if (!is_master && !silent) {
  981. X    printf("\rConnecting to NNTP server %s ... ", nntp_server);
  982. X    fl;
  983. X    }
  984. X    nntp_failed = 1;
  985. X
  986. X    sockt_rd = nntp_get_socket();
  987. X    if ((nntp_in = fdopen(sockt_rd, "r")) == NULL)
  988. X        return -1;
  989. X    sockt_wr = dup(sockt_rd);
  990. X    if ((nntp_out = fdopen(sockt_wr, "w")) == NULL) {
  991. X        nntp_in = NULL;               /* from above */
  992. X        return -1;
  993. X    }
  994. X
  995. X    /* Now get the server's signon message */
  996. X    response = nntp_get_server(line, sizeof(line));
  997. X    if (response < 0 || response >= ERROR_LEVEL) {
  998. X    if (!is_master) {
  999. X        user_error("Failed to connect to NNTP server.\n%s",
  1000. X                response >= ERROR_LEVEL ? line : syserr());
  1001. X        /* NOTREACHED */
  1002. X    } else {
  1003. X        log_entry('N', "Failed to connect to NNTP server.\n%s",
  1004. X              response >= ERROR_LEVEL ? line : syserr());
  1005. X        fclose(nntp_out);
  1006. X        fclose(nntp_in);
  1007. X        return -1;
  1008. X    }
  1009. X    }
  1010. X    if (!is_master && !silent) {
  1011. X    fputs("ok.\r", stdout);
  1012. X    fl;
  1013. X    }
  1014. X    is_connected = 1;
  1015. X    nntp_failed = 0;
  1016. X    return 0;
  1017. X}
  1018. X
  1019. X/* 
  1020. X * nntp_get_socket:  get a connection to the nntp server.
  1021. X *
  1022. X *     Doesn't return in case of errors.
  1023. X */
  1024. X
  1025. Xnntp_get_socket()
  1026. X{
  1027. X    int s;
  1028. X    struct sockaddr_in sin;
  1029. X    struct servent *getservbyname(), *sp;
  1030. X    struct hostent *gethostbyname(), *hp;
  1031. X#ifdef h_addr
  1032. X    int     x = 0;
  1033. X    register char **cp;
  1034. X#endif
  1035. X    int (*errfct)() = is_master ? sys_error : user_error;
  1036. X
  1037. X    if ((sp = getservbyname("nntp", "tcp")) ==  NULL)
  1038. X    (*errfct)("nntp/tcp: Unknown service.\n");
  1039. X
  1040. X    if ((hp = gethostbyname(nntp_server)) == NULL)
  1041. X    (*errfct)("NNTP server %s unknown.\n", nntp_server);
  1042. X
  1043. X    bzero((char *) &sin, sizeof(sin));
  1044. X    sin.sin_family = hp->h_addrtype;
  1045. X    sin.sin_port = sp->s_port;
  1046. X
  1047. X#ifdef  h_addr
  1048. X    /* get a socket and initiate connection -- use multiple addresses */
  1049. X
  1050. X    for (cp = hp->h_addr_list; cp && *cp; cp++) {
  1051. X    s = socket(hp->h_addrtype, SOCK_STREAM, 0);
  1052. X    if (s < 0)
  1053. X        (*errfct)("Can't get NNTP socket: %s\n", syserr());
  1054. X    bcopy(*cp, (char *)&sin.sin_addr, hp->h_length);
  1055. X
  1056. X    x = connect(s, (struct sockaddr *)&sin, sizeof (sin));
  1057. X    if (x == 0)
  1058. X        break;
  1059. X    if (!is_master)
  1060. X        msg("Connecting to %s: %s", nntp_server, syserr());
  1061. X    (void) close(s);
  1062. X    }
  1063. X    if (x < 0 && !is_master)
  1064. X    (*errfct)("Giving up on NNTP server %s!\n", nntp_server);
  1065. X#else                    /* no name server */
  1066. X    if ((s = socket(AF_INET, SOCK_STREAM, 0)) < 0)
  1067. X    (*errfct)("Can't get NNTP socket: %s\n", syserr());
  1068. X
  1069. X    /* And then connect */
  1070. X    bcopy(hp->h_addr, (char *) &sin.sin_addr, hp->h_length);
  1071. X    if (connect(s, (struct sockaddr *) &sin, sizeof(sin)) < 0) {
  1072. X    if (is_master)
  1073. X        (*errfct)("Connecting to %s: %s", nntp_server, syserr());
  1074. X    }
  1075. X    
  1076. X#endif
  1077. X    return s;
  1078. X}
  1079. X
  1080. X/* 
  1081. X * nntp_put_server:  send a line to the nntp server.
  1082. X *
  1083. X *     Expects to be connected to the server.  
  1084. X */
  1085. X
  1086. Xnntp_put_server(string)
  1087. Xchar *string;
  1088. X{
  1089. X#ifdef DEBUG
  1090. X    msg(">>> %s", string);
  1091. X#endif
  1092. X    fprintf(nntp_out, "%s\r\n", string);
  1093. X    if (fflush(nntp_out) == EOF) {
  1094. X    nntp_error();
  1095. X    }
  1096. X}
  1097. X
  1098. X/* 
  1099. X * nntp_get_server_line: get a line from the server.
  1100. X *
  1101. X *     Expects to be connected to the server.  
  1102. X *     The line can be any kind of line i.e., either response or text.
  1103. X */
  1104. X
  1105. Xnntp_get_server_line(string, size)
  1106. Xchar *string;
  1107. Xint size;
  1108. X{
  1109. X    register char *cp, *nl;
  1110. X
  1111. X    errno = 0;
  1112. X    if (fgets(string, size, nntp_in) == NULL) {
  1113. X    nntp_error();
  1114. X    return -1;
  1115. X    }
  1116. X    for (cp = string, nl = NULL; *cp != NUL; cp++) {
  1117. X    if (*cp == CR) {
  1118. X        nl = cp;
  1119. X        break;
  1120. X    }
  1121. X    if (nl == NULL && *cp == NL) 
  1122. X        nl = cp;
  1123. X    }    
  1124. X    if (nl != NULL) *nl = NUL;
  1125. X
  1126. X    return 0;
  1127. X}
  1128. X
  1129. X/* 
  1130. X * nntp_get_server: get a response line from the server.
  1131. X *
  1132. X *     Expects to be connected to the server.  
  1133. X *     The numerical value of the response is returned.
  1134. X */
  1135. X
  1136. Xnntp_get_server(string, size)
  1137. Xchar *string;
  1138. Xint size;
  1139. X{
  1140. X    if (nntp_get_server_line(string, size) < 0)
  1141. X    return -1;
  1142. X    return isdigit(*string) ? atoi(string) : 0;
  1143. X}
  1144. X
  1145. X/* 
  1146. X * nntp_close_server: close the connection to the server.
  1147. X */
  1148. X
  1149. Xnntp_close_server()
  1150. X{
  1151. X    if (!is_connected)
  1152. X    return;
  1153. X
  1154. X    if (!nntp_failed) {            /* avoid infinite recursion */
  1155. X    char line[NNTP_STRLEN];
  1156. X
  1157. X    nntp_put_server("QUIT");
  1158. X    (void) nntp_get_server(line, sizeof line);
  1159. X    }
  1160. X
  1161. X    (void) fclose(nntp_out);
  1162. X    (void) fclose(nntp_in);
  1163. X
  1164. X    is_connected = 0;
  1165. X}
  1166. X
  1167. X/* 
  1168. X * nntp_ask_server:  ask the server a question and return the answer.
  1169. X *
  1170. X *    Expects to be connected to the server.  
  1171. X *    Returns the numerical value of the reponse, or -1 in case of errors.
  1172. X */
  1173. X
  1174. Xnntp_ask_server(string, size)
  1175. Xchar *string;
  1176. Xint size;
  1177. X{
  1178. X    (void)nntp_put_server(string);
  1179. X
  1180. X    if (nntp_get_server(string, size) < 0)
  1181. X    return -1;
  1182. X#ifdef DEBUG
  1183. X    msg("<<< %.75s", string);
  1184. X#endif
  1185. X    return isdigit(*string) ? atoi(string) : 0;
  1186. X}
  1187. X
  1188. X/* 
  1189. X * nntp_error: signal an error in talking to the server.
  1190. X *
  1191. X *     An nn client terminates a session with the user.  
  1192. X *    The master simply closes the connection, which is probably 
  1193. X *    inadequate.  nntp_failed is set, for use by the master to
  1194. X *    terminate collection.
  1195. X */
  1196. X
  1197. Xnntp_error()
  1198. X{
  1199. X    nntp_failed = 1;
  1200. X    if (is_master) {
  1201. X    log_entry('N', "Lost connection to server %s: %s", nntp_server, syserr());
  1202. X    if (is_connected)
  1203. X        nntp_close_server();
  1204. X    } else {
  1205. X#ifdef DEBUG
  1206. X    printf("Can't talk to NNTP server %s: %s", nntp_server, syserr());
  1207. X    abort();
  1208. X#else
  1209. X    user_error("Can't talk to NNTP server %s: %s", nntp_server, syserr());
  1210. X#endif
  1211. X    }
  1212. X}
  1213. X
  1214. X/* 
  1215. X * nntp_copy_text: copy text response into file.
  1216. X *
  1217. X *     Sends COMMAND to the server, and copies the following text response
  1218. X *     into an open file.
  1219. X *    Return -1 on error, 0 otherwise.
  1220. X */
  1221. X
  1222. Xnntp_copy_text(fp, command)
  1223. XFILE *fp;
  1224. Xchar *command;
  1225. X{
  1226. X    char buf[NNTP_STRLEN];
  1227. X    char *cp;
  1228. X    int n, nlines;
  1229. X
  1230. X    strcpy(buf, command);
  1231. X    if ((n = nntp_ask_server(buf, sizeof buf)) < 0) {
  1232. X    return -1;
  1233. X    }
  1234. X    if (n >= ERROR_LEVEL)
  1235. X    return -1;
  1236. X
  1237. X    nlines = 0;
  1238. X    while ((n = nntp_get_server_line(buf, sizeof buf)) >= 0) {
  1239. X    cp = buf;
  1240. X    if (*cp == '.')
  1241. X        if (*++cp == '\0')
  1242. X        return nlines > 0 ? 0 : -1;
  1243. X    fputs(cp, fp);
  1244. X    putc('\n', fp);
  1245. X    nlines++;
  1246. X    }
  1247. X    return -1;
  1248. X}
  1249. X
  1250. X/* 
  1251. X * nntp_get_active:  get a copy of the active file.
  1252. X *
  1253. X *     If we are the master get a copy of the file from the nntp server.
  1254. X *     nnadmin just uses the one we already got.  In this way the master 
  1255. X *    can maintain a remote copy of the servers active file.  
  1256. X *    We try to be a little smart, if not inefficient, about the 
  1257. X *    modification times on the local active file.
  1258. X *    Even when the master is running on the nntp server, a separate
  1259. X *    copy of the active file will be made for access via NFS.
  1260. X */
  1261. X
  1262. Xnntp_get_active()
  1263. X{
  1264. X    FILE *old, *new;
  1265. X    char bufo[NNTP_STRLEN], bufn[NNTP_STRLEN];
  1266. X    char *new_name;
  1267. X    int same;
  1268. X
  1269. X    if (!is_master) 
  1270. X    return access(news_active, 4);
  1271. X    
  1272. X    if (!is_connected && nntp_server_init() < 0)
  1273. X        return -1;
  1274. X
  1275. X    new_name = mktemp(relative(db_directory, ".actXXXXXX"));
  1276. X
  1277. X    if ((new = fopen(new_name, "w+")) == NULL)
  1278. X    return -1;
  1279. X    if (nntp_copy_text(new, "list") < 0) {
  1280. X    fclose(new);
  1281. X    unlink(new_name);
  1282. X    return -1;
  1283. X    }
  1284. X    rewind(new);
  1285. X    same = 0;
  1286. X    if ((old = open_file(news_active, OPEN_READ)) != NULL) {
  1287. X    do {
  1288. X        fgets(bufo, sizeof bufo, old);
  1289. X        fgets(bufn, sizeof bufn, new);
  1290. X    } while (!feof(old) && !feof(new) && strcmp(bufo, bufn) == 0);
  1291. X    same = feof(old) && feof(new);
  1292. X    fclose(old);
  1293. X    }
  1294. X    fclose(new);
  1295. X
  1296. X    if (same) 
  1297. X    unlink(new_name);
  1298. X    else
  1299. X    if (rename(new_name, news_active) != 0)
  1300. X        sys_error("Cannot rename %s to %s", new_name, news_active);
  1301. X
  1302. X    return 0;
  1303. X}
  1304. X
  1305. X/* 
  1306. X * The following functions implements a simple lru cache of recently
  1307. X * accessed articles.  It is a simple way to improve effeciency.  Files
  1308. X * must be kept by name, because the rest of the code expects to be able
  1309. X * to open an article multiple times, and get separate file pointers.
  1310. X */
  1311. X
  1312. Xstruct cache {
  1313. X    char        *file_name;    /* file name */
  1314. X    article_number    art;        /* article stored in file */
  1315. X    group_header    *grp;        /* from this group */
  1316. X    unsigned        time;        /* time last accessed */
  1317. X} cache[NNTPCACHE];
  1318. X
  1319. Xstatic unsigned cur_time = 1;        /* virtual time */
  1320. X
  1321. X/* 
  1322. X * nntp_search_cache: search the cache for an (article, group) pair.
  1323. X *
  1324. X *     Returns a pointer to the slot where it is, null otherwise 
  1325. X */
  1326. X
  1327. Xstruct cache *nntp_search_cache(art, gh)
  1328. Xarticle_number art;
  1329. Xgroup_header *gh;
  1330. X{
  1331. X    struct cache *cptr = cache;
  1332. X    int i;
  1333. X
  1334. X    for (i = 0; i < NNTPCACHE; i++, cptr++)
  1335. X    if (cptr->art == art && cptr->grp == gh) {
  1336. X        cptr->time = cur_time++;
  1337. X        return cptr;
  1338. X    }
  1339. X    return NULL;
  1340. X}
  1341. X
  1342. X/* 
  1343. X * nntp_new_slot: get a free cache slot.
  1344. X *
  1345. X *     Returns a pointer to the allocated slot.  
  1346. X *     Frees the old filename, and allocates a new, unused filename.
  1347. X *    The user's files are in ~/.nn, and the master's are in LIB_DIRECTORY.
  1348. X */
  1349. X
  1350. Xstruct cache *nntp_new_slot()
  1351. X{
  1352. X    struct cache *cptr = cache;
  1353. X    int i, lru;
  1354. X    unsigned min_time = cur_time;
  1355. X    char name[24];
  1356. X    
  1357. X    for (i = 0; i < NNTPCACHE; i++, cptr++)
  1358. X    if (min_time > cptr->time) {
  1359. X        min_time = cptr->time;
  1360. X        lru = i;
  1361. X    }
  1362. X    cptr = &cache[lru];
  1363. X
  1364. X    if (cptr->file_name) {
  1365. X    unlink(cptr->file_name);
  1366. X    free(cptr->file_name);
  1367. X    }
  1368. X    sprintf(name, "nn%02d-XXXXXX", lru);
  1369. X    cptr->file_name =
  1370. X    copy_str(relative(is_master ? lib_directory : nn_directory,
  1371. X              mktemp(name)));
  1372. X    cptr->time = cur_time++;
  1373. X#ifdef DEBUG
  1374. X    msg("cache: select slot %d, file name %s", lru, cptr->file_name);
  1375. X#endif
  1376. X    return cptr;
  1377. X}
  1378. X
  1379. X/* 
  1380. X * nntp_clean_cache: clean up the cache.
  1381. X *
  1382. X *     Removes all allocated files.
  1383. X */
  1384. X
  1385. Xvoid nntp_clean_cache()
  1386. X{
  1387. X    struct cache *cptr = cache;
  1388. X    int i;
  1389. X
  1390. X    for (i = 0; i < NNTPCACHE; i++, cptr++)
  1391. X    if (cptr->file_name)
  1392. X        unlink(cptr->file_name);
  1393. X}
  1394. X
  1395. X/* 
  1396. X * nntp_set_group: set the server's current group.
  1397. X *
  1398. X *     Actual communication is delayed until an article is accessed, to
  1399. X *     avoid unnecessary traffic.
  1400. X */
  1401. X
  1402. Xnntp_set_group(gh)
  1403. Xgroup_header *gh;
  1404. X{
  1405. X    group_hd = gh;
  1406. X    is_set = 0;
  1407. X    return 0;
  1408. X}
  1409. X
  1410. X/* 
  1411. X * nntp_get_article: get an article from the server.
  1412. X *
  1413. X *     Returns a FILE pointer.
  1414. X *    If necessary the server's current group is set.
  1415. X *    The article (header and body) are copied into a file, so they
  1416. X *    are seekable (nn likes that).
  1417. X */
  1418. X
  1419. XFILE *nntp_get_article(article)
  1420. Xarticle_number article;
  1421. X{
  1422. X    char buf[NNTP_STRLEN];
  1423. X    FILE *tmp;
  1424. X    struct cache *cptr;
  1425. X    int n;
  1426. X    
  1427. X    if (!is_connected && nntp_server_init() < 0) {
  1428. X    return NULL;
  1429. X    }
  1430. X    
  1431. X    /* 
  1432. X     * Set the server group to the current group
  1433. X     */
  1434. X    if (is_set == 0) {
  1435. X    sprintf(buf, "group %s", group_hd->group_name);
  1436. X    if ((n = nntp_ask_server(buf, sizeof buf)) < 0){
  1437. X        return NULL;
  1438. X    }
  1439. X    if (n >= ERROR_LEVEL)
  1440. X        return NULL;
  1441. X    is_set = 1;
  1442. X    }
  1443. X    /* 
  1444. X     * Search the cache for the requested article,and allocate a new
  1445. X     * slot if necessary.
  1446. X     */
  1447. X    cptr = nntp_search_cache(article, group_hd);
  1448. X    if (cptr != 0) {
  1449. X    return open_file(cptr->file_name, OPEN_READ);
  1450. X    } 
  1451. X    cptr = nntp_new_slot();
  1452. X
  1453. X    if ((tmp = open_file(cptr->file_name, OPEN_CREATE)) == NULL)
  1454. X    return NULL;
  1455. X
  1456. X    /* 
  1457. X     * Copy the article.
  1458. X     */
  1459. X    sprintf(buf, "article %d", article);
  1460. X    if (nntp_copy_text(tmp, buf) < 0) {
  1461. X    fclose(tmp);
  1462. X    return NULL;
  1463. X    }
  1464. X    fclose(tmp);
  1465. X    if ((tmp = open_file(cptr->file_name, OPEN_READ)) != NULL) {
  1466. X    cptr->art = article;
  1467. X    cptr->grp = group_hd;
  1468. X    }
  1469. X    return tmp;
  1470. X}
  1471. X
  1472. X/* 
  1473. X * nntp_cleanup:  clean up after an nntp session.
  1474. X *
  1475. X *    Called from nn_exit().
  1476. X */
  1477. X
  1478. Xnntp_cleanup()
  1479. X{
  1480. X    if (is_connected)
  1481. X    nntp_close_server();
  1482. X    nntp_clean_cache();
  1483. X}
  1484. X#endif /* NNTP */
  1485. X
  1486. NO_NEWS_IS_GOOD_NEWS
  1487. chmod 0644 nntp.c || echo "restore of nntp.c fails"
  1488. set `wc -c nntp.c`;Sum=$1
  1489. if test "$Sum" != "15036"
  1490. then echo original size 15036, current size $Sum;fi
  1491. echo "x - extracting nntp.h (Text)"
  1492. sed 's/^X//' << 'NO_NEWS_IS_GOOD_NEWS' > nntp.h &&
  1493. X/*
  1494. X * Response codes for NNTP server
  1495. X *
  1496. X * @(#)nntp.h    1.7    (Berkeley) 1/11/88
  1497. X *
  1498. X * First digit:
  1499. X *
  1500. X *    1xx    Informative message
  1501. X *    2xx    Command ok
  1502. X *    3xx    Command ok so far, continue
  1503. X *    4xx    Command was correct, but couldn't be performed
  1504. X *        for some specified reason.
  1505. X *    5xx    Command unimplemented, incorrect, or a
  1506. X *        program error has occured.
  1507. X *
  1508. X * Second digit:
  1509. X *
  1510. X *    x0x    Connection, setup, miscellaneous
  1511. X *    x1x    Newsgroup selection
  1512. X *    x2x    Article selection
  1513. X *    x3x    Distribution
  1514. X *    x4x    Posting
  1515. X */
  1516. X
  1517. X#define    CHAR_INF    '1'
  1518. X#define    CHAR_OK        '2'
  1519. X#define    CHAR_CONT    '3'
  1520. X#define    CHAR_ERR    '4'
  1521. X#define    CHAR_FATAL    '5'
  1522. X
  1523. X#define    INF_HELP    100    /* Help text on way */
  1524. X#define    INF_DEBUG    199    /* Debug output */
  1525. X
  1526. X#define    OK_CANPOST    200    /* Hello; you can post */
  1527. X#define    OK_NOPOST    201    /* Hello; you can't post */
  1528. X#define    OK_SLAVE    202    /* Slave status noted */
  1529. X#define    OK_GOODBYE    205    /* Closing connection */
  1530. X#define    OK_GROUP    211    /* Group selected */
  1531. X#define    OK_GROUPS    215    /* Newsgroups follow */
  1532. X#define    OK_ARTICLE    220    /* Article (head & body) follows */
  1533. X#define    OK_HEAD        221    /* Head follows */
  1534. X#define    OK_BODY        222    /* Body follows */
  1535. X#define    OK_NOTEXT    223    /* No text sent -- stat, next, last */
  1536. X#define    OK_NEWNEWS    230    /* New articles by message-id follow */
  1537. X#define    OK_NEWGROUPS    231    /* New newsgroups follow */
  1538. X#define    OK_XFERED    235    /* Article transferred successfully */
  1539. X#define    OK_POSTED    240    /* Article posted successfully */
  1540. X
  1541. X#define CONT_XFER    335    /* Continue to send article */
  1542. X#define    CONT_POST    340    /* Continue to post article */
  1543. X
  1544. X#define    ERR_GOODBYE    400    /* Have to hang up for some reason */
  1545. X#define    ERR_NOGROUP    411    /* No such newsgroup */
  1546. X#define    ERR_NCING    412    /* Not currently in newsgroup */
  1547. X#define    ERR_NOCRNT    420    /* No current article selected */
  1548. X#define    ERR_NONEXT    421    /* No next article in this group */
  1549. X#define    ERR_NOPREV    422    /* No previous article in this group */
  1550. X#define    ERR_NOARTIG    423    /* No such article in this group */
  1551. X#define ERR_NOART    430    /* No such article at all */
  1552. X#define ERR_GOTIT    435    /* Already got that article, don't send */
  1553. X#define ERR_XFERFAIL    436    /* Transfer failed */
  1554. X#define    ERR_XFERRJCT    437    /* Article rejected, don't resend */
  1555. X#define    ERR_NOPOST    440    /* Posting not allowed */
  1556. X#define    ERR_POSTFAIL    441    /* Posting failed */
  1557. X
  1558. X#define    ERR_COMMAND    500    /* Command not recognized */
  1559. X#define    ERR_CMDSYN    501    /* Command syntax error */
  1560. X#define    ERR_ACCESS    502    /* Access to server denied */
  1561. X#define ERR_FAULT    503    /* Program fault, command not performed */
  1562. X
  1563. X/* RFC 977 defines this; don't change it. */
  1564. X
  1565. X#define    NNTP_STRLEN    512
  1566. NO_NEWS_IS_GOOD_NEWS
  1567. chmod 0644 nntp.h || echo "restore of nntp.h fails"
  1568. set `wc -c nntp.h`;Sum=$1
  1569. if test "$Sum" != "2530"
  1570. then echo original size 2530, current size $Sum;fi
  1571. echo "x - extracting nnusage.1m (Text)"
  1572. sed 's/^X//' << 'NO_NEWS_IS_GOOD_NEWS' > nnusage.1m &&
  1573. X.TH NNUSAGE 1M "Release 6.1"
  1574. X.UC 4
  1575. X.SH NAME
  1576. Xnnusage \- display \fInn\fP usage statistics
  1577. X.SH SYNOPSIS
  1578. X.B nnusage
  1579. X[ -t ]
  1580. X.SH DESCRIPTION
  1581. X.B nnusage
  1582. Xwill extract the usage entries from the log file and calculate the
  1583. Xtotal usage time for each \fInn\fP user.
  1584. X.LP
  1585. XWithout options, the output will be sorted according to user names.
  1586. X.LP
  1587. XWith the \-t option, \fInnusage\fP will list the users ordered after
  1588. Xthe total usage time.
  1589. X.LP
  1590. XSince it is possible to
  1591. Xsuspend
  1592. X\fInn\fP, or leave the terminal while \fInn\fP is active, \fInn\fP
  1593. Xtries to be intelligent when it calculates the usage time so it will
  1594. Xtruly report the actual time spent on news reading.
  1595. X.SH FILES
  1596. X.DT
  1597. X.ta \w'$lib/Log'u+3m
  1598. X$lib/Log    The log file
  1599. X.DT
  1600. X.SH SEE ALSO
  1601. Xnn(1), nncheck(1), nngoback(1), nngrep(1), nntidy(1)
  1602. X.br
  1603. Xnnadmin(1M), nnquery(1M), nnmaster(8)
  1604. X.SH NOTES
  1605. XThe \fInn\fP package must have been compiled with the STATISTICS
  1606. Xoption turned on to produce the usage entries in the log file.
  1607. X.LP
  1608. XOnly \fInn\fP sessions longer than 5 minutes are registered in the log file.
  1609. X.SH AUTHOR
  1610. XKim F. Storm, Texas Instruments A/S, Denmark
  1611. X.br
  1612. XE-mail: storm@texas.dk
  1613. X
  1614. X
  1615. NO_NEWS_IS_GOOD_NEWS
  1616. chmod 0644 nnusage.1m || echo "restore of nnusage.1m fails"
  1617. set `wc -c nnusage.1m`;Sum=$1
  1618. if test "$Sum" != "1119"
  1619. then echo original size 1119, current size $Sum;fi
  1620. echo "x - extracting nnusage.sh (Text)"
  1621. sed 's/^X//' << 'NO_NEWS_IS_GOOD_NEWS' > nnusage.sh &&
  1622. X# CONFIG file is inserted here
  1623. X
  1624. Xif [ x"$1" = x'-t' ] ; then
  1625. X    SORTMODE="+1r"
  1626. Xelse
  1627. X    SORTMODE=""
  1628. Xfi
  1629. X
  1630. Xgrep '^U:' $LIB/Log |
  1631. X
  1632. Xawk ' 
  1633. XNF == 7 { 
  1634. X    if (split($7, t, ".") == 2) u[$5] += t[1] * 60 + t[2]
  1635. X}
  1636. XEND {
  1637. X    for (n in u) {
  1638. X        name=substr(n, 2, length(n)-3)
  1639. X        printf("%s%16d.%02d\n", name, u[n]/60, u[n]%60);
  1640. X    }
  1641. X}' |
  1642. X
  1643. Xsort $SORTMODE
  1644. NO_NEWS_IS_GOOD_NEWS
  1645. chmod 0644 nnusage.sh || echo "restore of nnusage.sh fails"
  1646. set `wc -c nnusage.sh`;Sum=$1
  1647. if test "$Sum" != "326"
  1648. then echo original size 326, current size $Sum;fi
  1649. echo "x - extracting options.c (Text)"
  1650. sed 's/^X//' << 'NO_NEWS_IS_GOOD_NEWS' > options.c &&
  1651. X/*
  1652. X * generic option parsing 
  1653. X *
  1654. X * (c) Copyright 1988, Kim F. Storm, storm@texas.dk
  1655. X */
  1656. X
  1657. X#include "config.h"
  1658. X#include "options.h"
  1659. X
  1660. Xstatic char **save_argv, *usage_mesg;
  1661. Xstatic struct option_descr *save_optd;
  1662. X
  1663. Xchar *program_name(av)
  1664. Xchar **av;
  1665. X{
  1666. X    char *cp;
  1667. X    
  1668. X    /* skip "/path/" part of program name */
  1669. X    if (cp = strrchr(*av, '/')) 
  1670. X    return cp + 1;
  1671. X    else
  1672. X    return *av;
  1673. X}
  1674. X
  1675. X    
  1676. Xparse_options(ac, av, envname, options, usage)
  1677. Xint ac;
  1678. Xchar **av, *envname;
  1679. Xstruct option_descr options[];
  1680. Xchar *usage;
  1681. X{
  1682. X    register char *cp, opt;
  1683. X    register struct option_descr *od;
  1684. X    extern int atoi();
  1685. X    int files;
  1686. X    char **names;
  1687. X    char *envinit;
  1688. X
  1689. X    save_argv = av;
  1690. X    save_optd = options;
  1691. X    
  1692. X    if (options == NULL) return 0;
  1693. X
  1694. X    usage_mesg = usage;
  1695. X    
  1696. X    --ac;
  1697. X    names = ++av;    
  1698. X    files = 0;
  1699. X    
  1700. X    envinit = envname ? getenv(envname) : NULL;
  1701. X    cp = envinit;
  1702. X
  1703. X next_option:
  1704. X    
  1705. X    if (envinit) {
  1706. X    while (*cp && isspace(*cp)) cp++;
  1707. X    if (*cp == '-') {
  1708. X        cp++;
  1709. X        goto next_option;
  1710. X    }
  1711. X    if (*cp == NUL) {
  1712. X        envinit = NULL;
  1713. X        goto next_option;
  1714. X    }
  1715. X    } else
  1716. X    if (cp == NULL || *cp == NUL) {
  1717. X        if ((cp = *av++) == NULL) {
  1718. X        *names = NULL;
  1719. X        return files;
  1720. X        }
  1721. X        ac--;
  1722. X        
  1723. X        if (*cp != '-') {
  1724. X        *names++ = cp;
  1725. X        cp = NULL;
  1726. X        files++;
  1727. X        goto next_option;
  1728. X        }
  1729. X        
  1730. X        cp++; /* skip - */
  1731. X    }
  1732. X    
  1733. X    opt = *cp++;
  1734. X    
  1735. X    for (od = options; od->option_letter; od++) {
  1736. X    if (od->option_letter != opt) continue;
  1737. X    
  1738. X    switch (od->option_type) {
  1739. X        
  1740. X     case 1:    /* BOOL_OPTION */
  1741. X        
  1742. X        *((int *)(od->option_address)) = !*((int *)(od->option_address));
  1743. X        goto next_option;
  1744. X        
  1745. X     case 2:    /* STRING_OPTION */
  1746. X     case 3:    /* OPTIONAL_STRING */
  1747. X        
  1748. X        /* -oSTR or -o STR */
  1749. X        
  1750. X        while (*cp && isspace(*cp)) cp++;
  1751. X        
  1752. X        if (*cp == NUL) {
  1753. X        if (envinit || ac == 0) {
  1754. X            if (od->option_type == 3) goto opt_str;
  1755. X            error("missing string argumet to -%c", opt);
  1756. X        }
  1757. X        cp = *av++;
  1758. X        ac--;
  1759. X        }
  1760. X        
  1761. X        if (od->option_type == 3 && *cp == '-') goto opt_str;
  1762. X        
  1763. X        *(od->option_address) = cp;
  1764. X        
  1765. X        if (envinit) {
  1766. X        while (*cp && !isspace(*cp)) cp++;
  1767. X        if (*cp) *cp++ = NUL;
  1768. X        } else
  1769. X        cp = NULL;
  1770. X        
  1771. X        goto next_option;
  1772. X        
  1773. X     opt_str:
  1774. X        *(od->option_address) = od->option_default;
  1775. X        goto next_option;
  1776. X        
  1777. X     case 4:
  1778. X     case 5:
  1779. X        
  1780. X        /* -oN or -o N */
  1781. X        
  1782. X        while (*cp && isspace(*cp)) cp++;
  1783. X        
  1784. X        if (*cp) {
  1785. X        if (!isdigit(*cp)) {
  1786. X            if (od->option_type == 5) goto opt_int;
  1787. X            error("non-numeric argument to -%c", opt);
  1788. X        }
  1789. X        } else {
  1790. X        if (envinit || ac == 0 || !isdigit(**av)) {
  1791. X            if (od->option_type == 5) goto opt_int;
  1792. X            error("missing argument to -%c", opt);
  1793. X        }
  1794. X        
  1795. X        cp = *av++;
  1796. X        ac--;
  1797. X        }
  1798. X        *((int *)(od->option_address)) = atoi(cp);
  1799. X        while (isdigit(*cp)) cp++;
  1800. X        goto next_option;
  1801. X        
  1802. X     opt_int:
  1803. X        *((int *)(od->option_address)) = (int)(od->option_default);
  1804. X        goto next_option;
  1805. X    }
  1806. X    }
  1807. X    
  1808. X    error("unknown option '-%c'", opt);
  1809. X    /*NOTREACHED*/
  1810. X}
  1811. X
  1812. X
  1813. Xstatic error(message, option_letter)
  1814. Xchar *message, option_letter;
  1815. X{
  1816. X    char *prog_name = program_name(save_argv);
  1817. X    
  1818. X    fprintf(stderr, "%s: ", prog_name);
  1819. X    fprintf(stderr, message, option_letter);
  1820. X    fputc('\n', stderr);
  1821. X
  1822. X    fprintf(stderr, "usage: %s", prog_name);
  1823. X    
  1824. X    dump_options(1, "");
  1825. X    dump_options(2, " STR");
  1826. X    dump_options(3, " [STR]");
  1827. X    dump_options(4, " NUM");
  1828. X    dump_options(5, " [NUM]");
  1829. X        
  1830. X    if (usage_mesg) fprintf(stderr, usage_mesg);
  1831. X    fputc(NL, stderr);
  1832. X
  1833. X    nn_exit(9);
  1834. X}
  1835. X
  1836. Xstatic dump_options(type, tail)
  1837. Xint type;
  1838. Xchar *tail;
  1839. X{
  1840. X    register struct option_descr *od;
  1841. X    int any = 0;
  1842. X    
  1843. X    for (od = save_optd; od->option_letter; od++) {
  1844. X    if (od->option_type != type) continue;
  1845. X    fprintf(stderr, any ? "%c" : " -%c", od->option_letter );
  1846. X    any++;
  1847. X    }
  1848. X    
  1849. X    if (any && tail && tail[0]) {
  1850. X    fprintf(stderr, "%s", tail);
  1851. X    }
  1852. X}
  1853. NO_NEWS_IS_GOOD_NEWS
  1854. chmod 0644 options.c || echo "restore of options.c fails"
  1855. set `wc -c options.c`;Sum=$1
  1856. if test "$Sum" != "3891"
  1857. then echo original size 3891, current size $Sum;fi
  1858. echo "x - extracting options.h (Text)"
  1859. sed 's/^X//' << 'NO_NEWS_IS_GOOD_NEWS' > options.h &&
  1860. X/*
  1861. X * options.h - include file for generic option parsing 
  1862. X *
  1863. X * (c) Copyright 1988, Kim F. Storm, storm@texas.dk
  1864. X */
  1865. X
  1866. X/*
  1867. X * To use this routine, you must a table called an Option_Description.
  1868. X * Each element in this table describes one possible option: 
  1869. X *    Its option letter
  1870. X *    Its argument type (if any)
  1871. X *    Whether an argument is mandatory or optional 
  1872. X *    The address of the variable holding the option value
  1873. X *    The defualt value if argument is optional
  1874. X *
  1875. X * Example:
  1876. X *
  1877. X *    A program accepts the following options:
  1878. X *        -a    [no value]
  1879. X *        -b N    [a numeric value]
  1880. X *        -p [N]    [an optional numeric value]
  1881. X *        -t S    [a string value]
  1882. X *
  1883. X * The corresponding option description table would then look like:
  1884. X * 
  1885. X *    #include <options.h>
  1886. X *    int a_flg = 1, b_value = 0, p_value = 0;
  1887. X *    char *t_string = "default";
  1888. X *
  1889. X *    Option_Description( options ) {
  1890. X *        'a', Bool_Option(a_flg),
  1891. X *        'b', Int_Option(b_value),
  1892. X *        'p', Int_Option_Optional(p_value, -1),
  1893. X *        't', String_Option(t_string),
  1894. X *        '\0',
  1895. X *     }
  1896. X * To parse the argument list - and the contents of the environment variable 
  1897. X * XXINIT, all that has to be done is to issue the following call:
  1898. X *
  1899. X *    files = parse_options(argc, argv, "XXINIT", options, NULL);
  1900. X *
  1901. X * If no environment variable is associated with the program, use NULL as
  1902. X * the third parameter.
  1903. X *
  1904. X * Upon return, the elements argv[1] .. argv[files] will contain
  1905. X * the file names (and other 'non-options') that occur in the argument list.
  1906. X *
  1907. X * The last NULL argument may be replaced by your own 'usage routine'
  1908. X * which will be called in the following way:
  1909. X *
  1910. X *    usage(pname)
  1911. X *    char *pname; /+ argv[0] without path +/
  1912. X *
  1913. X * 
  1914. X * char *program_name(argv)
  1915. X *
  1916. X * return a pointer to the last component of argv[0] (the program name with
  1917. X * with the path deleted).
  1918. X *
  1919. X
  1920. X */
  1921. X
  1922. X
  1923. Xstruct option_descr {
  1924. X    char    option_letter;
  1925. X    char    option_type;
  1926. X    char **    option_address;
  1927. X    char *    option_default;
  1928. X} ;
  1929. X
  1930. X
  1931. X#define    Option_Description(name) \
  1932. X    struct option_descr name[] =
  1933. X
  1934. X#define    Bool_Option(addr) \
  1935. X    1, (char **)(&addr), (char *)0
  1936. X
  1937. X#define    String_Option(addr) \
  1938. X    2, &addr, (char *)0
  1939. X
  1940. X#define String_Option_Optional(addr, default) \
  1941. X    3, &addr, default
  1942. X
  1943. X#define    Int_Option(addr) \
  1944. X    4, (char **)(&addr), (char *)0
  1945. X
  1946. X#define    Int_Option_Optional(addr, default) \
  1947. X    5, (char **)(&addr), (char *)default
  1948. NO_NEWS_IS_GOOD_NEWS
  1949. chmod 0644 options.h || echo "restore of options.h fails"
  1950. set `wc -c options.h`;Sum=$1
  1951. if test "$Sum" != "2333"
  1952. then echo original size 2333, current size $Sum;fi
  1953. echo "x - extracting pack_date.c (Text)"
  1954. sed 's/^X//' << 'NO_NEWS_IS_GOOD_NEWS' > pack_date.c &&
  1955. X#include "config.h"
  1956. X
  1957. X/* #define DATE_TEST /* never define this !! */
  1958. X
  1959. X/*
  1960. X *    Calculate an approximate "time_stamp" value for a date
  1961. X *    string.  The actual value is not at all critical, 
  1962. NO_NEWS_IS_GOOD_NEWS
  1963. echo "End of part 11"
  1964. echo "File pack_date.c is continued in part 12"
  1965. echo "12" > s2_seq_.tmp
  1966. exit 0
  1967. ---
  1968. Kim F. Storm        storm@texas.dk        Tel +45 429 174 00
  1969. Texas Instruments, Marielundvej 46E, DK-2730 Herlev, Denmark
  1970.       No news is good news, but nn is better!
  1971.  
  1972.