home *** CD-ROM | disk | FTP | other *** search
/ Usenet 1994 October / usenetsourcesnewsgroupsinfomagicoctober1994disk2.iso / unix / volume9 / elm2 / part11 < prev    next >
Text File  |  1987-03-09  |  49KB  |  1,892 lines

  1. Subject:  v09i011:  ELM Mail System, Part11/19
  2. Newsgroups: mod.sources
  3. Approved: rs@mirror.TMC.COM
  4.  
  5. Submitted by: Dave Taylor <hplabs!taylor>
  6. Mod.sources: Volume 9, Issue 11
  7. Archive-name: elm2/Part11
  8.  
  9. #! /bin/sh
  10. # This is a shell archive.  Remove anything before this line,
  11. # then unpack it by saving it in a file and typing "sh file".
  12. # If this archive is complete, you will see the message:
  13. #        "End of archive 11 (of 19)."
  14. # Contents:  src/screen.c src/screen3270.q src/showmsg.c src/strings.c
  15. PATH=/bin:/usr/bin:/usr/ucb ; export PATH
  16. echo shar: Extracting \"src/screen.c\" \(11166 characters\)
  17. if test -f src/screen.c ; then 
  18.   echo shar: Will not over-write existing file \"src/screen.c\"
  19. else
  20. sed "s/^X//" >src/screen.c <<'END_OF_src/screen.c'
  21. X/**        screen.c        **/
  22. X
  23. X/**  screen display routines for ELM program 
  24. X
  25. X     (C) Copyright 1985, Dave Taylor
  26. X**/
  27. X
  28. X#include "headers.h"
  29. X
  30. X#define  minimum(a,b)    ((a) < (b) ? (a) : (b))
  31. X
  32. Xstatic   int  last_current     = -1;
  33. X
  34. Xchar *strcpy(), *strncpy(), *nameof();
  35. X
  36. Xshowscreen()
  37. X{
  38. X    char buffer[SLEN];
  39. X
  40. X    ClearScreen();
  41. X
  42. X    if (selected)
  43. X      sprintf(buffer, 
  44. X        "Mailbox is '%s' with %d shown out of %d [Elm %s]",
  45. X          nameof(infile), selected, message_count, VERSION);
  46. X    else
  47. X      sprintf(buffer, "Mailbox is '%s' with %d message%s [Elm %s]",
  48. X          nameof(infile), message_count, plural(message_count), VERSION);
  49. X    Centerline(1, buffer);
  50. X
  51. X    last_header_page = -1;         /* force a redraw regardless */
  52. X    show_headers();
  53. X
  54. X    if (mini_menu)
  55. X      show_menu();
  56. X
  57. X    show_last_error();
  58. X    
  59. X    if (hp_terminal) 
  60. X      define_softkeys(MAIN);
  61. X}
  62. X
  63. Xupdate_title()
  64. X{
  65. X    /** display a new title line, probably due to new mail arriving **/
  66. X
  67. X    char buffer[SLEN];
  68. X
  69. X    if (selected)
  70. X      sprintf(buffer, 
  71. X        "Mailbox is '%s' with %d shown out of %d [Elm %s]",
  72. X          nameof(infile), selected, message_count, VERSION);
  73. X    else
  74. X      sprintf(buffer, "Mailbox is '%s' with %d message%s [Elm %s]",
  75. X          nameof(infile), message_count, plural(message_count), VERSION);
  76. X
  77. X    ClearLine(1);
  78. X
  79. X    Centerline(1, buffer);
  80. X}
  81. X
  82. Xshow_menu()
  83. X{
  84. X    /** write main system menu... **/
  85. X
  86. X    if (user_level == 0) {    /* a rank beginner.  Give less options  */
  87. X      Centerline(LINES-7,
  88. X  "You can use any of the following commands by pressing the first character;");
  89. X          Centerline(LINES-6,
  90. X"D)elete mail,  M)ail a message,  R)eply to mail,  U)ndelete, or Q)uit");
  91. X      Centerline(LINES-5,
  92. X  "To read a message, press <return>.  j = move arrow down, k = move arrow up");
  93. X    } else {
  94. X    Centerline(LINES-7,
  95. X  "|=pipe, !=shell, ?=help, <n>=set current to n, /=search pattern");
  96. X        Centerline(LINES-6,
  97. X"A)lias, C)hange mailbox, D)elete, E)dit, F)orward, G)roup reply, M)ail,"); 
  98. X    Centerline(LINES-5,
  99. X  "N)ext, O)ptions, P)rint, R)eply, S)ave, T)ag, Q)uit, U)ndelete, or eX)it");
  100. X    }
  101. X}
  102. X
  103. Xint
  104. Xshow_headers()
  105. X{
  106. X    /** Display page of headers (10) if present.  First check to 
  107. X        ensure that header_page is in bounds, fixing silently if not.
  108. X        If out of bounds, return zero, else return non-zero 
  109. X        Modified to only show headers that are "visible" to ze human
  110. X        person using ze program, eh?
  111. X    **/
  112. X
  113. X    register int this_msg = 0, line = 4, last = 0, last_line, 
  114. X             displayed = 0;
  115. X    char newfrom[SLEN], buffer[SLEN];
  116. X    
  117. X    if (fix_header_page()) {
  118. X      dprint0(7, "show_headers returned FALSE 'cause of fix-header-page\n");
  119. X      return(FALSE);
  120. X    }
  121. X
  122. X    if (selected) {
  123. X      if ((header_page*headers_per_page) > selected) {
  124. X        dprint2(7, "show_headers returned FALSE since selected [%d] < %d\n",
  125. X            selected, header_page*headers_per_page);
  126. X        return(FALSE);     /* too far! too far! */
  127. X      }
  128. X
  129. X      dprint0(6,"** show_headers AND selected...\n");
  130. X
  131. X      if (header_page == 0) {
  132. X        this_msg = visible_to_index(1);    
  133. X        displayed = 0;
  134. X      }
  135. X      else {
  136. X        this_msg = visible_to_index(header_page * headers_per_page + 1);
  137. X        displayed = header_page * headers_per_page;
  138. X      }
  139. X
  140. X      dprint2(7,"this_msg (index) = %d  [header_page = %d]\n", this_msg, 
  141. X          header_page);
  142. X
  143. X      dprint1(7,"we've already displayed %d messages\n", displayed);
  144. X
  145. X      last = displayed+headers_per_page;
  146. X
  147. X      dprint1(7,"and the last msg on this page is %d\n", last);
  148. X    }
  149. X    else {
  150. X      if (header_page == last_header_page)     /* nothing to do! */
  151. X        return(FALSE);
  152. X
  153. X      /** compute last header to display **/
  154. X  
  155. X      this_msg = header_page * headers_per_page;
  156. X      last = this_msg + (headers_per_page - 1);
  157. X    }
  158. X
  159. X    if (last >= message_count) last = message_count-1;
  160. X
  161. X    /** Okay, now let's show the header page! **/
  162. X
  163. X    ClearLine(line);    /* Clear the top line... */
  164. X
  165. X    MoveCursor(line, 0);    /* and move back to the top of the page... */
  166. X
  167. X    while ((selected && displayed < last) || this_msg <= last) {
  168. X      tail_of(header_table[this_msg].from, newfrom, TRUE); 
  169. X
  170. X      if (selected) {
  171. X        if (this_msg == current-1) 
  172. X          build_header_line(buffer, &header_table[this_msg], ++displayed,
  173. X                  TRUE, newfrom);
  174. X        else
  175. X          build_header_line(buffer, &header_table[this_msg], 
  176. X                  ++displayed, FALSE, newfrom);
  177. X      } 
  178. X      else {
  179. X        if (this_msg == current-1) 
  180. X          build_header_line(buffer, &header_table[this_msg], this_msg+1, 
  181. X                      TRUE, newfrom);
  182. X        else
  183. X          build_header_line(buffer, &header_table[this_msg], 
  184. X                  this_msg+1, FALSE, newfrom);
  185. X      }
  186. X
  187. X      Write_to_screen("%s\r\n", 1, buffer);    /* avoid '%' probs */
  188. X      CleartoEOLN();
  189. X      line++;        /* for clearing up in a sec... */
  190. X
  191. X      if (selected) {
  192. X        if ((this_msg = next_visible(this_msg+1)-1) < 0)
  193. X          break;    /* GET OUTTA HERE! */
  194. X
  195. X        /* the preceeding looks gross because we're using an INDEX
  196. X           variable to pretend to be a "current" counter, and the 
  197. X           current counter is always 1 greater than the actual 
  198. X           index.  Does that make sense??
  199. X         */
  200. X      }
  201. X      else
  202. X        this_msg++;                    /* even dumber...  */
  203. X    }
  204. X
  205. X    dprint0(1,"** out of redraw loop! **\n");
  206. X
  207. X    if (mini_menu)
  208. X      last_line = LINES-8;
  209. X    else
  210. X      last_line = LINES-3;
  211. X
  212. X    while (line < last_line) {
  213. X      CleartoEOLN();
  214. X      Writechar('\r');
  215. X      Writechar('\n');
  216. X      line++;
  217. X    }
  218. X
  219. X    display_central_message();
  220. X
  221. X    last_current = current;
  222. X    last_header_page = header_page;
  223. X
  224. X    return(TRUE);
  225. X}
  226. X
  227. Xshow_current()
  228. X{
  229. X    /** Show the new header, with all the usual checks **/
  230. X
  231. X    register int first = 0, last = 0, last_line, new_line, i=0, j=0;
  232. X    char     newfrom[SLEN], old_buffer[SLEN], new_buffer[SLEN];
  233. X
  234. X    (void) fix_header_page();    /* Who cares what it does? ;-) */
  235. X
  236. X    /** compute last header to display **/
  237. X
  238. X    first = header_page * headers_per_page;
  239. X    last  = first + (headers_per_page - 1);
  240. X
  241. X    if (last > message_count) 
  242. X      last = message_count;
  243. X
  244. X    /** okay, now let's show the pointers... **/
  245. X
  246. X    /** have we changed??? **/
  247. X
  248. X    if (current == last_current) 
  249. X      return;
  250. X
  251. X    if (selected) {
  252. X      dprint2(2,"\nshow_current\n* last_current = %d, current = %d\n", 
  253. X            last_current, current);
  254. X      last_line = ((i=compute_visible(last_current-1)-1) %
  255. X             headers_per_page)+4;
  256. X      new_line  = ((j=compute_visible(current-1)-1) % headers_per_page)+4;
  257. X      dprint1(2,"* compute_visible(last-1)=%d\n", 
  258. X           compute_visible(last_current-1));
  259. X      dprint1(2,"* compute_visible(current-1)=%d\n", 
  260. X           compute_visible(current-1));
  261. X      dprint2(2,"* ending up with last_line = %d and new_line = %d\n",
  262. X           last_line, new_line);
  263. X    }
  264. X    else {
  265. X      last_line = ((last_current-1) % headers_per_page)+4;
  266. X      new_line  = ((current-1) % headers_per_page)+4;
  267. X    }
  268. X    
  269. X    dprint4(7,
  270. X       "--> show-current: last_current=%d [%d] and current=%d [%d]\n",
  271. X       last_current, i, current, j);
  272. X
  273. X    dprint2(7,"    maps to lines %d and %d\n", last_line, new_line);
  274. X
  275. X    if (has_highlighting && ! arrow_cursor) {
  276. X      /** build the old and new header lines... **/
  277. X  
  278. X      tail_of(header_table[current-1].from, newfrom, TRUE); 
  279. X      build_header_line(new_buffer, &header_table[current-1], 
  280. X             (selected? compute_visible(current-1) : current), 
  281. X          TRUE, newfrom);
  282. X
  283. X      if (last_current > 0) {  /* say we went from no mail to new... */
  284. X        tail_of(header_table[last_current-1].from, newfrom, TRUE); 
  285. X        build_header_line(old_buffer, &header_table[last_current-1], 
  286. X             (selected? compute_visible(last_current-1) : last_current), 
  287. X          FALSE, newfrom);
  288. X
  289. X        ClearLine(last_line);
  290. X        PutLine0(last_line, 0, old_buffer);
  291. X      }
  292. X      PutLine0(new_line, 0, new_buffer);
  293. X    }
  294. X    else {
  295. X      if (on_page(last_current)) 
  296. X        PutLine0(last_line,0,"  ");    /* remove old pointer... */
  297. X      if (on_page(current))
  298. X        PutLine0(new_line, 0,"->");
  299. X    }
  300. X    
  301. X    last_current = current;
  302. X}
  303. X
  304. Xbuild_header_line(buffer, entry, message_number, highlight, from)
  305. Xchar *buffer;
  306. Xstruct header_rec *entry;
  307. Xint message_number, highlight;
  308. Xchar *from;
  309. X{
  310. X    /** Build in buffer the message header ... entry is the current
  311. X        message entry, 'from' is a modified (displayable) from line, 
  312. X        'highlight' is either TRUE or FALSE, and 'message_number'
  313. X        is the number of the message.
  314. X    **/
  315. X
  316. X    /** Note: using 'strncpy' allows us to output as much of the
  317. X        subject line as possible given the dimensions of the screen.
  318. X        The key is that 'strncpy' returns a 'char *' to the string
  319. X        that it is handing to the dummy variable!  Neat, eh? **/
  320. X    
  321. X    char subj[LONG_SLEN],        /* to output subject */
  322. X         buff[NLEN];        /* keep start_highlight value */
  323. X
  324. X    if (strcmp(start_highlight,"->") != 0 && arrow_cursor) {
  325. X      strcpy(buff, start_highlight);
  326. X      strcpy(start_highlight, "->");
  327. X    }
  328. X
  329. X    strncpy(subj, entry->subject, COLUMNS-45);
  330. X
  331. X    subj[COLUMNS-45] = '\0';    /* insurance, eh? */
  332. X
  333. X    /* now THIS is a frightening format statement!!!  */
  334. X
  335. X    sprintf(buffer, "%s%s%c%c%c%-3d %3.3s %-2d %-18.18s (%d) %s%s%s", 
  336. X        highlight? ((has_highlighting && !arrow_cursor) ?
  337. X                   start_highlight : "->") : "  ",
  338. X        (highlight && has_highlighting && !arrow_cursor)? "  " : "",
  339. X        show_status(entry->status),
  340. X        (entry->status & DELETED? 'D' : ' '), 
  341. X        (entry->status & TAGGED?  '+' : ' '),
  342. X            message_number,
  343. X            entry->month, 
  344. X        atoi(entry->day), 
  345. X        from, 
  346. X        entry->lines, 
  347. X        (entry->lines / 1000   > 0? ""   :    /* spacing the  */
  348. X          entry->lines / 100   > 0? " "  :    /* same for the */
  349. X            entry->lines / 10  > 0? "  " :    /* lines in ()  */
  350. X                                    "   "),    /*   [wierd]    */
  351. X        subj,
  352. X        (highlight && has_highlighting && !arrow_cursor) ?
  353. X              end_highlight : "");
  354. X
  355. X    /** Actually, it's rather an impressive feat that we can
  356. X        do so much in essentially one statement!  (Of course,
  357. X        I'll bet the test suite for the printf routine isn't
  358. X        THIS rigorous either!!!) (to be honest, though, just 
  359. X        looking at this statement makes me chuckle...)
  360. X    **/
  361. X
  362. X    if (arrow_cursor)             /* restore! */
  363. X      strcpy(start_highlight, buff);
  364. X
  365. X}
  366. X
  367. Xint
  368. Xfix_header_page()
  369. X{
  370. X    /** this routine will check and ensure that the current header
  371. X        page being displayed contains messages!  It will silently
  372. X        fix 'header-page' if wrong.  Returns TRUE if changed.  **/
  373. X
  374. X    int last_page, old_header;
  375. X
  376. X    old_header = header_page;
  377. X
  378. X    last_page = (int) ((message_count-1) / headers_per_page);
  379. X    if (header_page > last_page) 
  380. X      header_page = last_page;
  381. X    else if (header_page < 0) 
  382. X          header_page = 0;
  383. X
  384. X    return(old_header != header_page);
  385. X}
  386. X
  387. Xint
  388. Xon_page(message)
  389. Xint message;
  390. X{
  391. X    /** Returns true iff the specified message is on the displayed page. **/
  392. X
  393. X    dprint1(6,"** on_page(%d) returns...", message);
  394. X
  395. X    if (selected) message = compute_visible(message-1);
  396. X
  397. X    if (message >= header_page * headers_per_page)
  398. X      if (message <= ((header_page+1) * headers_per_page)) {
  399. X        dprint0(6,"TRUE\n");
  400. X        return(TRUE);
  401. X      }
  402. X
  403. X    dprint0(6,"FALSE\n");
  404. X    return(FALSE);
  405. X}
  406. X
  407. Xshow_status(status)
  408. Xint status;
  409. X{
  410. X    /** This routine returns a single character indicative of
  411. X        the status of this message.  The precedence is;
  412. X
  413. X        F = form letter
  414. X        E = Expired message
  415. X            P = Priority message
  416. X        A = Action associated with message
  417. X        N = New message
  418. X        _ = (space) default 
  419. X    **/
  420. X
  421. X         if (status & FORM_LETTER) return( 'F' );
  422. X    else if (status & EXPIRED)     return( 'E' );
  423. X    else if (status & PRIORITY)    return( 'P' );
  424. X    else if (status & ACTION)      return( 'A' );
  425. X    else if (status & NEW)         return( 'N' );
  426. X    else                    return( ' ' );
  427. X}
  428. END_OF_src/screen.c
  429. if test 11166 -ne `wc -c <src/screen.c`; then
  430.     echo shar: \"src/screen.c\" unpacked with wrong size!?
  431. fi
  432. # end of overwriting check
  433. fi
  434. echo shar: Extracting \"src/screen3270.q\" \(11593 characters\)
  435. if test -f src/screen3270.q ; then 
  436.   echo shar: Will not over-write existing file \"src/screen3270.q\"
  437. else
  438. sed "s/^X//" >src/screen3270.q <<'END_OF_src/screen3270.q'
  439. XFrom hpccc!mcgregor@hplabs.ARPA Thu Jun  5 11:41:49 1986
  440. XReceived: from hplabs.ARPA by hpldat ; Thu, 5 Jun 86 11:41:32 pdt
  441. XMessage-Id: <8606051841.AA16872@hpldat>
  442. XReceived: by hplabs.ARPA ; Thu, 5 Jun 86 11:38:07 pdt
  443. XTo: hplabs!taylor@hplabs.ARPA
  444. XDate: Thu, 5 Jun 86 11:36:39 PDT
  445. XFrom: hpccc!mcgregor@hplabs.ARPA (Scott McGregor)
  446. XSubject: revised screen3270.q
  447. XTelephone: (415) 857-5875
  448. XPostal-Address: Hewlett-Packard, PO Box 10301, Mail stop 20CH, Palo Alto CA 943X03-0890
  449. XX-Mailer: ELM [version 1.0]
  450. X
  451. X
  452. X/*
  453. X * screen3270.q
  454. X *
  455. X *   Created for ELM, 5/86 to work (hopefully) on UTS systems with 3270
  456. X *   type terminals, by Scott L. McGregor, HP Corporate Computing Center.
  457. X *
  458. X */
  459. X
  460. X#include "headers.h"
  461. X#  include <sys/utsname.h>
  462. X#  include <sys/tubio.h>
  463. X
  464. X#  include <errno.h>
  465. X
  466. X
  467. X#  define  TTYIN        0        /* standard input */
  468. X#include <stdio.h>
  469. X#define MAXKEYS 101
  470. X#define OFF 0
  471. X#define UNKNOWN 0
  472. X#define ON 1
  473. X#define FALSE 0
  474. X#define TRUE 1
  475. X
  476. Xchar *error_name();
  477. X
  478. Xextern int _line, _col;
  479. X
  480. Xpfinitialize()
  481. X{
  482. X    char cp[80];
  483. X    
  484. X    dprint0(9,"pfinitialize()\n");
  485. X    pfinit();
  486. X    /*
  487. X     * load the system defined pfkeys
  488. X     */
  489. X    pfload("/usr/local/etc/.elmpfrc");
  490. X    /*
  491. X     * load the user's keys if any
  492. X     */
  493. X    strcat(cp,home);
  494. X    strcat(cp,"/.elmpfrc");
  495. X    pfload(cp);
  496. X    pfprint();
  497. X}
  498. X
  499. X/*
  500. X * note, inputs are limited to 80 characters! Any larger amount
  501. X * will be truncated without notice!
  502. X */
  503. X
  504. X
  505. X/*
  506. X * pfinit() initializes _pftable
  507. X */
  508. Xpfinit()
  509. X{
  510. X    int i,j;
  511. X    
  512. X    dprint0(9,"pfinit()\n");
  513. X    for (i=0;i<MAXKEYS;i++) {
  514. X        for (j=0;j<80;j++) {
  515. X            _pftable[i][j]='\0';
  516. X        }
  517. X    }
  518. X    return(0);
  519. X}
  520. X
  521. X
  522. X/*
  523. X * pfset(key) sets _pftable entries.
  524. X */
  525. Xpfset(key,return_string)
  526. Xint key;
  527. Xchar *return_string;
  528. X{
  529. X    int i;
  530. X
  531. X    dprint2(9,"pfset(%d,%s)\n",key,return_string);
  532. X    for (i=0;i<=80;i++) {
  533. X        if (i <= strlen(return_string))
  534. X            _pftable[key][i] = return_string[i];
  535. X        else _pftable[key][i] = '\0';
  536. X    }
  537. X    dprint2(9,"pfset: %d %s\n",key,_pftable[key]);
  538. X       
  539. X    return(0);
  540. X}
  541. X
  542. X/*
  543. X *  pfload(name) reads file "name" and parses the
  544. X *  key definitions in it into a table used by the
  545. X *  pfreturn.
  546. X */
  547. Xpfload(name)
  548. Xchar *name;
  549. X{
  550. X    FILE *pfdefs;
  551. X    int i,j,k;
  552. X    int key = 0;
  553. X    char defn[80];
  554. X    char newdefn[80];
  555. X
  556. X    dprint1(9,"pfload(%s)\n",name);
  557. X    if ((pfdefs = fopen(name,"r")) == NULL){
  558. X        dprint2(2,"%s pfrc open failed, %s \n",name,
  559. X            error_name(errno));
  560. X        return(0);
  561. X     }
  562. X
  563. X     /*
  564. X      * This program reads .elmpfrc files which it currently
  565. X      * expects to be in the form:
  566. X      * 
  567. X      * <pfkeynumber> <whitespace> <pfkeyvalue> [<whitespace> <comment>]
  568. X      * 
  569. X      * Pfkeynumber is an integer 1-24.  Whitespace can be one
  570. X      * or more blanks or tabs.  Pfkeyvalue is any string NOT
  571. X      * containing whitespace (however, \b for blank, \t for tab
  572. X      * and \n for newline can be used instead to indicate that
  573. X      * the indicated whitespace character is part of a command.
  574. X      * Note that the EnTER key will NOT be treated as a newline
  575. X      * command, so defining a newline key is a good idea!
  576. X      * Anything else appearing on the line after the pfkey is ignored
  577. X      * and so can be used as a comment.
  578. X      *
  579. X      * This may not be the best form for a file used by
  580. X      * humans to set parms, and if someone comes up with a
  581. X      * better one and a parser to read it, then this can be
  582. X      * replaced.
  583. X      *
  584. X      */
  585. X     
  586. X    dprint1(1,"%s pfrc opened\n",name);
  587. X    k = 0;
  588. X    while ( fscanf(pfdefs,"%d%s",&key,defn) != EOF ) {
  589. X        dprint2(9,"pfkey definition 1: %d %s\n",key,defn);
  590. X        if ((key < 0) || (key > MAXKEYS)) {
  591. X            dprint2(9,"pfkey defn failed: key=%d MAXKEYS=%d\n",key,MAXKEYS);
  592. X            k++;
  593. X        } else {
  594. X            dprint2(9,"pfkey definition 2: %d %s\n",key,defn);
  595. X            for (i=0,j=0;i<strlen(defn);i++) {
  596. X                if (defn[i]=='\\') {
  597. X                    if (defn[i+1]=='n') {
  598. X                        newdefn[j++]='\n'; i++;
  599. X                    }
  600. X                    if (defn[i+1]=='t') {
  601. X                        newdefn[j++]='\t'; i++;
  602. X                    }
  603. X                    if (defn[i+1]=='0') {
  604. X                        newdefn[j++]='\0'; i++;
  605. X                    }
  606. X                    if (defn[i+1]=='1') {
  607. X                        newdefn[j++]='\001'; i++;
  608. X                    }
  609. X                    if (defn[i+1]=='b') {
  610. X                        newdefn[j++]=' '; i++;
  611. X                    }
  612. X                }
  613. X                 else {
  614. X                    newdefn[j++]=defn[i];
  615. X                }
  616. X            }
  617. X            dprint2(9,"pfkey new definition: %d %s\n",key,newdefn);
  618. X            pfset(key,newdefn);
  619. X        }
  620. X    }
  621. X    dprint1(9,"pfkey definition table:  %s\n",_pftable);
  622. X    return(k);
  623. X}
  624. X
  625. X
  626. X/*
  627. X * pfreturn(key) returns the stored string for that pfkey.
  628. X */
  629. Xpfreturn(key,string)
  630. Xint key;
  631. Xchar string[];
  632. X{
  633. X    int i;
  634. X    
  635. X    dprint2(9,"pfreturn(%d,%s)\n",key,string);
  636. X    for (i=0;i<80;i++) {
  637. X        string[i] = _pftable[key][i];
  638. X    }
  639. X    dprint1(9,"pfreturn string=%s\n",string);
  640. X    return;
  641. X}
  642. X
  643. X
  644. X/*
  645. X * pfprint() prints all pfkey definitions
  646. X */
  647. Xpfprint()
  648. X
  649. X{
  650. X    int i;
  651. X    char string[80];
  652. X
  653. X    for (i=0;i<MAXKEYS;i++) {
  654. X        if (strlen(_pftable[i]) != 0)
  655. X        dprint2(9,"%d pf table entry=%s\n",i+1,_pftable[i]);
  656. X    }
  657. X}
  658. X
  659. X/*
  660. X * rowcol2offset(row,col) takes the row and column numbers and
  661. X * returns the offset into the array.
  662. X * row and column are assumed to vary from 0 to LINES-1, and COLUMNS-1
  663. X * respectively.
  664. X */
  665. Xrowcol2offset(row,col)
  666. Xint row, col;
  667. X{
  668. X    dprint2(9,"rowcol2offset(%d,%d)\n",row,col);
  669. X    
  670. X    if ((row <= LINES) && (row >= 0)) {
  671. X        if ((col <= COLUMNS) && (col >=0)) {
  672. X            return(row*COLUMNS+col);
  673. X        }
  674. X        else return(0);
  675. X    }
  676. X    else return(0);
  677. X}
  678. X
  679. X/*
  680. X * offset2row(offset) takes the offset returnes the row.
  681. X * row is assumed to vary from 0 to LINES-1.
  682. X */
  683. Xoffset2row(offset)
  684. Xint offset;
  685. X{
  686. X    int i;
  687. X    
  688. X    dprint1(9,"offset2row(%d)\n",offset);
  689. X    i =  (int) (offset / COLUMNS);
  690. X    dprint1(9,"offset2row returns= %d)\n",i);
  691. X    return(i);
  692. X}
  693. X
  694. X/*
  695. X * offset2col(offset) takes the offset returnes the col.
  696. X * col is assumed to vary from 0 to COLUMNS-1.
  697. X */
  698. Xoffset2col(offset)
  699. Xint offset;
  700. X{
  701. X    int i;
  702. X    
  703. X    dprint1(9,"offset2col(%d)\n",offset);
  704. X    i =  (int) (offset % COLUMNS);
  705. X    dprint1(9,"offset2col returns= %d)\n",i);
  706. X    return(i);
  707. X}
  708. X
  709. X/*
  710. X * Row(row) takes the row in 0 <= row < LINES and returns
  711. X * row in 0 < row <= LINES.
  712. X */
  713. XRow(row)
  714. Xint row;
  715. X{
  716. X    dprint1(9,"Row(%d)\n",row);
  717. X    return(row+1);
  718. X}
  719. X
  720. X/*
  721. X * Col(Col) takes the col in 0 <= col < COLUMNS and returns
  722. X * col in 0 < col <= COLUMNS.
  723. X */
  724. XCol(col)
  725. Xint col;
  726. X{
  727. X    dprint1(9,"Col(%d)\n",col);
  728. X    return(col+1);
  729. X}
  730. X
  731. X
  732. Xgethostname(hostname,size) /* get name of current host */
  733. Xint size;
  734. Xchar *hostname;
  735. X{
  736. X    /** Return the name of the current host machine.  UTS only **/
  737. X
  738. X    /** This routine compliments of Scott McGregor at the HP
  739. X        Corporate Computing Center **/
  740. X     
  741. X    int uname();
  742. X    struct utsname name;
  743. X
  744. X        dprint2(9,"gethostname(%s,%d)\n",hostname,size);
  745. X    (void) uname(&name);
  746. X    (void) strncpy(hostname,name.nodename,size-1);
  747. X    if (strlen(name.nodename) > SLEN)
  748. X      hostname[size] = '\0';
  749. X}
  750. X
  751. Xint
  752. Xisa3270()
  753. X{
  754. X    /** Returns TRUE and sets LINES and COLUMNS to the correct values
  755. X        for an Amdahl (IBM) tube screen, or returns FALSE if on a normal
  756. X        terminal (of course, next to a 3270, ANYTHING is normal!!) **/
  757. X
  758. X    struct tubiocb tubecb;
  759. X    
  760. X    dprint0(9,"isa3270()\n");
  761. X    if (ioctl(TTYIN, TUBGETMOD, &tubecb) == -1){
  762. X      return(FALSE);    /* not a tube! */
  763. X    }
  764. X    LINES   = tubecb.line_cnt - 2;
  765. X    COLUMNS = tubecb.col_cnt;
  766. X    if (!check_only && !mail_only) {
  767. X        isatube = TRUE;
  768. X        return(TRUE);
  769. X    }
  770. X     else {
  771. X         isatube = FALSE;
  772. X         return(FALSE);
  773. X    }
  774. X}
  775. X
  776. X/*
  777. X * ReadCh3270() reads a character from the 3270.
  778. X */
  779. Xint ReadCh3270()
  780. X{
  781. X        /** read a character from the display! **/
  782. X
  783. X    register int x;
  784. X    char tempstr[80];
  785. X        char ch;
  786. X    
  787. X    dprint0(9,"ReadCh3270()\n");
  788. X    if ((_input_buf_ptr > COLUMNS) ||
  789. X        (_input_buffer[_input_buf_ptr] == '\0')) {
  790. X        WriteScreen3270();
  791. X        for (x=0; x < COLUMNS; x++) _input_buffer[x] = '\0';
  792. X        panel (noerase, read) {
  793. X            #@ LINES+1,1# #INC,_input_buffer,COLUMNS#
  794. X        }
  795. X        dprint1(9,"ReadCh3270 _input_buffer=%s\n",_input_buffer);
  796. X        x=strlen(_input_buffer);
  797. X        pfreturn(qskp,tempstr);
  798. X        if (!strcmp(tempstr,"\001")) {
  799. X            if (strlen(_input_buffer) == 1) {
  800. X                tempstr[0]='\0';
  801. X            }
  802. X             else {
  803. X                tempstr[0]='\n';
  804. X                tempstr[1]='\0';
  805. X            }
  806. X        }
  807. X        dprint1(9,"ReadCh3270 pfkey=%s\n",tempstr);
  808. X        strcat(_input_buffer,tempstr);
  809. X        dprint1(9,"ReadCh3270 _input_buffer+pfkey=%s\n",_input_buffer);
  810. X        ch = _input_buffer[0];
  811. X        dprint1(9,"ReadCh3270 returns(%c)\n",ch);
  812. X        _input_buf_ptr = 1;
  813. X        return(ch);
  814. X    }
  815. X     else {
  816. X         ch = _input_buffer[_input_buf_ptr];
  817. X         dprint1(9,"ReadCh3270 returns(%c)\n",ch);
  818. X         _input_buf_ptr = _input_buf_ptr + 1;
  819. X         return(ch);
  820. X    }
  821. X}
  822. X
  823. X
  824. X/*
  825. X * WriteScreen3270() Loads a screen to the buffer.
  826. X *
  827. X */
  828. XWriteScreen3270()
  829. X{
  830. X    register int x;
  831. X    int currcol;
  832. X    int currrow;
  833. X    int i;
  834. X    int state = OFF;
  835. X    int prevrow = 1;
  836. X    int prevcol = 1;
  837. X    int prevptr = 0;
  838. X    int clear_state = ON;
  839. X    char tempstr[80];
  840. X    char copy_screen[66*132];
  841. X    
  842. X    dprint0(9,"WriteScreen3270()\n");
  843. X    prevrow = 1;
  844. X    prevcol = 1;
  845. X    prevptr = 0;
  846. X    state = OFF;
  847. X    for (x=0; x < LINES*COLUMNS; x++){
  848. X        if ((_internal_screen[x] == '\016')
  849. X        && (state == OFF)) {
  850. X            currrow = (x / COLUMNS ) + 1;
  851. X            currcol = (x % COLUMNS ) + 1 ;
  852. X            i = x - prevptr - 1;
  853. X            strncpy(copy_screen, (char *) (_internal_screen+(prevptr)),i);
  854. X            panel(erase=clear_state,write,noread) {
  855. X                #@ prevrow, prevcol # #ON,copy_screen,i #
  856. X            }
  857. X            clear_state = OFF;
  858. X            state = ON;
  859. X             /* prevrow = currrow; */
  860. X             /* prevcol = currcol; */
  861. X            prevrow = currrow + 1;
  862. X            prevcol = 0;
  863. X            prevptr = x+1;
  864. X        }
  865. X        else if ((_internal_screen[x] == '\017')
  866. X        && (state == ON)) {
  867. X            currrow = (x / COLUMNS ) + 1;
  868. X            currcol = (x % COLUMNS ) + 1;
  869. X            i = x - prevptr - 1;
  870. X            strncpy(copy_screen, (char *) (_internal_screen+(prevptr)),i);
  871. X            panel(erase = clear_state,write,noread) {
  872. X                #@ prevrow,prevcol # #OH,copy_screen,i #
  873. X            }
  874. X            clear_state = OFF;
  875. X            state = OFF;
  876. X             /* prevrow = currrow; */
  877. X             /* prevcol = currcol; */
  878. X            prevrow = currrow + 1;
  879. X            prevcol = 0;
  880. X        }
  881. X           else if (_internal_screen[x] < ' ') {
  882. X            _internal_screen[x] = ' ';
  883. X            prevptr = x + 1;
  884. X           }
  885. X    }
  886. X    /* write remainder of buffer */
  887. X    if (state == OFF) {
  888. X        currrow = (LINES) + 1 ;
  889. X        currcol = (COLUMNS ) + 1;
  890. X        i = x - prevptr  ;
  891. X        strncpy(copy_screen, (char *) (_internal_screen+(prevptr)),i);
  892. X        panel(erase=clear_state,write,noread) {
  893. X            #@ prevrow,prevcol # #ON,copy_screen,i #
  894. X        }
  895. X    }
  896. X    else {
  897. X        currrow = (LINES ) + 1 ;
  898. X        currcol = (COLUMNS ) + 1 ;
  899. X        i = x - prevptr ;
  900. X        strncpy(copy_screen, (char *) (_internal_screen+(prevptr)),i);
  901. X        panel(erase=clear_state,write,noread) {
  902. X            #@ prevrow,prevcol # #OH,copy_screen,i #
  903. X        }
  904. X    }
  905. X}
  906. X
  907. X
  908. X/*
  909. X * Clear3270
  910. X */
  911. XClear3270()
  912. X{
  913. X    int  i,j;
  914. X    
  915. X    dprint0(9,"Clear3270()\n");
  916. X    j = rowcol2offset(LINES,COLUMNS);
  917. X    for (i = 0; i < j; i++) {
  918. X        _internal_screen[i] = ' ';
  919. X    }
  920. X    return(0);
  921. X}
  922. X
  923. X/*
  924. X *  WriteChar3270(row,col) writes a character at the row and column.
  925. X */
  926. XWriteChar3270(row,col,ch)
  927. Xint row, col;
  928. Xchar ch;
  929. X{
  930. X    dprint3(9,"WriteChar3270(%d,%d,%c)\n",row,col,ch);
  931. X    _internal_screen[rowcol2offset(row,col)] = ch;
  932. X}
  933. X
  934. X/*
  935. X *  WriteLine3270(row,col) writes a line at the row and column.
  936. X */
  937. XWriteLine3270(row,col,line)
  938. Xint row, col;
  939. Xchar *line;
  940. X{
  941. X    int i, j, k;
  942. X    dprint3(9,"WriteLine3270(%d,%d,%s)\n",row,col,line);
  943. X        _line = row;
  944. X    _col = col;
  945. X    k = strlen(line);
  946. X    i=rowcol2offset(row,col);
  947. X    for (j=0; j<k; i++, j++) {
  948. X        if ((line[j] >= ' ')   ||
  949. X           (line[j] == '\016') ||
  950. X           (line[j] == '\017'))
  951. X        _internal_screen[i] = line[j];
  952. X        else _internal_screen[i] = ' ';
  953. X    }
  954. X   /*   _line = offset2row(i-1);  calling program updates location */
  955. X   /*   _col  = offset2col(i-1); */
  956. X    
  957. X}
  958. X
  959. X
  960. X/*
  961. X * ClearEOLN3270() clears the remainder of the current line on a 3270.
  962. X */
  963. XClearEOLN3270()
  964. X{
  965. X    int i,j ;
  966. X    
  967. X    dprint0(9,"ClearEOLN3270()\n");
  968. X    j = rowcol2offset(_line,COLUMNS);
  969. X    for (i=rowcol2offset(_line,_col); i < j; i++) {
  970. X        _internal_screen[i] = ' ';
  971. X    }
  972. X}
  973. X
  974. X/*
  975. X * ClearEOS3270() clears the remainder of the current screen on a 3270.
  976. X */
  977. XClearEOS3270()
  978. X{
  979. X    int i,j;
  980. X    
  981. X    dprint0(9,"ClearEOS3270()\n");
  982. X    j = rowcol2offset(LINES,COLUMNS);
  983. X        for (i = rowcol2offset(_line,_col); i < j; i++) {
  984. X        _internal_screen[i] = ' ';
  985. X    }
  986. X}
  987. X
  988. END_OF_src/screen3270.q
  989. if test 11593 -ne `wc -c <src/screen3270.q`; then
  990.     echo shar: \"src/screen3270.q\" unpacked with wrong size!?
  991. fi
  992. # end of overwriting check
  993. fi
  994. echo shar: Extracting \"src/showmsg.c\" \(10629 characters\)
  995. if test -f src/showmsg.c ; then 
  996.   echo shar: Will not over-write existing file \"src/showmsg.c\"
  997. else
  998. sed "s/^X//" >src/showmsg.c <<'END_OF_src/showmsg.c'
  999. X/**             showmsg.c            **/
  1000. X
  1001. X/** This file contains all the routines needed to display the specified
  1002. X    message.
  1003. X
  1004. X   These routines (C) Copyright 1986 Dave Taylor 
  1005. X
  1006. X   Modified 6/86 to use pager variable!!!   Hurrah!!!!
  1007. X   Modified 7/86 to have secure pipes.. *sigh*
  1008. X**/
  1009. X
  1010. X#include "headers.h"
  1011. X#include <ctype.h>
  1012. X#include <errno.h>
  1013. X#include <signal.h>
  1014. X
  1015. X#ifdef BSD
  1016. X# include <sys/wait.h>
  1017. X# undef tolower
  1018. X#endif
  1019. X
  1020. Xextern int errno;
  1021. X
  1022. Xchar *error_name(), *strcat(), *strcpy();
  1023. Xvoid   _exit();
  1024. X
  1025. Xint    memory_lock = FALSE;    /* is it available?? */
  1026. Xint    pipe_abort  = FALSE;    /* did we receive a SIGNAL(SIGPIPE)? */
  1027. X
  1028. Xint
  1029. Xshow_msg(number)
  1030. Xint number;
  1031. X{
  1032. X    /*** display number'th message.  Get starting and ending lines
  1033. X         of message from headers data structure, then fly through
  1034. X         the file, displaying only those lines that are between the
  1035. X         two!
  1036. X        Returns non-zero iff screen was changed
  1037. X    ***/
  1038. X
  1039. X    dprint0(8, "show_msg called\n");
  1040. X
  1041. X    if (number > message_count) {
  1042. X      error1("Only %d messages!", message_count);
  1043. X      return(0);
  1044. X    }
  1045. X    else if (number < 1) {
  1046. X      error("you can't read THAT message!");
  1047. X      return(0);
  1048. X    }
  1049. X
  1050. X    clearit(header_table[number-1].status, NEW);   /* it's been read now! */
  1051. X
  1052. X    memory_lock = FALSE;
  1053. X
  1054. X    /* some explaination for that last one - We COULD use memory locking
  1055. X       to speed up the paging, but the action of "ClearScreen" on a screen
  1056. X       with memory lock turned on seems to vary considerably (amazingly so)
  1057. X       so it's safer to only allow memory lock to be a viable bit of
  1058. X       trickery when dumping text to the screen in scroll mode.
  1059. X       Philosophical arguments should be forwarded to Bruce at the 
  1060. X       University of Walamazoo, Australia, via ACSNet  *wry chuckle* */
  1061. X
  1062. X    return(show_message(header_table[number-1].lines, 
  1063. X           header_table[number-1].offset,number));
  1064. X}
  1065. X
  1066. Xint
  1067. Xshow_message(lines, file_loc, msgnumber)
  1068. Xint lines, msgnumber;
  1069. Xlong file_loc;
  1070. X{
  1071. X    /*** Show the indicated range of lines from mailfile
  1072. X         for message 'msgnumber' by using 'display'
  1073. X         Returns non-zero iff screen was altered.
  1074. X    ***/
  1075. X
  1076. X    dprint3(9,"show_message(%d,%ld,%d)\n", lines, file_loc, msgnumber);
  1077. X
  1078. X    if (fseek(mailfile, file_loc, 0) != 0) {
  1079. X      dprint2(1,"Error: seek %d bytes into file, errno %s (show_message)\n",
  1080. X          file_loc, error_name(errno));
  1081. X      error2("ELM failed seeking %d bytes into file (%s)",
  1082. X          file_loc, error_name(errno));    
  1083. X      return(0);
  1084. X    }
  1085. X
  1086. X    if (feof(mailfile))
  1087. X      dprint0(1,"\n*** seek put us at END OF FILE!!! ***\n");
  1088. X
  1089. X    /* next read will get 'this' line so must be at end of previous */
  1090. X
  1091. X    Raw(OFF);
  1092. X    if (strcmp(pager,"builtin") == 0 || strcmp(pager,"internal") == 0)
  1093. X      display(lines, msgnumber);
  1094. X    else
  1095. X      secure_display(lines, msgnumber);
  1096. X    Raw(ON);
  1097. X    if (memory_lock) EndMemlock();    /* turn it off!! */
  1098. X
  1099. X    return(1);    /* we did it boss! */
  1100. X}
  1101. X    
  1102. X
  1103. X/** This next one is the 'pipe' file descriptor for writing to the 
  1104. X    pager process... **/
  1105. X
  1106. XFILE   *output_pipe, *popen();
  1107. X
  1108. Xint
  1109. Xdisplay(lines, msgnum)
  1110. Xint lines, msgnum;
  1111. X{
  1112. X    /** Display specified number of lines from file mailfile.
  1113. X        Note: This routine MUST be placed at the first line 
  1114. X        of the input file! 
  1115. X        Returns the same as the routine above (namely zero or one)
  1116. X    **/
  1117. X
  1118. X    char from_buffer[LONG_STRING], buffer[VERY_LONG_STRING], *full_month();
  1119. X
  1120. X    int lines_displayed = 0;    
  1121. X    int crypted = 0, gotten_key = 0;    /* encryption */
  1122. X    int weed_header, weeding_out = 0;    /* weeding    */ 
  1123. X    int mail_sent,                /* misc use   */
  1124. X        form_letter = FALSE,        /* Form ltr?  */
  1125. X        form_letter_section = 0,        /* section    */
  1126. X        builtin = FALSE;            /* our pager? */
  1127. X
  1128. X    dprint3(4,"displaying %d lines from message %d using %s\n", 
  1129. X        lines, msgnum, pager);
  1130. X
  1131. X    ClearScreen();
  1132. X
  1133. X    if (cursor_control) transmit_functions(OFF);
  1134. X
  1135. X    pipe_abort = FALSE;
  1136. X
  1137. X    builtin = (strcmp(pager, "builtin") == 0 || 
  1138. X           strcmp(pager,"internal") == 0);
  1139. X
  1140. X    if (form_letter = (header_table[msgnum-1].status&FORM_LETTER)) {
  1141. X      if (filter)
  1142. X        form_letter_section = 1;    /* initialize to section 1 */
  1143. X    }
  1144. X
  1145. X    if (builtin) 
  1146. X      start_builtin(lines);
  1147. X    else {
  1148. X      if ((output_pipe = popen(pager,"w")) == NULL) {
  1149. X        error2("Can't create pipe to %s [%s]", pager, 
  1150. X            error_name(errno));
  1151. X        dprint2(1,"\n*** Can't create pipe to %s - error %s ***\n\n",
  1152. X               pager, error_name(errno));
  1153. X        return(0);
  1154. X      }
  1155. X      dprint1(4,"Opened a write-only pipe to routine %s \n", pager);
  1156. X    }
  1157. X
  1158. X    if (title_messages) {
  1159. X
  1160. X      mail_sent = (strncmp(header_table[msgnum-1].from, "To:", 3) == 0);
  1161. X
  1162. X      tail_of(header_table[msgnum-1].from, from_buffer, FALSE);
  1163. X
  1164. X      sprintf(buffer, "\r%s #%d %s %s%s\t %s %s %s, %d at %s%s\n\r",
  1165. X           form_letter? "Form": "Message",
  1166. X            msgnum, mail_sent? "to" : "from", from_buffer,
  1167. X           (strlen(from_buffer) > 24? "\n\r": 
  1168. X             (strlen(from_buffer) > 16 ? "" : "\t")),
  1169. X           "Mailed",
  1170. X                full_month(header_table[msgnum-1].month), 
  1171. X           header_table[msgnum-1].day, 
  1172. X               atoi(header_table[msgnum-1].year) + 1900,
  1173. X               header_table[msgnum-1].time,
  1174. X           filter? "": "\n\r\n\r");
  1175. X
  1176. X      if (builtin)
  1177. X        display_line(buffer);
  1178. X      else
  1179. X        fprintf(output_pipe, "%s", buffer);
  1180. X
  1181. X      if (! mail_sent && matches_weedlist("To:") && filter &&
  1182. X          strcmp(header_table[current-1].to,username) != 0 &&
  1183. X          strlen(header_table[current-1].to) > 0) {
  1184. X        sprintf(buffer, "\n\r(message addressed to %s)\n\r", 
  1185. X            header_table[current-1].to);
  1186. X        if (builtin)
  1187. X          display_line(buffer);
  1188. X        else
  1189. X          fprintf(output_pipe, "%s", buffer);
  1190. X      }
  1191. X    
  1192. X      /** The test above is: if we didn't originally send the mail
  1193. X          (e.g. we're not reading "mail.sent") AND the user is currently
  1194. X          weeding out the "To:" line (otherwise they'll get it twice!)
  1195. X          AND the user is actually weeding out headers AND the message 
  1196. X          wasn't addressed to the user AND the 'to' address is non-zero 
  1197. X          (consider what happens when the message doesn't HAVE a "To:" 
  1198. X          line...the value is NULL but it doesn't match the username 
  1199. X          either.  We don't want to display something ugly like 
  1200. X          "(message addressed to )" which will just clutter up the 
  1201. X          display!).
  1202. X
  1203. X          And you thought programming was EASY!!!!
  1204. X      **/
  1205. X    }
  1206. X
  1207. X    weed_header = filter;    /* allow us to change it after header */
  1208. X
  1209. X    while (lines > 0 && pipe_abort == FALSE) {
  1210. X
  1211. X        if (fgets(buffer, VERY_LONG_STRING, mailfile) == NULL) {
  1212. X          if (lines_displayed == 0) {
  1213. X
  1214. X        /* AUGH!  Why do we get this occasionally???  */
  1215. X
  1216. X            dprint0(1,
  1217. X             "\n\n** Out of Sync!!  EOF with nothing read (display) **\n");
  1218. X        dprint0(1,"** closing and reopening mailfile... **\n\n");
  1219. X
  1220. X            if (!builtin) pclose(output_pipe);    /* close pipe NOW! */
  1221. X
  1222. X        if (mailfile != NULL)
  1223. X          fclose(mailfile);        /* huh? */
  1224. X
  1225. X        if ((mailfile = fopen(infile, "r")) == NULL) {
  1226. X          error("Sync error: can't re-open mailbox!!");
  1227. X          show_mailfile_stats();
  1228. X          emergency_exit();
  1229. X            }
  1230. X            return(show_message(lines, 
  1231. X                        header_table[msgnum-1].offset,
  1232. X                    msgnum));
  1233. X          }
  1234. X          if (!builtin) 
  1235. X            pclose(output_pipe);
  1236. X          if (lines == 0 && pipe_abort == FALSE) {    /* displayed it all */
  1237. X        if (!builtin)
  1238. X              PutLine0(LINES,0,"\rPress <return> to return to Elm: ");
  1239. X            else
  1240. X          printf("\n\r\n\rPress <return> to return to Elm: ");
  1241. X        fflush(stdout);
  1242. X            Raw(ON);
  1243. X            (void) ReadCh();
  1244. X            Raw(OFF);
  1245. X          }
  1246. X          return(TRUE);
  1247. X        }
  1248. X
  1249. X        if (strlen(buffer) > 0) 
  1250. X              no_ret(buffer);
  1251. X
  1252. X        if (strlen(buffer) == 0) {
  1253. X          weed_header = 0;        /* past header! */
  1254. X          weeding_out = 0;
  1255. X        }
  1256. X
  1257. X        lines--;
  1258. X        lines_displayed++;
  1259. X
  1260. X        if (form_letter && weed_header)
  1261. X        /* skip it.  NEVER display random headers in forms! */;
  1262. X        else if (weed_header && matches_weedlist(buffer)) 
  1263. X          weeding_out = 1;     /* aha!  We don't want to see this! */
  1264. X        else if (buffer[0] == '[') {
  1265. X          if (strcmp(buffer, START_ENCODE)==0)
  1266. X            crypted++;
  1267. X          else if (strcmp(buffer, END_ENCODE)==0)
  1268. X            crypted--;
  1269. X          else if (crypted) {
  1270. X                encode(buffer);
  1271. X            show_line(buffer, builtin);
  1272. X          }
  1273. X          else
  1274. X            show_line(buffer, builtin);
  1275. X        } 
  1276. X        else if (crypted) {
  1277. X          if (! gotten_key++) getkey(OFF);
  1278. X          encode(buffer);
  1279. X          show_line(buffer, builtin); 
  1280. X        }
  1281. X        else if (weeding_out) {
  1282. X          weeding_out = (whitespace(buffer[0]));    /* 'n' line weed */
  1283. X          if (! weeding_out)     /* just turned on! */
  1284. X            show_line(buffer, builtin);
  1285. X        } 
  1286. X        else if (form_letter && first_word(buffer,"***") && filter) {
  1287. X          strcpy(buffer,
  1288. X"\n------------------------------------------------------------------------------\n");
  1289. X          show_line(buffer, builtin);    /* hide '***' */
  1290. X          form_letter_section++;
  1291. X        }
  1292. X        else if (form_letter_section == 1 || form_letter_section == 3)
  1293. X          /** skip this stuff - we can't deal with it... **/;
  1294. X        else
  1295. X          show_line(buffer, builtin);
  1296. X    }
  1297. X
  1298. X        if (cursor_control) transmit_functions(ON);
  1299. X
  1300. X    if (! builtin) pclose(output_pipe);
  1301. X
  1302. X    if (lines == 0 && pipe_abort == FALSE) {      /* displayed it all! */
  1303. X      if (! builtin)
  1304. X        PutLine0(LINES,0,"\rPress <return> to return to Elm: ");
  1305. X      else
  1306. X        printf("\n\r\n\rPress <return> to return to Elm: ");
  1307. X      fflush(stdout);
  1308. X      Raw(ON);
  1309. X      (void) ReadCh();
  1310. X      Raw(OFF);
  1311. X    }
  1312. X    return(TRUE);
  1313. X}
  1314. X
  1315. Xshow_line(buffer, builtin)
  1316. Xchar *buffer;
  1317. Xint  builtin;
  1318. X{
  1319. X    /** Hands the given line to the output pipe.  'builtin' is true if
  1320. X        we're using the builtin pager.  **/ 
  1321. X    
  1322. X    if (builtin) {
  1323. X      strcat(buffer, "\n\r");
  1324. X      pipe_abort = display_line(buffer);
  1325. X    }
  1326. X    else {
  1327. X      errno = 0;
  1328. X      fprintf(output_pipe, "%s\n", buffer);
  1329. X    
  1330. X      if (errno != 0)
  1331. X        dprint1(1,"\terror %s hit!\n", error_name(errno));
  1332. X    }
  1333. X}
  1334. X
  1335. Xint
  1336. Xsecure_display(lines, msgnumber)
  1337. Xint lines, msgnumber;
  1338. X{
  1339. X    /** This is the cheap way to implement secure pipes - spawn a
  1340. X        child process running under the old userid, then open the
  1341. X        pager and feed the message to it.  When the subprocess ends
  1342. X        (the routine returns) simply return.  Simple and effective.
  1343. X        (too bad it's this much of a hassle to implement secure
  1344. X        pipes, though - I can imagine it being a constant problem!)
  1345. X    **/
  1346. X
  1347. X    int pid, w;
  1348. X#ifdef BSD
  1349. X    union wait status;
  1350. X#else
  1351. X    int status;
  1352. X#endif
  1353. X    register int (*istat)(), (*qstat)();
  1354. X    
  1355. X#ifdef NO_VM        /* machine without virtual memory! */
  1356. X    if ((pid = fork()) == 0) {
  1357. X#else
  1358. X    if ((pid = vfork()) == 0) {
  1359. X#endif
  1360. X
  1361. X      setgid(groupid);    /* and group id            */
  1362. X      setuid(userid);    /* back to the normal user! */
  1363. X
  1364. X      _exit(display(lines, msgnumber));
  1365. X    }
  1366. X
  1367. X    istat = signal(SIGINT, SIG_IGN);
  1368. X    qstat = signal(SIGQUIT, SIG_IGN);
  1369. X
  1370. X    while ((w = wait(&status)) != pid && w != -1)
  1371. X        ;
  1372. X
  1373. X    signal(SIGINT, istat);
  1374. X    signal(SIGQUIT, qstat);
  1375. X
  1376. X#ifdef BSD
  1377. X    return(status.w_retcode);
  1378. X#else
  1379. X    return(status);
  1380. X#endif
  1381. X}
  1382. END_OF_src/showmsg.c
  1383. if test 10629 -ne `wc -c <src/showmsg.c`; then
  1384.     echo shar: \"src/showmsg.c\" unpacked with wrong size!?
  1385. fi
  1386. # end of overwriting check
  1387. fi
  1388. echo shar: Extracting \"src/strings.c\" \(11005 characters\)
  1389. if test -f src/strings.c ; then 
  1390.   echo shar: Will not over-write existing file \"src/strings.c\"
  1391. else
  1392. sed "s/^X//" >src/strings.c <<'END_OF_src/strings.c'
  1393. X/**            strings.c        **/
  1394. X
  1395. X/** This file contains all the string oriented functions for the
  1396. X    ELM Mailer, and lots of other generally useful string functions! 
  1397. X
  1398. X    For BSD systems, this file also includes the function "tolower"
  1399. X    to translate the given character from upper case to lower case.
  1400. X
  1401. X    (C) Copyright 1985, Dave Taylor
  1402. X**/
  1403. X
  1404. X#include <stdio.h>
  1405. X#include "headers.h"
  1406. X#include <ctype.h>
  1407. X
  1408. X#ifdef BSD
  1409. X#undef tolower
  1410. X#undef toupper
  1411. X#endif
  1412. X
  1413. X/** forward declarations **/
  1414. X
  1415. Xchar *format_long(), *strip_commas(), *tail_of_string(), *shift_lower(),
  1416. X     *get_token(), *strip_parens(), *argv_zero(), *strcpy(), *strncpy();
  1417. X
  1418. X#ifdef BSD
  1419. X
  1420. Xint
  1421. Xtolower(ch)
  1422. Xchar ch;
  1423. X{
  1424. X    /** This should be a macro call, but if you use this as a macro
  1425. X        calls to 'tolower' where the argument is a function call will
  1426. X        cause the function to be called TWICE which is obviously the
  1427. X        wrong behaviour.  On the other hand, to just blindly translate
  1428. X        assuming the character is always uppercase can cause BIG
  1429. X        problems, so...
  1430. X    **/
  1431. X
  1432. X    return ( isupper(ch) ? ch - 'A' + 'a' : ch );
  1433. X}
  1434. X
  1435. Xint
  1436. Xtoupper(ch)
  1437. Xchar ch;
  1438. X{
  1439. X    /** see comment for above routine - tolower() **/
  1440. X
  1441. X    return ( islower(ch) ? ch - 'a' + 'A' : ch );
  1442. X}
  1443. X
  1444. X#endif
  1445. X
  1446. Xint
  1447. Xprintable_chars(string)
  1448. Xchar *string;
  1449. X{
  1450. X    /** Returns the number of "printable" (ie non-control) characters
  1451. X        in the given string... Modified 4/86 to know about TAB
  1452. X        characters being every eight characters... **/
  1453. X
  1454. X    register int count = 0, index;
  1455. X
  1456. X    for (index = 0; index < strlen(string); index++)
  1457. X      if (string[index] >= ' ') 
  1458. X        if (string[index] == '\t')
  1459. X          count += (7-(count % 8));
  1460. X        else
  1461. X          count++;
  1462. X
  1463. X    return(count);
  1464. X}
  1465. X
  1466. Xtail_of(from, buffer, header_line)
  1467. Xchar *from, *buffer;
  1468. Xint   header_line;
  1469. X{
  1470. X    /** Return last two words of 'from'.  This is to allow
  1471. X        painless display of long return addresses as simply the
  1472. X        machine!username.  Alternatively, if the first three
  1473. X        characters of the 'from' address are 'To:' and 'header_line'
  1474. X        is TRUE, then return the buffer value prepended with 'To '. 
  1475. X
  1476. X        Mangled to know about the PREFER_UUCP hack.  6/86
  1477. X    **/
  1478. X
  1479. X    /** Note: '!' delimits Usenet nodes, '@' delimits ARPA nodes,
  1480. X              ':' delimits CSNet & Bitnet nodes, and '%' delimits
  1481. X          multiple stage ARPA hops... **/
  1482. X
  1483. X    register int loc, i = 0, cnt = 0;
  1484. X    char     tempbuffer[SLEN];
  1485. X    
  1486. X#ifdef PREFER_UUCP
  1487. X    
  1488. X    /** let's see if we have an address appropriate for hacking **/
  1489. X
  1490. X    if (chloc(from,'!') != -1 && in_string(from, BOGUS_INTERNET))
  1491. X       from[strlen(from)-strlen(BOGUS_INTERNET)] = '\0';
  1492. X
  1493. X#endif
  1494. X
  1495. X    for (loc = strlen(from)-1; loc >= 0 && cnt < 2; loc--) {
  1496. X      if (from[loc] == BANG || from[loc] == AT_SIGN ||
  1497. X          from[loc] == COLON) cnt++;
  1498. X      if (cnt < 2) buffer[i++] = from[loc];
  1499. X    }
  1500. X
  1501. X    buffer[i] = '\0';
  1502. X
  1503. X    reverse(buffer);
  1504. X
  1505. X    if ((strncmp(buffer,"To:", 3) == 0) && header_line)
  1506. X      buffer[2] = ' ';
  1507. X    else if ((strncmp(from, "To:", 3) == 0) && header_line) {
  1508. X      sprintf(tempbuffer,"To %s", buffer); 
  1509. X      strcpy(buffer, tempbuffer);
  1510. X    }
  1511. X    else if (strncmp(buffer, "To:", 3) == 0) {
  1512. X      for (i=3; i < strlen(buffer); i++)
  1513. X        tempbuffer[i-3] = buffer[i];
  1514. X      tempbuffer[i-3] = '\0';
  1515. X      strcpy(buffer, tempbuffer);
  1516. X    }
  1517. X}
  1518. X
  1519. Xchar *format_long(inbuff, init_len)
  1520. Xchar *inbuff;
  1521. Xint   init_len;
  1522. X{
  1523. X    /** Return buffer with \n\t sequences added at each point where it 
  1524. X        would be more than 80 chars long.  It only allows the breaks at 
  1525. X        legal points (ie commas followed by white spaces).  init-len is 
  1526. X        the characters already on the first line...  Changed so that if 
  1527. X            this is called while mailing without the overhead of "elm", it'll 
  1528. X            include "\r\n\t" instead.
  1529. X        Changed to use ',' as a separator and to REPLACE it after it's
  1530. X        found in the output stream...
  1531. X    **/
  1532. X
  1533. X    static char ret_buffer[VERY_LONG_STRING];
  1534. X    register int index = 0, current_length = 0, depth=15, i;
  1535. X    char     buffer[VERY_LONG_STRING];
  1536. X    char     *word, *bufptr;
  1537. X
  1538. X    strcpy(buffer, inbuff);
  1539. X
  1540. X    bufptr = (char *) buffer;
  1541. X
  1542. X    current_length = init_len + 2;    /* for luck */
  1543. X
  1544. X    while ((word = get_token(bufptr,",", depth)) != NULL) {
  1545. X
  1546. X        /* first, decide what sort of separator we need, if any... */
  1547. X
  1548. X      if (strlen(word) + current_length > 80) {
  1549. X        if (index > 0) {
  1550. X          ret_buffer[index++] = ',';    /* close 'er up, doctor! */
  1551. X          if (mail_only)
  1552. X            ret_buffer[index++] = '\r';
  1553. X          ret_buffer[index++] = '\n';
  1554. X          ret_buffer[index++] = '\t';
  1555. X        }
  1556. X        
  1557. X        /* now add this pup! */
  1558. X
  1559. X        for (i=(word[0] == ' '? 1:0); i<strlen(word); i++)
  1560. X          ret_buffer[index++] = word[i];
  1561. X        current_length = strlen(word) + 8;    /* 8 = TAB */
  1562. X      }
  1563. X
  1564. X      else {    /* just add this address to the list.. */
  1565. X
  1566. X        if (index > 0) {
  1567. X          ret_buffer[index++] = ',';    /* comma added! */
  1568. X          ret_buffer[index++] = ' ';
  1569. X          current_length += 2;
  1570. X        }
  1571. X        for (i=(word[0] == ' '? 1:0); i<strlen(word); i++)
  1572. X          ret_buffer[index++] = word[i];
  1573. X        current_length += strlen(word);
  1574. X      }
  1575. X    
  1576. X      bufptr = NULL;
  1577. X    }
  1578. X    
  1579. X    ret_buffer[index] = '\0';
  1580. X
  1581. X    return( (char *) ret_buffer);
  1582. X}
  1583. X
  1584. Xchar *strip_commas(string)
  1585. Xchar *string;
  1586. X{
  1587. X    /** return string with all commas changed to spaces.  This IS
  1588. X        destructive and will permanently change the input string.. **/
  1589. X
  1590. X    register int i;
  1591. X
  1592. X    for (i=0; i < strlen(string); i++)
  1593. X      if (string[i] == COMMA)
  1594. X        string[i] = SPACE;
  1595. X
  1596. X    return( (char *) string);
  1597. X}
  1598. X
  1599. Xchar *strip_parens(string)
  1600. Xchar *string;
  1601. X{
  1602. X    /** Return string with all parenthesized information removed.
  1603. X        This is a non-destructive algorithm... **/
  1604. X
  1605. X    static char  buffer[VERY_LONG_STRING];
  1606. X    register int i, depth = 0, buffer_index = 0;
  1607. X
  1608. X    for (i=0; i < strlen(string); i++) {
  1609. X      if (string[i] == '(')
  1610. X        depth++;
  1611. X      else if (string[i] == ')') 
  1612. X        depth--;
  1613. X      else if (depth == 0)
  1614. X        buffer[buffer_index++] = string[i];
  1615. X    }
  1616. X    
  1617. X    buffer[buffer_index] = '\0';
  1618. X
  1619. X    return( (char *) buffer);
  1620. X}
  1621. X
  1622. Xmove_left(string, chars)
  1623. Xchar string[];
  1624. Xint  chars;
  1625. X{
  1626. X    /** moves string chars characters to the left DESTRUCTIVELY **/
  1627. X
  1628. X    register int i;
  1629. X
  1630. X    chars--; /* index starting at zero! */
  1631. X
  1632. X    for (i=chars; string[i] != '\0' && string[i] != '\n'; i++)
  1633. X      string[i-chars] = string[i];
  1634. X
  1635. X    string[i-chars] = '\0';
  1636. X}
  1637. X
  1638. Xremove_first_word(string)
  1639. Xchar *string;
  1640. X{    /** removes first word of string, ie up to first non-white space
  1641. X        following a white space! **/
  1642. X
  1643. X    register int loc;
  1644. X
  1645. X    for (loc = 0; string[loc] != ' ' && string[loc] != '\0'; loc++) 
  1646. X        ;
  1647. X
  1648. X    while (string[loc] == ' ' || string[loc] == '\t')
  1649. X      loc++;
  1650. X    
  1651. X    move_left(string, ++loc);
  1652. X}
  1653. X
  1654. Xsplit_word(buffer, first, rest)
  1655. Xchar *buffer, *first, *rest;
  1656. X{
  1657. X    /** Rip the buffer into first word and rest of word, translating it
  1658. X        all to lower case as we go along..
  1659. X    **/
  1660. X
  1661. X    register int i, j = 0;
  1662. X
  1663. X    /** skip leading white space, just in case.. **/
  1664. X
  1665. X    for (i=0; whitespace(buffer[i]); i++)    ;
  1666. X
  1667. X    /** now copy into 'first' until we hit white space or EOLN **/
  1668. X
  1669. X    for (j=0; i < strlen(buffer) && ! whitespace(buffer[i]); )
  1670. X      first[j++] = tolower(buffer[i++]);
  1671. X
  1672. X    first[j] = '\0';
  1673. X    
  1674. X    while (whitespace(buffer[i])) i++;
  1675. X
  1676. X    for (j=0; i < strlen(buffer); i++)
  1677. X      rest[j++] = tolower(buffer[i]);
  1678. X
  1679. X    rest[j] = '\0';
  1680. X
  1681. X    return;
  1682. X}
  1683. X
  1684. Xchar *tail_of_string(string, maxchars)
  1685. Xchar *string;
  1686. Xint  maxchars;
  1687. X{
  1688. X    /** Return a string that is the last 'maxchars' characters of the
  1689. X        given string.  This is only used if the first word of the string
  1690. X        is longer than maxchars, else it will return what is given to
  1691. X        it... 
  1692. X    **/
  1693. X
  1694. X    static char buffer[SLEN];
  1695. X    register int index, i;
  1696. X
  1697. X    for (index=0;! whitespace(string[index]) && index < strlen(string); 
  1698. X         index++)
  1699. X      ;
  1700. X
  1701. X    if (index < maxchars) {
  1702. X      strncpy(buffer, string, maxchars-2);    /* word too short */
  1703. X      buffer[maxchars-2] = '.';
  1704. X      buffer[maxchars-1] = '.';
  1705. X      buffer[maxchars]   = '.';
  1706. X      buffer[maxchars+1] = '\0';
  1707. X    } 
  1708. X    else {
  1709. X      i = maxchars;
  1710. X      buffer[i--] = '\0';
  1711. X      while (i > 1) 
  1712. X        buffer[i--] = string[index--];
  1713. X      buffer[2] = '.';
  1714. X      buffer[1] = '.';
  1715. X      buffer[0] = '.';
  1716. X    }
  1717. X
  1718. X    return( (char *) buffer);
  1719. X}
  1720. X
  1721. Xreverse(string)
  1722. Xchar *string;
  1723. X{
  1724. X    /** reverse string... pretty trivial routine, actually! **/
  1725. X
  1726. X    char buffer[SLEN];
  1727. X    register int i, j = 0;
  1728. X
  1729. X    for (i = strlen(string)-1; i >= 0; i--)
  1730. X      buffer[j++] = string[i];
  1731. X
  1732. X    buffer[j] = '\0';
  1733. X
  1734. X    strcpy(string, buffer);
  1735. X}
  1736. X
  1737. Xint
  1738. Xget_word(buffer, start, word)
  1739. Xchar *buffer, *word;
  1740. Xint start;
  1741. X{
  1742. X    /**    return next word in buffer, starting at 'start'.
  1743. X        delimiter is space or end-of-line.  Returns the
  1744. X        location of the next word, or -1 if returning
  1745. X        the last word in the buffer.  -2 indicates empty
  1746. X        buffer!  **/
  1747. X
  1748. X    register int loc = 0;
  1749. X
  1750. X    while (buffer[start] == ' ' && buffer[start] != '\0')
  1751. X      start++;
  1752. X
  1753. X    if (buffer[start] == '\0') return(-2);     /* nothing IN buffer! */
  1754. X
  1755. X    while (buffer[start] != ' ' && buffer[start] != '\0')
  1756. X      word[loc++] = buffer[start++];
  1757. X
  1758. X    word[loc] = '\0';
  1759. X    return(start);
  1760. X}
  1761. X
  1762. Xchar *shift_lower(string)
  1763. Xchar *string;
  1764. X{
  1765. X    /** return 'string' shifted to lower case.  Do NOT touch the
  1766. X        actual string handed to us! **/
  1767. X
  1768. X    static char buffer[LONG_SLEN];
  1769. X    register int i;
  1770. X
  1771. X    for (i=0; i < strlen(string); i++)
  1772. X      if (isupper(string[i]))
  1773. X        buffer[i] = tolower(string[i]);
  1774. X      else
  1775. X        buffer[i] = string[i];
  1776. X    
  1777. X    buffer[strlen(string)] = 0;
  1778. X    
  1779. X    return( (char *) buffer);
  1780. X}
  1781. X
  1782. XCenterline(line, string)
  1783. Xint line;
  1784. Xchar *string;
  1785. X{
  1786. X    /** Output 'string' on the given line, centered. **/
  1787. X
  1788. X    register int length, col;
  1789. X
  1790. X    length = strlen(string);
  1791. X
  1792. X    if (length > COLUMNS)
  1793. X      col = 0;
  1794. X    else
  1795. X      col = (COLUMNS - length) / 2;
  1796. X
  1797. X    PutLine0(line, col, string);
  1798. X}
  1799. X
  1800. Xchar *argv_zero(string)
  1801. Xchar *string;
  1802. X{
  1803. X    /** given a string of the form "/something/name" return a
  1804. X        string of the form "name"... **/
  1805. X
  1806. X    static char buffer[NLEN];
  1807. X    register int i, j=0;
  1808. X
  1809. X    for (i=strlen(string)-1; string[i] != '/'; i--)
  1810. X      buffer[j++] = string[i];
  1811. X    buffer[j] = '\0';
  1812. X
  1813. X    reverse(buffer);
  1814. X
  1815. X    return( (char *) buffer);
  1816. X}
  1817. X
  1818. X#define MAX_RECURSION        20        /* up to 20 deep recursion */
  1819. X
  1820. Xchar *get_token(source, keys, depth)
  1821. Xchar *source, *keys;
  1822. Xint   depth;
  1823. X{
  1824. X    /** This function is similar to strtok() (see "opt_utils")
  1825. X        but allows nesting of calls via pointers... 
  1826. X    **/
  1827. X
  1828. X    register int  last_ch;
  1829. X    static   char *buffers[MAX_RECURSION];
  1830. X    char     *return_value, *sourceptr;
  1831. X
  1832. X    if (depth > MAX_RECURSION) {
  1833. X       error1("get_token calls nested greater than %d deep!", 
  1834. X          MAX_RECURSION);
  1835. X       emergency_exit();
  1836. X    }
  1837. X
  1838. X    if (source != NULL)
  1839. X      buffers[depth] = source;
  1840. X    
  1841. X    sourceptr = buffers[depth];
  1842. X    
  1843. X    if (*sourceptr == '\0') 
  1844. X      return(NULL);        /* we hit end-of-string last time!? */
  1845. X
  1846. X    sourceptr += strspn(sourceptr, keys);      /* skip the bad.. */
  1847. X    
  1848. X    if (*sourceptr == '\0') {
  1849. X      buffers[depth] = sourceptr;
  1850. X      return(NULL);            /* we've hit end-of-string   */
  1851. X    }
  1852. X
  1853. X    last_ch = strcspn(sourceptr, keys);   /* end of good stuff   */
  1854. X
  1855. X    return_value = sourceptr;          /* and get the ret     */
  1856. X
  1857. X    sourceptr += last_ch;              /* ...value            */
  1858. X
  1859. X    if (*sourceptr != '\0')        /** don't forget if we're at end! **/
  1860. X      sourceptr++;                  
  1861. X    
  1862. X    return_value[last_ch] = '\0';          /* ..ending right      */
  1863. X
  1864. X    buffers[depth] = sourceptr;          /* save this, mate!    */
  1865. X
  1866. X    return((char *) return_value);         /* and we're outta here! */
  1867. X}
  1868. END_OF_src/strings.c
  1869. if test 11005 -ne `wc -c <src/strings.c`; then
  1870.     echo shar: \"src/strings.c\" unpacked with wrong size!?
  1871. fi
  1872. # end of overwriting check
  1873. fi
  1874. echo shar: End of archive 11 \(of 19\).
  1875. cp /dev/null ark11isdone
  1876. DONE=true
  1877. for I in 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 ; do
  1878.     if test ! -f ark${I}isdone ; then
  1879.     echo shar: You still need to run archive ${I}.
  1880.     DONE=false
  1881.     fi
  1882. done
  1883. if test "$DONE" = "true" ; then
  1884.     echo You have unpacked all 19 archives.
  1885.     echo "See the Instructions file"
  1886.     rm -f ark[1-9]isdone ark[1-9][0-9]isdone
  1887. fi
  1888. ##  End of shell archive.
  1889. exit 0
  1890.