home *** CD-ROM | disk | FTP | other *** search
/ Usenet 1994 January / usenetsourcesnewsgroupsinfomagicjanuary1994.iso / sources / misc / volume23 / tin / part02 < prev    next >
Encoding:
Text File  |  1991-09-25  |  49.7 KB  |  2,035 lines

  1. Newsgroups: comp.sources.misc
  2. From: iain@estevax.uucp (Iain J. Lea)
  3. Subject:  v23i016:  tin - threaded full screen newsreader v1.0 PL2, Part02/09
  4. Message-ID: <1991Sep25.205034.1844@sparky.imd.sterling.com>
  5. X-Md4-Signature: 1216384cf791ab26c63268a1db335011
  6. Date: Wed, 25 Sep 1991 20:50:34 GMT
  7. Approved: kent@sparky.imd.sterling.com
  8.  
  9. Submitted-by: iain@estevax.uucp (Iain J. Lea)
  10. Posting-number: Volume 23, Issue 16
  11. Archive-name: tin/part02
  12. Environment: BSD, SCO, ISC, SUNOS, SYSVR3, SYSVR4, ULTRIX, XENIX
  13.  
  14. #!/bin/sh
  15. # this is tin.shar.02 (part 2 of tin1.02)
  16. # do not concatenate these parts, unpack them in order with /bin/sh
  17. # file tin.1 continued
  18. #
  19. if touch 2>&1 | fgrep '[-amc]' > /dev/null
  20.  then TOUCH=touch
  21.  else TOUCH=true
  22. fi
  23. if test ! -r shar3_seq_.tmp; then
  24.     echo "Please unpack part 1 first!"
  25.     exit 1
  26. fi
  27. (read Scheck
  28.  if test "$Scheck" != 2; then
  29.     echo "Please unpack part $Scheck next!"
  30.     exit 1
  31.  else
  32.     exit 0
  33.  fi
  34. ) < shar3_seq_.tmp || exit 1
  35. echo "x - Continuing file tin.1"
  36. sed 's/^X//' << 'SHAR_EOF' >> tin.1 &&
  37. X.TP
  38. X\fBz\fP
  39. XMark article as unread.
  40. X.TP
  41. X\fB/\fP
  42. XArticle forward search.
  43. X.TP
  44. X\fB?\fP
  45. XArticle backward search
  46. X.TP
  47. X\fB|\fP
  48. XPipe current article / thread / articles matching pattern / tagged articles
  49. Xinto command.
  50. X.TP
  51. X\fB<\fP
  52. XGoto the first article in the current thread.
  53. X.TP
  54. X\fB>\fP
  55. XGoto the last article in the current thread.
  56. X.SH "OPTIONS MENU"
  57. XThis menu is accessed by pressing 'M' at all levels. It allows the user
  58. Xto customize the behaviour of tin. The options are saved to the file
  59. X\fI$HOME/.tin/tinrc\fP. The following options are settable:
  60. X.TP 4
  61. X\fBAuto save\fP
  62. XAutomatically save articles/threads by Archive-name: line in article
  63. Xheader. This is useful to set ON in conjunction with 'Save separate'.
  64. XUse <SPACE> to toggle ON/OFF and <CR> to set.
  65. X.TP
  66. X\fBSave separate\fP
  67. XSave articles/threads to separate files. Set ON if saving source code.
  68. XSet OFF if saving a coversational thread. Use <SPACE> to toggle ON/OFF
  69. Xand <CR> to set.
  70. X.TP
  71. X\fBMark saved read\fP
  72. XThis allows saved articles/threads to be automatically marked as
  73. Xhaving been read. Use <SPACE> to toggle ON/OFF and <CR> to set.
  74. X.TP
  75. X\fBKill articles\fP
  76. XThis allows the user to kill articles that match entries in the kill
  77. Xfile \fI$HOME/.tin/kill\fP. Use <SPACE> to toggle ON/OFF and <CR>
  78. Xto set.
  79. X.TP
  80. X\fBShow Author\fP
  81. XIf set ON the Subject: & From: (author) lines from the articles header
  82. Xare displayed. If set OFF longer Subject: lines are displayed. Use
  83. X<SPACE> to toggle ON/OFF and <CR> to set.
  84. X.TP
  85. X\fBDraw arrow\fP
  86. XAllows groups/articles to be selected by an arrow '->' if set ON or
  87. Xby an highlighted bar if set OFF. Use <SPACE> to toggle ON/OFF and
  88. X<CR> to set.
  89. X.TP
  90. X\fBPrint header\fP
  91. XThis allows the complete mail header or only the Siubject: and From:
  92. Xfields to be output when printing articles. Use <SPACE> to toggle
  93. XON/OFF and <CR> to set.
  94. X.TP
  95. X\fBGoto 1st unread\fP
  96. XThis allows the cursor to be placed at the first / last unread article
  97. Xupon entering a newsgroup with unread news. Use <SPACE> to toggle
  98. XON/OFF and <CR> to set.
  99. X.TP
  100. X\fBProcess type\fP
  101. XThis specifies the default type of post processing to perform on saved
  102. Xarticles. The following types of processing are allowed:
  103. X.in +.5i
  104. X.ti -\w'\(em'u
  105. X\(emnone.
  106. X.ti -\w'\(em'u
  107. X\(emunpacking of multi-part shell archives.
  108. X.ti -\w'\(em'u
  109. X\(emunpacking of multi-part uuencoded files.
  110. X.ti -\w'\(em'u
  111. X\(emunpacking of multi-part uuencoded files that produce a *.zoo archive
  112. Xwhose contents is listed.
  113. X.ti -\w'\(em'u
  114. X\(emunpacking of multi-part uuencoded files that produce a *.zoo archive
  115. Xwhose contents is extracted.
  116. X.ti -\w'\(em'u
  117. X\(emunpacking of multi-part patches for use by \fIpatch\fP and applying
  118. Xthem to specified source files.
  119. X.in -.5i
  120. XUse <SPACE> to toggle the required type and <CR> to set.
  121. X.TP
  122. X\fBSort articles by\fP
  123. XThis specifies how articles should be sorted. The following sort
  124. Xtypes are allowed:
  125. X.in +.5i
  126. X.ti -\w'\(em'u
  127. X\(emdon't sort articles (default).
  128. X.ti -\w'\(em'u
  129. X\(emsort articles by Subject: field (ascending & descending).
  130. X.ti -\w'\(em'u
  131. X\(emsort articles by From: field (ascending & descending).
  132. X.ti -\w'\(em'u
  133. X\(emsort articles by Date: field (ascending & descending).
  134. X.in -.5i
  135. XUse <SPACE> to set the required type.
  136. X.TP
  137. X\fBSave directory\fP
  138. XThe directory where articles/threads are to be saved. Default is
  139. X\fI$HOME/News\fP.
  140. X.TP
  141. X\fBMail directory\fP
  142. XThe directory where articles/threads are to be saved in mailbox format.
  143. XThis feature is mainly for use with the Elm mail program. It allows
  144. Xthe user to save articles/threads/groups simply by giving '=' as
  145. Xthe filename to save to.
  146. X.TP
  147. X\fBPrinter\fP
  148. XThe printer program with options that is to be used to print
  149. Xarticles. Default is lpr for BSD machines and lp for SysV machines.
  150. X.SH "KILL ARTICLE MENU"
  151. XThis menu is accessed by pressing '^K' at the group and page levels. It
  152. Xallows the user to kill an article that matches the current Subject:
  153. Xline, From: line or a string entered by the user. The user entered string
  154. Xcan be applied to the Subject: or From: lines of an article. The kill
  155. Xdescription can be limited to the current newsgroup or it can apply to all newsgroups.
  156. XOnce entered the user can abort the command and not save the kill
  157. Xdescription, edit the kill file or save the kill description.
  158. X.PP
  159. XOn starting tin the users killfile \fI$HOME/.tin/kill\fP is read and
  160. Xon entering a newsgroup any kill descriptions are applied. Articles
  161. Xthat match a kill description are marked killed and are not displayed.
  162. X.PP
  163. XThe 'Kill articles' option needs to be set ON in the Options Menu
  164. X(selected by 'M' at any level) to activate this command.
  165. X.SH "POSTING ARTICLES"
  166. XTin allows posting of articles, follow-up to already posted articles
  167. Xand replying direct through mail to the author of an article.
  168. X.PP
  169. XUse the 'w' command to post an article to a newsgroup. After entering
  170. Xthe post subject the default editor (ie. vi) or the editor specified
  171. Xby the $EDITOR enviroment variable will be started and the article can
  172. Xbe entered. To crosspost articles simply add a comma and the name of
  173. Xthe newsgroup(s) to the end of the Newsgroups: line at the beginning of
  174. Xthe article. After saving and exiting the editor you are asked if you
  175. Xwish to a)bort posting the article, e)dit the article again or p)ost
  176. Xthe article to the specified newsgroup(s).
  177. X.PP
  178. XUse the 'W' command to display a history of the articles you have posted.
  179. XThe date the article was posted, which newsgroups the article was
  180. Xposted to and the articles subject line are displayed.
  181. X.PP
  182. XUse the 'f' / 'F' command to post a follow-up article to an already
  183. Xposted article. The 'F' command will copy the text of the original
  184. Xarticle into the editor. The editing procedure is the same as when
  185. Xposting an article with the 'w' command.
  186. X.PP
  187. XUse the 'r' / 'R' command to reply direct through mail to the author
  188. Xof an already posted article. The 'R' command will copy the text of
  189. Xthe original article into the editor. The editing procedure is the
  190. Xsame as when posting an article with the 'w' command. After saving
  191. Xand exiting the editor you are asked if you wish to a)bort sending
  192. Xthe article, e)dit the article again or s)end the article to the
  193. Xauthor.
  194. X.SH "MAILING PIPING PRINTING AND SAVING ARTICLES"
  195. XThe command interface to mail ('m'), pipe ('|'), print ('o') and save
  196. X('s') articles is the same for ease of use.
  197. X.PP
  198. XThe initial command will ask you to select which a)rticle, t)hread,
  199. Xr)egex pattern, t)agged articles you wish to mail, pipe etc.
  200. X.PP
  201. XTagged articles must have already been tagged with the 'T' command.
  202. XAll tagged articles can be untagged by the 'U' untag command.
  203. X.PP
  204. XIf regex pattern matching is selected you are asked to enter a regular
  205. Xexpression (ie. to match all articles subject lines containing 'net News'
  206. Xyou must enter '*net News*'). Any articles that match the
  207. Xentered expression will be mailed, piped etc.
  208. X.PP
  209. XTo save articles to a mailbox with the name of the current newsgroup
  210. X(ie. Alt.sources) enter '=' or '=<mailbox name>' when asked for the
  211. Xsave filename.
  212. X.PP
  213. XTo save articles in <newsgroup name>/<filename> format enter '+<filename>'.
  214. X.PP
  215. XWhen saving articles you can specify whether the saved files should be
  216. Xpost processed (ie. unshar shell archive, uudecode multiple parts etc).
  217. XA default process type can be set by the 'Process type:' in the 'M'
  218. Xoptions menu.
  219. X.SH SIGNATURES
  220. XTin will recognize a signature in either \fI$HOME/.signature\fP or
  221. X\fI$HOME/.Sig\fP. If \fI$HOME/.signature\fP exists, then the signature
  222. Xwill be pulled into the editor for Tin mail commands.  A signature in
  223. X\fI$HOME/.signature\fP will not be pulled into the editor for posting
  224. Xcommands since the inews program will append the signature itself.
  225. X.PP
  226. XA signature in \fI$HOME/.Sig\fP will be pulled into the editor for both
  227. Xposting and mailing commands.
  228. X.PP
  229. XThe following is an example of a \fI$HOME/.Sig\fP file:
  230. X.RS
  231. X.nf
  232. XNAME   Iain Lea
  233. XEMAIL  iain@estevax.uucp    ...!unido!estevax!iain
  234. XSNAIL  Siemens AG, ANL 433SZ, 8510 Fuerth-Bislohe, Germany
  235. XPHONE  +49-911-331963 (home)  +49-911-895-3853 (work)
  236. X.fi
  237. X.RE
  238. X.SH "ENVIROMENT VARIABLES"
  239. X.TP
  240. X\fBTINDIR\fP
  241. XDefine this variable if you do not want tin's .tin directory in $HOME/.tin.
  242. X(ie. if you want all tin's private files in /tmp/.tin you would set
  243. XTINDIR to contain /tmp.
  244. X.TP
  245. X\fBNNTPSERVER\fP
  246. XThe default NNTP server to remotely read news from. This variable only
  247. Xneeds to be set if the -r command line option is specified and the file
  248. X\fI/etc/nntpserver\fP does not exist.
  249. X.TP
  250. X\fBORGANIZATION\fP
  251. XSet the mail header field Organization: to the contents of the
  252. Xvariable instead of the system default. This variable has precedence
  253. Xover the file \fI$HOME/.tin/organization\fP that may also contain an
  254. Xorganization string.
  255. X.TP
  256. X\fBREPLYTO\fP
  257. XSet the mail header field Reply-To: to the return address specified
  258. Xby the variable.
  259. XThis is useful if the machine is not registered in the UUCP mail maps
  260. Xor if you wish to recieve replies at a different machine.
  261. XThis variable has precedence over the file \fI$HOME/.tin/reply_to\fP
  262. Xthat may also contain a return address.
  263. X.TP
  264. X\fBADD_ADDRESS\fP
  265. XThis can contain an address to append to the return address when replying
  266. Xdirectly through mail to somebody whose mail address is not directly
  267. Xrecognized by the local host. For example say the return address is
  268. X\fIuser@bigvax\fP, but \fIbigvax\fP is not recognized by your host, so
  269. Xtherfore the mail will not reach \fIuser\fP. But the host \fIlittevax\fP
  270. Xis known to recognize your host and \fIbigvax\fP, so if ADD_ADDRESS is set
  271. X(ie. 'setenv ADD_ADDRESS @littevax' for csh or 'set ADD_ADDRESS @littevax'
  272. Xand 'export ADD_ADDRESS' for sh) the address \fIuser@bigvax@littlevax\fP will
  273. Xbe used and the mail will reach \fIuser@bigvax\fP.
  274. XThis variable has precedence over the file \fI$HOME/.tin/add_address\fP
  275. Xthat may also contain an address.
  276. X.TP
  277. X\fBBUG_ADDRESS\fP
  278. XIf the 'B' command bug report mail address is not correct this variable should be
  279. Xset to the correct mail address. This variable has precedence over the file
  280. X\fI$HOME/.tin/bug_address\fP that may also contain a mail address.
  281. X.TP
  282. X\fBEDITOR\fP
  283. XThis variable has precedence over the default editor (ie. vi) that is
  284. Xused in all editing operations within tin (ie. posting 'w', replying 'rR',
  285. Xfollow-ups 'fF' and bug reports 'B').
  286. X.SH TIPS AND TRICKS
  287. XThe following newsgroups provide useful information concerning news software:
  288. X.in +.5i
  289. X.ti -\w'\(em'u
  290. X\(emnews.software.readers (info. about news user agents tin,rn,nn,vn etc.)
  291. X.ti -\w'\(em'u
  292. X\(emnews.software.nntp (info. about NNTP)
  293. X.ti -\w'\(em'u
  294. X\(emnews.software.b (info. about news transport agents Bnews & Cnews)
  295. X.PP
  296. XMany prompts (ie. 'Mark everything as read? (y/n): y') within tin 
  297. Xoffer a default choice that the cursor is positioned on. By pressing
  298. X<CR> the default value is taken.
  299. X.PP
  300. XWhen tin is run in an xterm window it will resize itself each time the xterm
  301. Xis resized.
  302. X.SH FILES
  303. X.nf
  304. X.ta \w'\fI$HOME/.tin/organization\fP   'u
  305. X\fI$HOME/.newsrc\fP    newgroups subscribed to.
  306. X\fI$HOME/.tin/tinrc\fP    options.
  307. X\fI$HOME/.tin/.index\fP    newsgroup index files directory.
  308. X\fI$HOME/.tin/kill\fP    kill file.
  309. X\fI$HOME/.tin/posted\fP    history of articles posted by user.
  310. X\fI$HOME/.tin/active\fP    used by -n option for notifying user of new groups.
  311. X\fI$HOME/.tin/organization\fP    string to replace default organization.
  312. X\fI$HOME/.tin/reply_to\fP    host address to use in Reply-To: mail header.
  313. X\fI$HOME/.tin/add_address\fP    address to add to when replying through mail.
  314. X\fI$HOME/.tin/bug_address\fP    address to send bug reports to.
  315. X\fI$HOME/.signature\fP    signature.
  316. X\fI$HOME/.Sig\fP    signature.
  317. X.fi
  318. X.SH BUGS
  319. XThere are bugs somewhere among the creeping featurism. Any bugs found
  320. Xshould be reported by the 'B' (bug report) command.
  321. X.PP
  322. XThere is a bug when article killing is switched ON/OFF at article
  323. Xpage level and the 't' command is used to return to group selection
  324. Xindex.
  325. X.PP
  326. XWhen articles have been unkilled, all articles will be marked
  327. Xunread even if they have already been read.
  328. X.PP
  329. XKilling articles when tin is setuid causes strange behaviour.
  330. X.PP
  331. XWill not uudecode some of the images in alt.binaries.pictures because
  332. Xmore than one image is in the multi-part file to uudecode. Only the
  333. Xfirst image will be uudecoded.
  334. X.PP
  335. XDoes not handle Xref: headers for cross-posted articles.
  336. X.SH HISTORY
  337. XBased on the tass newsreader that was developed by Rich Skrenta and posted
  338. Xto alt.sources in March 1991. Tass was itself heavily infleuenced by NOTES
  339. Xwhich was developed at the University of Illinois in the 1970's.
  340. X.SH CREDITS
  341. X.TP
  342. XRich Skrenta
  343. Xauthor of tass v3.2 which this newsreader used as its base.
  344. X.TP
  345. XSyd Weinstein
  346. Xcurses.c is taken from the elm mailreader
  347. X.TP
  348. XRich Salz
  349. Xauthor of wildmat.c pattern matching routines.
  350. X.TP
  351. XChris Smith
  352. Xauthor of multi-part uudecode routine.
  353. X.SH AUTHOR
  354. X.TP
  355. XIain Lea
  356. X(iain@estevax.uucp or ...!unido!estevax!iain)
  357. SHAR_EOF
  358. echo "File tin.1 is complete" &&
  359. $TOUCH -am 0924085491 tin.1 &&
  360. chmod 0600 tin.1 ||
  361. echo "restore of tin.1 failed"
  362. set `wc -c tin.1`;Wc_c=$1
  363. if test "$Wc_c" != "26331"; then
  364.     echo original size 26331, current size $Wc_c
  365. fi
  366. # ============= wildmat.3 ==============
  367. echo "x - extracting wildmat.3 (Text)"
  368. sed 's/^X//' << 'SHAR_EOF' > wildmat.3 &&
  369. X.TH WILDMAT 3
  370. X.SH NAME
  371. Xwildmat \- perform shell-style wildcard matching
  372. X.SH SYNOPSIS
  373. X.nf
  374. X.B "int"
  375. X.B "wildmat(text, pattern)"
  376. X.B "    char        *text;"
  377. X.B "    char        *pattern;"
  378. X.fi
  379. X.SH DESCRIPTION
  380. X.I Wildmat
  381. Xcompares the
  382. X.I text
  383. Xagainst the
  384. X.I pattern
  385. Xand
  386. Xreturns non-zero if the pattern matches the text.
  387. XThe pattern is interpreted similar to shell filename wildcards, and not
  388. Xas a full regular expression such as those handled by the
  389. X.IR grep (1)
  390. Xfamily of programs or the
  391. X.IR regex (3)
  392. Xor
  393. X.IR regexp (3)
  394. Xset of routines.
  395. X.PP
  396. XThe pattern is interpreted according to the following rules:
  397. X.TP
  398. X.BI \e x
  399. XTurns off the special meaning of
  400. X.I x
  401. Xand matches it directly; this is used mostly before a question mark or
  402. Xasterisk, and is not valid inside square brackets.
  403. X.TP
  404. X.B ?
  405. XMatches any single character.
  406. X.TP
  407. X.B *
  408. XMatches any sequence of zero or more characters.
  409. X.TP
  410. X.BI [ x...y ]
  411. XMatches any single character specified by the set
  412. X.IR x...y .
  413. XA minus sign may be used to indicate a range of characters.
  414. XThat is,
  415. X.I [0\-5abc]
  416. Xis a shorthand for
  417. X.IR [012345abc] .
  418. XMore than one range may appear inside a character set;
  419. X.I [0-9a-zA-Z._]
  420. Xmatches almost all of the legal characters for a host name.
  421. XThe close bracket,
  422. X.IR ] ,
  423. Xmay be used if it is the first character in the set.
  424. XThe minus sign,
  425. X.IR \- ,
  426. Xmay be used if it is either the first or last character in the set.
  427. X.TP
  428. X.BI [^ x...y ]
  429. XThis matches any character
  430. X.I not
  431. Xin the set
  432. X.IR x...y ,
  433. Xwhich is interpreted as described above.
  434. XFor example,
  435. X.I [^]\-]
  436. Xmatches any character other than a close bracket or minus sign.
  437. X.SH "BUGS AND LIMITATIONS"
  438. XThere is no way to end a range with a close bracket.
  439. X.SH HISTORY
  440. XWritten by Rich $alz <rsalz@bbn.com> in 1986, and posted to Usenet
  441. Xseveral times since then, most notably in comp.sources.misc in
  442. XMarch, 1991.
  443. X.PP
  444. XLars Mathiesen <thorinn@diku.dk> enhanced the multi-asterisk failure
  445. Xmode in early 1991.
  446. X.PP
  447. XRich and Lars increased the efficiency of star patterns and reposted it
  448. Xto comp.sources.misc in April, 1991.
  449. X.PP
  450. XRobert Elz <kre@munnari.oz.au> added minus sign and close bracket handling
  451. Xin June, 1991.
  452. X.PP
  453. X.de R$
  454. XThis is revision \\$3, dated \\$4.
  455. X..
  456. X.R$ $Id: wildmat.3,v 1.7 91/07/05 17:50:16 rsalz Exp $
  457. X.SH "SEE ALSO"
  458. Xgrep(1), regex(3), regexp(3).
  459. SHAR_EOF
  460. $TOUCH -am 0923175591 wildmat.3 &&
  461. chmod 0600 wildmat.3 ||
  462. echo "restore of wildmat.3 failed"
  463. set `wc -c wildmat.3`;Wc_c=$1
  464. if test "$Wc_c" != "2247"; then
  465.     echo original size 2247, current size $Wc_c
  466. fi
  467. # ============= art.c ==============
  468. echo "x - extracting art.c (Text)"
  469. sed 's/^X//' << 'SHAR_EOF' > art.c &&
  470. X/*
  471. X *  Project   : tin - a visual threaded usenet newsreader
  472. X *  Module    : art.c
  473. X *  Author    : R.Skrenta / I.Lea
  474. X *  Created   : 01-04-91
  475. X *  Updated   : 24-09-91
  476. X *  Release   : 1.0
  477. X *  Notes     :
  478. X *  Copyright : (c) Copyright 1991 by Rich Skrenta & Iain Lea
  479. X *                You may  freely  copy or  redistribute  this software,
  480. X *              so  long as there is no profit made from its use, sale
  481. X *              trade or  reproduction.  You may not change this copy-
  482. X *              right notice, and it must be included in any copy made
  483. X */
  484. X
  485. X#include    "tin.h"
  486. X
  487. X#define HEADER_LEN    1024
  488. X
  489. Xextern int errno;
  490. X
  491. Xchar index_file[LEN+1];
  492. Xchar *glob_art_group;
  493. Xint index_file_killed = FALSE;
  494. Xlong last_read_article;
  495. X
  496. X
  497. X/*
  498. X *  Convert a string to a long, only look at first n characters
  499. X */
  500. X
  501. Xlong my_atol (s, n)
  502. X    char *s;
  503. X    int n;
  504. X{
  505. X    long ret = 0;
  506. X
  507. X    while (*s && n--) {
  508. X        if (*s >= '0' && *s <= '9')
  509. X            ret = ret * 10 + (*s - '0');
  510. X        else
  511. X            return -1;
  512. X        s++;
  513. X    }
  514. X
  515. X    return ret;
  516. X}
  517. X
  518. X
  519. X/*
  520. X *  Construct the pointers to the basenotes of each thread
  521. X *  arts[] contains every article in the group.  inthread is
  522. X *  set on each article that is after the first article in the
  523. X *  thread.  Articles which have been expired have their thread
  524. X *  set to -2 (ART_EXPIRED).
  525. X */
  526. X
  527. Xvoid find_base ()
  528. X{
  529. X    int i;
  530. X
  531. X    top_base = 0;
  532. X
  533. X    for (i = 0; i < top; i++)
  534. X        if (! arts[i].inthread && arts[i].thread != ART_EXPIRED) {
  535. X            if (top_base >= max_art)
  536. X                expand_art ();
  537. X            base[top_base++] = i;
  538. X        }
  539. X}
  540. X
  541. X/* 
  542. X *  Count the number of non-expired and non-killed articles in arts[]
  543. X */
  544. X
  545. Xint num_of_arts ()
  546. X{
  547. X    int sum = 0;
  548. X    register int i;
  549. X
  550. X    for (i = 0; i < top; i++) {
  551. X        if (arts[i].thread != ART_EXPIRED && ! arts[i].tagged) {
  552. X            sum++;
  553. X        }
  554. X    }
  555. X
  556. X    return sum;
  557. X}
  558. X
  559. X/*
  560. X *  Do we have an entry for article art?
  561. X */
  562. X
  563. Xint valid_artnum (art)
  564. X    long art;
  565. X{
  566. X    register int i;
  567. X
  568. X    for (i = 0; i < top; i++)
  569. X        if (arts[i].artnum == art)
  570. X            return i;
  571. X
  572. X    return -1;
  573. X}
  574. X
  575. X
  576. X/*
  577. X *  Return TRUE if arts[] contains any expired articles
  578. X *  (articles we have an entry for which don't have a corresponding
  579. X *   article file in the spool directory)
  580. X */
  581. X
  582. Xint purge_needed ()
  583. X{
  584. X    register int i;
  585. X
  586. X    for (i = 0; i < top; i++)
  587. X        if (arts[i].thread == ART_EXPIRED)
  588. X            return TRUE;
  589. X
  590. X    return FALSE;
  591. X}
  592. X
  593. X
  594. X/*
  595. X *  Main group indexing routine.  Group should be the name of the
  596. X *  newsgroup, i.e. "comp.unix.amiga".  group_path should be the
  597. X *  same but with the .'s turned into /'s: "comp/unix/amiga"
  598. X *
  599. X *  Will read any existing index, create or incrementally update
  600. X *  the index by looking at the articles in the spool directory,
  601. X *  and attempt to write a new index if necessary.
  602. X */
  603. X
  604. Xvoid index_group (group, group_path)
  605. X    char *group;
  606. X    char *group_path;
  607. X{
  608. X    int killed = FALSE;
  609. X    int modified = FALSE;
  610. X    glob_art_group = group;
  611. X
  612. X#ifdef SIGTSTP
  613. X    if (do_sigtstp) {
  614. X#ifdef POSIX_JOB_CONTROL
  615. X        sigemptyset (&art_act.sa_mask);
  616. X        art_act.sa_flags = SA_RESTART | SA_RESETHAND;
  617. X        art_act.sa_handler = art_suspend;
  618. X        sigaction (SIGTSTP, &art_act, 0L);
  619. X#else
  620. X        signal (SIGTSTP, art_suspend);
  621. X#endif
  622. X    }
  623. X#endif
  624. X
  625. X#ifdef SIGWINCH
  626. X    signal (SIGWINCH, art_resize);
  627. X#endif
  628. X
  629. X    if (! update) {
  630. X        sprintf (msg, txt_group, group);
  631. X        wait_message (msg);
  632. X    }
  633. X
  634. X    hash_reclaim ();
  635. X    free_art_array ();
  636. X
  637. X    if (local_index)
  638. X        find_local_index (group);
  639. X    else
  640. X        sprintf (index_file, "%s/%s/%s", spooldir, group_path, INDEXDIR);
  641. X
  642. X    /*
  643. X     *  load articles from index file if it exists
  644. X     */
  645. X    load_index ();
  646. X
  647. X    /*
  648. X     *  load killed articles into arts[] because kill arts is OFF
  649. X     */
  650. X    if (! kill_articles && index_file_killed) {
  651. X        index_file_killed = FALSE;
  652. X        last_read_article = 0L;
  653. X    }
  654. X
  655. X    /*
  656. X     *  add any articles to arts[] that are new or were killed
  657. X     */
  658. X    modified = read_group (group, group_path);
  659. X
  660. X    /*
  661. X     *  compare kill descriptions to arts[] and kill mark any that match
  662. X     */
  663. X    killed = kill_any_articles (group);
  664. X    
  665. X    if (modified || killed || purge_needed()) {
  666. X        if (local_index) {        /* writing index in home directory */
  667. X            set_real_uid_gid ();    /* so become them */
  668. X        }
  669. X
  670. X        if (killed) {
  671. X            reload_index_file (group, killed);
  672. X        } else {
  673. X            dump_index (group, FALSE);
  674. X            make_threads (FALSE);
  675. X            find_base ();
  676. X        }
  677. X
  678. X        if (local_index) {
  679. X            set_tin_uid_gid ();
  680. X        }
  681. X    } else {
  682. X        make_threads (FALSE);
  683. X        find_base ();
  684. X    }
  685. X    
  686. X    if ((modified || killed) && ! update) {
  687. X        clear_message();
  688. X    }
  689. X}
  690. X
  691. X/*
  692. X *  Index a group.  Assumes any existing index has already been
  693. X *  loaded.
  694. X */
  695. X
  696. Xint read_group (group, group_path)
  697. X    char *group;
  698. X    char *group_path;
  699. X{
  700. X    int fd;
  701. X    long art;
  702. X    int count;
  703. X    int display_groupname = TRUE; 
  704. X    int modified = FALSE;
  705. X    int respnum;
  706. X    register int i;
  707. X    
  708. X    setup_base (group, group_path);    /* load article numbers into base[] */
  709. X    count = 0;
  710. X
  711. X    for (i = 0; i < top_base; i++) {    /* for each article # */
  712. X        art = base[i];
  713. X
  714. X/*
  715. X *  Do we already have this article in our index?  Change thread from
  716. X *  -2 (ART_EXPIRED) to -1 (ART_NORMAL) if so and skip the header eating.
  717. X */
  718. X
  719. X        if ((respnum = valid_artnum (art)) >= 0 || art <= last_read_article) {
  720. X            arts[respnum].thread = ART_NORMAL;
  721. X            arts[respnum].unread = ART_UNREAD;
  722. X            continue;
  723. X        }
  724. X
  725. X        if (! modified) {
  726. X            modified = TRUE;   /* we've modified the index */
  727. X                               /* it will need to be re-written */
  728. X        }
  729. X
  730. X        if ((fd = open_header_fd (group_path, art)) < 0) {
  731. X            continue;
  732. X        }
  733. X        
  734. X        /*
  735. X         *  Add article to arts[]
  736. X         */
  737. X        if (top >= max_art)
  738. X            expand_art();
  739. X
  740. X        arts[top].artnum = art;
  741. X        arts[top].thread = ART_NORMAL;
  742. X
  743. X        set_article (&arts[top]);
  744. X
  745. X        if (! parse_headers (fd, &arts[top])) {
  746. X            continue;
  747. X        }
  748. X        close (fd);
  749. X        last_read_article = arts[top].artnum;    /* used if arts are killed */
  750. X        top++;
  751. X
  752. X        if (++count % 10 == 0 && ! update) {
  753. X            if (display_groupname) {
  754. X                MoveCursor (0, 0);
  755. X                CleartoEOLN ();
  756. X                center_line (0, TRUE, group);
  757. X                display_groupname = FALSE;
  758. X            }
  759. X            sprintf (msg, txt_indexing, count);
  760. X            wait_message (msg);
  761. X        }
  762. X    }
  763. X
  764. X    return modified;
  765. X}
  766. X
  767. X
  768. X/*
  769. X *  Go through the articles in arts[] and use .thread to snake threads
  770. X *  through them.  Use the subject line to construct threads.  The
  771. X *  first article in a thread should have .inthread set to FALSE, the
  772. X *  rest TRUE.  Only do unexprired articles we haven't visited yet
  773. X *  (arts[].thread == -1 ART_NORMAL).
  774. X */
  775. X
  776. Xvoid make_threads (rethread)
  777. X    int rethread;
  778. X{
  779. X    register int i;
  780. X    register int j;
  781. X
  782. X    /*
  783. X     *  .thread & .inthread need to be reset if re-threading arts[]
  784. X     */
  785. X    if (rethread) {
  786. X        for (i=0 ; i < top ; i++) {
  787. X            arts[i].thread = ART_NORMAL;
  788. X            arts[i].inthread = FALSE;
  789. X        }
  790. X    }
  791. X
  792. X    switch (sort_art_type) {
  793. X        case SORT_BY_NOTHING:        /* don't sort at all */
  794. X            qsort (arts, top, sizeof (struct header), artnum_comp);
  795. X            break;
  796. X        case SORT_BY_SUBJ_DESCEND:
  797. X        case SORT_BY_SUBJ_ASCEND:
  798. X            qsort (arts, top, sizeof (struct header), subj_comp);
  799. X            break;
  800. X        case SORT_BY_FROM_DESCEND:
  801. X        case SORT_BY_FROM_ASCEND:
  802. X            qsort (arts, top, sizeof (struct header), from_comp);
  803. X            break;
  804. X        case SORT_BY_DATE_DESCEND:
  805. X        case SORT_BY_DATE_ASCEND:
  806. X            qsort (arts, top, sizeof (struct header), date_comp);
  807. X            break;
  808. X        default:
  809. X            break;
  810. X    }
  811. X
  812. X    for (i = 0; i < top; i++) {
  813. X        if (arts[i].thread == ART_NORMAL) {
  814. X            for (j = i+1; j < top; j++) {
  815. X                if (arts[j].thread != ART_EXPIRED &&
  816. X                    ((arts[i].subject == arts[j].subject) ||
  817. X                    ((arts[i].part || arts[i].patch) &&
  818. X                    arts[i].archive == arts[j].archive))) {
  819. X                        arts[i].thread = j;
  820. X                        arts[j].inthread = TRUE;
  821. X                        break;
  822. X                }
  823. X            }
  824. X        }
  825. X    }
  826. X}
  827. X
  828. X/*
  829. X *  Return a pointer into s eliminating any leading Re:'s.  Example:
  830. X *
  831. X *      Re: Reorganization of misc.jobs
  832. X *      ^   ^
  833. X */
  834. X
  835. Xchar *eat_re (s)
  836. X    char *s;
  837. X{
  838. X
  839. X    while (*s == 'r' || *s == 'R') {
  840. X        if ((*(s+1) == 'e' || *(s+1) == 'E')) {
  841. X            if (*(s+2) == ':')
  842. X                s += 3;
  843. X            else if (*(s+2) == '^' && isdigit(*(s+3)) && *(s+4) == ':')
  844. X                s += 5;            /* hurray nn */
  845. X            else
  846. X                break;
  847. X        } else
  848. X            break;
  849. X        while (*s == ' ')
  850. X            s++;
  851. X    }
  852. X
  853. X    return s;
  854. X}
  855. X
  856. X/*
  857. X *  Hash the subjects (after eating the Re's off) for a quicker
  858. X *  thread search later.  We store the hashes for subjects in the
  859. X *  index file for speed.
  860. X */
  861. X
  862. Xlong hash_s (s)
  863. X    char *s;
  864. X{
  865. X    long h = 0;
  866. X    unsigned char *t = (unsigned char *) s;
  867. X
  868. X    while (*t)
  869. X        h = h * 64 + *t++;
  870. X
  871. X    return h;
  872. X}
  873. X
  874. X
  875. Xint parse_headers (fd, h)
  876. X    int fd;
  877. X    struct header *h;
  878. X{
  879. X    char buf[HEADER_LEN];
  880. X    char buf2[HEADER_LEN];
  881. X    char *ptr, *ptrline, *s;
  882. X    int n = 0, len = 0, lineno = 0;
  883. X    int flag;
  884. X    int got_subject = FALSE;
  885. X    int got_from = FALSE;
  886. X    int got_date = FALSE;
  887. X    int got_archive = FALSE;
  888. X    
  889. X    if ((n = read (fd, buf, HEADER_LEN)) <= 0) {
  890. X        return FALSE;
  891. X    }
  892. X
  893. X    buf[n-1] = '\0';
  894. X
  895. X    ptr = buf;
  896. X
  897. X    while (1) {
  898. X        for (ptrline = ptr; *ptr && *ptr != '\n'; ptr++) {
  899. X            if (((*ptr) & 0x7F) < 32) {
  900. X                *ptr = ' ';
  901. X            }
  902. X        }
  903. X        flag = *ptr;
  904. X        *ptr++ = '\0';
  905. X        lineno++;
  906. X
  907. X        if (! got_from && strncmp(ptrline, "From: ", 6) == 0) {
  908. X            my_strncpy(buf2, ptrline+6, HEADER_LEN);
  909. X            h->from = hash_str (buf2);
  910. X            got_from = TRUE;
  911. X        } else if (! got_subject && strncmp(ptrline, "Subject: ", 9) == 0) {
  912. X            my_strncpy (buf2, ptrline+9, HEADER_LEN);
  913. X            s = eat_re (buf2);
  914. X            h->subject = hash_str (eat_re (s));
  915. X            got_subject = TRUE;
  916. X        } else if (! got_date && strncmp(ptrline, "Date: ", 6) == 0) {
  917. X            my_strncpy (buf2, ptrline+6, HEADER_LEN);
  918. X            parse_date (buf2 ,h->date);
  919. X            got_date = TRUE;
  920. X        } else if (strncmp(ptrline, "Archive-name: ", 14) == 0) {
  921. X            if ((s = (char *) strchr (ptrline+14, '/')) != NULL) {
  922. X                my_strncpy(buf2, ptrline+14, HEADER_LEN);
  923. X                if (strncmp (s+1,"part",4) == 0 ||
  924. X                    strncmp (s+1,"Part",4) == 0) {
  925. X                    h->part = str_dup (s+5);
  926. X                    len = (int) strlen (h->part);
  927. X                    if (h->part[len-1] == '\n') {
  928. X                        h->part[len-1] = '\0';
  929. X                    }
  930. X                } else {
  931. X                    if (strncmp (s+1,"patch",5) == 0 ||
  932. X                        strncmp (s+1,"Patch",5) == 0) {
  933. X                        h->patch = str_dup (s+6);
  934. X                        len = (int) strlen (h->patch);
  935. X                        if (h->patch[len-1] == '\n') {
  936. X                            h->patch[len-1] = '\0';
  937. X                        }
  938. X                    }
  939. X                }
  940. X                if (h->part || h->patch) {
  941. X                    s = buf2;
  942. X                    while (*s && *s != '/')
  943. X                        s++;
  944. X                    *s = '\0';    
  945. X                    s = buf2;
  946. X                    h->archive = hash_str (s);
  947. X                    got_archive = TRUE;
  948. X                }
  949. X            }
  950. X        }
  951. X
  952. X        if (! flag || lineno > 25 || got_archive) {
  953. X            debug_print_header (h);
  954. X            return TRUE;
  955. X        }
  956. X    }
  957. X    /* NOTREACHED */
  958. X}
  959. X
  960. X/* 
  961. X *  Write out an index file.  Write the group name first so if
  962. X *  local indexing is done we can disambiguate between group name
  963. X *  hash collisions by looking at the index file.
  964. X *
  965. X *  NOTE: check out the add_string routine in hashstr.c to
  966. X *  understand what *iptr is doing in this routine.
  967. X */
  968. X
  969. Xvoid dump_index (group, killed)
  970. X    char *group;
  971. X    int killed;
  972. X{
  973. X    char nam[LEN+1];
  974. X    FILE *fp;
  975. X    int *iptr;
  976. X    int realnum;
  977. X    register int i;
  978. X
  979. X    sprintf (nam, "%s.%d", index_file, getpid());
  980. X    if ((fp = fopen (nam, "w")) == NULL) {
  981. X        error_message (txt_cannot_open, nam);
  982. X        return;
  983. X    }
  984. X
  985. X    /*
  986. X     *  dump group header info.
  987. X     */
  988. X    if (sort_art_type != SORT_BY_NOTHING) {
  989. X        qsort (arts, top, sizeof (struct header), artnum_comp);
  990. X    }
  991. X    fprintf(fp, "%s\n", group);
  992. X    fprintf(fp, "%d\n", num_of_arts ());
  993. X    if (last_read_article > arts[top-1].artnum) {
  994. X        fprintf(fp, "%ld\n", last_read_article);
  995. X    } else {
  996. X        fprintf(fp, "%ld\n", arts[top-1].artnum);
  997. X    }
  998. X    if (index_file_killed && killed) {
  999. X        fprintf (fp, "KILLED\n");
  1000. X    } else {
  1001. X        fprintf (fp, "COMPLETE\n");
  1002. X    }
  1003. X
  1004. X    /*
  1005. X     *  dump articles
  1006. X     */
  1007. X    realnum = 0; 
  1008. X    for (i = 0; i < top; i++) {
  1009. X        if (arts[i].thread != ART_EXPIRED && ! arts[i].tagged) { 
  1010. X            debug_print_header (&arts[i]);
  1011. X
  1012. X            fprintf(fp, "%ld\n", arts[i].artnum);
  1013. X
  1014. X            iptr = (int *) arts[i].subject;
  1015. X            iptr--;
  1016. X
  1017. X            if (! arts[i].subject) {
  1018. X                fprintf(fp, " \n");
  1019. X            } else if (*iptr < 0 || *iptr > top) {
  1020. X                fprintf(fp, " %s\n", arts[i].subject);
  1021. X                *iptr = realnum;
  1022. X/*
  1023. X            } else if (arts[*iptr].tagged) {
  1024. X                fprintf(fp, " %s\n", arts[i].subject);
  1025. X                *iptr = realnum;
  1026. X            } else if (killed && *iptr == i) {
  1027. X*/
  1028. X            } else if (killed || *iptr == i) {
  1029. X                fprintf(fp, " %s\n", arts[i].subject);
  1030. X            } else {
  1031. X                fprintf(fp, "%%%d\n", *iptr);
  1032. X            }
  1033. X    
  1034. X            iptr = (int *) arts[i].from;
  1035. X            iptr--;
  1036. X
  1037. X            if (! arts[i].from) {
  1038. X                fprintf (fp, " \n");
  1039. X            } else if (*iptr < 0 || *iptr > top) {
  1040. X                fprintf (fp, " %s\n", arts[i].from);
  1041. X                *iptr = realnum;
  1042. X/*
  1043. X            } else if (arts[*iptr].tagged) {
  1044. X                fprintf(fp, " %s\n", arts[i].from);
  1045. X                *iptr = realnum;
  1046. X            } else if (killed && *iptr == i) {
  1047. X*/            
  1048. X            } else if (killed || *iptr == i) {
  1049. X                fprintf(fp, " %s\n", arts[i].from);
  1050. X            } else {
  1051. X                fprintf(fp, "%%%d\n", *iptr);
  1052. X            }
  1053. X
  1054. X            fprintf (fp, "%s\n", arts[i].date);
  1055. X            
  1056. X            iptr = (int *) arts[i].archive;
  1057. X            iptr--;
  1058. X
  1059. X            if (! arts[i].archive) {
  1060. X                fprintf (fp, "\n");
  1061. X            } else if (*iptr < 0 || *iptr > top) {
  1062. X                fprintf (fp, " %s\n", arts[i].archive);
  1063. X                *iptr = realnum;
  1064. X/*
  1065. X            } else if (arts[*iptr].tagged) {
  1066. X                fprintf (fp, " %s\n", arts[i].archive);
  1067. X                *iptr = realnum;
  1068. X*/                
  1069. X            } else if (arts[i].part || arts[i].patch) {
  1070. X/*
  1071. X                if (killed && *iptr == i) {
  1072. X*/                
  1073. X                if (killed || *iptr == i) {
  1074. X                    fprintf(fp, " %s\n", arts[i].archive);
  1075. X                } else {
  1076. X                    fprintf (fp, "%%%d\n", *iptr);
  1077. X                }
  1078. X            } else {
  1079. X                fprintf (fp, "\n");
  1080. X            }
  1081. X            
  1082. X            if (! arts[i].part) {
  1083. X                fprintf (fp, " \n");
  1084. X            } else {
  1085. X                fprintf (fp, "%s\n", arts[i].part);
  1086. X            }
  1087. X
  1088. X            if (! arts[i].patch) {
  1089. X                fprintf (fp, " \n");
  1090. X            } else {
  1091. X                fprintf (fp, "%s\n", arts[i].patch);
  1092. X            }
  1093. X
  1094. X            realnum++;
  1095. X        }
  1096. X    }
  1097. X    fclose (fp);
  1098. X    chmod (index_file, 0644);
  1099. X    rename_file (nam, index_file);
  1100. X    if (debug) {
  1101. X        sprintf (msg, "/bin/cp %s INDEX", index_file);
  1102. X        system (msg);
  1103. X    }
  1104. X}
  1105. X
  1106. X/*
  1107. X *  strncpy that stops at a newline and null terminates
  1108. X */
  1109. X
  1110. Xvoid my_strncpy(p, q, n)
  1111. X    char *p;
  1112. X    char *q;
  1113. X    int n;
  1114. X{
  1115. X    while (n--) {
  1116. X        if (! *q || *q == '\n')
  1117. X            break;
  1118. X        *p++ = *q++;
  1119. X    }
  1120. X    *p = '\0';
  1121. X}
  1122. X
  1123. X/*
  1124. X *  Read in an index file.
  1125. X *
  1126. X *  index file header 
  1127. X *    1.  newsgroup name (ie. alt.sources)
  1128. X *    2.  number of articles (ie. 26)
  1129. X *    3.  number of last read article (ie. 210)
  1130. X *    4.  Is this a complete/killed index file (ie. COMPLETE/KILLED)
  1131. X *  index file record
  1132. X *    1.  article number   (ie. 183)                [mandatory]
  1133. X *    2.  Subject: line    (ie. Which newsreader?)  [mandatory]
  1134. X *    3.  From: line       (ie. iain@norisc)        [mandatory]
  1135. X *    4.  Date: of posting (ie. 911231125959)       [mandatory]
  1136. X *    5.  Archive: name    (ie. compiler)           [optional]
  1137. X *    6.  Part number of Archive: name  (ie. 01)    [optional]
  1138. X *    7.  Patch number of Archive: name (ie. 01)    [optional]
  1139. X */
  1140. X
  1141. Xint load_index ()
  1142. X{
  1143. X    int error = 0;
  1144. X    int i, n;
  1145. X    char buf[LEN+1], *p;
  1146. X    FILE *fp;
  1147. X
  1148. X    top = 0;
  1149. X    last_read_article = 0L;
  1150. X
  1151. X    if ((fp = fopen (index_file, "r")) == NULL) {
  1152. X        return FALSE;
  1153. X    }
  1154. X
  1155. X    debug_print_comment ("*** LOADING ***");
  1156. X
  1157. X    /*
  1158. X     *  load header - discard group name, num. of arts in index file after any arts were killed
  1159. X     */
  1160. X    if (fgets(buf, sizeof buf, fp) == NULL ||
  1161. X        fgets(buf, sizeof buf, fp) == NULL) {
  1162. X        error = 0;            
  1163. X        goto corrupt_index;    
  1164. X    }
  1165. X    i = atoi (buf);
  1166. X
  1167. X    /*
  1168. X     * num. of last_read_article including any that were killed
  1169. X     */
  1170. X    if (fgets(buf, sizeof buf, fp) == NULL) {
  1171. X        error = 1;                
  1172. X        goto corrupt_index;    
  1173. X    }                            
  1174. X    last_read_article = (long) atol (buf);
  1175. X    
  1176. X    /*
  1177. X     * is index file complete or were articles killed when it was dumped
  1178. X     */
  1179. X    if (fgets(buf, sizeof buf, fp) == NULL) {
  1180. X        error = 2;                
  1181. X        goto corrupt_index;    
  1182. X    }
  1183. X    index_file_killed = (buf[0] == 'K' ? TRUE : FALSE);
  1184. X    
  1185. X    /*
  1186. X     *  load articles
  1187. X     */
  1188. X    for (; top < i ; top++) {
  1189. X        if (top >= max_art) {
  1190. X            expand_art ();
  1191. X        }
  1192. X
  1193. X        arts[top].thread = ART_EXPIRED;
  1194. X        set_article (&arts[top]);
  1195. X
  1196. X        /*
  1197. X         * Article no.
  1198. X         */
  1199. X        if (fgets(buf, sizeof buf, fp) == NULL) {
  1200. X            error = 3;
  1201. X            goto corrupt_index;
  1202. X        }
  1203. X        arts[top].artnum = (long) atol (buf);
  1204. X
  1205. X        /*
  1206. X         * Subject:
  1207. X         */
  1208. X        if (fgets(buf, sizeof buf, fp) == NULL) {
  1209. X            error = 4;
  1210. X            goto corrupt_index;
  1211. X        }
  1212. X
  1213. X        if (buf[0] == '%') {
  1214. X            n = atoi (&buf[1]);
  1215. X            if (n >= top || n < 0) {
  1216. X                error = 5;
  1217. X                goto corrupt_index;
  1218. X            }
  1219. X            arts[top].subject = arts[n].subject;
  1220. X        } else if (buf[0] == ' ') {
  1221. X            for (p = &buf[1];  *p && *p != '\n'; p++)
  1222. X                continue;    
  1223. X            *p = '\0';
  1224. X            arts[top].subject = hash_str (&buf[1]);
  1225. X        } else {
  1226. X            error = 6;
  1227. X            goto corrupt_index;
  1228. X        }
  1229. X            
  1230. X        /*
  1231. X         * From:
  1232. X         */
  1233. X        if (fgets(buf, sizeof buf, fp) == NULL) {
  1234. X            error = 7;
  1235. X            goto corrupt_index;
  1236. X        }
  1237. X
  1238. X        if (buf[0] == '%') {
  1239. X            n = atoi (&buf[1]);
  1240. X            if (n >= top || n < 0) {
  1241. X                error = 8;
  1242. X                goto corrupt_index;
  1243. X            }
  1244. X            arts[top].from = arts[n].from;
  1245. X        } else if (buf[0] == ' ') {
  1246. X            for (p = &buf[1];  *p && *p != '\n'; p++)
  1247. X                continue;
  1248. X            *p = '\0';
  1249. X            arts[top].from = hash_str (&buf[1]);
  1250. X        } else {
  1251. X            error = 9;
  1252. X            goto corrupt_index;
  1253. X        }
  1254. X
  1255. X        /*
  1256. X         * Date:
  1257. X         */
  1258. X        if (fgets(buf, sizeof buf, fp) == NULL) {
  1259. X            error = 10;
  1260. X            goto corrupt_index;
  1261. X        }
  1262. X
  1263. X        buf[strlen (buf)-1] = '\0';
  1264. X        my_strncpy (arts[top].date, buf, 12);
  1265. X
  1266. X        /*
  1267. X         * Archive-name:
  1268. X         */
  1269. X        if (fgets(buf, sizeof buf, fp) == NULL) {
  1270. X            error = 11;
  1271. X            goto corrupt_index;
  1272. X        }
  1273. X
  1274. X        if (buf[0] == '\n') {
  1275. X            arts[top].archive = (char *) 0;
  1276. X        } else if (buf[0] == '%') {
  1277. X            n = atoi (&buf[1]);
  1278. X            if (n > top || n < 0) {
  1279. X                error = 12;
  1280. X                goto corrupt_index;
  1281. X            }
  1282. X            arts[top].archive = arts[n].archive;
  1283. X        } else if (buf[0] == ' ') {
  1284. X            for (p = &buf[1]; *p && *p != '\n' ; p++)
  1285. X                continue;
  1286. X            *p = '\0';
  1287. X            arts[top].archive = hash_str (&buf[1]);
  1288. X        } else {
  1289. X            error = 13;
  1290. X            goto corrupt_index;
  1291. X        }
  1292. X
  1293. X        /*
  1294. X         * part no.
  1295. X         */
  1296. X        if (fgets(buf, sizeof buf, fp) == NULL) {
  1297. X            error = 14;
  1298. X            goto corrupt_index;
  1299. X        }
  1300. X
  1301. X        if (buf[0] != ' ') { 
  1302. X            buf[strlen (buf)-1] = '\0';
  1303. X            arts[top].part = str_dup (buf);
  1304. X        }
  1305. X
  1306. X        /*
  1307. X         * patch no.
  1308. X         */
  1309. X        if (fgets(buf, sizeof buf, fp) == NULL) {
  1310. X            error = 15;
  1311. X            goto corrupt_index;
  1312. X        }
  1313. X
  1314. X        if (buf[0] != ' ') { 
  1315. X            buf[strlen (buf)-1] = '\0';
  1316. X            arts[top].patch = str_dup (buf);
  1317. X        }
  1318. X
  1319. X        debug_print_header (&arts[top]);
  1320. X    }
  1321. X
  1322. X    fclose(fp);
  1323. X    return TRUE;
  1324. X
  1325. Xcorrupt_index:
  1326. X    if (! update) {
  1327. X        sprintf (msg, txt_corrupt_index, index_file, error, top); 
  1328. X        error_message (msg, "");
  1329. X    }
  1330. X
  1331. X    if (debug) {
  1332. X        sprintf (msg, "cp %s INDEX.BAD", index_file);
  1333. X        system (msg);
  1334. X    }
  1335. X
  1336. X    unlink (index_file);
  1337. X    top = 0;
  1338. X    return FALSE;
  1339. X}
  1340. X
  1341. X
  1342. X/*
  1343. X *  Look in the local $HOME/RCDIR/INDEXDIR (or wherever) directory for the
  1344. X *  index file for the given group.  Hashing the group name gets
  1345. X *  a number.  See if that #.1 file exists; if so, read first line.
  1346. X *  Group we want?  If no, try #.2.  Repeat until no such file or
  1347. X *  we find an existing file that matches our group.
  1348. X */
  1349. X
  1350. Xvoid find_local_index (group)
  1351. X    char *group;
  1352. X{
  1353. X    unsigned long h;
  1354. X    static char buf[LEN+1];
  1355. X    int i;
  1356. X    char *p;
  1357. X    FILE *fp;
  1358. X
  1359. X    h = hash_groupname (group);
  1360. X
  1361. X    i = 1;
  1362. X    while (1) {
  1363. X        sprintf(index_file, "%s/%lu.%d", indexdir, h, i);
  1364. X
  1365. X        if ((fp = fopen(index_file, "r")) == NULL) {
  1366. X            return;
  1367. X        }
  1368. X
  1369. X        if (fgets(buf, sizeof buf, fp) == NULL) {
  1370. X            fclose(fp);
  1371. X            return;
  1372. X        }
  1373. X        fclose(fp);
  1374. X
  1375. X        for (p = buf; *p && *p != '\n'; p++)
  1376. X            continue;
  1377. X        *p = '\0';
  1378. X
  1379. X        if (strcmp(buf, group) == 0)
  1380. X            return;
  1381. X
  1382. X        i++;
  1383. X    }
  1384. X}
  1385. X
  1386. X/*
  1387. X *  Run the index file updater only for the groups we've loaded.
  1388. X */
  1389. X
  1390. Xvoid do_update()
  1391. X{
  1392. X    int i, j;
  1393. X    char group_path[LEN+1];
  1394. X    char *p;
  1395. X    long epoch;
  1396. X    
  1397. X    if (verbose) {
  1398. X        time (&epoch);
  1399. X        printf ("%s", ctime (&epoch));
  1400. X        fflush (stdout);
  1401. X    }
  1402. X
  1403. X    for (i = 0; i < local_top; i++) {
  1404. X        strcpy(group_path, active[my_group[i]].name);
  1405. X        for (p = group_path; *p; p++) {
  1406. X            if (*p == '.') {
  1407. X                *p = '/';
  1408. X            }
  1409. X        }
  1410. X        if (verbose) {
  1411. X            printf ("%s %s\n", (catchup ? "Catchup" : "Updating"),
  1412. X                    active[my_group[i]].name);
  1413. X            fflush (stdout);
  1414. X        }
  1415. X        index_group (active[my_group[i]].name, group_path);
  1416. X        if (catchup) {
  1417. X            for (j = 0; j < top; j++) {
  1418. X                arts[j].unread = ART_READ;
  1419. X            }
  1420. X            update_newsrc (active[my_group[i]].name, my_group[i], FALSE);
  1421. X        }
  1422. X    }
  1423. X
  1424. X    if (verbose) {
  1425. X        time (&epoch);
  1426. X        printf ("%s", ctime (&epoch));
  1427. X        fflush (stdout);
  1428. X    }
  1429. X}
  1430. X
  1431. X/*
  1432. X *  Save any new articles to savedir and mark arts read and mail user
  1433. X *  and inform how many arts in which groups were saved.
  1434. X */
  1435. X
  1436. Xvoid save_any_news ()
  1437. X{
  1438. X    char buf[LEN], log[LEN], *p;
  1439. X    char group_path[LEN];
  1440. X    extern FILE *note_fp;
  1441. X    FILE *fp, *fp_log;
  1442. X    int i, j, print_group;
  1443. X    int    log_opened = TRUE;
  1444. X    int saved_arts = 0;
  1445. X    int saved_groups = 0;
  1446. X    long epoch;
  1447. X
  1448. X    sprintf (log, "%s/log", rcdir);
  1449. X    if ((fp_log = fopen (log, "w")) == NULL) {
  1450. X        error_message (txt_cannot_open, log);
  1451. X        fp_log = stdout;
  1452. X        verbose = FALSE;
  1453. X        log_opened = FALSE;
  1454. X    } 
  1455. X    
  1456. X    time (&epoch);
  1457. X    fprintf (fp_log, "To: %s\r\n", userid);
  1458. X    fprintf (fp_log, "Subject: NEWS LOG %s\r\n", ctime (&epoch));
  1459. X
  1460. X    for (i = 0; i < local_top; i++) {
  1461. X        strcpy (group_path, active[my_group[i]].name);
  1462. X        for (p = group_path; *p; p++) {
  1463. X            if (*p == '.') {
  1464. X                *p = '/';
  1465. X            }
  1466. X        }
  1467. X        
  1468. X        index_group (active[my_group[i]].name, group_path);
  1469. X        read_newsrc_line (active[my_group[i]].name);
  1470. X        print_group = TRUE;
  1471. X
  1472. X        for (j = 0; j < top; j++) {
  1473. X            if (arts[j].unread == ART_UNREAD)  {
  1474. X                if (print_group) {    
  1475. X                    sprintf (msg, "Saving %s...\r\n", active[my_group[i]].name);
  1476. X                    fprintf (fp_log, "%s", msg);
  1477. X                    if (verbose) {
  1478. X                        printf ("%s", msg);
  1479. X                    }
  1480. X                    print_group = FALSE;
  1481. X                    saved_groups++;
  1482. X                    sprintf (buf, "%s/dummy", group_path);
  1483. X                    create_path (buf);
  1484. X                }
  1485. X                sprintf (msg, "[%5ld]  %s\r\n", arts[j].artnum, arts[j].subject);
  1486. X                fprintf (fp_log, "%s", msg);
  1487. X                if (verbose) {
  1488. X                    printf ("%s", msg);
  1489. X                }
  1490. X                saved_arts++;
  1491. X                
  1492. X                sprintf (buf, "%s/%s/%ld", savedir, group_path, arts[j].artnum);
  1493. X                if ((fp = fopen (buf, "w")) == NULL) {
  1494. X                    fprintf (fp_log, txt_cannot_open, buf);
  1495. X                    error_message (txt_cannot_open, buf);
  1496. X                    continue;
  1497. X                }
  1498. X                
  1499. X                open_note (arts[j].artnum, group_path);
  1500. X
  1501. X                fseek (note_fp, 0L, 0);
  1502. X
  1503. X                copy_fp (note_fp, fp, (char *) 0);
  1504. X                
  1505. X                note_cleanup ();
  1506. X                
  1507. X                fclose (fp);
  1508. X            }
  1509. X        }
  1510. X        if (catchup) {
  1511. X            for (j = 0; j < top; j++) {
  1512. X                arts[j].unread = ART_READ;
  1513. X            }
  1514. X            update_newsrc (active[my_group[i]].name, my_group[i], FALSE);
  1515. X        }
  1516. X    }
  1517. X    sprintf (msg, "\r\nSaved %d articles from %d groups\r\n", saved_arts, saved_groups);
  1518. X    fprintf (fp_log, "%s", msg);
  1519. X    if (verbose) {
  1520. X        printf ("%s", msg);
  1521. X    }
  1522. X        
  1523. X    if (log_opened) {
  1524. X        fclose (fp_log);
  1525. X        if (verbose) {
  1526. X            printf ("Mailing log to %s\r\n", userid);
  1527. X        }
  1528. X        sprintf (buf, "%s \"%s\" < %s", mailer, userid, log);
  1529. X        if (! invoke_cmd (buf)) {
  1530. X            error_message (txt_command_failed_s, buf);
  1531. X        }
  1532. X    }
  1533. X}
  1534. X
  1535. X/*
  1536. X *  reload index after any articles have been killed
  1537. X */
  1538. Xvoid reload_index_file (group, killed)
  1539. X    char *group;
  1540. X    int killed;
  1541. X{
  1542. X    char group_path[LEN+1];
  1543. X    char *p;
  1544. X
  1545. X    if (local_index) {            /* writing index in home directory */
  1546. X        set_real_uid_gid ();    /* so become them */
  1547. X    }
  1548. X
  1549. X    strcpy (group_path, group);            /* turn comp.unix.amiga into */
  1550. X    for (p = group_path; *p; p++)        /* comp/unix/amiga */
  1551. X        if (*p == '.')
  1552. X            *p = '/';
  1553. X
  1554. X    if (killed) {
  1555. X        if (! update) {
  1556. X            wait_message ("Killing...");
  1557. X        }
  1558. X        index_file_killed = TRUE;
  1559. X        setup_base (group, group_path);
  1560. X        dump_index (group, killed);
  1561. X        load_index ();
  1562. X    } else {
  1563. X        if (! update) {
  1564. X            wait_message ("Unkilling...");
  1565. X        }
  1566. X        if (local_index) {
  1567. X            find_local_index (group);
  1568. X        } else {
  1569. X            sprintf (index_file, "%s/%s/%s", spooldir, group_path, INDEXDIR);
  1570. X        }
  1571. X
  1572. X        unlink (index_file);    /* delete index file */
  1573. X
  1574. X        index_file_killed = FALSE;
  1575. X        last_read_article = 0L;
  1576. X
  1577. X        if (read_group (group, group_path)) {
  1578. X            dump_index (group, killed);
  1579. X        }
  1580. X    }
  1581. X
  1582. X    make_threads (TRUE);
  1583. X    find_base ();
  1584. X
  1585. X    if (local_index) {
  1586. X        set_tin_uid_gid ();
  1587. X    }
  1588. X
  1589. X    return; 
  1590. X}
  1591. X
  1592. X/*
  1593. X * convert date from "24 Jul 91 12:59:59" to "910724125959"
  1594. X */
  1595. X
  1596. Xchar *parse_date (date, str)
  1597. X    char *date;
  1598. X    char *str;
  1599. X{
  1600. X    char buf[4];
  1601. X    int i = 3;
  1602. X
  1603. X    if (date[1] == ' ') {    /* ie. "2 Aug..." instead of "12 Aug... */
  1604. X        str[4] = '0';        /* day */
  1605. X        str[5] = date[0];
  1606. X        i = 2;
  1607. X    } else {
  1608. X        str[4] = date[0];        /* day */
  1609. X        str[5] = date[1];
  1610. X    }
  1611. X    
  1612. X    buf[0] = date[i++];        /* month in Jan,Feb,.. form */
  1613. X    buf[1] = date[i++];
  1614. X    buf[2] = date[i++];
  1615. X    buf[3] = '\0';
  1616. X
  1617. X    i++;
  1618. X    
  1619. X    str[0] = date[i++];        /* year */
  1620. X    str[1] = date[i++];
  1621. X    
  1622. X    i++;
  1623. X    
  1624. X    if (strcmp (buf, "Jan") == 0) {        /* convert Jan to 01 etc */
  1625. X        str[2] = '0';
  1626. X        str[3] = '1';
  1627. X    } else if (strcmp (buf, "Feb") == 0) {
  1628. X        str[2] = '0';
  1629. X        str[3] = '2';
  1630. X    } else if (strcmp (buf, "Mar") == 0) {
  1631. X        str[2] = '0';
  1632. X        str[3] = '3';
  1633. X    } else if (strcmp (buf, "Apr") == 0) {
  1634. X        str[2] = '0';
  1635. X        str[3] = '4';
  1636. X    } else if (strcmp (buf, "May") == 0) {
  1637. X        str[2] = '0';
  1638. X        str[3] = '5';
  1639. X    } else if (strcmp (buf, "Jun") == 0) {
  1640. X        str[2] = '0';
  1641. X        str[3] = '6';
  1642. X    } else if (strcmp (buf, "Jul") == 0) {
  1643. X        str[2] = '0';
  1644. X        str[3] = '7';
  1645. X    } else if (strcmp (buf, "Aug") == 0) {
  1646. X        str[2] = '0';
  1647. X        str[3] = '8';
  1648. X    } else if (strcmp (buf, "Sep") == 0) {
  1649. X        str[2] = '0';
  1650. X        str[3] = '9';
  1651. X    } else if (strcmp (buf, "Oct") == 0) {
  1652. X        str[2] = '1';
  1653. X        str[3] = '0';
  1654. X    } else if (strcmp (buf, "Nov") == 0) {
  1655. X        str[2] = '1';
  1656. X        str[3] = '1';
  1657. X    } else if (strcmp (buf, "Dec") == 0) {
  1658. X        str[2] = '1';
  1659. X        str[3] = '2';
  1660. X    } else {
  1661. X        str[2] = '0';
  1662. X        str[3] = '0';
  1663. X    }
  1664. X    
  1665. X    str[6] = date[i++];        /* hour */
  1666. X    str[7] = date[i++];
  1667. X
  1668. X    i++;
  1669. X    
  1670. X    str[8] = date[i++];        /* minutes */
  1671. X    str[9] = date[i++];
  1672. X    
  1673. X    i++;
  1674. X    
  1675. X    str[10] = date[i++];    /* seconds */
  1676. X    str[11] = date[i++];
  1677. X
  1678. X    str[12] = '\0';        /* terminate string */
  1679. X
  1680. X    return (str);
  1681. X}
  1682. X
  1683. X
  1684. Xint artnum_comp (p1, p2)
  1685. X    char *p1;
  1686. X    char *p2;
  1687. X{
  1688. X    struct header *s1 = (struct header *) p1;
  1689. X    struct header *s2 = (struct header *) p2;
  1690. X
  1691. X    /* s1->artnum less than s2->artnum */
  1692. X    if (s1->artnum < s2->artnum) {
  1693. X        return -1;
  1694. X    }
  1695. X    /* s1->artnum greater than s2->artnum */
  1696. X    if (s1->artnum > s2->artnum) {
  1697. X        return 1;
  1698. X    }
  1699. X    return 0;
  1700. X}
  1701. X
  1702. X
  1703. Xint subj_comp (p1, p2)
  1704. X    char *p1;
  1705. X    char *p2;
  1706. X{
  1707. X    struct header *s1 = (struct header *) p1;
  1708. X    struct header *s2 = (struct header *) p2;
  1709. X
  1710. X    /* s1->subject less than s2->subject */
  1711. X    if (strcmp (s1->subject, s2->subject) < 0) {
  1712. X        return (sort_art_type == SORT_BY_SUBJ_DESCEND ? -1 : 1);
  1713. X    }
  1714. X    /* s1->subject greater than s2->subject */
  1715. X    if (strcmp (s1->subject, s2->subject) > 0) {
  1716. X        return (sort_art_type == SORT_BY_SUBJ_DESCEND ? 1 : -1);
  1717. X    }
  1718. X    return 0;
  1719. X}
  1720. X
  1721. X
  1722. Xint from_comp (p1, p2)
  1723. X    char *p1;
  1724. X    char *p2;
  1725. X{
  1726. X    struct header *s1 = (struct header *) p1;
  1727. X    struct header *s2 = (struct header *) p2;
  1728. X
  1729. X    /* s1->from less than s2->from */
  1730. X    if (strcmp (s1->from, s2->from) < 0) {
  1731. X        return (sort_art_type == SORT_BY_FROM_DESCEND ? -1 : 1);
  1732. X    }
  1733. X    /* s1->from greater than s2->from */
  1734. X    if (strcmp (s1->from, s2->from) > 0) {
  1735. X        return (sort_art_type == SORT_BY_FROM_DESCEND ? 1 : -1);
  1736. X    }
  1737. X    return 0;
  1738. X}
  1739. X
  1740. X
  1741. Xint date_comp (p1, p2)
  1742. X    char *p1;
  1743. X    char *p2;
  1744. X{
  1745. X    struct header *s1 = (struct header *) p1;
  1746. X    struct header *s2 = (struct header *) p2;
  1747. X
  1748. X    /* s1->date less than s2->date */
  1749. X    if (strcmp (s1->date, s2->date) < 0) {
  1750. X        return (sort_art_type == SORT_BY_DATE_DESCEND ? -1 : 1);
  1751. X    }
  1752. X    /* s1->date greater than s2->date */
  1753. X    if (strcmp (s1->date, s2->date) > 0) {
  1754. X        return (sort_art_type == SORT_BY_DATE_DESCEND ? 1 : -1);
  1755. X    }
  1756. X    return 0;
  1757. X}
  1758. X
  1759. X
  1760. Xvoid set_article (art)
  1761. X    struct header *art;
  1762. X{    
  1763. X    art->subject = (char *) 0;
  1764. X    art->from = (char *) 0;
  1765. X    art->date[0] = '\0';
  1766. X    art->archive = (char *) 0;
  1767. X    art->part = (char *) 0;
  1768. X    art->patch = (char *) 0;
  1769. X    art->unread = ART_UNREAD;
  1770. X    art->inthread = FALSE;
  1771. X    art->tagged = FALSE;
  1772. X}
  1773. SHAR_EOF
  1774. $TOUCH -am 0924081391 art.c &&
  1775. chmod 0600 art.c ||
  1776. echo "restore of art.c failed"
  1777. set `wc -c art.c`;Wc_c=$1
  1778. if test "$Wc_c" != "26620"; then
  1779.     echo original size 26620, current size $Wc_c
  1780. fi
  1781. # ============= curses.c ==============
  1782. echo "x - extracting curses.c (Text)"
  1783. sed 's/^X//' << 'SHAR_EOF' > curses.c &&
  1784. X/*
  1785. X *  curses.c
  1786. X */
  1787. X/*
  1788. X *  This is a screen management library borrowed with permission from the
  1789. X *  Elm mail system (a great mailer--I highly recommend it!).
  1790. X *
  1791. X *  I've hacked this library to only provide what Tass needs.
  1792. X *
  1793. X *  Original copyright follows:
  1794. X */
  1795. X
  1796. X/*******************************************************************************
  1797. X *  The Elm Mail System  -  $Revision: 2.1 $   $State: Exp $
  1798. X *
  1799. X *             Copyright (c) 1986 Dave Taylor
  1800. X ******************************************************************************/
  1801. X
  1802. X#include <stdio.h>
  1803. X#include <curses.h>
  1804. X#include <sys/ioctl.h>
  1805. X
  1806. X#ifdef TRUE
  1807. X#undef TRUE
  1808. X#define        TRUE        1
  1809. X#endif
  1810. X
  1811. X#ifdef FALSE
  1812. X#undef FALSE
  1813. X#define        FALSE        0
  1814. X#endif
  1815. X
  1816. X#define        BACKSPACE    '\b'
  1817. X#define        VERY_LONG_STRING    2500
  1818. X
  1819. Xint LINES=23;
  1820. Xint COLS=80;
  1821. X
  1822. Xint inverse_okay = TRUE;
  1823. X
  1824. X#ifdef BSD
  1825. X#  ifndef BSD4_1
  1826. X#    include <sgtty.h>
  1827. X#  else
  1828. X#    include <termio.h>
  1829. X#  endif
  1830. X#else
  1831. X#  ifndef SYSV
  1832. X#    include <termio.h>
  1833. X#  endif
  1834. X#endif
  1835. X
  1836. X
  1837. X#include <ctype.h>
  1838. X
  1839. X/*
  1840. X#ifdef BSD
  1841. X#undef tolower
  1842. X#endif
  1843. X*/
  1844. X
  1845. X#define TTYIN    0
  1846. X
  1847. X#ifdef SHORTNAMES
  1848. X# define _clearinverse    _clrinv
  1849. X# define _cleartoeoln    _clrtoeoln
  1850. X# define _cleartoeos    _clr2eos
  1851. X#endif
  1852. X
  1853. X#ifndef BSD
  1854. Xstruct termio _raw_tty, 
  1855. X              _original_tty;
  1856. X#else
  1857. X#define TCGETA    TIOCGETP
  1858. X#define TCSETAW    TIOCSETP
  1859. X
  1860. Xstruct sgttyb _raw_tty,
  1861. X          _original_tty;
  1862. X#endif
  1863. X
  1864. Xstatic int _inraw = 0;                  /* are we IN rawmode?    */
  1865. X
  1866. X#define DEFAULT_LINES_ON_TERMINAL    24
  1867. X#define DEFAULT_COLUMNS_ON_TERMINAL    80
  1868. X
  1869. Xstatic
  1870. Xchar *_clearscreen, *_moveto, *_cleartoeoln, *_cleartoeos,
  1871. X    *_setinverse, *_clearinverse, *_setunderline, *_clearunderline;
  1872. X
  1873. Xstatic
  1874. Xint _lines,_columns;
  1875. X
  1876. Xstatic char _terminal[1024];              /* Storage for terminal entry */
  1877. Xstatic char _capabilities[1024];           /* String for cursor motion */
  1878. X
  1879. Xstatic char *ptr = _capabilities;    /* for buffering         */
  1880. X
  1881. Xint    outchar();            /* char output for tputs */
  1882. Xchar  *tgetstr(),                    /* Get termcap capability */
  1883. X      *tgoto();                /* and the goto stuff    */
  1884. X
  1885. Xint InitScreen()
  1886. X{
  1887. X    int  tgetent();      /* get termcap entry */
  1888. X    char termname[40], *p;
  1889. X    char *strcpy(), *getenv();
  1890. X    
  1891. X    if ((p = getenv("TERM")) == NULL) {
  1892. X        fprintf(stderr,
  1893. X          "TERM variable not set; Tass requires screen capabilities\n");
  1894. X        return(FALSE);
  1895. X    }
  1896. X    if (strcpy(termname, p) == NULL) {
  1897. X        fprintf(stderr,"Can't get TERM variable\n");
  1898. X        return(FALSE);
  1899. X    }
  1900. X    if (tgetent(_terminal, termname) != 1) {
  1901. X        fprintf(stderr,"Can't get entry for TERM\n");
  1902. X        return(FALSE);
  1903. X    }
  1904. X
  1905. X    /* load in all those pesky values */
  1906. X    _clearscreen       = tgetstr("cl", &ptr);
  1907. X    _moveto            = tgetstr("cm", &ptr);
  1908. X    _cleartoeoln       = tgetstr("ce", &ptr);
  1909. X    _cleartoeos        = tgetstr("cd", &ptr);
  1910. X    _lines                 = tgetnum("li");
  1911. X    _columns       = tgetnum("co");
  1912. X    _setinverse        = tgetstr("so", &ptr);
  1913. X    _clearinverse      = tgetstr("se", &ptr);
  1914. X    _setunderline      = tgetstr("us", &ptr);
  1915. X    _clearunderline    = tgetstr("ue", &ptr);
  1916. X
  1917. X    if (!_clearscreen) {
  1918. X        fprintf(stderr,
  1919. X            "Terminal must have clearscreen (cl) capability\n");
  1920. X        return(FALSE);
  1921. X    }
  1922. X    if (!_moveto) {
  1923. X        fprintf(stderr,
  1924. X            "Terminal must have cursor motion (cm)\n");
  1925. X        return(FALSE);
  1926. X    }
  1927. X    if (!_cleartoeoln) {
  1928. X        fprintf(stderr,
  1929. X            "Terminal must have clear to end-of-line (ce)\n");
  1930. X        return(FALSE);
  1931. X    }
  1932. X    if (!_cleartoeos) {
  1933. X        fprintf(stderr,
  1934. X            "Terminal must have clear to end-of-screen (cd)\n");
  1935. X        return(FALSE);
  1936. X    }
  1937. X    if (_lines == -1)
  1938. X        _lines = DEFAULT_LINES_ON_TERMINAL;
  1939. X    if (_columns == -1)
  1940. X        _columns = DEFAULT_COLUMNS_ON_TERMINAL;
  1941. X    return(TRUE);
  1942. X}
  1943. X
  1944. Xvoid ScreenSize(num_lines, num_columns)
  1945. X    int *num_lines, *num_columns;
  1946. X{
  1947. X    /** returns the number of lines and columns on the display. **/
  1948. X
  1949. X    if (_lines == 0) _lines = DEFAULT_LINES_ON_TERMINAL;
  1950. X    if (_columns == 0) _columns = DEFAULT_COLUMNS_ON_TERMINAL;
  1951. X
  1952. X    *num_lines = _lines - 1;        /* assume index from zero*/
  1953. X    *num_columns = _columns;        /* assume index from one */
  1954. X}
  1955. X
  1956. Xvoid ClearScreen()
  1957. X{
  1958. X    /* clear the screen: returns -1 if not capable */
  1959. X
  1960. X    tputs(_clearscreen, 1, outchar);
  1961. X    fflush(stdout);      /* clear the output buffer */
  1962. X}
  1963. X
  1964. Xvoid MoveCursor(row, col)
  1965. Xint row, col;
  1966. X{
  1967. X    /** move cursor to the specified row column on the screen.
  1968. X            0,0 is the top left! **/
  1969. X
  1970. X    char *stuff, *tgoto();
  1971. X
  1972. X    stuff = tgoto(_moveto, col, row);
  1973. X    tputs(stuff, 1, outchar);
  1974. X    fflush(stdout);
  1975. X}
  1976. X
  1977. Xvoid CleartoEOLN()
  1978. X{
  1979. X    /** clear to end of line **/
  1980. X
  1981. X    tputs(_cleartoeoln, 1, outchar);
  1982. X    fflush(stdout);  /* clear the output buffer */
  1983. X}
  1984. X
  1985. Xvoid CleartoEOS()
  1986. X{
  1987. X    /** clear to end of screen **/
  1988. X
  1989. X    tputs(_cleartoeos, 1, outchar);
  1990. X    fflush(stdout);  /* clear the output buffer */
  1991. X}
  1992. X
  1993. X
  1994. Xvoid StartInverse()
  1995. X{
  1996. X    /** set inverse video mode **/
  1997. X
  1998. X    if (_setinverse && inverse_okay)
  1999. X        tputs(_setinverse, 1, outchar);
  2000. X    fflush(stdout);
  2001. X}
  2002. X
  2003. X
  2004. Xvoid EndInverse()
  2005. X{
  2006. X    /** compliment of startinverse **/
  2007. X
  2008. X    if (_clearinverse && inverse_okay)
  2009. X        tputs(_clearinverse, 1, outchar);
  2010. X    fflush(stdout);
  2011. X}
  2012. X
  2013. X
  2014. Xint RawState()
  2015. X{
  2016. X    /** returns either 1 or 0, for ON or OFF **/
  2017. X
  2018. X    return( _inraw );
  2019. X}
  2020. X
  2021. SHAR_EOF
  2022. echo "End of tin1.02 part 2"
  2023. echo "File curses.c is continued in part 3"
  2024. echo "3" > shar3_seq_.tmp
  2025. exit 0
  2026.  
  2027. exit 0 # Just in case...
  2028. -- 
  2029. Kent Landfield                   INTERNET: kent@sparky.IMD.Sterling.COM
  2030. Sterling Software, IMD           UUCP:     uunet!sparky!kent
  2031. Phone:    (402) 291-8300         FAX:      (402) 291-4362
  2032. Please send comp.sources.misc-related mail to kent@uunet.uu.net.
  2033.