home *** CD-ROM | disk | FTP | other *** search
/ NetNews Usenet Archive 1993 #3 / NN_1993_3.iso / spool / alt / sources / 3114 < prev    next >
Encoding:
Internet Message Format  |  1993-01-25  |  103.0 KB

  1. Xref: sparky alt.sources:3114 eunet.sources:12
  2. Path: sparky!uunet!olivea!hal.com!decwrl!parc!biosci!uwm.edu!spool.mu.edu!howland.reston.ans.net!paladin.american.edu!news.univie.ac.at!hp4at!usenet
  3. From: paul@eunet.co.at (Paul Gillingwater)
  4. Newsgroups: alt.sources,eunet.sources
  5. Subject: EEP 1.61 -- bug-fix point release of .newsrc editor
  6. Summary: User friendly newsgroup subscription browser
  7. Message-ID: <1k6qmeINNblt@hp4at.eunet.co.at>
  8. Date: 27 Jan 93 20:18:22 GMT
  9. References: <1k4hm7INNo7l@nic.umass.edu>
  10. Followup-To: alt.sources.d
  11. Organization: EUnet EDV Dienstleistungs GmbH A-1010 Wien Austria
  12. Lines: 3366
  13. NNTP-Posting-Host: hp4at.eunet.co.at
  14.  
  15. Submitted-by: paul@actrix.co.at
  16. Archive-name: EEP 1.61 .newsrc editor (for rn, trn, nn)
  17.  
  18. What the heck -- everybody loves source, and the net has unlimited
  19. bandwidth (NOT!), so here it is again with a few tweaks to remove the
  20. nasty core dumps.  Enjoy!
  21.  
  22. # This is a shell archive.  Remove anything before this line,
  23. # then unpack it by saving it in a file and typing "sh file".
  24. #
  25. # Wrapped by Paul Gillingwater <paul@hp4at> on Wed Jan 27 21:07:48 1993
  26. #
  27. # This archive contains:
  28. #    README        eep.1        eep.h        eepmain.c    
  29. #    eepmenu.c    eepmisc.c    eepview.c    makefile    
  30. #    makefile.dos    makefile.unx    
  31. #
  32.  
  33. LANG=""; export LANG
  34. PATH=/bin:/usr/bin:$PATH; export PATH
  35.  
  36. echo x - README
  37. sed 's/^@//' >README <<'@EOF'
  38.     EEP (Easy Editor Program for .newsrc)
  39.  
  40.     (c) 1990-1993 Paul Gillingwater <paul@actrix.co.at>     
  41.  
  42.  
  43. This is EEP version 1.61, an experimental .newsrc editor program.
  44.  
  45. It is intended to work with news readers such as ``rn'' and ``trn'', 
  46. that create a .newsrc file in the $HOME directory of each user, in 
  47. order to track what messages have been read by them.  A major design
  48. objective is that it should be easy to use, even by someone who is
  49. not familiar with other UNIX editors.  There is however a distinct
  50. bias towards vi in some of the commands, although this is redeemed
  51. somewhat by use of curses, which means that up and down arrows may
  52. be used on many systems.  Another redeeming feature is the on-line
  53. help, activated with the ``?'' key.
  54.  
  55. Note that EEP is _NOT_ intended to be called from within ``rn'' or 
  56. ``trn'' by shelling out, since it will modify the .newsrc which is 
  57. presently on disk -- and which the newsreader may have open.
  58.  
  59. Eep was written by Paul Gillingwater <paul@actrix.co.at>.
  60. It was initially written for Actrix Information Exchange, a 
  61. Public Access UNIX BBS in Wellington, New Zealand.
  62.  
  63. This entire work is Copyrighted (C) 1991 by Paul Gillingwater, as
  64. the sole original author (apart from where otherwise explicitly
  65. acknowledged).  The source code provided is available for anyone to
  66. use in any way, EXCEPT that you may not sell it or pretend that you
  67. wrote it.  I would be happy if people wish to incorporate part or
  68. all of this in some other product, as long as you acknowledge me as
  69. original author of these parts.  If this code is to be incorporated 
  70. into a commercial product, I request that you contact me with regard 
  71. to licensing.
  72.  
  73. You may contact me care of:
  74.     Actrix Information Exchange
  75.     PO Box 11-410
  76.     Wellington
  77.     NEW ZEALAND
  78.  
  79. **** WARNING:  This program will modify your .newsrc file.  Please
  80. ensure you have a safe copy of this file to prevent an unfortunate 
  81. accident.
  82.  
  83. Revision History
  84. ================
  85. Version 1.0  ``Warts'n'All'' version released on limited (NZ only)
  86. distribution.  Made available via anon. FTP or UUCP from Actrix.
  87. Sent to various people to test porting to other platforms, and
  88. implemented on Actrix BBS since it might be useful for new users.  
  89. Released: July 20, 1991
  90.  
  91. Version 1.1  This version records the order in which newsgroups are
  92. found in the .newsrc file, and will maintain that order for display
  93. and output purposes.  In addition, any newsgroups which don't yet
  94. have any entry in the newsgroups file or newslocal will be sorted so
  95. they appear first -- this will help find new newsgroups quickly (but
  96. it's not a full solution).  
  97.  
  98. Version 1.2  A small but important change.  It seems that ``rn'' will
  99. check your .newsrc for newsgroup names not present, then will try to
  100. present you with a large list of so-called "New" newsgroups.  It
  101. seems the only way to stop this is to leave all the unsubscribed
  102. newsgroups in the .newsrc.  This seems somehow inelegant, but
  103. necessary.
  104.  
  105. Version 1.3  After a long hiatus, several important bug fixes 
  106. contributed by various net.people.  Credits follow:
  107.  
  108. Dean Roth <sysop@mixcom.com>
  109. Paul Close <pdc@sgi.com>
  110. Arjan de Vet <devet@win.tue.nl>
  111.  
  112. As well as bug fixes, some additional functionality:
  113.  o Newsgroups may be indexed by number (':' command)
  114.  o MS-DOS version developed using PDCurses
  115.  o Compatible with the Waffle system (MS-DOS and UNIX)
  116.  
  117. Version 1.4  The list of newsgroups now has a top and bottom,
  118. and commands to get there.  Newsgroups may be deleted from
  119. the .newsrc with the 'd' command.  The 'd' command (delete) 
  120. is now a toggle.
  121.  
  122. January 1993: Version 1.5:  Major new command is 'v'
  123. for view, which will open the directory and look through
  124. headers in articles to allow browsing subjects, keywords, 
  125. authors, dates, etc.  Using dirent.h for directory access.
  126.  
  127. Version 1.6:  Major rewrite of initialization code to change order of
  128. reading files.  Now the active file is read first, then the newsgroup
  129. and newslocal files, finally the .newsrc.  Changed the 'a' command so
  130. that it's a toggle, swapping between alphabetic sorting and the original
  131. order from the .newsrc.   Various minor pointer arithmetic bug fixes.
  132. A new feature is the ability to mark newsgroups with the "." key,
  133. then move them to above the cursor position.  Note that if multiple
  134. groups are marked then moved, they may not stay in the same relative
  135. order because of the sorting used.
  136.  
  137. Version 1.61: minor bug fixes to avoid feeding NULL pointers to
  138. strlen(), plus other improvements.
  139.  
  140. ------------------------------------------------------------
  141. News Descriptions
  142. =================
  143. A major benefit of Eep is that it is intended to provide a mechanism
  144. for selecting news groups based on more than just their name.  By
  145. using lists provided by Gene Spafford, a brief description of each
  146. newsgroup is presented on screen, allowing novices to make decisions
  147. on what to read more easily.  Although both ``rn'' and ``trn'' offer
  148. the L command to list news groups, this is unwieldy if you want to
  149. search for specific key words in newsgroup names or descriptions.
  150. Furthermore, Eep eliminates the necessity to actually type in the
  151. name of a newsgroup when subscribing to it.  Simply point and play!
  152.  
  153. Eep tries to be user friendly in moving around the .newsrc file.
  154. Experienced UNIX users will wish to use Emacs or vi to edit their
  155. @.newsrc for themselves.  Eep is intended for novice users, and is
  156. a good addition to UNIX based BBS systems.
  157.  
  158. Other features of Eep include the ability to subscribe or unsubscribe 
  159. to a newsgroup, delete newsgroups, and catch up on messages.  
  160.  
  161. Eep will read a list of newsgroups and descriptions from two files,
  162. "/usr/lib/news/newsgroups" or "/usr/lib/news/newslocal".
  163.  
  164. The NEWSGROUPS file contains one line per news group.  The first
  165. ``word'' (in the UNIX sense) on the line is the newsgroup name,
  166. separated from its description by a space or tab.  The NEWSLOCAL
  167. file is similar in structure, and is intended to contain groups
  168. which are purely local to your system, as well as the top level
  169. names of all of your hierarchies, plus other names as you desire for
  170. descriptive purposes.  
  171.  
  172. e.g.  few articles are posted in comp.unix, but it makes a great
  173. place to hold the description for all the directories under it.
  174.  
  175. Installation
  176. ------------
  177.  
  178. This release assumes that all news files are stored on one
  179. file system, and that the paths to the files may be safely hard
  180. coded.  Please edit the eep.h file to change where those paths may
  181. be on your system.
  182.  
  183. The /usr/lib/news/newsgroups should exist as a result of allowing the
  184. checkgroups script (part of 'C' news) to run.  Alternatively, you can
  185. create the file manually by editing the messages which are posted in 
  186. news.announce.newusers on a regular basis by the net.god, Gene Spafford.  
  187. You may embed comments in the file by starting the line with a ``#''.  
  188. The /usr/lib/news/newslocal file contains your own local newsgroups 
  189. (ones not covered by Gene's postings), or ones specific to your country.  
  190. It is also used to keep descriptions of top level hierarchies (which will 
  191. be used in a future version of this software).
  192.  
  193. The format of the newsgroups and newslocal files is very simple:
  194.  
  195. news.group.name description all on one line
  196.  
  197. The first space or tab is the separator.
  198.  
  199. Edit the makefile to set your preferences for compiler, then
  200. run ``make''.  I have tried to make the code relatively
  201. portable.  Please send me context diffs for any changes you'd like
  202. to suggest (or bugs, of which there are still a few).
  203.  
  204. For those with early Sun OS variants (prior to Solaris, which decided to
  205. do SYSV properly), mattair@ds62.synercom.hounix.org (Charles Mattair) writes:
  206.  
  207. > Sun-os is mostly a BSD derivative with the
  208. > brain dead BSD curses as its default libraries and include files.  However,
  209. > the SYSV variant is around under /usr/5bin/... and if you link with
  210. > /usr/5bin/cc, you automagically get SYSV curses and its associated include
  211. > files.
  212.  
  213. Oh, and for those who are wondering: "Why call it EEP?".  Well, may
  214. I refer you to Sol Libes' excellent book, ``Life With UNIX''.  There
  215. is a reference in there to an aspect of UniForum conferences in New
  216. Zealand, and the types of jokes found there.  Then consider that one
  217. can start a command by prefixing it with the name of the shell to
  218. execute, e.g.:
  219.     sh eep
  220.  
  221. <baaaah!>
  222. -----------
  223. The ``To Do'' list:
  224.  
  225. Although I use NNTP to fetch my news, I have not incorporated any of
  226. the hooks for NNTP in this version.  This is definitely planned, but
  227. not for some time.  I would welcome efforts in this direction from
  228. others.  Should EEP become a newsreader?  No, there are too many already.
  229.  
  230. The Help screen is really ugly.  
  231.  
  232. This version of eep will maintain the order of the .newsrc, except
  233. that newsgroups not in the .newsrc will be sorted AFTER other newsgroups.
  234.  
  235. Information on a newsgroup will include the actual number of
  236. articles found in the directory -- only upon request.  Note that
  237. this will impact portability, since it will have to open
  238. directories.
  239.  
  240. A later version will allow the ``!'' key to shell out to UNIX.
  241. Because some sites will use eep from inside a BBS, this may not be
  242. appropriate.  For that reason, the -! flag may be used to set the
  243. no_shell boolean.
  244.  
  245. The next version should have all files read (optionally) from
  246. environment variables.
  247.  
  248. The MS-DOS version will work with Waffle.  It will read the STATIC file
  249. as its active file, and will look at the user's JOIN file to obtain the
  250. equivalent of .newsrc.
  251.  
  252. Cut and paste, or at least mark and move.
  253.  
  254. Write out file for use with dynafeed.
  255.  
  256.  
  257. Design issues.
  258. --------------
  259.  
  260. (a)  Should eep sort the news groups into alphabetical order for
  261. reading?  The early version of eep will sort by group name when
  262. displaying descriptions on screen.  It seems useful to track the
  263. order in which they are read from the .newsrc, so that this order
  264. can be restored (if desired) by the user.  Further, it would follow
  265. from this that they should be displayed in this way, and that groups
  266. (or multiple groups) should be able to be moved around from one
  267. place to another.  On screen, this could be accomplished by marking
  268. the groups with a ``['' on the left edge of the screen, and use
  269. numbering, as well as Lotus-123 like anchoring for moving (NOT
  270. copying) the newsgroup lines.  Of course, ESC will cancel.
  271.  
  272. (b)  If we change the ordering mechanism to use the ability to move
  273. groups around, it may be more appropriate to change the indexing
  274. method into a linked list.  This linked list would be based upon the
  275. order in which the news groups were first read from the user's
  276. @.newsrc.
  277.  
  278. (c)  How should we deal with newsgroups that are not in the user's
  279. @.newsrc?  If they have trimmed down their .newsrc, then that is
  280. their choice.  We shouldn't litter it with a whole heap of
  281. unsubscribed news groups.  That implies that when we write out the
  282. @.newsrc, we don't output the unsubscribed ones.  But what if the
  283. user wants to keep track of how far they had read?  Philosophical
  284. issue, I guess, but I'm tempted to say that it's just too bad.   If
  285. the want to subscribe later, they can always catch up.
  286.  
  287. (d)  I don't fully understand (because I haven't tried to find out)
  288. the mechanism by which rn and trn detect that new news groups are
  289. present.  I suspect it is something to do with determining whether
  290. the last modification date of the active file is subsequent to the
  291. most recent modification date of one's .newsrc, but I don't know how
  292. it knows which groups specifically are new.  Eep is likely to
  293. interfere with this mechanism, because it will recreate the .newsrc,
  294. thus making to possible to miss automatic notification of new
  295. newsgroups.  The newsgroups will still appear in Eep's list of
  296. course.
  297.  
  298. (e) Some hooks are present to allow the root user to modify the
  299. active file.  This will be a later addition, along with possible
  300. editing of the newsgroups file to add descriptions.
  301.  
  302. (f) Early user feedback indicates the sorting the .newsrc is rather
  303. antisocial behaviour.  As a quick hack, it may be useful to record
  304. the order of appearance from the .newsrc file, and sort the array
  305. into this order immediately prior to displaying the opening screen.
  306. (This has been done as of version 1.1).
  307. There will of course be some newsgroups' descriptions NOT present 
  308. in the .newsrc, including the distributions file, as well as new
  309. newsgroups.  If we set the default value of the sorting field to 9999
  310. then these will appear at the end of the file.  This is good
  311. if they are new newsgroups, but maybe not so good if they are top
  312. level distribution names.  
  313.  
  314. (g)  One addition that has been planned since the germination of eep
  315. has been to present the newsgroups in a hierarchical structure.
  316. Currently there are close to 2,000 newsgroups (in New Zealand) and
  317. this is growing fast.  Having one long list, however you scroll
  318. through it, is not good enough for newer users.  Eventually, eep
  319. will show an opening menu of just the top-level groups (i.e. ones
  320. with not "." in their name).  Users will then "drill-down" to the
  321. next level, and thus will see the hierarchy as an inverted tree.  As
  322. a prelude to this, and taking (h) into account, it seems appropriate
  323. to add code to take the top-level names separately, and not show
  324. them in the main list.  
  325.  
  326. (h) Most of the top-level names may be found in the
  327. /usr/lib/news/distributions file, however not all.  For example,
  328. there are some news groups which have a single name, e.g. control,
  329. junk.  Should these be shown at all?  I think so -- but it's not
  330. appropriate to treat them as top levels.  Perhaps these specials
  331. could be kept in newslocal file.
  332. @EOF
  333.  
  334. chmod 600 README
  335.  
  336. echo x - eep.1
  337. sed 's/^@//' >eep.1 <<'@EOF'
  338. @.TH EEP 1 "Eep v1.6" "by Paul Gillingwater"
  339. @.SH NAME
  340. eep v1.6 -- Easy Editor Program for .newsrc
  341. @.SH SYNOPSIS
  342. @.B eep [-p]
  343. @.PP
  344. @.SH DESCRIPTION
  345. @.I Eep\^
  346. is a user-friendly full-screen editor designed to 
  347. modify the .newsrc file associated with the
  348. @.B rn
  349. or
  350. @.B trn
  351. news readers.  It is NOT intended to be used while the .newsrc
  352. is currently open, i.e. by shelling out.
  353. @.P
  354. The program will read the .newsrc file from the $HOME directory
  355. of the user, and will combine this with a list of descriptions
  356. taken from /usr/lib/news/newsgroups and /usr/lib/news/newslocal.
  357. Further information will be taken from the news active file, which
  358. is maintained by the ``B'' or ``C'' news systems.
  359. @.P
  360. Curses is used to draw a list of the newsgroups, which may be
  361. navigated using arrow keys as well as commands similar to the 
  362. @.B vi
  363. editor.  Newsgroups which are currently subscribed to will be
  364. prefixed with a ``+'' character, while those not subscribed to 
  365. will have a SPACE.  If the newsgroup is invalid in same way, e.g.
  366. it is bogus (not known in the active file) it will be marked with
  367. a ``?''.  Pressing the ENTER key will show further information about 
  368. the highlighted newsgroup, including its position in the file, low
  369. and high message numbers, and whether it is active, inactive or
  370. moderated.  Other commands allow the user to subscribe or
  371. unsubscribe to newsgroups, delete them from their .newsrc
  372. or catch up (mark all messages as read).
  373. @.P
  374. @.SH
  375. COMMANDS
  376. @.P
  377. Here is a summary of commands available (upper and lower case
  378. letters are treated the same.)  Note that some commands have more
  379. than one character to activate them, to offer variety of choice.
  380. @.P
  381. @.B
  382. Movement
  383. @.nf
  384.  
  385.    ^n         )    Move pointer down one line
  386.    SPACE      )
  387.    j          )
  388.    Down_Arrow )
  389.  
  390.    ^p         )    Move pointer up one line
  391.    BACKSPACE  )
  392.    k          )
  393.    Up_Arrow   )
  394.  
  395.    ^d         )    Move pointer down one page
  396.    ^f         )
  397.  
  398.    ^u         )    Move pointer up one page
  399.    ^b         )
  400.  
  401.    t          )    Move to top of file
  402.    ^          )
  403.  
  404.    b          )    Move to bottom of file
  405.    $          )
  406.  
  407.    :          )    Select newsgroup by number
  408. @.fi
  409. @.PP
  410. @.B
  411. Searching
  412. @.nf
  413.  
  414.    /          )    Search for string of text
  415.  
  416.    n          )    Search again
  417. @.fi
  418. @.PP
  419. @.B
  420. Subscription
  421. @.nf
  422.  
  423.    s          )    Subscribe to current newsgroup
  424.    +          )
  425.  
  426.    u          )    Unsubscribe to current newsgroup
  427.    -          )
  428.  
  429.    d          )    Toggle delete flag on current newsgroup
  430. @.fi
  431. @.PP
  432. @.B
  433. Other
  434. @.nf
  435.  
  436.    ?          )    Display help screen
  437.    h          )
  438.  
  439.    p          )    Change type of pointer
  440.  
  441.    a          )    Toggle: Alphabetise the list or back to .newsrc order
  442.  
  443.    c          )    Catch up (mark all messages as read)
  444.  
  445.    .          )    Toggle mark.  This is used to select
  446.                    groups to be moved with the (m) Move
  447.                    command.
  448.  
  449.    m          )    Move marked groups to current position.
  450.  
  451.    i          )    Show more information on newsgroup
  452.  
  453.    r          )    Redraw screen
  454.    ^L         )
  455.  
  456.    v          )    View list of messages in newsgroup
  457.    =          )
  458.    ENTER      )
  459. @.fi
  460. @.PP
  461. @.B
  462. Exit  
  463. @.nf
  464.  
  465.    q          )    Quit without saving changes
  466.    ESC        )
  467.    ^C         )
  468.    DEL        )
  469.  
  470.    x          )    Exit and save changes
  471. @.fi
  472. @.PP
  473. Options are:
  474. @.TP 1.0i
  475. @.B "  -p"
  476. Change the pointer used from scroll bar to simple arrow.  This is
  477. faster at low modem speeds when moving through the file.
  478.  
  479. @.SH AUTHOR
  480. Paul Gillingwater, Actrix Information Exchange <paul@actrix.gen.nz>
  481. @.SH WARNINGS
  482. Your existing .newsrc will be renamed to .newsrc.old, deleting any
  483. existing file of that name.  This will occur only when you exit with
  484. the
  485. @.B "``x''"
  486. command.  Until then,
  487. @.B "eep's"
  488. working file is .newsrc.new.
  489. @.P
  490. New newsgroups will not automatically be notified.  Newsgroups 
  491. which are not present in your .newsrc file, but are found in the
  492. active file, will be sorted to the bottom of the list.
  493. @.P
  494. When moving a set of newsgroups with the mark and move commands,
  495. the set will not necessarily stay in the same relative order.
  496. @.P
  497. Bogus newsgroups (those not found in the active file, but present
  498. in your .newsrc) will be (optionally) deleted.
  499. @.SH FILES
  500. $HOME/.newsrc                 file to be modified
  501. @.br
  502. $HOME/.newsrc.old             backup of previous .newsrc
  503. @.br
  504. $HOME/.newsrc.eep             temporary working file
  505. @.br
  506. /usr/lib/news/newsgroups      } names and descriptions 
  507. @.br
  508. /usr/lib/news/newslocal       } of newsgroups
  509. @.br
  510. /usr/lib/news/active          active newsgroups file
  511. @.SH BUG REPORTS TO
  512. Paul Gillingwater <paul@actrix.gen.nz>
  513. @.SH COPYRIGHTS
  514. @.ps 18
  515. \fB\(co\fR\s12 Copyright 1991-1993 by Paul Gillingwater
  516. @EOF
  517.  
  518. chmod 600 eep.1
  519.  
  520. echo x - eep.h
  521. cat >eep.h <<'@EOF'
  522. /*  eep.h
  523.  
  524. This is the common header file for the eep program.
  525.  
  526. */
  527.  
  528. #ifdef UNIX
  529. #define NEWSBASE "/usr/spool/news/"  /* where news lives */
  530. #define ACTIVEFILE "/usr/lib/news/active" /* active file */
  531. #define NEWSGROUPS "/usr/lib/news/newsgroups" /* descriptions of newsgroups */
  532. #define NEWSLOCAL "/usr/lib/news/newslocal" /* local newsgroups */
  533. #define NEWSRC ".newsrc" /* filename for .newsrc */
  534. #define CRLF    "\n"
  535. #define SHELL "/bin/ksh" /* default shell */
  536. #endif /* UNIX */
  537.  
  538. #ifdef DOS
  539. #define NEWSBASE "/news/"  /* where news lives */
  540. #define ACTIVEFILE "active" /* active file */
  541. #define NEWSGROUPS "newsgroups" /* descriptions of newsgroups */
  542. #define NEWSLOCAL "newslocal" /* local newsgroups */
  543. #define NEWSRC "newsrc" /* filename for .newsrc */
  544. #define CRLF    "\r\n"
  545. #endif /* DOS */
  546.  
  547. #ifndef FALSE
  548. #define FALSE 0        /* this may be redefined elsewhere */
  549. #endif
  550. #ifndef TRUE
  551. #define TRUE 1        /* this may be redefined elsewhere */
  552. #endif
  553. #define EEPLINES 24    /* lines shown on screen */
  554. #define EEPCOLUMNS 80    /* columns shown on screen */
  555. #define EEPPAGE EEPLINES-2    /* page size for scrolling */
  556. #define BUFSIZE 4096    /* general line buffer length */
  557. #define MAXLEVELS 20    /* depth of news hierachies */
  558. #ifdef DOS        /* limitations of memory models */
  559. #define MAXLINES 6000   /* max. number of news groups */
  560. #define MAXARTS 600
  561. #endif /* DOS */
  562. #ifdef UNIX
  563. #define MAXLINES 20000   /* thank Cthulu for virtual memory */
  564. #define MAXARTS 2000    
  565. #endif /* UNIX */
  566. #define MAXDIST 40    /* number of top level distribution names */
  567. #define TIMEOUT 2    /* keyboard inactivity timer in minutes */
  568. #define SHELLDENY 3    /* uid for user to deny shell access to */
  569.  
  570. /* This is the primary data storage structure.  It combines both the
  571. active file and the .newsrc file.  We'll malloc() space for each
  572. element of the structure, and use an array of pointers to access it
  573. */
  574.  
  575. struct actif    {
  576.  
  577. char    *name,  /* news group */
  578.     *desc,    /* description of newsgroup */
  579.     *hilo;    /* high to low range from my .newsrc */
  580.  
  581. long    hi;     /* hi message number from active file */
  582.  
  583. int    index,    /* used as alternate index into array */
  584.         mark;   /* used to mark groups to be moved */
  585.  
  586. char    flag,    /* [ynm] from active file */
  587.     status;    /* [:!] from .newsrc file */
  588.  
  589. struct  actif *depth;    /* pointer to next entry in ``depth'' list */
  590. };
  591.  
  592. @EOF
  593.  
  594. chmod 600 eep.h
  595.  
  596. echo x - eepmain.c
  597. cat >eepmain.c <<'@EOF'
  598. /*------------------------------------------------------------------------
  599.        Name: eepmain.c
  600.  
  601. This program makes it easier for users to change their .newsrc file.  
  602. It uses curses to implement a simple editor, which allows users to 
  603. join or unjoin newsgroups, from outside of rn or trn.
  604.  
  605.      Author: Paul Gillingwater, paul@actrix.gen.nz
  606.  
  607. Usage:
  608.     eep [-p] [-!]
  609.  
  610. Options:
  611.     -p:  Use a terse pointer on screen instead of a bar
  612.     -!:  Set flag to disallow user shell out
  613.  
  614. ------------------------------------------------------------------------*/
  615.  
  616. #include <stdio.h>
  617. #include <string.h>
  618. #include <ctype.h>
  619. #include <signal.h>
  620.  
  621. #ifdef ANSI
  622. #include <stdlib.h>
  623. #else
  624. #define void int
  625. extern char *malloc();
  626. extern char *getenv();
  627. #endif /* ANSI */
  628.  
  629. #include "eep.h"
  630.  
  631. /* Some function definitions */
  632.  
  633. extern void    newsmain();
  634.  
  635. char    buffer[BUFSIZE], /* general purpose line buffer */
  636.         tmp[BUFSIZE],
  637.         s_hi[30],       /* strings for numbers */
  638.         s_lo[30],       /* strings for numbers */
  639.         s_flag[8];
  640. char    t_status,       /* from .newsrc file */
  641.         t_flag;         /* from active file */
  642. char    *ptr;           /* general purpose pointer */
  643. char    *bog_msg = "Bogus newsgroup (not in active file)";
  644.  
  645. int     eepoint = 0,    /* -p flag for screen pointer type */
  646.         noshell = 0,    /* -! flag to deny shell-out */
  647.         uid = 0,        /* real user id */
  648.         result;         /* flag for searching */
  649.  
  650. FILE    *fnewsrc,
  651.         *factive;
  652.  
  653. struct  actif *act[MAXLINES];  /* here's the main array */
  654. struct  actif *topact[MAXDIST]; /* array for top level distributions only */
  655. struct  actif *aptr;    /* temporary pointer */
  656.  
  657. int     i_active,    /* index into actif arrays */
  658.         c_active,    /* number of elements in act array */
  659.         t_active,    /* number of elements in topact array */
  660.         bog_count;    /* count of bogus newsgroups */
  661. int     high,low,mid;    /* used for binary chop searching */
  662.  
  663. /* The levels array contains the Head pointers which control the
  664. linked lists of newsgroups grouped by their "depth" in the
  665. hierarchy, and sorted alphabetically.  E.g. alt, sci, soc are 
  666. level 0, alt.pagan, sci.physics are level 1 and so on. */
  667.  
  668. struct actif *levels[MAXLEVELS];  /* keep track of levels with this array */
  669.  
  670. int    i_levels;    /* index into array */
  671.  
  672. /* Comparison functions for qsort.  These functions are used for
  673. sorting the array of pointers that point to our actif data structures. */
  674.  
  675. int    qcompare(item1,item2)    /* sort by name */
  676. struct actif    **item1, **item2;
  677. {
  678. struct actif    *ptr1, *ptr2;
  679.     ptr1 = (struct actif *) *item1;
  680.     ptr2 = (struct actif *) *item2;
  681.         return (strcmp(ptr1->name, ptr2->name));
  682. }
  683.  
  684. int    icompare(item1,item2)    /* sort by index number */
  685. struct actif    **item1, **item2;
  686. {
  687. struct actif    *ptr1, *ptr2;
  688.     ptr1 = (struct actif *) *item1;
  689.     ptr2 = (struct actif *) *item2;
  690.         return (ptr1->index - ptr2->index);
  691. }
  692.  
  693. /* This routine will read the a file (descfile) containing names and
  694.    descriptions of newsgroups, matching it with the active file.  */
  695.  
  696. void    read_desc(descfile)
  697. FILE    *descfile;
  698. {
  699. char    *name, *desc;  /* pointers to name and description */
  700.  
  701.     i_active = 0;      /* start with first one of course */
  702.     aptr = (struct actif *)NULL;
  703.  
  704.     while (fgets(buffer,BUFSIZE,descfile) != (char *)NULL) {
  705.  
  706.     /* ignore comment lines or blank lines */
  707.         switch(buffer[0]) {
  708.         case '#':
  709.         case '\n':
  710.         case '\r':
  711.         case '\0':
  712.             continue;
  713.         }
  714.  
  715.     /* Get name and description.  If either is absent, skip this line. */
  716.         if ((name = strtok(buffer, " \t\r\n")) == (char *)NULL) continue;
  717.         if ((desc = strtok((char *)NULL, "\r\n")) == (char *)NULL) continue;
  718.  
  719.     /* Advance over any whitespace preceding description */
  720.         while ((*(desc) == ' ') || (*(desc) == '\t')) desc++;
  721.  
  722.     /* Although we cannot assume that the newsgroups are in alphabetical
  723.      * order, we'll try looking at the next one anyway before doing a
  724.      * search. */
  725.  
  726.         if (i_active <= c_active)    /* range check */
  727.             aptr = act[i_active];
  728.  
  729.     /* This next line should never happen.  It would only occur if
  730.      * we've run out of things to match! */
  731.  
  732.         if (aptr == (struct actif *)NULL) {
  733.             printf("This should never happen!\n");
  734.             continue;
  735.         }
  736.  
  737.     /* Now compare the name read with current position in active file */
  738.  
  739.         if (strcmp(name,aptr->name) == 0) {
  740.  
  741. /* Here we look for the best description possible, i.e. one that is not 
  742. null, and preferably longest (i.e. not just a '?').  This will be because 
  743. we may find duplicate lines in the newsgroups file.  Also, it may be 
  744. possible to find no description at all. */
  745.  
  746.             if (aptr->desc != (char *)NULL) {
  747.             /* don't accept an inferior description */
  748.                 if (strlen(desc) <= strlen(aptr->desc)) continue;
  749.  
  750. #ifdef UNIX /* free up the memory previously occupied */
  751.                 if ((aptr->desc != (char *)NULL) &&
  752.                     (aptr->desc != bog_msg)) free(aptr->desc);
  753. #endif /* UNIX */
  754.             }
  755.  
  756.         /* allocate space for string + null byte, then copy it in */
  757.             if ((aptr->desc = (char *)malloc(strlen(desc)+1)) == (char *)NULL) {
  758.                 printf("Fatal error while allocating memory!\n");
  759.                 exit(1);
  760.             }
  761.             strcpy(aptr->desc,desc);
  762.             i_active++;      /* advance index to next pointer */
  763.             if (i_active == c_active) i_active = 0;
  764.             continue;
  765.         }
  766.  
  767.     /*  Here we begin a binary chop search, comparing the newsgroup
  768.      *  name we have just read from the newsgroups file with the list
  769.      *  of newsgroups read from the active file.  */
  770.  
  771.         low = 0;
  772.         high = c_active - 1;
  773. loop1:
  774.         if (low <= high) {
  775.             mid = (low+high)/2;
  776.             aptr = act[mid];
  777.             result = strcmp(name,aptr->name);
  778.             if (result == 0) {
  779.  
  780.                 if (aptr->desc != (char *)NULL) {
  781.                 /* don't accept an inferior description */
  782.                     if (strlen(desc) <= strlen(aptr->desc)) continue;
  783.  
  784. #ifdef UNIX /* free up the memory previously occupied */
  785.                     if ((aptr->desc != (char *)NULL) &&
  786.                         (aptr->desc != bog_msg)) free(aptr->desc);
  787. #endif /* UNIX */
  788.                 }
  789.  
  790.             /* allocate space for string + null byte, then copy it in */
  791.                 if ((aptr->desc = (char *)malloc(strlen(desc)+1))
  792.                    == (char *)NULL) {
  793.                     printf("Fatal error while allocating memory!\n");
  794.                     exit(1);
  795.                 }
  796.                 strcpy(aptr->desc,desc);
  797.  
  798.                 i_active = mid + 1;
  799.                 if (i_active == c_active) i_active = 0;
  800.                 continue; /* with read loop */
  801.             } else
  802.             if (result > 0) { /* after */
  803.                 low = mid+1;
  804.                 goto loop1;
  805.             } else
  806.             if (result < 0) { /* before */
  807.                 high = mid-1;
  808.                 goto loop1;
  809.             }
  810.         }
  811.     }
  812.     fclose(descfile); 
  813. }
  814.  
  815. /* Initialise the various chunks of memory and read in files. */
  816.  
  817. void    initial()
  818. {
  819.  
  820. int    newsrc_order;    /* track reading order from .newsrc */
  821. int    warning = FALSE; /* if warning messages have been issued */
  822.  
  823. char   *name,   /* pointers into line in active file */
  824.        *hi,
  825.        *lo,
  826.        *flag;
  827.  
  828. #ifdef UNIX
  829.     uid = getuid();
  830.     /* For systems with a fixed BBS login, we have this hack
  831.     to deny shell access. */
  832.     if (uid == SHELLDENY) noshell++;
  833. #endif /* UNIX */
  834.  
  835. /* CHANGES for JAN 1993: ************************************
  836.  
  837. Because NEWSGROUPS can often contain lots of duplicates, while
  838. the active file is "cleaner", let's read the active file FIRST,
  839. then scan the NEWSGROUPS and NEWSLOCAL file looking for
  840. descriptions.  We should try to find the best description, i.e.
  841. one that is not null, not a '?', and not "alt group".
  842. A simple sequential read of these files is adequate.  Note
  843. that the active file is authoritative when it comes to bogus
  844. groups, which means we could simply ignore entries from the
  845. NEWSGROUPS file that aren't in the active file.  
  846.  
  847. When we read the .newsrc, we still need to add entries for
  848. bogus groups, to give people the option of keeping strange
  849. stuff in their .newsrc.  This means we should prompt before
  850. saving whether they want to lose the bogus groups or not.
  851.  
  852. *************************************************************/
  853.  
  854.     c_active = 0;    /* count the newsgroups as we go. */
  855.  
  856.     if ((factive = fopen(ACTIVEFILE, "r")) == (FILE *)NULL) {
  857.         printf("Fatal: Unable to read %s\n",ACTIVEFILE);
  858.         exit(1);
  859.     }
  860.  
  861.     while (fgets(buffer,BUFSIZE,factive) != (char *)NULL) {
  862.  
  863.     /* ignore comment lines or blank lines */
  864.         switch(buffer[0]) {
  865.         case '#':
  866.         case '\n':
  867.         case '\r':
  868.         case '\0':
  869.             continue;
  870.         }
  871.  
  872.     /* strip off newlines or returns */
  873.         while ((ptr = strchr(buffer,'\n')) != (char *)NULL) *ptr = '\0';
  874.         while ((ptr = strchr(buffer,'\r')) != (char *)NULL) *ptr = '\0';
  875.  
  876.     /* process line from active file -- low message number ignored. */
  877.         if ((name = strtok(buffer, " \t\r\n")) == (char *)NULL) continue;
  878.         if ((hi   = strtok((char *)NULL, " \t\r\n")) == (char *)NULL) continue;
  879.         if ((lo   = strtok((char *)NULL, " \t\r\n")) == (char *)NULL) continue;
  880.         if ((flag = strtok((char *)NULL, " \t\r\n")) == (char *)NULL) continue;
  881.  
  882.     /* check validity of data.  Must have something in each position. */
  883.  
  884.     /* Now allocate a chunk of memory for actif */
  885.         if ((aptr = (struct actif *) malloc(sizeof(struct actif)))
  886.             == (struct actif *)NULL) {
  887.             printf("Fatal error while allocating memory!\n");
  888.             exit(1);
  889.         }
  890.         act[c_active] = aptr; /* record this pointer */
  891.  
  892.     /* allocate space for string + null byte, then copy it in */
  893.         if ((aptr->name = (char *)malloc(strlen(name)+1)) == (char *)NULL) {
  894.             printf("Fatal error while allocating memory!\n");
  895.             exit(1);
  896.         }
  897.  
  898.     /* now move data into the structure */
  899.         strcpy(aptr->name,name);
  900.         aptr->hi = atol(hi);
  901.         aptr->hilo = (char *)NULL;
  902.         aptr->desc = (char *)NULL;
  903.         aptr->mark = 0;            /* mark flag */
  904.         aptr->flag = flag[0];      /* from active file */
  905.         aptr->status = '!';        /* from .newsrc */
  906.         aptr->index = 9999;        /* sort unknowns at end */
  907.         aptr->depth = (struct actif *) NULL;
  908.         c_active++;
  909.     }
  910.     fclose(factive);
  911.  
  912.     if (c_active == 0) {
  913.         printf("Fatal: Can't find any news groups in %s\n",ACTIVEFILE);
  914.         exit(1);
  915.     }
  916.  
  917. /* Sort the list by newsgroup name. */
  918.     qsort( act, (unsigned) c_active,(unsigned) sizeof(act[0]), qcompare);
  919.  
  920. /* Now open NEWSGROUPS and NEWSLOCAL to look for descriptions.  If
  921.    these files don't exist, it's no great tragedy -- but the whole
  922.    point of EEP will be missed.  */
  923.      
  924.     if ((factive = fopen(NEWSGROUPS, "r")) != (FILE *)NULL)
  925.         read_desc(factive);
  926.  
  927.     if ((factive = fopen(NEWSLOCAL, "r")) != (FILE *)NULL)
  928.         read_desc(factive);
  929.  
  930.  /* Let's now build chains of pointers for each of the 
  931.     hierarchies of news groups, taking our initial pointer
  932.     from the array levels[]. */
  933.  
  934.     i_levels = 0;
  935.     while (i_levels < MAXLEVELS) 
  936.         levels[i_levels++] = (struct actif *) NULL; /* null array */
  937.  
  938.  /* Work backwards through the array, building the linked list.
  939.     We also record the array index in each record.  If we were
  940.     only accessing it as an array, this would be redundant, but
  941.     we're using multiple linked lists of pointers as well.  
  942.     We work backwards to ensure our chains are NULL terminated.  */
  943.  
  944.     i_active = c_active - 1;
  945.     while (i_active >= 0) {
  946.         aptr = act[i_active];
  947.         ptr = aptr->name;
  948.         i_levels = 0;
  949.         while ((ptr = strchr(ptr,'.')) != (char *)NULL) {
  950.             i_levels++;
  951.             ptr++;
  952.         }
  953.         if (i_levels < MAXLEVELS) {
  954.             aptr->depth = levels[i_levels];
  955.             levels[i_levels] = aptr;
  956.         }
  957.         i_active--;
  958.     }
  959.  
  960. /* This code will be re-used later when parsing newsgroups */
  961. /* let's check the pointers in levels[] */
  962. /*
  963.     i_levels = 0;
  964.     while (i_levels < MAXLEVELS) {
  965.         aptr = levels[i_levels];
  966.         while (aptr != (struct actif *)NULL) {
  967.             sprintf(tmp,"%-25s %-.67s\n",
  968.                 aptr->name,
  969.                 aptr->desc);
  970.             printf(tmp);
  971.             aptr = (struct actif *) aptr->depth;
  972.         }
  973.         i_levels++;
  974.     }
  975. */
  976.  
  977.  
  978.     /* Now read in and match up our personal .newsrc
  979.        Get HOME from the environment. */
  980.  
  981.     if ((ptr = getenv("HOME")) != (char *)NULL)
  982.         sprintf(tmp, "%s/%s", ptr, NEWSRC);
  983.     else
  984.         sprintf(tmp, "%s", NEWSRC);   /* default to current directory */
  985.  
  986.     if ((fnewsrc = fopen(tmp, "r")) == (FILE *)NULL) {
  987.         printf("Fatal: Unable to read the file %s\n", tmp);
  988.         printf("Please create it using the rn or trn news reader.\n");
  989.         exit(1);
  990.     }
  991.  
  992.     i_active = 0;
  993.     newsrc_order = 1;
  994.     bog_count = 0;
  995.  
  996.     while (fgets(buffer,BUFSIZE,fnewsrc) != (char *)NULL) {
  997.  
  998.     /* ignore comment lines */
  999.         switch(buffer[0]) {
  1000.         case '#':
  1001.         case '\n':
  1002.         case '\r':
  1003.         case '\0':
  1004.             continue;
  1005.         }
  1006.  
  1007.     /* strip off CR and LF */
  1008.         while ((ptr = strchr(buffer,'\n')) != (char *)NULL) *ptr = '\0';
  1009.         while ((ptr = strchr(buffer,'\r')) != (char *)NULL) *ptr = '\0';
  1010.  
  1011.     /* don't try to match a null string */
  1012.         if (strlen(buffer) == 0) continue; 
  1013.  
  1014. /* Now examine the character that terminates the newsgroup name.
  1015.    If it's a colon ':', then this means the newsgroup is active
  1016.    for the user.  If it's an exclamation mark, then it is
  1017.    inactive (unsubscribed).  Anything else means the
  1018.    newsgroup may not be valid.  */
  1019.  
  1020.         t_status = ' ';  /* default to SPACE */
  1021.         if ((ptr = strchr(buffer,':')) != (char *)NULL) {
  1022.             t_status = ':';
  1023.             *ptr = '\0'; /* null terminate newsgroup */
  1024.             ptr++;    /* point to rest of line (hilo) */
  1025.         } else if ((ptr = strchr(buffer,'!')) != (char *)NULL) {
  1026.             t_status = '!';
  1027.             *ptr = '\0'; /* null terminate newsgroup */
  1028.             ptr++;    /* point to rest of line (hilo) */
  1029.         }
  1030.     /* advance past any whitespace */
  1031. /* clean up this code to handle strange formats -- use strtok() */
  1032.         while ((*ptr == ' ') || (*ptr == '\t')) ptr++;
  1033.         strcpy(tmp,ptr);    /* this is hilo */
  1034.  
  1035.         aptr = act[i_active]; /* range check */
  1036.         if ((aptr != (struct actif *)NULL) 
  1037.            && (strcmp(buffer,aptr->name) == 0)) {
  1038.             if ((aptr->hilo = (char *)malloc(strlen(tmp)+1)) == (char *)NULL) {
  1039.                 printf("Error allocating memory!\n");
  1040.                 exit(1);
  1041.             }
  1042.             strcpy(aptr->hilo,tmp);
  1043.             aptr->status = t_status;
  1044.             aptr->index = newsrc_order++;
  1045.             i_active++;
  1046.             if (i_active == c_active) i_active = 0;
  1047.             continue;
  1048.         }
  1049.  
  1050.         low = 0;
  1051.         high = c_active - 1;
  1052. loop2:
  1053.         if (low <= high) {
  1054.             mid = (low+high)/2;
  1055.             aptr = act[mid];
  1056.             result = strcmp(buffer,aptr->name);
  1057.             if (result == 0) {
  1058.                 if ((aptr->hilo = (char *)malloc(strlen(ptr)+1)) 
  1059.                     == (char *)NULL) {
  1060.                     printf("Error allocating memory!\n");
  1061.                     return;
  1062.                 }
  1063.                 strcpy(aptr->hilo,tmp);
  1064.                 aptr->status = t_status;
  1065.                 aptr->index = newsrc_order++;
  1066.                 i_active = mid + 1;
  1067.             /* This next hack is necessary to prevent the
  1068.             corner case where mid has pointed at the very
  1069.             last entry in the act strucutre. */
  1070.                 if (i_active == c_active)  i_active = 0;
  1071.                 continue; /* read loop */
  1072.             } else
  1073.             if (result > 0) { /* after */
  1074.                 low = mid+1;
  1075.                 goto loop2; 
  1076.             } else
  1077.             if (result < 0) { /* before */
  1078.                 high = mid-1;
  1079.                 goto loop2; 
  1080.             }
  1081.         } else {
  1082.             bog_count++;    /* must be bogus! */
  1083.  
  1084.             if ((aptr = (struct actif *) malloc(sizeof(struct actif)))
  1085.                 == (struct actif *)NULL) {
  1086.                 printf("Fatal error while allocating memory!\n");
  1087.                 exit(1);
  1088.             }
  1089.             act[c_active] = aptr; 
  1090.  
  1091.             if ((aptr->name = (char *)malloc(strlen(buffer)+1)) 
  1092.                == (char *)NULL) {
  1093.                 printf("Fatal error while allocating memory!\n");
  1094.                 exit(1);
  1095.             }
  1096.             strcpy(aptr->name,buffer);
  1097.  
  1098.             if (strlen(tmp) > 0) {
  1099.                 if ((aptr->hilo = (char *)malloc(strlen(tmp)+1)) 
  1100.                    == (char *)NULL) {
  1101.                     printf("Fatal error while allocating memory!\n");
  1102.                     exit(1);
  1103.                 }
  1104.                 strcpy(aptr->hilo,tmp);
  1105.             }
  1106.             aptr->hi = 0;
  1107.             aptr->mark = 0;
  1108.             aptr->desc = "Bogus newsgroup (not in active file)";
  1109.             aptr->flag = '\0';
  1110.             aptr->status = t_status;
  1111.             aptr->index = newsrc_order++;
  1112.             aptr->depth = (struct actif *) NULL;
  1113.             c_active++;
  1114.             if (c_active < 2) continue;
  1115.             qsort( act, (unsigned) c_active,(unsigned) sizeof(act[0]), 
  1116.                qcompare);
  1117.             continue; /* with read loop */
  1118.         }
  1119.     }
  1120.     fclose(fnewsrc);
  1121.  
  1122.     if (bog_count > 0) {
  1123.         if (bog_count == 1)
  1124.             printf("There was 1 bogus newsgroup in your .newsrc.\n");
  1125.         else
  1126.             printf("There were %d bogus newsgroups in your .newsrc.\n",
  1127.                 bog_count);
  1128.         warning = TRUE;
  1129.     }
  1130.  
  1131.     /* Now let's sort this lot into the order that we originally
  1132.     read it from the .newsrc in.  New newsgroups will be forced
  1133.     to the top, making it easier for them to be spotted.  */
  1134.  
  1135.     qsort( act, (unsigned) c_active, (unsigned) sizeof(act[0]), icompare);
  1136.  
  1137. #ifdef UNIX
  1138.     /* Now let's see if we can create a new .newsrc in $HOME */
  1139.     if ((ptr = getenv("HOME")) != (char *)NULL)
  1140.         sprintf(tmp, "%s/.newsrc.eep", ptr);
  1141.     else    sprintf(tmp, ".newsrc.eep");
  1142.  
  1143.     if ((fnewsrc = fopen(tmp, "w")) == (FILE *)NULL) {
  1144.        printf("warning: cannot create .newsrc.eep -- check permissions\n");
  1145.        warning = TRUE;
  1146.     }
  1147. #endif /* UNIX */
  1148.  
  1149.     if (warning) {
  1150.         printf("\nPress ENTER to continue.");
  1151.         gets(buffer);
  1152.     }
  1153. }
  1154.  
  1155. main(argc, argv)
  1156. int    argc;
  1157. char    **argv;
  1158. {
  1159. int    ch;
  1160. int    timer;
  1161.  
  1162. #ifdef UNIX
  1163.     while ((ch = getopt(argc,argv,"ap")) != EOF) switch(ch) {
  1164.         case 'p':
  1165.             eepoint++;
  1166.             break;
  1167.         case '!':
  1168.             noshell++;
  1169.             break;
  1170.         case '?':
  1171.             fprintf(stderr,"usage: eep [-p] [-!]\n\n");
  1172.             fprintf(stderr,"-p means use terse pointer\n");
  1173.             fprintf(stderr,"-! means deny shell out\n");
  1174.             fprintf(stderr,"\nUse man eep for more info.\n");
  1175.             exit (2);
  1176.     }
  1177. #endif /* UNIX */
  1178.  
  1179.     fprintf(stderr,"eep version 1.61 .newsrc editor\n");
  1180.  
  1181. #ifdef DOS
  1182. /*    msleep(0L);*/    /* invoke once to set precision */
  1183. #endif /* DOS */
  1184.  
  1185.     initial();    /* read in newsgroups, active and .newsrc */
  1186.     newsmain();
  1187.  
  1188. #ifdef UNIX  /* just can't get free() working under MS-DOS.  Sigh.  */
  1189.     /* garbage collection */
  1190.     i_active = 0;
  1191.     while (i_active < c_active) {
  1192.         aptr = act[i_active];
  1193.         if (aptr != (struct actif *)NULL) {
  1194.             if (aptr->hilo != (char *)NULL) free(aptr->hilo);
  1195.             if ((aptr->desc != (char *)NULL) &&
  1196.                 (aptr->desc != bog_msg)) free(aptr->desc);
  1197.             if (aptr->name != (char *)NULL) free(aptr->name);
  1198.             free((struct actif *)aptr);
  1199.         }
  1200.         i_active++;
  1201.     }
  1202. #endif /* UNIX */
  1203.     exit(0);
  1204. } /* end of eepmain.c */
  1205. @EOF
  1206.  
  1207. chmod 600 eepmain.c
  1208.  
  1209. echo x - eepmenu.c
  1210. cat >eepmenu.c <<'@EOF'
  1211. /*  This module contains the code for the user interface for eep.  */
  1212.  
  1213. #include    <stdio.h>
  1214. #include    <string.h>
  1215. #include    <curses.h>
  1216.  
  1217. #ifdef UNIX
  1218. #include    <ctype.h>
  1219. #include    <signal.h>
  1220. #endif /* UNIX */
  1221.  
  1222. #ifdef ANSI
  1223. #include <stdlib.h>
  1224. #else
  1225. #define void int
  1226. extern char *malloc();
  1227. #endif /* ANSI */
  1228.  
  1229. #include    "eep.h"        /* local changes */
  1230.  
  1231. /* Function declarations for forward references */
  1232.  
  1233. void    showlist();
  1234.  
  1235. extern int    qcompare();    /* sort alphabetically */
  1236. extern int    icompare();    /* sort by index */
  1237. extern char    *shift_lower();    /* see eepmisc.c */
  1238. extern char    buffer[],     /* general purpose line buffer */
  1239.         tmp[];
  1240.  
  1241. extern struct  actif *act[];    /* main data structure */
  1242. extern struct  actif *topact[]; /* top level names only */
  1243.  
  1244. extern int    i_active,    /* index into arrays */
  1245.         c_active,    /* number of lines in act array */
  1246.         t_active,    /* number of lines in act array */
  1247.         eepoint,     /* pointer switch */
  1248.         bog_count,   /* bogus newsgroups */
  1249.         noshell;     /* noshell switch */
  1250.  
  1251.  
  1252. extern FILE    *fnewsrc;    /* .newsrc.eep */
  1253.  
  1254. /* The levels array contains the Head pointers which control the
  1255. linked lists of newsgroups grouped by their "depth" in the
  1256. hierarchy, and sorted alphabetically.  E.g. alt, sci, soc are 
  1257. level 0, alt.pagan, sci.physics are level 1 and so on. */
  1258.  
  1259. extern struct actif *levels[];  /* keep track of levels with this array */
  1260. extern struct actif *aptr;    /* temporary pointer */
  1261. extern int    i_levels;    /* index into array */
  1262.  
  1263. /* Global variables */
  1264.  
  1265. int    scrnpos = 0,    /* position of highlight on screen */
  1266.     current = 0,    /* matching highlight in data structure */
  1267.     top = 0,    /* data element on top of screen */
  1268.     eeplines = 0,    /* number of lines on screen */
  1269.     eepcolumns = 0;    /* columns on screen */
  1270.  
  1271. WINDOW    *over;        /* used for on-screen help window etc. */
  1272. void showhelp();    /* forward declaration */
  1273.  
  1274. /* pad() -- this function will take a pointer to a string and a 
  1275. numeric argument, and will pad the string with spaces to reach
  1276. the desired length.  If the string is too long, it will be 
  1277. truncated to fit.  The function will return a pointer to a
  1278. static buffer that will contain the padded
  1279. string (or original if it's long enough already). 
  1280. If str is a NULL pointer, we'll return spaces only. */
  1281.  
  1282. char    *pad(str,len)
  1283. char    *str;
  1284. int    len;
  1285. {
  1286. static char mybuf[BUFSIZE];
  1287.  
  1288. int    count;
  1289.     mybuf[0] = '\0';
  1290.     if (len >= BUFSIZE) return(mybuf); /* safety */
  1291.     /* In case we are passed a NULL pointer... */
  1292.     if (str == (char *) NULL) {
  1293.         mybuf[0] = '\0';
  1294. /*        while (strlen(mybuf) < len) strcat(mybuf," "); */
  1295.         count = 0;
  1296.         while (count++ < len) strcat(mybuf," ");
  1297.         return(mybuf);
  1298.     }
  1299.     if (strlen(str) >= len) {
  1300.         strncpy(mybuf,str,len);
  1301.         return(mybuf);
  1302.     }
  1303.     strcpy(mybuf,str);
  1304. /*    while (strlen(mybuf) < len) */
  1305.     count = len;
  1306.     while (count-- > 0)
  1307.         strcat(mybuf," ");
  1308.     return(mybuf);
  1309. }
  1310.  
  1311. /* newsmain()  -- this provides the mainline user interface. */
  1312.  
  1313. void    newsmain()
  1314. {
  1315.  
  1316. char    *ptr;
  1317. char    search[BUFSIZE];    /* search string buffer */
  1318. int    found = FALSE,    /* flag to say we've found something */
  1319.     quit = FALSE,    /* flag used when ready to quit */
  1320.     alphabetized = FALSE, /* flag showing if sorted */
  1321.     index,        /* used when searching */
  1322.     counter,    /* used when counting */
  1323.     ch;        /* input character */
  1324.  
  1325.     /* Work out how many lines and columns we have.
  1326.     Use the environment variables first -- if they don't
  1327.     exist, we'll try terminfo.  Yes, I know terminfo
  1328.     should do this, but don't bet on it for all versions.
  1329.     If that doesn't work, default to the ones in eep.h. */
  1330.  
  1331.  
  1332.     if ((ptr = getenv("LINES")) != (char *)NULL)
  1333.         eeplines = atoi(ptr);
  1334.     if ((ptr = getenv("COLUMNS")) != (char *)NULL)
  1335.         eepcolumns = atoi(ptr);
  1336.  
  1337. /* Now let's try to use the ones defined by curses. */
  1338.     if (eeplines <= 0)
  1339.         eeplines = LINES;
  1340.     if (eepcolumns <= 0)
  1341.         eepcolumns = COLS;
  1342.     if (eeplines <= 0)
  1343.         eeplines = EEPLINES;
  1344.     if (eepcolumns <= 0)
  1345.         eepcolumns = EEPCOLUMNS;
  1346.     if ((eeplines < 19) || (eepcolumns < 80)) {
  1347.         printf("Sorry, EEP needs at least 19 lines by 80 columns.\n");
  1348.         exit(1);
  1349.     }
  1350.  
  1351.     initscr();    /* set up for curses */
  1352.     raw();
  1353.     noecho();
  1354.     nonl();
  1355.     keypad(stdscr,TRUE);
  1356.     erase();    /* clear screen */
  1357.  
  1358. #ifdef UNIX
  1359.     idlok(stdscr,TRUE);
  1360. #endif /* UNIX */
  1361.  
  1362.     showlist(0);
  1363.     while (quit == FALSE) {
  1364.     ch = getch();
  1365.     switch(ch) {
  1366.         case 'q':    /* quit */
  1367.         case 'Q':
  1368.         case '\033':    /* ESCAPE by itself */
  1369.         case '\003':    /* for those who like ^C */
  1370.         case '\177':    /* for those who like INTR */
  1371.             move(eeplines-1,0);
  1372.             deleteln();
  1373.             addstr("Do you want to quit without saving your changes? [n]: ");
  1374.             refresh();
  1375.             if (tolower(getch()) == (int) 'y') quit = TRUE;
  1376.             deleteln();
  1377.             move(scrnpos,0);
  1378.             refresh();
  1379.             break;
  1380.  
  1381.         case '?':    /* on-line help */
  1382.         case 'h':
  1383.         case 'H':
  1384.         case KEY_F(1):
  1385.             over = newwin(19,60,0,5);
  1386.             if (!eepoint) wstandout(over);
  1387.             box(over,'\0','\0');
  1388.             if (!eepoint) wstandend(over);
  1389.             wmove(over,2,5);
  1390.             wstandout(over);
  1391.             waddstr(over," eep v1.6: .newsrc & JOIN file editor ");
  1392.             wstandend(over);
  1393.             wmove(over,3,5);
  1394.             waddstr(over,"by Paul Gillingwater, paul@actrix.co.at");
  1395.             wmove(over,5,5);
  1396.             waddstr(over,":   Pick by number    /   Search");
  1397.             wmove(over,6,5);
  1398.             waddstr(over,"a   (un)Alphabetise   b   Bottom of file");
  1399.             wmove(over,7,5);
  1400.             waddstr(over,"c   Catch up          d   (un)Delete ");
  1401.             wmove(over,8,5);
  1402.             waddstr(over,"i   Show info         j   Next line");
  1403.             wmove(over,9,5);
  1404.             waddstr(over,"k   Previous line     n   Search next");
  1405.             wmove(over,10,5);
  1406.             waddstr(over,"p   Pointer change    r   Redraw screen");
  1407.             wmove(over,11,5);
  1408.             waddstr(over,"s   Subscribe         t   Top of file");
  1409.             wmove(over,12,5);
  1410.             waddstr(over,"u   Unsubscribe       v   View subjects");
  1411.             wmove(over,13,5);
  1412.             waddstr(over,"x   Save and exit     q   Quit without saving");
  1413.             wmove(over,14,5);
  1414.             waddstr(over,"^D  Page down         ^U  Page up");
  1415.             wmove(over,15,5);
  1416.             waddstr(over,".   (un)Mark group    m   Move marked groups");
  1417.             wmove(over,17,5);
  1418.             sprintf(buffer,"There are %d available newsgroups.",c_active);
  1419.             waddstr(over,buffer);
  1420.             wmove(over,18,5);
  1421.             wstandout(over);
  1422.             waddstr(over," Press SPACE BAR to exit from help ");
  1423.             wstandend(over);
  1424.             wrefresh(over);
  1425.             ch = wgetch(over);
  1426.             delwin(over);
  1427.             touchwin(stdscr);
  1428.             refresh();
  1429.             break;
  1430.  
  1431.         case ':':    /* enter number */
  1432.             move(eeplines - 1,0);
  1433.             deleteln();
  1434.             addch(':');
  1435.             refresh();
  1436.             getbuf(search);
  1437.             index = atoi(search);
  1438.             if ((index <= 0) || (index > c_active)) {
  1439.                 move(eeplines - 1,0);
  1440.                 printw("Newsgroups available: %d ",c_active);
  1441.                 refresh();
  1442.                 sleep(2);
  1443.                 deleteln();
  1444.                 move(scrnpos,0);
  1445.                 refresh();
  1446.                 break;
  1447.             }
  1448.             current = index - 1;
  1449.             showlist(current);
  1450.             break;
  1451.  
  1452.         case '.':    /* mark group -- used to move later */
  1453.             aptr = act[current];
  1454.             if (aptr->mark == 0)
  1455.                 aptr->mark = 1;
  1456.             else
  1457.                 aptr->mark = 0;
  1458.             goto    scroll_down;
  1459.             break;
  1460.  
  1461.         case 'm':    /* move marked groups */
  1462.         case 'M':
  1463.             i_active = 0;
  1464.             while (i_active < c_active) {
  1465.                 if ((aptr = act[i_active]) == (struct actif *)NULL) {
  1466.                     i_active++;
  1467.                     continue;
  1468.                 }
  1469.                 if (aptr->mark == 0) {
  1470.                     i_active++;
  1471.                     continue;
  1472.                 }
  1473.                 aptr->index = current;
  1474.                 aptr->mark = 0;
  1475.                 i_active++;
  1476.             }
  1477.             qsort( act, (unsigned) c_active, 
  1478.                     (unsigned) sizeof(act[0]), icompare);
  1479.             alphabetized = FALSE;
  1480.  
  1481.             /* Renumber to match current order. */
  1482.             i_active = 0;
  1483.             while (i_active < c_active) {
  1484.                 if ((aptr = act[i_active]) == (struct actif *)NULL) {
  1485.                     i_active++;
  1486.                     continue;
  1487.                 }
  1488.                 aptr->index = ++i_active;
  1489.             }
  1490.             showlist(current);
  1491.             break;
  1492.  
  1493.         case 'r':    /* redraw */
  1494.         case 'R':    /* redraw */
  1495.         case '\014':    /* form feed ^L */
  1496.             touchwin(stdscr);
  1497.             clearok(stdscr,TRUE);
  1498.             refresh();
  1499.             break;
  1500.  
  1501.         case 'v':    /* view newsgroup */
  1502.         case '=':    /* view newsgroup */
  1503.         case '\r':
  1504.         case '\n':    /* new line to select a group */
  1505.             if ((aptr = act[current]) == (struct actif *)NULL) 
  1506.                 break;
  1507.             eepview(aptr->name);
  1508.             showlist(current);
  1509.             break;
  1510.  
  1511.         case 'i':
  1512.             if ((aptr = act[current]) == (struct actif *)NULL) 
  1513.                 break;
  1514.             over = newwin(18,65,0,5);
  1515.             if (!eepoint) wstandout(over);
  1516.             box(over,'\0','\0');
  1517.             if (!eepoint) wstandend(over);
  1518.             wmove(over,2,5);
  1519.             wprintw(over,"Number: %d",current + 1);
  1520.             wmove(over,3,5);
  1521.             wprintw(over,"Name: %.50s",pad(aptr->name,50));
  1522.             wmove(over,4,5);
  1523.             wprintw(over,"Desc: %.50s",pad(aptr->desc,50));
  1524.             wmove(over,6,5);
  1525.             wprintw(over,"This news group is ");
  1526.             switch(aptr->flag) {
  1527.             case 'y':
  1528.                 wprintw(over,"Active.");
  1529.                 break;
  1530.             case 'n':
  1531.                 wprintw(over,"Not Active.");
  1532.                 break;
  1533.             case 'm':
  1534.                 wprintw(over,"Moderated.");
  1535.                 break;
  1536.             default:
  1537.                 wprintw(over,"Invalid.");
  1538.             }
  1539.             wmove(over,7,5);
  1540.             wprintw(over,"Your .newsrc says this is ");
  1541.             switch(aptr->status) {
  1542.             case ':':
  1543.                 wprintw(over,"Subscribed to.");
  1544.                 break;
  1545.             case '!':
  1546.                 wprintw(over,"Not Subscribed to.");
  1547.                 break;
  1548.             default:
  1549.                 wprintw(over,"Not valid.");
  1550.                 break;
  1551.             }
  1552.             if (aptr->hi > 0) {
  1553.                 wmove(over,9,5);
  1554.                 wprintw(over,"Active high message is %ld",
  1555.                     aptr->hi);
  1556.                 wmove(over,11,5);
  1557.                 wprintw(over,"Messages already read by you include:");
  1558.                 wmove(over,12,5);
  1559.                 wprintw(over,"%s",pad(aptr->hilo,50));
  1560.             }
  1561.             wmove(over,17,5);
  1562.             wstandout(over);
  1563.             waddstr(over," Press SPACE BAR to continue ");
  1564.             wstandend(over);
  1565.             wrefresh(over);
  1566.             ch = wgetch(over);
  1567.             delwin(over);
  1568.             touchwin(stdscr);
  1569.             refresh();
  1570.             break;
  1571.  
  1572.         case '/':    /* search */
  1573.             move(eeplines - 1,0);
  1574.             deleteln();
  1575.             addch('/');
  1576.             refresh();
  1577.             getbuf(search);
  1578.             if (strlen(search) == 0) {
  1579.                 move(eeplines - 1,0);
  1580.                 deleteln();
  1581.                 move(scrnpos,0);
  1582.                 refresh();
  1583.                 break;
  1584.             }
  1585.             found = FALSE;
  1586.             /* make it lower case for searching */
  1587.             strcpy(search,shift_lower(search));
  1588.             index = current + 1;    /* track progress */
  1589.             if (index >= c_active) index = 0;
  1590.               
  1591.             while (index != current) {
  1592.                 aptr = act[index];
  1593.                 if (aptr != (struct actif *)NULL) {
  1594.                     if (in_string(shift_lower(aptr->name),search) ||
  1595.                         in_string(shift_lower(aptr->desc),search)) {
  1596.                         /* found match */
  1597.                         found = TRUE;
  1598.                         current = index;
  1599.                         showlist(current);
  1600.                         break;
  1601.                     }
  1602.                 }
  1603.                 index++;
  1604.                 if (index >= c_active) index = 0;
  1605.             }
  1606.             if (!found) {
  1607.                 beep();
  1608.                 move(scrnpos,0);
  1609.                 refresh();
  1610.             }
  1611.             break;
  1612.  
  1613.         case 't':
  1614.         case 'T':
  1615.         case '^':    /* top of list */
  1616.             current = 0;
  1617.             scrnpos = 0;
  1618.             showlist(current);
  1619.             break;
  1620.  
  1621.         case 'b':
  1622.         case 'B':
  1623.         case '$':    /* bottom of list */
  1624.             current = c_active - 1;
  1625.             scrnpos = eeplines - 2;
  1626.             showlist(current);
  1627.             break;
  1628.  
  1629.         case 'n':    /* look for next occurence */
  1630.         case 'N':
  1631.             if (!found) {
  1632.                 beep();   /* nothing to find */
  1633.                 break;
  1634.             }
  1635.             index = current;    /* track progress */
  1636.             while (++index != current) {
  1637.                 if (index >= c_active) index = 0;
  1638.                 aptr = act[index];
  1639.                 if (in_string(shift_lower(aptr->name),search) ||
  1640.                     in_string(shift_lower(aptr->desc),search)) {
  1641.                     /* found match */
  1642.                     found = TRUE;
  1643.                     current = index;
  1644.                     showlist(current);
  1645.                     break;
  1646.                 }
  1647.             }
  1648.             break;
  1649.  
  1650.         case 'a':    /* change sorting order */
  1651.         case 'A':
  1652.             move(eeplines-1,0);
  1653.             deleteln();
  1654.             if (alphabetized) {
  1655.                 addstr("Do you wish to sort in .newsrc order? [n]: ");
  1656.                 refresh();
  1657.                 if (tolower(getch()) != (int) 'y') {
  1658.                     deleteln();
  1659.                     move(scrnpos,0);
  1660.                     refresh();
  1661.                     break;
  1662.                 }
  1663.                 qsort( act, (unsigned) c_active, 
  1664.                     (unsigned) sizeof(act[0]), icompare);
  1665.                 alphabetized = FALSE;
  1666.             } else {
  1667.                 addstr("Do you wish to sort alphabetically? [n]: ");
  1668.                 refresh();
  1669.                 if (tolower(getch()) != (int) 'y') {
  1670.                     deleteln();
  1671.                     move(scrnpos,0);
  1672.                     refresh();
  1673.                     break;
  1674.                 }
  1675.                 qsort( act, (unsigned) c_active, 
  1676.                     (unsigned) sizeof(act[0]), qcompare);
  1677.                 alphabetized = TRUE;
  1678.             }
  1679.             deleteln();
  1680.             move(scrnpos,0);
  1681.             showlist(current);
  1682.             break;
  1683.  
  1684.         case '+':    /* subscribe to this newsgroup */
  1685.         case 's':
  1686.         case 'S':
  1687.             aptr = act[current];
  1688.             /* check if group is valid */
  1689.             if ((aptr->flag == 'y') ||
  1690.                 (aptr->flag == 'm')) {
  1691.                 aptr->status = ':';
  1692.                 move(scrnpos,2);
  1693.                 standout();
  1694.                 addch('+');
  1695.                 standend();
  1696.                 move(scrnpos,0);
  1697.             } else {
  1698.                 move(eeplines - 1,0);
  1699.                 printw("Not a valid newsgroup -- cannot subscribe");
  1700.                 refresh();
  1701.                 sleep(2);
  1702.                 deleteln();
  1703.                 move(scrnpos,0);
  1704.             }
  1705.             refresh();
  1706.             goto    scroll_down;
  1707.             break;
  1708.  
  1709.         case '-':    /* unsubscribe to this newsgroup */
  1710.         case 'u':    /* also means unjoin */
  1711.         case 'U':
  1712.             aptr = act[current];
  1713.             /* check if group is valid */
  1714.             aptr->status = '!';
  1715.             if ((aptr->flag == 'y') ||
  1716.                 (aptr->flag == 'm')) {
  1717.                 move(scrnpos,2);
  1718.                 standout();
  1719.                 addch(' ');
  1720.                 standend();
  1721.                 move(scrnpos,0);
  1722.             } else {
  1723.                 move(eeplines - 1,0);
  1724.                 printw("Not a valid newsgroup -- unsubscribing anyway");
  1725.                 refresh();
  1726.                 sleep(2);
  1727.                 deleteln();
  1728.                 move(scrnpos,0);
  1729.             }
  1730.             refresh();
  1731.             goto    scroll_down;
  1732.             break;
  1733.  
  1734.         case ' ':
  1735.         case 'j':
  1736.         case 'J':    /* join */
  1737.         case '\016':    /* for emacs users */
  1738.         case KEY_DOWN:
  1739. scroll_down:
  1740.         /* Don't move if we're at the end. */
  1741.             if (current == c_active - 1) {
  1742.                 beep();
  1743.                 break;
  1744.             }
  1745.             /* remove highlights by redrawing line */
  1746.             aptr = act[current];
  1747.             move(scrnpos,0);
  1748.             if (!eepoint) standend();
  1749.             if (aptr->mark == 0)
  1750.                 printw("  ");
  1751.             else
  1752.                 printw("* ");
  1753.             switch(aptr->status) {
  1754.             case ':':    printw("+"); /* subscribed */
  1755.                     break;
  1756.             case '!':    printw(" "); /* unsubscribed */
  1757.                     break;
  1758.             default:     printw("?"); /* mystery! */
  1759.             }
  1760.             if (!eepoint) {
  1761.                 printw("%.28s ", pad(aptr->name,28));
  1762.                 printw("%.46s ", pad(aptr->desc,46));
  1763.             }
  1764.             refresh();
  1765.             current++;
  1766.             if (++scrnpos == eeplines - 1) { /* scroll up */
  1767.                 move(0,0);
  1768.                 deleteln();
  1769.                 move(eeplines - 2,0);
  1770.                 insertln();
  1771.                 refresh();
  1772.                 scrnpos = eeplines - 2;
  1773.                 top++;
  1774.             };
  1775.             /* Now paint our new position */
  1776.             move(scrnpos,0);
  1777.             aptr = act[current];
  1778.             if (!eepoint) standout();
  1779.             if (aptr->mark == 0)
  1780.                 printw("-");
  1781.             else
  1782.                 printw("*");
  1783.             switch(aptr->status) {
  1784.             case ':':    printw(">+"); /* subscribed */
  1785.                     break;
  1786.             case '!':    printw("> "); /* unsubscribed */
  1787.                     break;
  1788.             default:    printw(">?"); /* mystery! */
  1789.             }
  1790.             printw("%.28s ", pad(aptr->name,28));
  1791.             printw("%.46s ", pad(aptr->desc,46));
  1792.             if (!eepoint) standend();
  1793.             move(scrnpos,0);
  1794.             refresh();
  1795.             break;
  1796.  
  1797.         case 'k':
  1798.         case 'K':
  1799.         case '\010':    /* backspace */
  1800.         case '\020':    /* for emacs users */
  1801.         case KEY_UP:
  1802.         /* Don't move if we're at the top. */
  1803.             if (current == 0) {
  1804.                 beep();
  1805.                 break;
  1806.             }
  1807.             move(scrnpos,0);
  1808.             aptr = act[current];
  1809.             if (!eepoint) standend();
  1810.             if (aptr->mark == 0)
  1811.                 printw("  ");
  1812.             else
  1813.                 printw("* ");
  1814.             switch(aptr->status) {
  1815.             case ':':    printw("+"); /* subscribed */
  1816.                     break;
  1817.             case '!':    printw(" "); /* unsubscribed */
  1818.                     break;
  1819.             default:    printw("?"); /* mystery! */
  1820.             }
  1821.             if (!eepoint) {
  1822.                 printw("%.28s ", pad(aptr->name,28));
  1823.                 printw("%.46s ", pad(aptr->desc,46));
  1824.             }
  1825.             move(scrnpos,0);
  1826.             refresh();
  1827.             current--;
  1828.             if (--scrnpos == -1) {
  1829.                 move(eeplines - 2,0);
  1830.                 deleteln();
  1831.                 move(0,0);
  1832.                 insertln();
  1833.                 refresh();
  1834.                 move(0,0);
  1835.                 scrnpos = 0;
  1836.                 if (top > 0) top--;
  1837.             }; /* cause scroll */
  1838.             move(scrnpos,0);
  1839.             aptr = act[current];
  1840.             if (!eepoint) standout();
  1841.             if (aptr->mark == 0)
  1842.                 printw("-");
  1843.             else
  1844.                 printw("*");
  1845.             switch(aptr->status) {
  1846.             case ':':    printw(">+"); /* subscribed */
  1847.                     break;
  1848.             case '!':    printw("> "); /* unsubscribed */
  1849.                     break;
  1850.             default:     printw(">?"); /* mystery! */
  1851.             }
  1852.             printw("%.28s ", pad(aptr->name,28));
  1853.             printw("%.46s ", pad(aptr->desc,46));
  1854.             if (!eepoint) standend();
  1855.             move(scrnpos,0);
  1856.             refresh();
  1857.             break;
  1858.  
  1859.  
  1860.         case KEY_NPAGE: /* next page */
  1861.         case '\004':    /* ^D */
  1862.         case '\006':    /* ^F */
  1863.  
  1864.             /* If a pagedown will go past the end
  1865.             of the list, simply position at the end. */
  1866.  
  1867.             if (current == c_active - 1) {
  1868.                 beep();
  1869.                 break;
  1870.             }
  1871.             if ((current += EEPPAGE) >= c_active) {
  1872.                 current = c_active - 1;
  1873.                 if ((c_active - current) < (eeplines - 2))
  1874.                     scrnpos = eeplines - 2;
  1875.             } else  if (scrnpos + EEPPAGE < eeplines - 2)
  1876.                     scrnpos += EEPPAGE; 
  1877.             showlist(current);
  1878.             break;
  1879.  
  1880.         case KEY_PPAGE: /* previous page */
  1881.         case '\025':    /* ^U */
  1882.         case '\002':    /* ^B */
  1883.             if (current == 0) {
  1884.                 beep();
  1885.                 break;
  1886.             }
  1887.             if ((current -= EEPPAGE) < 0) {
  1888.                 current = 0;
  1889.                 scrnpos = 0;
  1890.             } else  if ((scrnpos - EEPPAGE) >= 0)
  1891.                     scrnpos -= EEPPAGE;
  1892.             showlist(current);
  1893.             break;
  1894.  
  1895.             case 'p':   /* change type of pointer */
  1896.         case 'P':
  1897.             if (eepoint) eepoint = FALSE;
  1898.             else eepoint = TRUE;
  1899.             showlist(current);
  1900.             break;
  1901.  
  1902.             case 'c':   /* catch up */
  1903.         case 'C':
  1904.             aptr = act[current];
  1905.             /* only if it's a real news group */
  1906.             if (aptr->hi <= 0)    goto scroll_down;
  1907.             if ((aptr->flag == 'y') ||
  1908.                 (aptr->flag == 'm') ||
  1909.                 (aptr->flag == 'n')) {
  1910.                 move(eeplines-1,0);
  1911.                 deleteln();
  1912.                 addstr("Catch up this news group? [n]: ");
  1913.                 refresh();
  1914.                 if (tolower(getch()) == 'y') {
  1915.                     sprintf(buffer,"1-%ld", aptr->hi);
  1916.                     if (aptr->hilo != (char *)NULL) {
  1917.                         if (aptr->hilo[0] == '0')
  1918.                             sprintf(buffer,"0-%ld", aptr->hi);
  1919.                     }
  1920.                     aptr->hilo = (char *)malloc(strlen(buffer)+1);
  1921.                     if (aptr->hilo == (char *)NULL) {
  1922.                         fprintf(stderr, "Fatal memory allocation error.\n");
  1923.                         exit(2);
  1924.                     }
  1925.                     strcpy(aptr->hilo,buffer);
  1926.                     deleteln();
  1927.                     move(eeplines-1,0);
  1928.                     printw("High message now %ld", aptr->hi);
  1929.                 } else deleteln();
  1930.                 move(scrnpos,0);
  1931.                 refresh();
  1932.             }
  1933.             goto scroll_down;
  1934.             break;
  1935.  
  1936.         case 'd':    /* delete from .newsrc */
  1937.         case 'D':
  1938.             move(eeplines-1,0);
  1939.             deleteln();
  1940.             aptr = act[current];
  1941.             if (aptr->status == 0) {
  1942.                 printw("Group %s already marked for deletion.  Undelete? ",
  1943.                     aptr->name);
  1944.                 refresh();
  1945.                 if (tolower(getch()) == (int) 'y') {
  1946.                     move(eeplines-1,0);
  1947.                     deleteln();
  1948.                     aptr->status = '!';
  1949.                     printw("Group %s undeleted.",
  1950.                         aptr->name);
  1951.                 } else {
  1952.                     move(eeplines-1,0);
  1953.                     deleteln();
  1954.                     printw("Group %s marked for deletion.",
  1955.                         aptr->name);
  1956.                 }
  1957.                 move(scrnpos,0);
  1958.                 refresh();
  1959.                 goto scroll_down;
  1960.                 break;
  1961.             }
  1962.             printw("Delete newsgroup %s? ",aptr->name);
  1963.             refresh();
  1964.             if (tolower(getch()) == (int) 'y') {
  1965.                 aptr = act[current];
  1966.                 aptr->status = 0;
  1967.                 deleteln();
  1968.                 move(eeplines-1,0);
  1969.                 printw("Group %s marked for deletion.",
  1970.                     aptr->name);
  1971.             } else {
  1972.                 deleteln();
  1973.                 move(eeplines-1,0);
  1974.                 printw("Group %s NOT marked for deletion.",
  1975.                     aptr->name);
  1976.             }
  1977.             move(scrnpos,0);
  1978.             refresh();
  1979.             goto scroll_down;
  1980.             break;
  1981.  
  1982.             case 'x':   /* write the local .newsrc and exit */
  1983.         case 'X':
  1984.             move(eeplines-1,0);
  1985.             deleteln();
  1986.             if (fnewsrc == (FILE *) NULL) {
  1987.                 addstr("Couldn't open .newsrc.eep -- write failed.");
  1988.                 refresh();
  1989.                 break;
  1990.             }
  1991.             addstr("Do you want to save and exit? [n]: ");
  1992.             refresh();
  1993.             if (tolower(getch()) != (int) 'y') {
  1994.                 deleteln();
  1995.                 move(scrnpos,0);
  1996.                 refresh();
  1997.                 break;
  1998.             }
  1999.             if (bog_count > 0) {
  2000.                 move(eeplines-1,0);
  2001.                 deleteln();
  2002.                 addstr("Delete bogus groups from your .newsrc? [n]: ");
  2003.                 refresh();
  2004.                 if (tolower(getch()) != (int) 'y') bog_count = 0;
  2005.             }
  2006.             deleteln();
  2007.             index = 0;
  2008.             counter = 0;
  2009.             while (index < c_active) {
  2010.                 aptr = act[index];
  2011.                 if ((aptr->flag == '\0') ||
  2012.                     (aptr->status == '\0') ||
  2013.                     (aptr->name == (char *)NULL)) {
  2014.             /* must be bogus -- shall we forget it? */
  2015.                     if (bog_count != 0) {
  2016.                         index++;
  2017.                         continue;
  2018.                     }
  2019.                 }
  2020.  
  2021.                 fprintf(fnewsrc,"%s",
  2022.                     aptr->name);
  2023.                 if (aptr->status == ':') {
  2024.                     fprintf(fnewsrc,": ");
  2025.                 } else {
  2026.                     fprintf(fnewsrc,"! ");
  2027.                 }
  2028.                 if (aptr->hilo != (char *)NULL) {
  2029.                     fprintf(fnewsrc,"%s", aptr->hilo);
  2030.                 }
  2031.                 fprintf(fnewsrc,"\n");
  2032.                 counter++;
  2033.                 index++;
  2034.                 if (counter % 100 == 0) {
  2035.                     move(eeplines-1,0);
  2036.                     printw("%d",counter);
  2037.                     refresh();
  2038.                 }
  2039.             }
  2040.             fclose(fnewsrc);
  2041.  
  2042.             printf("\rTotal of %d lines written.\r\n",counter);
  2043.             if (bog_count > 0) {
  2044.                 if (bog_count == 1)
  2045.                     printf("There was 1 bogus newsgroup deleted.\r\n");
  2046.                 else
  2047.                     printf("There were %d bogus newsgroups deleted.\r\n",
  2048.                         bog_count);
  2049.             }
  2050.  
  2051.             fnewsrc = (FILE *) NULL;
  2052.             if ((ptr = getenv("HOME")) != (char *)NULL) {
  2053.                 sprintf(tmp, "%s/%s", ptr, NEWSRC);
  2054.                 sprintf(buffer, "%s/%s.old", ptr, NEWSRC);
  2055.             } else {
  2056.                 sprintf(tmp, NEWSRC);   
  2057.                 sprintf(buffer, "%s.old", NEWSRC);   
  2058.             }
  2059. #ifdef UNIX
  2060.  
  2061. /* Narrative: we rename the current .newsrc to .newsrc.old -- but
  2062.  * first we unlink any existing .newsrc.old.  We have just written
  2063.  * the modified .newsrc to .newsrc.new, and now we rename it to
  2064.  * replace the .newsrc -- which should be safely backed-up.
  2065.  */
  2066.  
  2067.             unlink(buffer);    /* don't care about errors */
  2068.  
  2069.             if (link(tmp,buffer) < 0) {
  2070.                 fprintf(stderr,"[1] .newsrc not replaced.");
  2071.                 break;
  2072.             }
  2073.             if (unlink(tmp) < 0) {
  2074.                 fprintf(stderr,"[2] .newsrc not replaced.");
  2075.                 break;
  2076.             }
  2077.             if ((ptr = getenv("HOME")) != (char *)NULL)
  2078.                 sprintf(buffer, "%s/.newsrc.eep", ptr);
  2079.             else
  2080.                 sprintf(buffer, ".newsrc.eep");
  2081.  
  2082.             if (link(buffer,tmp) < 0) {
  2083.                 fprintf(stderr,"[3] .newsrc not replaced.");
  2084.                 break;
  2085.             }
  2086.             if (unlink(buffer) < 0) {
  2087.                 fprintf(stderr,"[4] .newsrc not replaced.");
  2088.                 break;
  2089.             }
  2090. #endif /* UNIX */
  2091.             noraw();
  2092.             endwin();    /* terminate */
  2093.             return;
  2094.             break;
  2095.         }
  2096.     }
  2097.     move(eeplines - 1,0);
  2098.     clrtoeol();
  2099.     refresh();
  2100.     noraw();
  2101.     endwin();    /* terminate */
  2102. }
  2103.  
  2104.  
  2105. /* This routine will show the window that opens into the current list
  2106. of newsgroup lines.  Current is the one that will be highlighted.  
  2107. The global variable scrnpos is used to indicate where this line 
  2108. is on the screen, with 0 being at the top line. 
  2109.  
  2110. The intention of this routine is that it should be used whenever
  2111. there is a major change to the cursor position within the list.  This 
  2112. may be the result of a search, or jumping to the top or bottom of
  2113. the file, or using page up or page down keys.  The desired behaviour
  2114. is that the highlighted line should stay roughly in the same position
  2115. on the screen when this move occurs.  We can calculate the relative
  2116. position of the current line by subtracting the scrnpos from current
  2117. to begin the index.  */
  2118.  
  2119. void    showlist(current)
  2120. int    current;
  2121. {
  2122. int    index, counter;
  2123.  
  2124.     counter = 0;
  2125.     index = current - scrnpos;
  2126.     while (counter < (eeplines - 1)) {
  2127.         move(counter,0);
  2128.         if ((index >= 0) && (index < c_active)) {
  2129.             if (counter == scrnpos) {
  2130.                 if (!eepoint) standout();
  2131.                 if (aptr->mark == 0)
  2132.                     printw("->");
  2133.                 else
  2134.                     printw("*>");
  2135.             } else if (aptr->mark == 0)
  2136.                 printw("  ");
  2137.             else
  2138.                 printw("* ");
  2139.             aptr = act[index];
  2140.             /* Show whether subscribed or not */
  2141.             switch(aptr->status) {
  2142.             case ':':    printw("+"); /* subscribed */
  2143.                     break;
  2144.             case '!':    printw(" "); /* unsubscribed */
  2145.                     break;
  2146.             default:    printw("?"); /* mystery! */
  2147.             }
  2148.             
  2149.             printw("%.28s ", pad(aptr->name,28));
  2150.             printw("%.46s ", pad(aptr->desc,46));
  2151.             move(counter,0);
  2152.             if ((counter == scrnpos) && !eepoint) 
  2153.                 standend();
  2154.         } else printw("%.79s", pad((char *)NULL,79));
  2155.         index++;
  2156.         counter++;
  2157.     }
  2158.     move(eeplines - 1,0);
  2159.     deleteln();
  2160.     printw("There are %d available newsgroups",c_active);
  2161.     move(eeplines - 1,60);
  2162.     printw("Press ? for Help");
  2163.     move(scrnpos,0);
  2164.     refresh();
  2165. }
  2166. @EOF
  2167.  
  2168. chmod 600 eepmenu.c
  2169.  
  2170. echo x - eepmisc.c
  2171. cat >eepmisc.c <<'@EOF'
  2172. /*
  2173.     eepmisc.c  -- Miscellaneous functions 
  2174. */
  2175.  
  2176. #ifdef DOS
  2177. #include    <dos.h>
  2178. #endif /* DOS */
  2179.  
  2180. #include    <stdio.h>
  2181. #include    <ctype.h>
  2182. #include    <curses.h>
  2183.  
  2184. #ifdef ANSI
  2185. #include <stdlib.h>
  2186. #else
  2187. #define void int
  2188. extern char *malloc();
  2189. #endif /* ANSI */
  2190.  
  2191. #include    "eep.h"
  2192.  
  2193.  
  2194. extern int    qcompare();    /* sort alphabetically */
  2195. extern char    *shift_lower();    /* see eepmisc.c */
  2196. extern char    buffer[],     /* general purpose line buffer */
  2197.         tmp[];
  2198.  
  2199. extern struct  actif *act[];    /* main data structure */
  2200. extern struct  actif *topact[]; /* top level names only */
  2201.  
  2202. extern int  i_active,   /* index into arrays */
  2203.     c_active,    /* number of lines in act array */
  2204.     t_active,    /* number of lines in act array */
  2205.     eepoint,    /* pointer switch */
  2206.     noshell;    /* noshell switch */
  2207.  
  2208. /* The levels array contains the Head pointers which control the
  2209. linked lists of newsgroups grouped by their "depth" in the
  2210. hierarchy, and sorted alphabetically.  E.g. alt, sci, soc are 
  2211. level 0, alt.pagan, sci.physics are level 1 and so on. */
  2212.  
  2213. extern struct actif *levels[];  /* keep track of levels with this array */
  2214. extern struct actif *aptr;    /* temporary pointer */
  2215. extern int    i_levels;    /* index into array */
  2216.  
  2217.  
  2218. WINDOW    *over;    /* used for on-screen help window etc. */
  2219.  
  2220. #ifdef UNIX
  2221. int    time_out = TIMEOUT; /* keyboard inactivity timer */
  2222.  
  2223. /* Process the alarm signal here.  This will cause a keyboard timeout
  2224.    to occur, and exit from the system.  */
  2225.  
  2226. int    catchalarm(signo)
  2227. int    signo;
  2228. {
  2229.     printf("Keyboard inactivity timer caused exit.\r\n");
  2230.     fflush(stdout);
  2231.     exit(0);
  2232. }
  2233. #endif /* UNIX */
  2234.  
  2235. /* getbuf() -- read input into the buffer */
  2236.  
  2237. void    getbuf(buf)
  2238. char    *buf;
  2239. {
  2240. int    count = 0,
  2241.     c;    /* character */
  2242.  
  2243.     while (count < BUFSIZE - 1) { /* -1 to allow for nul */
  2244.         c = getch();
  2245.         switch(c) {
  2246.         case KEY_BREAK:
  2247.         case '\033':
  2248.         case '\003':
  2249. #ifdef DOS
  2250.         case KEY_EXIT:
  2251. #endif /* DOS */
  2252.             deleteln();
  2253.             refresh();
  2254.             buf[0] = '\0';
  2255.             return;
  2256.             break;    /* just in case */
  2257.         case KEY_BACKSPACE:
  2258.         case '\010':
  2259.         case '\177':
  2260.         case KEY_LEFT:
  2261.             if (count == 0)    continue;
  2262.             addstr("\010 \010");
  2263.             refresh();
  2264.             count--;
  2265.             continue;
  2266.         case KEY_ENTER:
  2267.         case '\n':
  2268.         case '\r':
  2269.             buf[count] = '\0';
  2270.             return;
  2271.             break;
  2272.         }
  2273.         /* yes, this is very ASCII and doesn't take into
  2274.         account non-English alphabets.  This is because
  2275.         I'm not planning to fix curses in free software. */
  2276.         if ((c < '\020') || /* some other control code */
  2277.             (c > '\177'))
  2278.             continue;    /* ignore it */
  2279.         buf[count] = c;
  2280.         addch(buf[count]);
  2281.         refresh();
  2282.         count++;
  2283.     }
  2284.     buf[count] = '\0';    /* nul terminate the string */
  2285. }
  2286.  
  2287. /* wrap -- this function will take a string, and word wrap it to
  2288. within the defined width by inserting CRLF at appropriate places. 
  2289. The result will be output directly.  An offset may be defined, 
  2290. which will not be used on the first line only. */
  2291.  
  2292. void    wrap(string,width,offset)
  2293. char    *string;
  2294. int    width,
  2295.     offset;
  2296. {
  2297. char    temp[80];    /* temporary buffer */
  2298. char    *p,*q;    /* traverse the string with this */
  2299. int    count;    /* keep track of characters */
  2300. int    offcount;
  2301. int    first_line;
  2302.  
  2303.     if (width == 0) {
  2304.         printf("Can't wrap to zero width, sorry!");
  2305.         printf(CRLF);
  2306.         return;
  2307.     }
  2308.     if (strlen(string) == 0) return;
  2309.     p = string;
  2310.     q = temp;
  2311.     count = 0;
  2312.     first_line = 0;    /* no offset for first line */
  2313.     while (*p != '\000') {
  2314.         *q = *p;
  2315.         if (count == width) { /* check for wrapping */
  2316.             while (*p != ' ') {
  2317.                 q--;
  2318.                 p--;
  2319.                 if (--count == 0) { /* can't wrap! */
  2320.                     /* so we truncate */
  2321.                     q = temp + width;
  2322.                     p = p + width; /* carry on... */
  2323.                     break;
  2324.                 }
  2325.             }
  2326.             *q = '\0';  /* terminate output here */
  2327.             if (first_line != 0) {
  2328.                 offcount = offset;
  2329.                 while (offcount-- > 0) printf(" ");
  2330.             }
  2331.             first_line = 1;
  2332.             printf(temp);
  2333.             printf(CRLF);
  2334.             count = -1;    /* new line */
  2335.             q = temp - 1;
  2336.         }
  2337.         count++;
  2338.         p++;
  2339.         q++;
  2340.     }
  2341.     *q = '\0';
  2342.     if (first_line != 0) {
  2343.         offcount = offset;
  2344.         while (offcount-- > 0) printf(" ");
  2345.     }
  2346.     printf(temp);
  2347.     printf(CRLF);
  2348. }
  2349.  
  2350.  
  2351. /* These next two routines simply lifted from the Elm mail package, 
  2352. by Dave Taylor.  If you need a nice mail system, you won't have to 
  2353. look much further than Elm!  -- P.G. */
  2354.  
  2355. char    *shift_lower(string)
  2356. char    *string;
  2357. {
  2358.     /** return 'string' shifted to lower case.  Do NOT touch the
  2359.         actual string handed to us! **/
  2360.  
  2361.     static char buf[BUFSIZE];
  2362.     register char *bufptr = buf;
  2363.  
  2364.         if (string == (char *)NULL) {
  2365.             *bufptr = '\0';
  2366.             return((char *)buf);
  2367.         }
  2368.  
  2369.     for (; *string; string++, bufptr++)
  2370.       if (isupper(*string))
  2371.         *bufptr = tolower(*string);
  2372.       else
  2373.         *bufptr = *string;
  2374.     
  2375.     *bufptr = 0;
  2376.     
  2377.     return( (char *) buf);
  2378. }
  2379.  
  2380.  
  2381. int    in_string(buf, pat)
  2382. char    *buf, *pat;
  2383. {
  2384.     /** Returns TRUE iff pat occurs IN ITS ENTIRETY in buf. **/ 
  2385.  
  2386.     register int i = 0, j = 0;
  2387.     
  2388.     while (buf[i] != '\0') {
  2389.       while (buf[i++] == pat[j++]) 
  2390.         if (pat[j] == '\0') 
  2391.           return(TRUE);
  2392.       i = i - j + 1;
  2393.       j = 0;
  2394.     }
  2395.     return(FALSE);
  2396. }
  2397.  
  2398. #ifdef DOS
  2399. /*
  2400.  *  Here are some timing routines.  Call msleep() with parameter
  2401.  *  of 0L to establish precision the first time. 
  2402.  */
  2403.  
  2404.  
  2405. typedef struct
  2406. { /* time holder */
  2407.   int hour;
  2408.   int minute;
  2409.   int sec;
  2410.   int hsec;
  2411. } TIME, *TIME_PTR;
  2412.  
  2413. #define GET_TIME    0x2c        /* time request        */
  2414. #define DOS_INT        0x21        /* int to call DOS    */
  2415. #define INIT        60        /* initial clock set    */
  2416.  
  2417. /*
  2418.  * get_time(n)
  2419.  * TIME_PTR n;
  2420.  *
  2421.  * fills timetype structure n with current time using DOS interrupt 21
  2422.  *
  2423.  */
  2424.  
  2425. get_time(n)
  2426. TIME_PTR n;
  2427. {
  2428.   union REGS inregs;
  2429.   union REGS outregs;
  2430.  
  2431.   inregs.h.ah = GET_TIME;
  2432.  
  2433.   int86(DOS_INT, &inregs, &outregs);
  2434.  
  2435.   n->hour = outregs.h.ch;
  2436.   n->minute  = outregs.h.cl;
  2437.   n->sec  = outregs.h.dh;
  2438.   n->hsec = outregs.h.dl;
  2439.  
  2440.   return(0);
  2441. }
  2442.  
  2443. /* This will sleep for x seconds. */
  2444.  
  2445. void    sleep(x)
  2446. int x;
  2447. {
  2448.   int i;
  2449.   unsigned s;
  2450.   TIME n;               /* current time record */
  2451.  
  2452.   i = 0;
  2453.   get_time(&n);
  2454.   s = n.sec;
  2455.  
  2456.   while (i < x){
  2457.     while (s == n.sec)
  2458.       get_time(&n);
  2459.     s = n.sec;
  2460.     ++i;
  2461.   }
  2462. }
  2463.  
  2464. void    msleep(ms)
  2465.   long ms;
  2466. { /* sleep for ms miliseconds */
  2467.   static long estimate = 2000L; /* loops per milisecond */
  2468. #define OFFSET 2000L
  2469.   long loops;
  2470.   TIME n1, n2;
  2471.   unsigned int mydelay;
  2472.   
  2473.   /* If the value is 0, then we try to calculate the number of miliseconds
  2474.    * that make up a clock tick, then estimate a timing loop that will
  2475.    * delay for 1 milisecond.  */
  2476.  
  2477.   if (ms == 0L) {
  2478.  
  2479.     /* Loop until we see a change in time. */
  2480.  
  2481. try_again:
  2482.     get_time(&n1);
  2483.     for (loops = estimate*10; loops == 0; loops--)
  2484.         loops = (loops << 1) / 2; 
  2485.     get_time(&n2); 
  2486.     printf("Estimating... %ld\n", estimate);
  2487.     
  2488.     if ((n1.hsec == n2.hsec) && (n1.sec == n2.sec)) {
  2489.     estimate += OFFSET;
  2490.     goto try_again;
  2491.     }
  2492.  
  2493.     /* Calculate the difference in hundredths of seconds.  Note
  2494.      * that is is possible that n2.sec is less than n1.sec if
  2495.      * the minute has just changed, in which case we cheat.
  2496.      */
  2497.     if (n2.minute != n1.minute) n2.sec = n1.sec + 1;
  2498.     mydelay = (100*n2.sec + n2.hsec) - (100*n1.sec + n1.hsec);
  2499.  
  2500.     /* this is the first guess */
  2501.     estimate = estimate / ((long)mydelay * 10);
  2502.  
  2503.     get_time(&n1);
  2504.     for (loops = INIT*estimate*10; loops > 0; loops--)
  2505.         loops = (loops << 1) / 2;
  2506.     get_time(&n2);
  2507.     if (n2.minute != n1.minute) n2.sec = n1.sec + 1;
  2508.     mydelay = (100*n2.sec + n2.hsec) - (100*n1.sec + n1.hsec);
  2509.     if (mydelay == 0) mydelay = INIT;
  2510.     estimate = (INIT*estimate) / ((long)mydelay);
  2511.     printf("Estimate %ld loops per millisecond\n",estimate);
  2512.     for (loops = 10; loops > 0; loops--) {
  2513.     printf("Tick... ");
  2514.         msleep(1000L);
  2515.     }
  2516.     printf(" BOOM!\n");
  2517.   }
  2518.   for (loops = (long)ms*estimate; loops > 0; loops--)
  2519.     loops = (loops << 1) / 2; /* do nothing really */
  2520. }
  2521. #endif /* DOS */
  2522. @EOF
  2523.  
  2524. chmod 600 eepmisc.c
  2525.  
  2526. echo x - eepview.c
  2527. cat >eepview.c <<'@EOF'
  2528. /*------------------------------------------------------------------------
  2529.        Name: eepview.c
  2530.  
  2531. This module provides code to open a newsgroup directory, construct a
  2532. list of all the messages, and display the list, along with subjects
  2533. and names of the posters, plus size of messages.  This will replace the
  2534. full screen, and allow scrolling down with a pointer in a similar way
  2535. to the main screen.
  2536.  
  2537. ------------------------------------------------------------------------*/
  2538.  
  2539. #include <stdio.h>
  2540. #include <string.h>
  2541. #include <curses.h>
  2542.  
  2543. #ifdef UNIX
  2544. #include <ctype.h>
  2545. #include <errno.h>
  2546. #include <signal.h>
  2547. #endif /* UNIX */
  2548.  
  2549. #ifdef ANSI
  2550. #include <stdlib.h>
  2551. #else
  2552. #define void int
  2553. extern char *malloc();
  2554. #endif /* ANSI */
  2555.  
  2556. #ifdef DIRENT    /* defined in makefile */
  2557. #include <dirent.h>    /* or <sys/dirent.h> */
  2558. #define direct dirent
  2559. #else
  2560. #include "ndir.h"
  2561. #endif /* DIRENT */
  2562.  
  2563. #ifdef DOS
  2564. #include <dos.h>
  2565. #endif /* DOS */
  2566.  
  2567. #include "eep.h"
  2568.  
  2569. /* function definitions */
  2570. void    showarts();
  2571.  
  2572. FILE    *farticle;
  2573.  
  2574. struct newsview    {
  2575.  
  2576. long    artnum;        /* article number */
  2577. long    size;        /* size in bytes */
  2578. char    *from;        /* poster */
  2579. char    *date;        /* date */
  2580. char    *msgid;        /* Msg-id: */
  2581. char    *subject;    /* Subject: */
  2582. char    *keywords;    /* Keywords: */
  2583. char    *references;    /* References: */
  2584. };
  2585.  
  2586. int    news_index;    /* index into newsview */
  2587. int    art_count;    /* count of articles */
  2588. int    art_current;  /* current article index */
  2589. long    artnum_tmp;    /* temporary article number */
  2590. char     *dir;        /* pointer to directory name */
  2591. int     viewpos = 0;    /* view position */
  2592.  
  2593. char    nirvana[] = "(null)";
  2594.  
  2595. extern int eeplines;
  2596. extern int eepoint;
  2597. extern int current;
  2598. extern WINDOW *over;
  2599. extern char buffer[];
  2600. extern char *shift_lower();
  2601.  
  2602. struct newsview *art_ptr; /* pointer to news structure */
  2603.  
  2604. struct newsview *article[MAXARTS];    /* array of pointers to articles struct */
  2605.  
  2606. int    artnum_compare(item1,item2)    /* sort by article number */
  2607. struct newsview    **item1, **item2;
  2608. {
  2609. struct newsview *ptr1, *ptr2;
  2610. long int diff;
  2611.  
  2612.     ptr1 = *item1;
  2613.     ptr2 = *item2;
  2614.     diff = ptr1->artnum - ptr2->artnum;
  2615.     if (diff == 0L) return(0);
  2616.     if (diff > 0L) return(1);
  2617.     if (diff < 0L) return(-1);
  2618. }
  2619.  
  2620. #ifdef DOS
  2621.  
  2622. static void free_dircontents (struct _dircontents *);
  2623.  
  2624. #define ATTRIBUTES    (_A_RDONLY | _A_HIDDEN | _A_SYSTEM | _A_SUBDIR)
  2625.  
  2626. DIR *
  2627. opendir (char *name)
  2628. {
  2629.   struct find_t find_buf;
  2630.   DIR *dirp;
  2631.   struct _dircontents *dp;
  2632.   char name_buf[_MAX_PATH + 1];
  2633.   char *slash = "";
  2634.  
  2635.   if (!name)
  2636.     name = "";
  2637.   else if (*name)
  2638.     {
  2639.       char *s;
  2640.       int l = strlen (name);
  2641.  
  2642.       s = name + l - 1;
  2643.       if ( !(l == 2 && *s == ':') && *s != '\\' && *s != '/')
  2644.     slash = "/";    /* save to insert slash between path and "*.*" */
  2645.     }
  2646.  
  2647.   strcat (strcat (strcpy (name_buf, name), slash), "*.*");
  2648.  
  2649.   dirp = (DIR *) malloc (sizeof (DIR));
  2650.   if (dirp == (DIR *)0)
  2651.     return (DIR *)0;
  2652.  
  2653.   dirp->dd_loc = 0;
  2654.   dirp->dd_contents = dirp->dd_cp = (struct _dircontents *) 0;
  2655.  
  2656.   if (_dos_findfirst (name_buf, ATTRIBUTES, &find_buf))
  2657.     {
  2658.       free (dirp);
  2659.       return (DIR *)0;
  2660.     }
  2661.  
  2662.   do
  2663.     {
  2664.       dp = (struct _dircontents *) malloc (sizeof (struct _dircontents));
  2665.       if (dp == (struct _dircontents *)0)
  2666.     {
  2667.       free_dircontents (dirp->dd_contents);
  2668.       return (DIR *)0;
  2669.     }
  2670.  
  2671.       dp->_d_entry = (char *)malloc (strlen (find_buf.name) + 1);
  2672.       if (dp->_d_entry == (char *)0)
  2673.     {
  2674. /*      free(dp); */
  2675.       free_dircontents (dirp->dd_contents);
  2676.       return (DIR *)0;
  2677.     }
  2678.  
  2679.       if (dirp->dd_contents)
  2680.     dirp->dd_cp = dirp->dd_cp->_d_next = dp;
  2681.       else
  2682.     dirp->dd_contents = dirp->dd_cp = dp;
  2683.  
  2684.       strcpy (dp->_d_entry, find_buf.name);
  2685.  
  2686.       dp->_d_next = (struct _dircontents *)0;
  2687.  
  2688.     } while (! _dos_findnext (&find_buf));
  2689.  
  2690.   dirp->dd_cp = dirp->dd_contents;
  2691.  
  2692.   return dirp;
  2693. }
  2694.  
  2695.  
  2696. void
  2697. closedir (DIR *dirp)
  2698. {
  2699.   free_dircontents (dirp->dd_contents);
  2700.   free (dirp);
  2701. }
  2702.  
  2703.  
  2704. struct direct *
  2705. readdir (DIR *dirp)
  2706. {
  2707.   static struct direct dp;
  2708.  
  2709.   if (dirp->dd_cp == (struct _dircontents *)0)
  2710.     return (struct direct *)0;
  2711.   dp.d_namlen = dp.d_reclen =
  2712.     strlen ((char *)strcpy (dp.d_name, dirp->dd_cp->_d_entry));
  2713.   strlwr (dp.d_name);        /* JF */
  2714.   dp.d_ino = 0;
  2715.   dirp->dd_cp = dirp->dd_cp->_d_next;
  2716.   dirp->dd_loc++;
  2717.  
  2718.   return &dp;
  2719. }
  2720.  
  2721.  
  2722. void
  2723. seekdir (DIR *dirp, long off)
  2724. {
  2725.   long i = off;
  2726.   struct _dircontents *dp;
  2727.  
  2728.   if (off < 0)
  2729.     return;
  2730.   for (dp = dirp->dd_contents; --i >= 0 && dp; dp = dp->_d_next)
  2731.     ;
  2732.   dirp->dd_loc = off - (i + 1);
  2733.   dirp->dd_cp = dp;
  2734. }
  2735.  
  2736.  
  2737. long
  2738. telldir (DIR *dirp)
  2739. {
  2740.   return dirp->dd_loc;
  2741. }
  2742.  
  2743.  
  2744. /* Garbage collection */
  2745.  
  2746. static void
  2747. free_dircontents (struct _dircontents *dp)
  2748. {
  2749.   struct _dircontents *odp;
  2750.  
  2751.   while (dp)
  2752.     {
  2753.       if (dp->_d_entry)
  2754.     free ((struct _dirdesc *)dp->_d_entry);
  2755.       dp = (odp = dp)->_d_next;
  2756.       free ((struct _dirdesc *)odp);
  2757.     }
  2758. }
  2759. #endif /* DOS */
  2760.  
  2761. /* Main EEP routines start here */
  2762.  
  2763.  
  2764. void    eepview(groupname)
  2765. char    *groupname;    /* place to find articles */
  2766. {
  2767.   static DIR *directory;
  2768.   struct direct *entry = (struct direct *)0;
  2769.   char *name = "";
  2770.  
  2771. char    *ptr;        /* general pointer */
  2772. char    search[BUFSIZE];    /* search string buffer */
  2773. int    found = FALSE,    /* flag to say we've found something */
  2774.     quit = FALSE,    /* flag used when ready to quit */
  2775.     index,        /* used when searching */
  2776.     top,
  2777.     counter,    /* used when counting */
  2778.     ch;        /* input character */
  2779.  
  2780.     
  2781.     DIR *dirp;
  2782.     struct direct *direntp;
  2783.  
  2784.     if (strlen(groupname) == 0) return;
  2785.     /* Allow two extra bytes, one for terminating null and
  2786.          * the other for the separating '/' if needed. 
  2787.          */
  2788.     dir = (char *)malloc(strlen(NEWSBASE)+strlen(groupname)+2);
  2789.     strcpy(dir,NEWSBASE);
  2790.     if (dir[strlen(dir)-1] != '/') strcat(dir,"/");    
  2791.     strcat(dir,groupname);
  2792.     /* convert newsgroup name to directory name */
  2793.     while ((ptr = strchr(dir,'.')) != (char *) NULL)
  2794.         *ptr = '/';
  2795.     if ((dirp = opendir(dir)) == NULL)
  2796.     {
  2797.         move(eeplines - 1,0);
  2798.         deleteln();
  2799.         printw("Cannot open %s",dir);
  2800.         move(current,0);
  2801.         refresh();
  2802.         sleep(2);
  2803.         return;
  2804.     }
  2805.     move(eeplines - 1,0);
  2806.     deleteln();
  2807.     printw("Opening %s",dir);
  2808.     refresh();
  2809.     news_index = 0;
  2810.     errno = 0;
  2811.     while ((direntp = readdir(dirp)) != NULL) {
  2812.         artnum_tmp = atol(direntp->d_name);
  2813.         if (artnum_tmp <= 0) continue;
  2814.  
  2815.         /* atol() will be fooled by filenames that start with
  2816.          * digits, so let's confirm that it has only digits. */
  2817.  
  2818.         ptr = direntp->d_name;
  2819.         while (isdigit((int)(*ptr))) ptr++;
  2820.         if (*ptr != '\0') break; /* should point to terminating null */
  2821.  
  2822.         if ((art_ptr = (struct newsview *)malloc(sizeof(struct newsview))) == 
  2823.             (struct newsview *) NULL) {
  2824.             fprintf(stderr,"Memory allocation error!\n");
  2825.             exit(1);
  2826.         }
  2827.         article[news_index] = art_ptr;
  2828.         art_ptr->artnum = artnum_tmp;
  2829.         art_ptr->size = 0L;
  2830.         art_ptr->from = nirvana;
  2831.         art_ptr->date = nirvana;
  2832.         art_ptr->msgid = nirvana;
  2833.         art_ptr->subject = nirvana;
  2834.         art_ptr->keywords = nirvana;
  2835.         art_ptr->references = nirvana;
  2836.         news_index++;
  2837.     }
  2838.     closedir(dirp);
  2839.     if (errno != 0)
  2840.         fprintf(stderr,"Error reading directory %s\n",dir);
  2841.  
  2842.     art_count = news_index;
  2843.     if (art_count == 0) {
  2844.         move(eeplines - 1,0);
  2845.         deleteln();
  2846.         printw("No articles found in %s",dir);
  2847.         refresh();
  2848.         sleep(2);
  2849.         return;
  2850.     }
  2851.  
  2852. /* We have now read in the directory, keeping only those file names
  2853.  * which are numeric.  (We're ignoring aberrant files with leading
  2854.  * zeros -- this assumes standard news files.)
  2855.  *   Now we shall sort this into numerical order. */
  2856.  
  2857.     qsort( article, (unsigned) art_count, 
  2858.         (int) sizeof(article[0]), artnum_compare);
  2859.  
  2860.  /* Now let's read each file to get some basic info on it.  */
  2861.  
  2862.     news_index = 0;
  2863.     while (news_index < art_count) {
  2864.         /* Start by initializing the data element */
  2865.         art_ptr = article[news_index];
  2866.         if (art_ptr == (struct newsview *)NULL) {
  2867.             news_index++;
  2868.             continue;
  2869.         }
  2870.         sprintf(buffer, "%s/%ld", dir, art_ptr->artnum); 
  2871.         if ((farticle = fopen(buffer,"r")) == (FILE *)NULL) {
  2872.             news_index++;
  2873.             continue;
  2874.         }
  2875.         while (fgets(buffer,BUFSIZE,farticle) != (char *)NULL) {
  2876.  
  2877.             /* Since we're reading headers, break at the first
  2878.              * line that isn't part of a header, especially
  2879.              * the blank line that separates the header from
  2880.              * the body of the message
  2881.              */
  2882.             if ((buffer[0] == '#') ||
  2883.                 (buffer[0] == '\n') ||
  2884.                 (buffer[0] == '\r') ||
  2885.                 (buffer[0] == '\0'))
  2886.                 break;
  2887.     
  2888.         /* Now allocate memory for article headers */
  2889.  
  2890.             ptr = shift_lower(strtok(buffer," \t\r\n"));
  2891.  
  2892.             if (strcmp(ptr,"from:") == 0) {
  2893.                 ptr = strtok((char *)NULL,"\r\n");
  2894.                 if ((art_ptr->from = (char *) malloc(strlen(ptr)+1)) 
  2895.                     == (char *)NULL) {
  2896.                     printf("Error while allocating memory!\n");
  2897.                     sleep(2);
  2898.                     return;
  2899.                 }
  2900.                 strcpy(art_ptr->from,ptr);
  2901.                 continue;
  2902.             }
  2903.  
  2904.             if (strcmp(ptr,"date:") == 0) {
  2905.                 ptr = strtok((char *)NULL,"\r\n");
  2906.                 if ((art_ptr->date = (char *) malloc(strlen(ptr)+1)) 
  2907.                     == (char *)NULL) {
  2908.                     printf("Error while allocating memory!\n");
  2909.                     sleep(2);
  2910.                     return;
  2911.                 }
  2912.                 strcpy(art_ptr->date,ptr);
  2913.                 continue;
  2914.             }
  2915.  
  2916.             if (strcmp(ptr,"message-id:") == 0) {
  2917.                 ptr = strtok((char *)NULL,"\r\n");
  2918.                 if ((art_ptr->msgid = (char *) malloc(strlen(ptr)+1)) 
  2919.                     == (char *)NULL) {
  2920.                     printf("Error while allocating memory!\n");
  2921.                     sleep(2);
  2922.                     return;
  2923.                 }
  2924.                 strcpy(art_ptr->msgid,ptr);
  2925.                 continue;
  2926.             }
  2927.  
  2928.             if (strcmp(ptr,"subject:") == 0) {
  2929.                 ptr = strtok((char *)NULL,"\r\n");
  2930.                 if ((art_ptr->subject = (char *) malloc(strlen(ptr)+1)) 
  2931.                     == (char *)NULL) {
  2932.                     printf("Error while allocating memory!\n");
  2933.                     sleep(2);
  2934.                     return;
  2935.                 }
  2936.                 strcpy(art_ptr->subject,ptr);
  2937.                 continue;
  2938.             }
  2939.  
  2940.             if (strcmp(ptr,"references:") == 0) {
  2941.                 ptr = strtok((char *)NULL,"\r\n");
  2942.                 if ((art_ptr->references = (char *) malloc(strlen(ptr)+1)) 
  2943.                     == (char *)NULL) {
  2944.                     printf("Error while allocating memory!\n");
  2945.                     sleep(2);
  2946.                     return;
  2947.                 }
  2948.                 strcpy(art_ptr->references,ptr);
  2949.                 continue;
  2950.             }
  2951.  
  2952.             if (strcmp(ptr,"keywords:") == 0) {
  2953.                 ptr = strtok((char *)NULL,"\r\n");
  2954.                 if ((art_ptr->keywords = (char *) malloc(strlen(ptr)+1)) 
  2955.                     == (char *)NULL) {
  2956.                     printf("Error while allocating memory!\n");
  2957.                     sleep(2);
  2958.                     return;
  2959.                 }
  2960.                 strcpy(art_ptr->keywords,ptr);
  2961.                 continue;
  2962.             }
  2963.         }
  2964.     fclose(farticle);
  2965.         news_index++;
  2966.     }
  2967.     
  2968.  
  2969. #ifdef UNIX
  2970.     idlok(stdscr,TRUE);
  2971. #endif /* UNIX */
  2972.  
  2973.     showarts(0);
  2974.     art_current = 0;
  2975.  
  2976.     while (quit == FALSE) {
  2977.     ch = getch();
  2978.     switch(ch) {
  2979.         case 'q':    /* quit */
  2980.         case 'Q':
  2981.         case '\033':    /* ESCAPE by itself */
  2982.         case '\003':    /* for those who like ^C */
  2983.         case '\177':    /* for those who like INTR */
  2984.             quit = TRUE;
  2985.             break;
  2986.  
  2987.         case '?':    /* on-line help */
  2988.         case 'h':
  2989.         case 'H':
  2990.         case KEY_F(1):
  2991.             over = newwin(18,60,0,5);
  2992.             if (!eepoint) wstandout(over);
  2993.             box(over,'\0','\0');
  2994.             if (!eepoint) wstandend(over);
  2995.             wmove(over,2,5);
  2996.             wstandout(over);
  2997.             waddstr(over," eep v1.6: .newsrc & JOIN file editor ");
  2998.             wstandend(over);
  2999.             wmove(over,3,5);
  3000.             waddstr(over,"       Newsgroup browser submenu");
  3001.             wmove(over,5,5);
  3002.             waddstr(over,"t   Top of file     b   Bottom of file");
  3003.             wmove(over,6,5);
  3004.             waddstr(over,"i   Show info       j   Next line");
  3005.             wmove(over,7,5);
  3006.             waddstr(over,"k   Previous line   p   Pointer change");
  3007.             wmove(over,8,5);
  3008.             waddstr(over,"q   Quit to main    r   Redraw screen");
  3009.             wmove(over,9,5);
  3010.             waddstr(over,"^D  Page down       ^U  Page up");
  3011.             wmove(over,10,5);
  3012.             waddstr(over,"?   This help");
  3013.             wmove(over,17,5);
  3014.             wstandout(over);
  3015.             waddstr(over," Press SPACE BAR to exit from help ");
  3016.             wstandend(over);
  3017.             wrefresh(over);
  3018.             ch = wgetch(over);
  3019.             delwin(over);
  3020.             touchwin(stdscr);
  3021.             refresh();
  3022.             break;
  3023.  
  3024.         case 'r':    /* redraw */
  3025.         case 'R':    /* redraw */
  3026.         case '\014':    /* form feed ^L */
  3027.             touchwin(stdscr);
  3028.             clearok(stdscr,TRUE);
  3029.             refresh();
  3030.             break;
  3031.  
  3032.         case 'i':
  3033.             if ((art_ptr = article[art_current]) == (struct newsview *)NULL) 
  3034.                 break;
  3035.             over = newwin(18,67,0,2);
  3036.             if (!eepoint) wstandout(over);
  3037.             box(over,'\0','\0');
  3038.             if (!eepoint) wstandend(over);
  3039.             wmove(over,2,2);
  3040.             wprintw(over,"No. : %ld",art_ptr->artnum);
  3041.             wmove(over,3,2);
  3042.             wprintw(over,"From: %.57s",art_ptr->from);
  3043.             wmove(over,4,2);
  3044.             wprintw(over,"Subj: %.57s",art_ptr->subject);
  3045.             wmove(over,5,2);
  3046.             wprintw(over,"MsID: %.57s",art_ptr->msgid);
  3047.             wmove(over,6,2);
  3048.             wprintw(over,"Refs: %.57s",art_ptr->references);
  3049.             wmove(over,7,2);
  3050.             wprintw(over,"Date: %.57s",art_ptr->date);
  3051.             wmove(over,8,2);
  3052.             wprintw(over,"Keys: %.57s",art_ptr->keywords);
  3053.             wmove(over,17,9);
  3054.             wstandout(over);
  3055.             waddstr(over," Press SPACE BAR to continue ");
  3056.             wstandend(over);
  3057.             wrefresh(over);
  3058.             ch = wgetch(over);
  3059.             delwin(over);
  3060.             touchwin(stdscr);
  3061.             refresh();
  3062.             break;
  3063.  
  3064.  
  3065.         case 't':
  3066.         case 'T':
  3067.         case '^':    /* top of list */
  3068.             art_current = 0;
  3069.             viewpos = 0;
  3070.             showarts(art_current);
  3071.             break;
  3072.  
  3073.         case 'b':
  3074.         case 'B':
  3075.         case '$':    /* bottom of list */
  3076.             art_current = art_count - 1;
  3077.             if (art_current < eeplines - 1) 
  3078.                 viewpos = art_current;
  3079.             else
  3080.                 viewpos = eeplines - 2;
  3081.             showarts(art_current);
  3082.             break;
  3083.  
  3084.  
  3085.         case ' ':
  3086.         case 'j':
  3087.         case 'J':    /* join */
  3088.         case '\016':    /* for emacs users */
  3089.         case KEY_DOWN:
  3090. scroll_down:
  3091.         /* Don't move if we're at the end. */
  3092.             if (art_current == art_count - 1) {
  3093.                 beep();
  3094.                 break;
  3095.             }
  3096.             /* remove highlights by redrawing line */
  3097.             art_ptr = article[art_current];
  3098.             move(viewpos,0);
  3099.             clrtoeol();
  3100.             if (!eepoint) standend();
  3101.             if ((art_current >= 0) && (art_current < art_count)) {
  3102.                 printw("  ");
  3103.                 art_ptr = article[art_current];
  3104.                 printw("%8ld", art_ptr->artnum);
  3105.                 move(viewpos,11);
  3106.                 printw("%.38s", art_ptr->subject);
  3107.                 move(viewpos,50);
  3108.                 printw("%.28s", art_ptr->from);
  3109.                 move(viewpos,0);
  3110.             } else clrtoeol();
  3111.             art_current++;
  3112.             if (++viewpos == eeplines - 1) { /* scroll up */
  3113.                 move(0,0);
  3114.                 deleteln();
  3115.                 move(eeplines - 2,0);
  3116.                 insertln();
  3117.                 viewpos = eeplines - 2;
  3118.                 top++;
  3119.             };
  3120.             /* Now paint our new position */
  3121.             move(viewpos,0);
  3122.             clrtoeol();
  3123.             art_ptr = article[art_current];
  3124.             if (!eepoint) standout();
  3125.             if ((art_current >= 0) && (art_current < art_count)) {
  3126.                 printw("->");
  3127.                 art_ptr = article[art_current];
  3128.                 printw("%8ld", art_ptr->artnum);
  3129.                 move(viewpos,11);
  3130.                 printw("%.38s", art_ptr->subject);
  3131.                 move(viewpos,50);
  3132.                 printw("%.28s", art_ptr->from);
  3133.                 move(viewpos,0);
  3134.             } else clrtoeol();
  3135.             if (!eepoint) standend();
  3136.             move(viewpos,0);
  3137.             refresh();
  3138.             break;
  3139.  
  3140.         case 'k':
  3141.         case 'K':
  3142.         case '\010':    /* backspace */
  3143.         case '\020':    /* for emacs users */
  3144.         case KEY_UP:
  3145.         /* Don't move if we're at the top. */
  3146.             if (art_current == 0) {
  3147.                 beep();
  3148.                 break;
  3149.             }
  3150.             move(viewpos,0);
  3151.             clrtoeol();
  3152.             if (!eepoint) standend();
  3153.             if ((art_current >= 0) && (art_current < art_count)) {
  3154.                 printw("  ");
  3155.                 art_ptr = article[art_current];
  3156.                 printw("%8ld", art_ptr->artnum);
  3157.                 move(viewpos,11);
  3158.                 printw("%.38s", art_ptr->subject);
  3159.                 move(viewpos,50);
  3160.                 printw("%.28s", art_ptr->from);
  3161.                 move(viewpos,0);
  3162.             } else clrtoeol();
  3163.             refresh();
  3164.             art_current--;
  3165.             if (--viewpos == -1) {
  3166.                 move(eeplines - 2,0);
  3167.                 deleteln();
  3168.                 move(0,0);
  3169.                 insertln();
  3170.                 refresh();
  3171.                 move(0,0);
  3172.                 viewpos = 0;
  3173.                 if (top > 0) top--;
  3174.             }; /* cause scroll */
  3175.             move(viewpos,0);
  3176.             clrtoeol();
  3177.             art_ptr = article[art_current];
  3178.             if (!eepoint) standout();
  3179.             if ((art_current >= 0) && (art_current < art_count)) {
  3180.                 printw("->");
  3181.                 art_ptr = article[art_current];
  3182.                 printw("%8ld", art_ptr->artnum);
  3183.                 move(viewpos,11);
  3184.                 printw("%.38s", art_ptr->subject);
  3185.                 move(viewpos,50);
  3186.                 printw("%.28s", art_ptr->from);
  3187.                 move(viewpos,0);
  3188.             } else clrtoeol();
  3189.             if (!eepoint) standend();
  3190.             move(viewpos,0);
  3191.             refresh();
  3192.             break;
  3193.  
  3194.  
  3195.         case KEY_NPAGE: /* next page */
  3196.         case '\004':    /* ^D */
  3197.         case '\006':    /* ^F */
  3198.  
  3199.             /* If a pagedown will go past the end
  3200.             of the list, simply position at the end. */
  3201.  
  3202.             if (art_current == art_count - 1) {
  3203.                 beep();
  3204.                 break;
  3205.             }
  3206.             if ((art_current += EEPPAGE) >= art_count) {
  3207.                 art_current = art_count - 1;
  3208.                 if ((art_count - art_current) < (eeplines - 2))
  3209.                     viewpos = eeplines - 2;
  3210.             } else  if (viewpos + EEPPAGE < eeplines - 2)
  3211.                     viewpos += EEPPAGE; 
  3212.             showarts(art_current);
  3213.             break;
  3214.  
  3215.         case KEY_PPAGE: /* previous page */
  3216.         case '\025':    /* ^U */
  3217.         case '\002':    /* ^B */
  3218.             if (art_current == 0) {
  3219.                 beep();
  3220.                 break;
  3221.             }
  3222.             if ((art_current -= EEPPAGE) < 0) {
  3223.                 art_current = 0;
  3224.                 viewpos = 0;
  3225.             } else  if ((viewpos - EEPPAGE) >= 0)
  3226.                     viewpos -= EEPPAGE;
  3227.             showarts(art_current);
  3228.             break;
  3229.  
  3230.             case 'p':   /* change type of pointer */
  3231.         case 'P':
  3232.             if (eepoint) eepoint = FALSE;
  3233.             else eepoint = TRUE;
  3234.             showarts(art_current);
  3235.             break;
  3236.  
  3237.         }
  3238.     }
  3239.     /* Now some garbage collection to free some memory */
  3240. #ifdef UNIX       /* can't make it work with MS-DOS!!!! */
  3241.     news_index = 0;
  3242.     while (news_index < art_count) {
  3243.         art_ptr = article[news_index];
  3244.         if (art_ptr != (struct newsview *)NULL) {
  3245.             if (art_ptr->from != nirvana) free(art_ptr->from);
  3246.             if (art_ptr->date != nirvana) free(art_ptr->date);
  3247.             if (art_ptr->msgid != nirvana) free(art_ptr->msgid);
  3248.             if (art_ptr->subject != nirvana) free(art_ptr->subject);
  3249.             if (art_ptr->keywords != nirvana) free(art_ptr->keywords);
  3250.             if (art_ptr->references != nirvana) free(art_ptr->references);
  3251.             free(art_ptr);
  3252. /*          printf("%d: Freeing %ld\n",news_index,(long)art_ptr); */
  3253.         }
  3254.         news_index++;
  3255.     }
  3256. #endif /* UNIX */
  3257. }
  3258.  
  3259. /* showarts() -- show list of articles by number */
  3260.  
  3261. void    showarts(art_current)
  3262. int    art_current;
  3263. {
  3264. int    index, counter;
  3265.  
  3266.     erase();    /* clear screen */
  3267.     counter = 0;
  3268.     index = art_current - viewpos;
  3269.     while (counter < (eeplines - 1)) {
  3270.         move(counter,0);
  3271.         if (counter == viewpos) {
  3272.             if (!eepoint) standout();
  3273.             printw("->");
  3274.         } else    printw("  ");
  3275.         if ((index >= 0) && (index < art_count)) {
  3276.             art_ptr = article[index];
  3277.             printw("%8ld", art_ptr->artnum);
  3278.             move(counter,11);
  3279.             printw("%.38s", art_ptr->subject);
  3280.             move(counter,50);
  3281.             printw("%.28s", art_ptr->from);
  3282.             move(counter,0);
  3283.             if ((counter == viewpos) && !eepoint) 
  3284.                 standend();
  3285.         } else clrtoeol();
  3286.         index++;
  3287.         counter++;
  3288.     }
  3289.     move(eeplines - 1,0);
  3290.     clrtoeol();
  3291.     printw("There are %d articles in %s",art_count,dir);
  3292.     move(eeplines - 1,60);
  3293.     printw("Press ? for Help");
  3294.     move(viewpos,0);
  3295.     refresh();
  3296. }
  3297. @EOF
  3298.  
  3299. chmod 600 eepview.c
  3300.  
  3301. echo x - makefile
  3302. cat >makefile <<'@EOF'
  3303. #
  3304. # Makefile for eep .newsrc editor
  3305. #
  3306. # The following flags are important:
  3307. #  UNIX   - compiles UNIX version of code 
  3308. #  DIRENT - does your system have dirent.h?
  3309. #           See eep.h -- you might need to change path to sys/dirent.h
  3310. #  ANSI   - Try to follow ANSI conventions
  3311. #
  3312.  
  3313. CC=cc
  3314. CCFLAGS=-O -DUNIX -DDIRENT -DANSI
  3315. LIBS=-lcurses
  3316.  
  3317. HDR=eep.h
  3318.  
  3319. eep:        eepmain.o eepmisc.o eepmenu.o eepview.o
  3320.         $(CC) -o eep eepmain.o eepmisc.o eepmenu.o eepview.o $(LIBS)
  3321.         strip eep
  3322.  
  3323. eepmain.o:    eepmain.c $(HDR)
  3324.         $(CC) $(CCFLAGS) -c eepmain.c
  3325.  
  3326. eepmisc.o:    eepmisc.c $(HDR)
  3327.         $(CC) $(CCFLAGS) -c eepmisc.c
  3328.  
  3329. eepmenu.o:    eepmenu.c $(HDR)
  3330.         $(CC) $(CCFLAGS) -c eepmenu.c
  3331.  
  3332. eepview.o:    eepview.c $(HDR)
  3333.         $(CC) $(CCFLAGS) -c eepview.c
  3334. @EOF
  3335.  
  3336. chmod 600 makefile
  3337.  
  3338. echo x - makefile.dos
  3339. cat >makefile.dos <<'@EOF'
  3340. #
  3341. # Makefile for eep .newsrc editor
  3342. #
  3343. # DOS version was compiled with Zortech C++ v2.1.
  3344. # The curses libraries were prepared using the PDCurses package for MS-DOS
  3345.  
  3346. CC=ztc 
  3347. CCFLAGS= =30000 -b -DDOS -mli -DANSI
  3348. LIB=lcurses.lib
  3349.  
  3350. eep.exe:          eepmain.obj eepmisc.obj eepmenu.obj eepview.obj
  3351.         blink eepmain+eepmisc+eepmenu+eepview,eep,eep,$(LIB)
  3352.  
  3353. eepmain.obj:    eepmain.c eep.h
  3354.         $(CC) $(CCFLAGS) -c eepmain.c
  3355.  
  3356. eepmisc.obj:    eepmisc.c eep.h
  3357.         $(CC) $(CCFLAGS) -c eepmisc.c
  3358.  
  3359. eepmenu.obj:    eepmenu.c eep.h
  3360.         $(CC) $(CCFLAGS) -c eepmenu.c
  3361.  
  3362. eepview.obj:    eepview.c eep.h
  3363.         $(CC) $(CCFLAGS) -c eepview.c
  3364.  
  3365. getopt.obj:    getopt.c getopt.h debug.h
  3366.         $(CC) $(CCFLAGS) -c getopt.c
  3367.  
  3368. @EOF
  3369.  
  3370. chmod 600 makefile.dos
  3371.  
  3372. echo x - makefile.unx \[linked to makefile\]
  3373. ln makefile makefile.unx
  3374.  
  3375. chmod 600 makefile.unx
  3376.  
  3377. exit 0
  3378. -- 
  3379. Paul Gillingwater
  3380. Home system: paul@actrix.co.at (Waffle)
  3381.