home *** CD-ROM | disk | FTP | other *** search
/ Usenet 1994 January / usenetsourcesnewsgroupsinfomagicjanuary1994.iso / sources / unix / volume22 / elm2.3 / part21 < prev    next >
Encoding:
Internet Message Format  |  1990-06-07  |  49.2 KB

  1. Subject:  v22i080:  ELM mail syste, release 2.3, Part21/26
  2. Newsgroups: comp.sources.unix
  3. Approved: rsalz@uunet.UU.NET
  4. X-Checksum-Snefru: f1fdbb4c c5394e13 05a19944 5c9ca15e
  5.  
  6. Submitted-by: Syd Weinstein <syd@dsinc.dsi.com>
  7. Posting-number: Volume 22, Issue 80
  8. Archive-name: elm2.3/part21
  9.  
  10. ---- Cut Here and unpack ----
  11. #!/bin/sh
  12. # this is part 21 of a multipart archive
  13. # do not concatenate these parts, unpack them in order with /bin/sh
  14. # file src/returnadd.c continued
  15. #
  16. CurArch=21
  17. if test ! -r s2_seq_.tmp
  18. then echo "Please unpack part 1 first!"
  19.      exit 1; fi
  20. ( read Scheck
  21.   if test "$Scheck" != $CurArch
  22.   then echo "Please unpack part $Scheck next!"
  23.        exit 1;
  24.   else exit 0; fi
  25. ) < s2_seq_.tmp || exit 1
  26. echo "x - Continuing file src/returnadd.c"
  27. sed 's/^X//' << 'SHAR_EOF' >> src/returnadd.c
  28. X
  29. X    buffer[0] = '\0';
  30. X
  31. X    ok = (int) (fgets(buf2, SLEN, mailfile) != NULL);
  32. X    if (ok)
  33. X      if(buf2[strlen(buf2)-1] == '\n') lines--; /* got a full line */
  34. X
  35. X    while (ok && lines) {
  36. X      buf[0] = '\0';
  37. X      strncat(buf, buf2, SLEN);
  38. X      ok = (int) (fgets(buf2, SLEN, mailfile) != NULL);
  39. X      if (ok)
  40. X        if(buf2[strlen(buf2)-1] == '\n') lines--; /* got a full line */
  41. X      while (ok && lines && whitespace(buf2[0])) {
  42. X        if (buf[strlen(buf)-1] == '\n')
  43. X          buf[strlen(buf)-1] = '\0';
  44. X        strncat(buf, buf2, (SLEN-strlen(buf)-1));
  45. X        ok = (int) (fgets(buf2, SLEN, mailfile) != NULL);
  46. X        if (ok)
  47. X          if(buf2[strlen(buf2)-1] == '\n') lines--; /* got a full line */
  48. X        }
  49. X
  50. X/* At this point, "buf" contains the unfolded header line, while "buf2" contains
  51. X   the next single line of text from the mail file */
  52. X
  53. X      if (first_word(buf, "From ")) 
  54. X        sscanf(buf, "%*s %s", hold_return);
  55. X      else if (first_word(buf, ">From")) {
  56. X        sscanf(buf,"%*s %s %*s %*s %*s %*s %*s %*s %*s %s %s", 
  57. X               name1, name2, alt_name2);
  58. X        if (strcmp(name2, "from") == 0)        /* remote from xyz  */
  59. X          strcpy(name2, alt_name2);
  60. X        else if (strcmp(name2, "by") == 0)    /* forwarded by xyz */
  61. X          strcpy(name2, alt_name2);
  62. X        add_site(buffer, name2, lastname);
  63. X      }
  64. X
  65. X#ifdef USE_EMBEDDED_ADDRESSES
  66. X
  67. X      else if (first_word(buf, "From:")) {
  68. X        get_address_from("From:", buf, hold_return);
  69. X        buffer[0] = '\0';
  70. X          }
  71. X          else if (first_word(buf, "Reply-To:")) {
  72. X        get_address_from("Reply-To:", buf, buffer);
  73. X        return(using_to);
  74. X          }
  75. X
  76. X#endif
  77. X
  78. X      else if (strlen(buf) < 2)    /* done with header */
  79. X            lines = 0; /* let's get outta here!  We're done!!! */
  80. X    }
  81. X
  82. X    if (buffer[0] == '\0')
  83. X      strcpy(buffer, hold_return); /* default address! */
  84. X    else
  85. X      add_site(buffer, name1, lastname);    /* get the user name too! */
  86. X
  87. X    if (first_word(buffer, "To:")) {    /* for backward compatibility */
  88. X      get_existing_address(buffer,msgnum);
  89. X      using_to = TRUE;
  90. X    }
  91. X    else {
  92. X      /*
  93. X       * KLUDGE ALERT - DANGER WILL ROBINSON
  94. X       * We can't just leave a bare login name as the return address,
  95. X       * or it will be alias-expanded.
  96. X       * So we qualify it with the current host name (and, maybe, domain).
  97. X       * Sigh.
  98. X       */
  99. X
  100. X      if (chloc(buffer, '@') < 0
  101. X       && chloc(buffer, '%') < 0
  102. X       && chloc(buffer, '!') < 0)
  103. X      {
  104. X#ifdef INTERNET
  105. X        sprintf(buffer + strlen(buffer), "@%s", hostfullname);
  106. X#else
  107. X        strcpy(buf, buffer);
  108. X        sprintf(buffer, "%s!%s", hostname, buf);
  109. X#endif
  110. X      }
  111. X
  112. X      /*
  113. X       * If we have a space character,
  114. X       * or we DON'T have '!' or '@' chars,
  115. X       * append the user-readable name.
  116. X       */
  117. X      if (chloc(headers[msgnum]->from, ' ') >= 0 ||
  118. X          (chloc(headers[msgnum]->from, '!') < 0 &&
  119. X           chloc(headers[msgnum]->from, '@') < 0)) {
  120. X           sprintf(buffer + strlen(buffer),
  121. X               " (%s)", headers[msgnum]->from);
  122. X          }
  123. X    }
  124. X
  125. X    return(using_to);
  126. X}
  127. X
  128. Xget_existing_address(buffer, msgnum)
  129. Xchar *buffer;
  130. Xint msgnum;
  131. X{
  132. X    /** This routine is called when the message being responded to has
  133. X        "To:xyz" as the return address, signifying that this message is
  134. X        an automatically saved copy of a message previously sent.  The
  135. X        correct to address can be obtained fairly simply by reading the
  136. X        To: header from the message itself and (blindly) copying it to
  137. X        the given buffer.  Note that this header can be either a normal
  138. X        "To:" line (Elm) or "Originally-To:" (previous versions e.g.Msg)
  139. X    **/
  140. X
  141. X    char mybuf[LONG_STRING];
  142. X    register char ok = 1, in_to = 0;
  143. X
  144. X    buffer[0] = '\0';
  145. X
  146. X    /** first off, let's get to the beginning of the message... **/
  147. X
  148. X    if(msgnum < 0 || msgnum >= message_count || message_count < 1) {
  149. X      dprint(1, (debugfile,
  150. X        "Error: %d not a valid message number message_count = %d (%s)",
  151. X        msgnum, message_count, "get_existing_address"));
  152. X      error1("%d not a valid message number!");
  153. X      return;
  154. X    }
  155. X        if (fseek(mailfile, headers[msgnum]->offset, 0) == -1) {
  156. X        dprint(1, (debugfile, 
  157. X            "Error: seek %ld bytes into file hit errno %s (%s)", 
  158. X            headers[msgnum]->offset, error_name(errno), 
  159. X            "get_existing_address"));
  160. X        error2("Couldn't seek %d bytes into the file (%s).",
  161. X               headers[msgnum]->offset, error_name(errno));
  162. X        return;
  163. X        }
  164. X        /** okay!  Now we're there!  **/
  165. X
  166. X        while (ok) {
  167. X          ok = (int) (fgets(mybuf, LONG_STRING, mailfile) != NULL);
  168. X      no_ret(mybuf);    /* remove return character */
  169. X
  170. X          if (first_word(mybuf, "To: ")) {
  171. X        in_to = TRUE;
  172. X        strcpy(buffer, (char *) mybuf + strlen("To: "));
  173. X          }
  174. X      else if (first_word(mybuf, "Original-To:")) {
  175. X        in_to = TRUE;
  176. X        strcpy(buffer, (char *) mybuf + strlen("Original-To:"));
  177. X      }
  178. X      else if (in_to && whitespace(mybuf[0])) {
  179. X        strcat(buffer, " ");        /* tag a space in   */
  180. X        strcat(buffer, (char *) mybuf + 1);    /* skip 1 whitespace */
  181. X      }
  182. X      else if (strlen(mybuf) < 2)
  183. X        return;                /* we're done for!  */
  184. X      else
  185. X        in_to = 0;
  186. X      }
  187. X}
  188. SHAR_EOF
  189. echo "File src/returnadd.c is complete"
  190. chmod 0444 src/returnadd.c || echo "restore of src/returnadd.c fails"
  191. echo "x - extracting src/save_opts.c (Text)"
  192. sed 's/^X//' << 'SHAR_EOF' > src/save_opts.c &&
  193. X
  194. Xstatic char rcsid[] = "@(#)$Id: save_opts.c,v 4.1 90/04/28 22:44:00 syd Exp $";
  195. X
  196. X/*******************************************************************************
  197. X *  The Elm Mail System  -  $Revision: 4.1 $   $State: Exp $
  198. X *
  199. X *             Copyright (c) 1986, 1987 Dave Taylor
  200. X *             Copyright (c) 1988, 1989, 1990 USENET Community Trust
  201. X *******************************************************************************
  202. X * Bug reports, patches, comments, suggestions should be sent to:
  203. X *
  204. X *    Syd Weinstein, Elm Coordinator
  205. X *    elm@DSI.COM            dsinc!elm
  206. X *
  207. X *******************************************************************************
  208. X * $Log:    save_opts.c,v $
  209. X * Revision 4.1  90/04/28  22:44:00  syd
  210. X * checkin of Elm 2.3 as of Release PL0
  211. X * 
  212. X *
  213. X ******************************************************************************/
  214. X
  215. X/** This file contains the routine needed to allow the users to change the
  216. X    Elm parameters and then save the configuration in a ".elm/elmrc" file in
  217. X    their home directory.  With any luck this will allow them never to have
  218. X    to actually EDIT the file!!
  219. X
  220. X**/
  221. X
  222. X#include "headers.h"
  223. X#include <errno.h>
  224. X
  225. X#undef onoff
  226. X#define   onoff(n)    (n == 1? "ON":"OFF")
  227. X
  228. X#define absolute(x)        ((x) < 0? -(x) : (x))
  229. X
  230. Xextern  int errno;
  231. Xextern char version_buff[];
  232. X
  233. Xchar *error_name(), *sort_name();
  234. Xlong  ftell();
  235. X
  236. X#include "save_opts.h"
  237. X
  238. XFILE *elminfo;        /* informational file as needed... */
  239. X
  240. Xsave_options()
  241. X{
  242. X    /** Save the options currently specified to a file.  This is a
  243. X        fairly complex routine since it tries to put in meaningful
  244. X        comments and such as it goes along.  The comments are
  245. X        extracted from the file ELMRC_INFO as defined in the sysdefs.h
  246. X        file.  THAT file has the format;
  247. X
  248. X        varname
  249. X          <comment>
  250. X          <comment>
  251. X        <blank line>
  252. X
  253. X        and each comment is written ABOVE the variable to be added.  This
  254. X        program also tries to make 'pretty' stuff like the alternatives
  255. X        and such.
  256. X    **/
  257. X
  258. X    FILE *newelmrc; 
  259. X    char  oldfname[SLEN], newfname[SLEN];
  260. X
  261. X    sprintf(newfname, "%s/%s", home, elmrcfile);
  262. X    sprintf(oldfname, "%s/%s", home, old_elmrcfile);
  263. X
  264. X    /** first off, let's see if they already HAVE a .elm/elmrc file **/
  265. X
  266. X    save_file_stats(newfname);
  267. X    if (access(newfname, ACCESS_EXISTS) != -1) {
  268. X      /** YES!  Copy it to the file ".old.elmrc".. **/
  269. X      if (rename(newfname, oldfname) < 0)
  270. X        dprint(2, (debugfile, "Unable to rename %s to %s\n", 
  271. X           newfname, oldfname));
  272. X      (void) chown(oldfname, userid, groupid);
  273. X
  274. X    }
  275. X
  276. X    /** now let's open the datafile if we can... **/
  277. X
  278. X    if ((elminfo = fopen(ELMRC_INFO, "r")) == NULL) 
  279. X      error1("Warning: saving without comments! Can't get to %s.", 
  280. X          ELMRC_INFO);
  281. X
  282. X    /** next, open the new .elm/elmrc file... **/
  283. X
  284. X    if ((newelmrc = fopen(newfname, "w")) == NULL) {
  285. X       error2("Can't save configuration! Can't write to %s [%s].",
  286. X           newfname, error_name(errno));
  287. X       return;
  288. X    }
  289. X    
  290. X    save_user_options(elminfo, newelmrc);
  291. X    restore_file_stats(newfname);
  292. X
  293. X    error1("Options saved in file %s.", newfname);
  294. X}
  295. X
  296. Xsave_user_options(elminfo_fd, newelmrc)
  297. XFILE *elminfo_fd, *newelmrc;
  298. X{
  299. X    /** save the information in the file.  If elminfo_fd == NULL don't look
  300. X        for comments!
  301. X    **/
  302. X
  303. X    if (elminfo_fd != NULL) 
  304. X      build_offset_table(elminfo_fd);
  305. X
  306. X    fprintf(newelmrc,     
  307. X          "#\n# .elm/elmrc - options file for the ELM mail system\n#\n");
  308. X
  309. X    if (strlen(full_username) > 0)
  310. X      fprintf(newelmrc, "# Saved automatically by ELM %s for %s\n#\n\n",
  311. X          version_buff, full_username);
  312. X    else
  313. X      fprintf(newelmrc, "# Saved automatically by ELM %s\n#\n\n", version_buff);
  314. X    fprintf(newelmrc,"# For yes/no settings with ?, ON means yes, OFF means no\n\n");
  315. X
  316. X    save_option_string(CALENDAR, raw_calendar_file, newelmrc, FALSE);
  317. X    save_option_string(EDITOR, raw_editor, newelmrc, FALSE);
  318. X
  319. X    save_option_char(ESCAPECHAR, escape_char, newelmrc);
  320. X
  321. X    save_option_string(FULLNAME, full_username, newelmrc, FALSE);
  322. X    save_option_string(RECEIVEDMAIL, raw_recvdmail, newelmrc, FALSE);
  323. X    save_option_string(MAILDIR, raw_folders, newelmrc, FALSE);
  324. X    save_option_string(TMPDIR, temp_dir, newelmrc, FALSE);
  325. X    save_option_string(PAGER, raw_pager, newelmrc, FALSE);
  326. X    save_option_string(PREFIX, prefixchars, newelmrc, TRUE);
  327. X    save_option_string(PRINT, raw_printout, newelmrc, FALSE);
  328. X    save_option_string(SENTMAIL, raw_sentmail, newelmrc, FALSE);
  329. X    save_option_string(SHELL, raw_shell, newelmrc, FALSE);
  330. X
  331. X    save_option_string(LOCALSIGNATURE, raw_local_signature,
  332. X       newelmrc, FALSE);
  333. X    save_option_string(REMOTESIGNATURE, raw_remote_signature,
  334. X      newelmrc, FALSE);
  335. X    save_option_on_off(SIGDASHES, sig_dashes, newelmrc);
  336. X
  337. X    save_option_sort(SORTBY, newelmrc);
  338. X
  339. X    save_option_on_off(ALWAYSDELETE, always_del, newelmrc);
  340. X    save_option_on_off(ALWAYSSTORE, always_store, newelmrc);
  341. X    save_option_on_off(ALWAYSKEEP, always_keep, newelmrc);
  342. X    save_option_on_off(ARROW, arrow_cursor, newelmrc);
  343. X    save_option_on_off(ASK, question_me, newelmrc);
  344. X    save_option_on_off(ASKCC, prompt_for_cc, newelmrc);
  345. X    save_option_string(ATTRIBUTION, attribution, newelmrc, FALSE);
  346. X    save_option_on_off(AUTOCOPY, auto_copy, newelmrc);
  347. X
  348. X    save_option_number(BOUNCEBACK, bounceback, newelmrc);
  349. X
  350. X    save_option_on_off(COPY, auto_cc, newelmrc);
  351. X    save_option_on_off(FORCENAME, force_name, newelmrc);
  352. X    save_option_on_off(FORMS, (allow_forms != NO), newelmrc);
  353. X    save_option_on_off(KEEPEMPTY, keep_empty_files, newelmrc);
  354. X    save_option_on_off(KEYPAD, hp_terminal, newelmrc);
  355. X    save_option_on_off(MENU, mini_menu, newelmrc);
  356. X    save_option_on_off(MOVEPAGE, move_when_paged, newelmrc);
  357. X    save_option_on_off(NAMES, names_only, newelmrc);
  358. X    save_option_on_off(NOHEADER, noheader, newelmrc);
  359. X    save_option_on_off(POINTNEW, point_to_new, newelmrc);
  360. X    save_option_on_off(PROMPTAFTER, prompt_after_pager, newelmrc);
  361. X    save_option_on_off(RESOLVE, resolve_mode, newelmrc);
  362. X    save_option_on_off(SAVENAME, save_by_name, newelmrc);
  363. X    save_option_on_off(SOFTKEYS, hp_softkeys, newelmrc);
  364. X
  365. X    save_option_number(TIMEOUT, (int) timeout, newelmrc);
  366. X
  367. X    save_option_on_off(TITLES, title_messages, newelmrc);
  368. X
  369. X    save_option_number(USERLEVEL, user_level, newelmrc);
  370. X
  371. X    save_option_on_off(WARNINGS, warnings, newelmrc);
  372. X    save_option_on_off(WEED, filter, newelmrc);
  373. X
  374. X    save_option_weedlist(WEEDOUT, newelmrc);
  375. X    save_option_alternatives(ALTERNATIVES, alternative_addresses, newelmrc);
  376. X
  377. X    fclose(newelmrc);
  378. X    if ( elminfo_fd != NULL ) {
  379. X        fclose(elminfo_fd);
  380. X    }
  381. X}
  382. X
  383. Xsave_option_string(iindex, value, fd, underscores)
  384. Xint iindex, underscores;
  385. Xchar *value;
  386. XFILE *fd;
  387. X{
  388. X    /** Save a string option to the file... only subtlety is when we
  389. X        save strings with spaces in 'em - translate to underscores!
  390. X    **/
  391. X
  392. X    char     buffer[SLEN], *bufptr;
  393. X    
  394. X    add_comment(iindex, fd);
  395. X    
  396. X    strcpy(buffer, value);
  397. X
  398. X    if (underscores)
  399. X      for (bufptr = buffer; *bufptr; bufptr++)
  400. X        if (*bufptr == SPACE) *bufptr = '_';
  401. X
  402. X    fprintf(fd, "%s = %s\n\n", save_info[iindex].name, buffer);
  403. X}
  404. X       
  405. Xsave_option_sort(iindex, fd)
  406. Xint iindex;
  407. XFILE *fd;
  408. X{
  409. X    /** save the current sorting option to a file **/
  410. X
  411. X    add_comment(iindex, fd);
  412. X
  413. X    fprintf(fd, "%s = %s\n\n", save_info[iindex].name,
  414. X        sort_name(SHORT));
  415. X}
  416. X
  417. Xsave_option_char(iindex, value, fd)
  418. Xint iindex;
  419. Xchar value;
  420. XFILE *fd;
  421. X{
  422. X    /** Save a character option to the file **/
  423. X
  424. X    add_comment(iindex, fd);
  425. X    
  426. X    fprintf(fd, "%s = %c\n\n", save_info[iindex].name, value);
  427. X}
  428. X
  429. Xsave_option_number(iindex, value, fd)
  430. Xint iindex, value;
  431. XFILE *fd;
  432. X{
  433. X    /** Save a binary option to the file - boy is THIS easy!! **/
  434. X
  435. X    add_comment(iindex, fd);
  436. X    
  437. X    fprintf(fd, "%s = %d\n\n", save_info[iindex].name, value);
  438. X}
  439. X
  440. Xsave_option_on_off(iindex, value, fd)
  441. Xint iindex, value;
  442. XFILE *fd;
  443. X{
  444. X    /** Save a binary option to the file - boy is THIS easy!! **/
  445. X
  446. X    add_comment(iindex, fd);
  447. X    
  448. X    fprintf(fd, "%s = %s\n\n", save_info[iindex].name, onoff(value));
  449. X}
  450. X
  451. Xsave_option_weedlist(iindex, fd)
  452. Xint iindex;
  453. XFILE *fd;
  454. X{
  455. X    /** save a list of weedout headers to the file **/
  456. X
  457. X    int length_so_far = 0, i;
  458. X
  459. X    add_comment(iindex, fd);
  460. X
  461. X    length_so_far = strlen(save_info[iindex].name) + 4;
  462. X
  463. X    fprintf(fd, "%s = ", save_info[iindex].name);
  464. X
  465. X    /** first off, skip till we get past the default list **/
  466. X
  467. X    for (i = 0; i < weedcount; i++) 
  468. X      if (strcmp(weedlist[i],"*end-of-defaults*") == 0)
  469. X        break;
  470. X
  471. X    while (i < weedcount) {
  472. X      if (strcmp(weedlist[i], "*end-of-defaults*") != 0)
  473. X        break;
  474. X      i++;    /* and get PAST it too! */
  475. X    }
  476. X
  477. X    while (i < weedcount) {
  478. X      if (strlen(weedlist[i]) + length_so_far > 78) {
  479. X        fprintf(fd, "\n\t");
  480. X        length_so_far = 8;
  481. X      }
  482. X      fprintf(fd, "\"%s\" ", weedlist[i]);
  483. X      length_so_far += (strlen(weedlist[i]) + 4);
  484. X      i++;
  485. X    }
  486. X    fprintf(fd, "\t\"*end-of-user-headers*\"\n\n");
  487. X}
  488. X
  489. Xsave_option_alternatives(iindex, list, fd)
  490. Xint iindex;
  491. Xstruct addr_rec *list;
  492. XFILE *fd;
  493. X{
  494. X    /** save a list of options to the file **/
  495. X    int length_so_far = 0;
  496. X    struct addr_rec     *alternate;
  497. X
  498. X    if (list == NULL) return;    /* nothing to do! */
  499. X
  500. X    add_comment(iindex, fd);
  501. X
  502. X    alternate = list;    /* don't LOSE the top!! */
  503. X
  504. X    length_so_far = strlen(save_info[iindex].name) + 4;
  505. X
  506. X    fprintf(fd, "%s = ", save_info[iindex].name);
  507. X
  508. X    while (alternate != NULL) {
  509. X      if (strlen(alternate->address) + length_so_far > 78) {
  510. X        fprintf(fd, "\n\t");
  511. X        length_so_far = 8;
  512. X      }
  513. X      fprintf(fd, "%s  ", alternate->address);
  514. X      length_so_far += (strlen(alternate->address) + 3);
  515. X      alternate = alternate->next;
  516. X    }
  517. X    fprintf(fd, "\n\n");
  518. X}
  519. X
  520. Xadd_comment(iindex, fd)
  521. Xint iindex;
  522. XFILE *fd;
  523. X{    
  524. X    /** get to and add the comment to the file **/
  525. X    char buffer[SLEN];
  526. X
  527. X    /** first off, add the comment from the comment file, if available **/
  528. X
  529. X    if (save_info[iindex].offset > 0L) {
  530. X      if (fseek(elminfo, save_info[iindex].offset, 0) == -1) {
  531. X        dprint(1,(debugfile,
  532. X           "** error %s seeking to %ld in elm-info file!\n",
  533. X           error_name(errno), save_info[iindex].offset));
  534. X      }
  535. X      else while (fgets(buffer, SLEN, elminfo) != NULL) {
  536. X        if (buffer[0] != '#') 
  537. X           break;
  538. X        else
  539. X           fprintf(fd, "%s", buffer);
  540. X      }
  541. X    }
  542. X}
  543. X
  544. Xbuild_offset_table(elminfo_fd)
  545. XFILE *elminfo_fd;
  546. X{
  547. X    /** read in the info file and build the table of offsets.
  548. X        This is a rather laborious puppy, but at least we can
  549. X        do a binary search through the array for each element and
  550. X        then we have it all at once!
  551. X    **/
  552. X
  553. X    char line_buffer[SLEN];
  554. X    
  555. X    while (fgets(line_buffer, SLEN, elminfo_fd) != NULL) {
  556. X      if (strlen(line_buffer) > 1)
  557. X        if (line_buffer[0] != '#' && !whitespace(line_buffer[0])) {
  558. X           no_ret(line_buffer);
  559. X           if (find_and_store_loc(line_buffer, ftell(elminfo_fd))) {
  560. X             dprint(1, (debugfile,"** Couldn't find and store \"%s\" **\n", 
  561. X             line_buffer));
  562. X           }
  563. X        }
  564. X    }
  565. X}
  566. X
  567. Xfind_and_store_loc(name, offset)
  568. Xchar *name;
  569. Xlong  offset;
  570. X{
  571. X    /** given the name and offset, find it in the table and store it **/
  572. X
  573. X    int first = 0, last, middle, compare;
  574. X
  575. X    last = NUMBER_OF_SAVEABLE_OPTIONS;
  576. X
  577. X    while (first <= last) {
  578. X
  579. X      middle = (first+last) / 2;
  580. X
  581. X      if ((compare = strcmp(name, save_info[middle].name)) < 0) /* a < b */
  582. X        last = middle - 1;
  583. X      else if (compare == 0) {                    /* a = b */
  584. X        save_info[middle].offset = offset;
  585. X        return(0);
  586. X      }
  587. X      else  /* greater */                        /* a > b */
  588. X        first = middle + 1; 
  589. X    }
  590. X
  591. X    return(-1);
  592. X}
  593. SHAR_EOF
  594. chmod 0444 src/save_opts.c || echo "restore of src/save_opts.c fails"
  595. echo "x - extracting src/savecopy.c (Text)"
  596. sed 's/^X//' << 'SHAR_EOF' > src/savecopy.c &&
  597. X
  598. Xstatic char rcsid[] = "@(#)$Id: savecopy.c,v 4.1 90/04/28 22:44:02 syd Exp $";
  599. X
  600. X/*******************************************************************************
  601. X *  The Elm Mail System  -  $Revision: 4.1 $   $State: Exp $
  602. X *
  603. X *             Copyright (c) 1986, 1987 Dave Taylor
  604. X *             Copyright (c) 1988, 1989, 1990 USENET Community Trust
  605. X *******************************************************************************
  606. X * Bug reports, patches, comments, suggestions should be sent to:
  607. X *
  608. X *    Syd Weinstein, Elm Coordinator
  609. X *    elm@DSI.COM            dsinc!elm
  610. X *
  611. X *******************************************************************************
  612. X * $Log:    savecopy.c,v $
  613. X * Revision 4.1  90/04/28  22:44:02  syd
  614. X * checkin of Elm 2.3 as of Release PL0
  615. X * 
  616. X *
  617. X ******************************************************************************/
  618. X
  619. X/** Save a copy of the specified message in a folder.
  620. X
  621. X**/
  622. X
  623. X#include "headers.h"
  624. X#ifdef I_TIME
  625. X#  include <time.h>
  626. X#endif
  627. X#ifdef I_SYSTIME
  628. X#  include <sys/time.h>
  629. X#endif
  630. X
  631. X#include <errno.h>
  632. X
  633. Xchar *format_long();
  634. Xchar *error_name(), *error_description();
  635. Xchar *ctime();
  636. X
  637. Xextern char in_reply_to[SLEN];    /* In-Reply-To: string */
  638. Xextern int errno;
  639. X
  640. Xchar *strcat(), *strcpy();
  641. Xunsigned long sleep();
  642. Xlong  time();
  643. X
  644. Xsave_copy(to, cc, bcc, filename, copy_file, form)
  645. Xchar *to, *cc, *bcc, *filename, *copy_file;
  646. Xint form;
  647. X{
  648. X    /** This routine appends a copy of the outgoing message to the
  649. X        file specified.  **/
  650. X
  651. X    FILE *save,        /* file id for file to save to */
  652. X         *message,        /* file id for file with message body */
  653. X         *write_header_info();
  654. X    char  buffer[SLEN],    /* read buffer                */
  655. X          savename[SLEN];    /* name of file saving into    */
  656. X  
  657. X
  658. X    /* presume copy_file is okay as is for now */
  659. X    strcpy(savename, copy_file);
  660. X
  661. X    /* if save-by-name wanted */
  662. X    if((strcmp(copy_file, "=") == 0)  || (strcmp(copy_file, "=?") == 0)) {
  663. X
  664. X      get_return_name(to, buffer, TRUE);    /* determine 'to' login */
  665. X      if (strlen(buffer) == 0) {
  666. X
  667. X        /* can't get file name from 'to' -- use sent_mail instead */
  668. X        dprint(3, (debugfile,
  669. X        "Warning: get_return_name couldn't break down %s\n", to));
  670. X        error1(
  671. X"Cannot determine `to' name to save by! Saving to \"sent\" folder %s instead.",
  672. X          sent_mail);
  673. X        strcpy(savename, "<");
  674. X        sleep(3);
  675. X      } else
  676. X        sprintf(savename, "=%s", buffer);        /* good! */
  677. X    }
  678. X
  679. X    expand_filename(savename, TRUE);    /* expand special chars */
  680. X
  681. X    /* If saving conditionally by logname but folder doesn't exist
  682. X     * save to sent folder instead. */
  683. X    if((strcmp(copy_file, "=?") == 0)
  684. X          && (access(savename, ACCESS_EXISTS) != 0)) {
  685. X      dprint(5, (debugfile,
  686. X        "Conditional save by name: file %s doesn't exist - using \"<\".\n",
  687. X        savename));
  688. X      strcpy(savename, "<");
  689. X      expand_filename(savename, TRUE);
  690. X    }
  691. X
  692. X    if ((errno = can_open(savename, "a"))) {
  693. X      dprint(2, (debugfile,
  694. X      "Error: attempt to autosave to a file that can't be appended to!\n"));
  695. X      dprint(2, (debugfile, "\tfilename = \"%s\"\n", savename));
  696. X      dprint(2, (debugfile, "** %s - %s **\n", error_name(errno),
  697. X          error_description(errno)));
  698. X
  699. X      /* Lets try sent_mail before giving up */
  700. X      if(strcmp(sent_mail, savename) == 0) {
  701. X        /* we are ALREADY using sent_mail! */
  702. X        error1("Cannot save to %s!", savename);
  703. X        sleep(3);
  704. X        return(FALSE);
  705. X      }
  706. X
  707. X      if ((errno = can_open(sent_mail, "a"))) {
  708. X        dprint(2, (debugfile,
  709. X      "Error: attempt to autosave to a file that can't be appended to!\n"));
  710. X        dprint(2, (debugfile, "\tfilename = \"%s\"\n", sent_mail));
  711. X        dprint(2, (debugfile, "** %s - %s **\n", error_name(errno),
  712. X            error_description(errno)));
  713. X        error2("Cannot save to %s nor to \"sent\" folder %s!",
  714. X            savename, sent_mail);
  715. X        sleep(3);
  716. X        return(FALSE);
  717. X      }
  718. X      error2("Cannot save to %s! Saving to \"sent\" folder %s instead.",
  719. X          savename, sent_mail);
  720. X      sleep(3);
  721. X      strcpy(savename, sent_mail);
  722. X    }
  723. X
  724. X    save_file_stats(savename);
  725. X
  726. X    /* Write header */
  727. X    if ((save = write_header_info(savename, to, cc, bcc,
  728. X          form == YES, TRUE)) == NULL)
  729. X      return(FALSE);
  730. X
  731. X    /* Now add file with message as handed to mailer */
  732. X    if ((message = fopen(filename, "r")) == NULL) {
  733. X      fclose(save);
  734. X      dprint(1, (debugfile,
  735. X         "Error: Couldn't read folder %s (save_copy)\n", filename));
  736. X      dprint(1, (debugfile, "** %s - %s **\n", error_name(errno),
  737. X          error_description(errno)));
  738. X      error1("Couldn't read folder %s!", filename);
  739. X      sleep(3);
  740. X      return(FALSE);
  741. X    }
  742. X
  743. X        copy_message_across(message, save, TRUE);
  744. X
  745. X
  746. X    fclose(save);
  747. X    fclose(message);
  748. X
  749. X    restore_file_stats(savename);
  750. X
  751. X    return(TRUE);
  752. X}
  753. Xchar *
  754. Xcf_english(fn)
  755. Xchar *fn;
  756. X{
  757. X    /** Return "English" expansion for special copy file name abbreviations
  758. X    or just the file name  **/
  759. X
  760. X    if(!*fn)
  761. X      return("<no save>");
  762. X    else if(!fn[1]) {
  763. X      if(*fn == '=')
  764. X    return("<unconditionally save by name>");
  765. X      else if(*fn == '<')
  766. X    return("<\"sent\" folder>");
  767. X    } else if ((fn[0] == '=') && (fn[1] == '?'))
  768. X      return("<conditionally save by name>");
  769. X
  770. X    return(fn);
  771. X}
  772. X
  773. X#define NCF_PROMPT    "Save copy in (use '?' for help/to list folders): "
  774. Xint
  775. Xname_copy_file(fn)
  776. Xchar *fn;
  777. X{
  778. X    /** Prompt user for name of file for saving copy of outbound msg to.
  779. X    Return true if we need a redraw. **/
  780. X
  781. X    int redraw = 0;    /* set when we ask for help = need redraw */
  782. X    char buffer[SLEN], origbuffer[SLEN];
  783. X    static char helpmsg[LONG_STRING];
  784. X
  785. X    /* expand passed copy file name into English */
  786. X    strcpy(buffer, cf_english(fn));
  787. X
  788. X    /* prepare screen with instructions */
  789. X    MoveCursor(LINES-2, 0);
  790. X    CleartoEOS();
  791. X    PutLine0(LINES-2, 0, NCF_PROMPT);
  792. X
  793. X    while(1) {
  794. X
  795. X      /* get file name from user input */
  796. X      strcpy(origbuffer, buffer);
  797. X      optionally_enter(buffer, LINES-2, strlen(NCF_PROMPT), FALSE, FALSE);
  798. X
  799. X      if(strcmp(buffer, "?") != 0) { /* got what we wanted - non-help choice */
  800. X
  801. X    if(strcmp(origbuffer, buffer) != 0)
  802. X      /* user changed from our English expansion 
  803. X       * so we'd better copy user input to fn
  804. X       */
  805. X      strcpy(fn, buffer);
  806. X
  807. X    /* else user presumably left our English expansion - no change in fn */
  808. X
  809. X    /* display English expansion of new user input a while */
  810. X    PutLine1(LINES-2, strlen(NCF_PROMPT), cf_english(fn));
  811. X    MoveCursor(LINES, 0);
  812. X    sleep(1);
  813. X    MoveCursor(LINES-2, 0);
  814. X    CleartoEOS();
  815. X
  816. X    return(redraw);
  817. X      }
  818. X
  819. X      /* give help and list folders */
  820. X      redraw = TRUE;
  821. X      if(!*helpmsg)     /* help message not yet formulated */
  822. X        sprintf(helpmsg,
  823. X    "\n\r%s\n\r%s%s%s\n\r%s\n\r%s\n\r%s\n\r%s\n\r%s\n\r\n\r",
  824. X    "Enter: <nothing> to not save a copy of the message,",
  825. X    "       '<'       to save in your \"sent\" folder (", sent_mail, "),",
  826. X    "       '='       to save by name (the folder name depends on whom the",
  827. X    "                     message is to, in the end),",
  828. X    "       '=?'      to save by name if the folder already exists,",
  829. X    "                     and if not, to your \"sent\" folder,",
  830. X    "       or a filename (a leading '=' denotes your folder directory).");
  831. X
  832. X      list_folders(4, helpmsg);
  833. X      PutLine0(LINES-2, 0, NCF_PROMPT);
  834. X
  835. X      /* restore as default to English version of the passed copy file name */
  836. X      strcpy(buffer, cf_english(fn));
  837. X
  838. X    }
  839. X}
  840. SHAR_EOF
  841. chmod 0444 src/savecopy.c || echo "restore of src/savecopy.c fails"
  842. echo "x - extracting src/screen.c (Text)"
  843. sed 's/^X//' << 'SHAR_EOF' > src/screen.c &&
  844. X
  845. Xstatic char rcsid[] = "@(#)$Id: screen.c,v 4.1 90/04/28 22:44:04 syd Exp $";
  846. X
  847. X/*******************************************************************************
  848. X *  The Elm Mail System  -  $Revision: 4.1 $   $State: Exp $
  849. X *
  850. X *             Copyright (c) 1986, 1987 Dave Taylor
  851. X *             Copyright (c) 1988, 1989, 1990 USENET Community Trust
  852. X *******************************************************************************
  853. X * Bug reports, patches, comments, suggestions should be sent to:
  854. X *
  855. X *    Syd Weinstein, Elm Coordinator
  856. X *    elm@DSI.COM            dsinc!elm
  857. X *
  858. X *******************************************************************************
  859. X * $Log:    screen.c,v $
  860. X * Revision 4.1  90/04/28  22:44:04  syd
  861. X * checkin of Elm 2.3 as of Release PL0
  862. X * 
  863. X *
  864. X ******************************************************************************/
  865. X
  866. X/**  screen display routines for ELM program 
  867. X
  868. X**/
  869. X
  870. X#include "headers.h"
  871. X
  872. X#define  minimum(a,b)    ((a) < (b) ? (a) : (b))
  873. X
  874. Xstatic   int  last_current     = -1;
  875. X
  876. Xchar *strcpy(), *strncpy(), *nameof(), *show_status(), *index();
  877. X
  878. Xextern char version_buff[];
  879. X
  880. Xshowscreen()
  881. X{
  882. X
  883. X    ClearScreen();
  884. X
  885. X    update_title();
  886. X
  887. X    last_header_page = -1;         /* force a redraw regardless */
  888. X    show_headers();
  889. X
  890. X    if (mini_menu)
  891. X      show_menu();
  892. X
  893. X    show_last_error();
  894. X    
  895. X    if (hp_terminal) 
  896. X      define_softkeys(MAIN);
  897. X}
  898. X
  899. Xupdate_title()
  900. X{
  901. X    /** display a new title line, probably due to new mail arriving **/
  902. X
  903. X    char buffer[SLEN];
  904. X
  905. X    if (selected)
  906. X      sprintf(buffer, 
  907. X          "%s is '%s' with %d shown out of %d [ELM %s]",
  908. X          (folder_type == SPOOL ? "Mailbox" : "Folder"),
  909. X          nameof(cur_folder), selected, message_count, version_buff);
  910. X    else
  911. X      sprintf(buffer, "%s is '%s' with %d message%s [ELM %s]",
  912. X          (folder_type == SPOOL ? "Mailbox" : "Folder"),
  913. X          nameof(cur_folder), message_count,
  914. X          plural(message_count), version_buff);
  915. X
  916. X    ClearLine(1);
  917. X
  918. X    Centerline(1, buffer);
  919. X}
  920. X
  921. Xshow_menu()
  922. X{
  923. X    /** write main system menu... **/
  924. X
  925. X    if (user_level == 0) {    /* a rank beginner.  Give less options  */
  926. X      Centerline(LINES-7,
  927. X  "You can use any of the following commands by pressing the first character;");
  928. X          Centerline(LINES-6,
  929. X"d)elete or u)ndelete mail,  m)ail a message,  r)eply or f)orward mail,  q)uit");
  930. X      Centerline(LINES-5,
  931. X  "To read a message, press <return>.  j = move down, k = move up, ? = help");
  932. X    } else {
  933. X    Centerline(LINES-7,
  934. X  "|=pipe, !=shell, ?=help, <n>=set current to n, /=search pattern");
  935. X        Centerline(LINES-6,
  936. X"a)lias, C)opy, c)hange folder, d)elete, e)dit, f)orward, g)roup reply, m)ail,"
  937. X); 
  938. X    Centerline(LINES-5,
  939. X  "n)ext, o)ptions, p)rint, q)uit, r)eply, s)ave, t)ag, u)ndelete, or e(x)it");
  940. X    }
  941. X}
  942. X
  943. Xint
  944. Xshow_headers()
  945. X{
  946. X    /** Display page of headers (10) if present.  First check to 
  947. X        ensure that header_page is in bounds, fixing silently if not.
  948. X        If out of bounds, return zero, else return non-zero 
  949. X        Modified to only show headers that are "visible" to ze human
  950. X        person using ze program, eh?
  951. X    **/
  952. X
  953. X    register int this_msg = 0, line = 4, last = 0, last_line, 
  954. X             displayed = 0, using_to;
  955. X    char newfrom[SLEN], buffer[SLEN];
  956. X    
  957. X    if (fix_header_page())
  958. X      return(FALSE);
  959. X
  960. X    if (selected) {
  961. X      if ((header_page*headers_per_page) > selected)
  962. X        return(FALSE);     /* too far! too far! */
  963. X
  964. X      this_msg = visible_to_index(header_page * headers_per_page + 1);
  965. X      displayed = header_page * headers_per_page;
  966. X
  967. X      last = displayed+headers_per_page;
  968. X
  969. X    }
  970. X    else {
  971. X      if (header_page == last_header_page)     /* nothing to do! */
  972. X        return(FALSE);
  973. X
  974. X      /** compute last header to display **/
  975. X  
  976. X      this_msg = header_page * headers_per_page;
  977. X      last = this_msg + (headers_per_page - 1);
  978. X    }
  979. X
  980. X    if (last >= message_count) last = message_count-1;
  981. X
  982. X    /** Okay, now let's show the header page! **/
  983. X
  984. X    ClearLine(line);    /* Clear the top line... */
  985. X
  986. X    MoveCursor(line, 0);    /* and move back to the top of the page... */
  987. X
  988. X    while ((selected && displayed < last) || this_msg <= last) {
  989. X      using_to = tail_of(headers[this_msg]->from, newfrom,
  990. X        headers[this_msg]->to); 
  991. X
  992. X      if (this_msg == current-1) 
  993. X        build_header_line(buffer, headers[this_msg], this_msg+1,
  994. X                TRUE, newfrom, using_to);
  995. X      else
  996. X        build_header_line(buffer, headers[this_msg], 
  997. X                this_msg+1, FALSE, newfrom, using_to);
  998. X      if (selected) 
  999. X        displayed++;
  1000. X
  1001. X      if (this_msg == current-1 && has_highlighting && ! arrow_cursor) {
  1002. X          StartInverse();
  1003. X          Write_to_screen("%s\n\r", 1, buffer);    /* avoid '%' probs */
  1004. X          EndInverse();
  1005. X      } else
  1006. X          Write_to_screen("%s\n\r", 1, buffer);    /* avoid '%' probs */
  1007. X      CleartoEOLN();
  1008. X      line++;        /* for clearing up in a sec... */
  1009. X
  1010. X      if (selected) {
  1011. X        if ((this_msg = next_message(this_msg, FALSE)) < 0)
  1012. X          break;    /* GET OUTTA HERE! */
  1013. X
  1014. X        /* the preceeding looks gross because we're using an INDEX
  1015. X           variable to pretend to be a "current" counter, and the 
  1016. X           current counter is always 1 greater than the actual 
  1017. X           index.  Does that make sense??
  1018. X         */
  1019. X      }
  1020. X      else
  1021. X        this_msg++;                    /* even dumber...  */
  1022. X    }
  1023. X
  1024. X    /* clear unused lines */
  1025. X
  1026. X    if (mini_menu)
  1027. X      last_line = LINES-8;
  1028. X    else
  1029. X      last_line = LINES-4;
  1030. X
  1031. X    while (line < last_line) {
  1032. X      CleartoEOLN();
  1033. X      NewLine();
  1034. X      line++;
  1035. X    }
  1036. X
  1037. X    display_central_message();
  1038. X
  1039. X    last_current = current;
  1040. X    last_header_page = header_page;
  1041. X
  1042. X    return(TRUE);
  1043. X}
  1044. X
  1045. Xshow_current()
  1046. X{
  1047. X    /** Show the new header, with all the usual checks **/
  1048. X
  1049. X    register int first = 0, last = 0, last_line, new_line, using_to;
  1050. X    char     newfrom[SLEN], old_buffer[SLEN], new_buffer[SLEN];
  1051. X
  1052. X    (void) fix_header_page();    /* Who cares what it does? ;-) */
  1053. X
  1054. X    /** compute the first and last header on this page **/
  1055. X    first = header_page * headers_per_page + 1;
  1056. X    last  = first + (headers_per_page - 1);
  1057. X
  1058. X    /* if not a full page adjust last to be the real last */
  1059. X    if (selected && last > selected)
  1060. X      last = selected;
  1061. X    if (!selected && last > message_count) 
  1062. X      last = message_count;
  1063. X
  1064. X    /** okay, now let's show the pointers... **/
  1065. X
  1066. X    /** have we changed??? **/
  1067. X    if (current == last_current) 
  1068. X      return;
  1069. X
  1070. X    if (selected) {
  1071. X      last_line = ((compute_visible(last_current)-1) %
  1072. X             headers_per_page)+4;
  1073. X      new_line  = ((compute_visible(current)-1) % headers_per_page)+4;
  1074. X    } else {
  1075. X      last_line = ((last_current-1) % headers_per_page)+4;
  1076. X      new_line  = ((current-1) % headers_per_page)+4;
  1077. X    }
  1078. X    
  1079. X    if (has_highlighting && ! arrow_cursor) {
  1080. X  
  1081. X      using_to = tail_of(headers[current-1]->from, newfrom,
  1082. X        headers[current-1]->to); 
  1083. X      build_header_line(new_buffer, headers[current-1],  current,
  1084. X          TRUE, newfrom, using_to);
  1085. X
  1086. X      /* clear last current if it's in proper range */
  1087. X      if (last_current > 0        /* not a dummy value */
  1088. X          && compute_visible(last_current) <= last
  1089. X          && compute_visible(last_current) >= first) {
  1090. X
  1091. X        dprint(5, (debugfile, 
  1092. X          "\nlast_current = %d ... clearing [1] before we add [2]\n", 
  1093. X           last_current));
  1094. X        dprint(5, (debugfile, "first = %d, and last = %d\n\n",
  1095. X          first, last));
  1096. X
  1097. X        using_to = tail_of(headers[last_current-1]->from, newfrom,
  1098. X          headers[last_current-1]->to); 
  1099. X        build_header_line(old_buffer, headers[last_current-1], 
  1100. X         last_current, FALSE, newfrom, using_to);
  1101. X
  1102. X        ClearLine(last_line);
  1103. X        PutLine0(last_line, 0, old_buffer);
  1104. X      }
  1105. X      MoveCursor(new_line, 0);
  1106. X      StartInverse();
  1107. X      Write_to_screen("%s", 1, new_buffer);
  1108. X      EndInverse();
  1109. X    }
  1110. X    else {
  1111. X      if (on_page(last_current-1)) 
  1112. X        PutLine0(last_line,0,"  ");    /* remove old pointer... */
  1113. X      if (on_page(current-1))
  1114. X        PutLine0(new_line, 0,"->");
  1115. X    }
  1116. X    
  1117. X    last_current = current;
  1118. X}
  1119. X
  1120. Xbuild_header_line(buffer, entry, message_number, highlight, from, really_to)
  1121. Xchar *buffer;
  1122. Xstruct header_rec *entry;
  1123. Xint message_number, highlight, really_to;
  1124. Xchar *from;
  1125. X{
  1126. X    /** Build in buffer the message header ... entry is the current
  1127. X        message entry, 'from' is a modified (displayable) from line, 
  1128. X        'highlight' is either TRUE or FALSE, and 'message_number'
  1129. X        is the number of the message.
  1130. X    **/
  1131. X
  1132. X    /** Note: using 'strncpy' allows us to output as much of the
  1133. X        subject line as possible given the dimensions of the screen.
  1134. X        The key is that 'strncpy' returns a 'char *' to the string
  1135. X        that it is handing to the dummy variable!  Neat, eh? **/
  1136. X    
  1137. X    int who_width = 18, subj_width;
  1138. X    char *dot = index(from, '.');
  1139. X    char *bang = index(from, '!');
  1140. X
  1141. X    /* truncate 'from' to 18 characters -
  1142. X     * this includes the leading "To" if really_to is true.
  1143. X     * Note:
  1144. X     *    'from' is going to be of three forms
  1145. X     *        - full name (truncate on the right for readability)
  1146. X     *        - logname@machine (truncate on the right to preserve
  1147. X     *            logname over machine name
  1148. X     *        - machine!logname -- a more complex situation
  1149. X     *            If this form doesn't fit, either machine
  1150. X     *            or logname are long. If logname is long,
  1151. X     *            we can stand to loose part of it, so we
  1152. X     *            truncate on the right. If machine name is
  1153. X     *            long, we'd better truncate on the left,
  1154. X     *            to insure we get the logname. Now if the
  1155. X     *            machine name is long, it will have "." in
  1156. X     *            it.
  1157. X     *    Therfore, we truncate on the left if there is a "." and a "!"
  1158. X     *    in 'from', else we truncate on the right.
  1159. X     */
  1160. X
  1161. X    /* Note that one huge sprintf() is too hard for some compilers. */
  1162. X    sprintf(buffer, "%s%s%c%-3d %3.3s %-2d ",
  1163. X        (highlight && arrow_cursor)? "->" : "  ",
  1164. X        show_status(entry->status),
  1165. X        (entry->status & TAGGED?  '+' : ' '),
  1166. X            message_number,
  1167. X            entry->month, 
  1168. X        atoi(entry->day));
  1169. X
  1170. X    /* show "To " in a way that it can never be truncated. */
  1171. X    if (really_to) {
  1172. X      strcat(buffer, "To ");
  1173. X      who_width -= 3;
  1174. X    }
  1175. X
  1176. X    /* truncate 'from' on left if needed.
  1177. X     * sprintf will truncate on right afterward if needed. */
  1178. X    if ((strlen(from) > who_width) && dot && bang && (dot < bang)) {
  1179. X      from += (strlen(from) - who_width);
  1180. X    }
  1181. X
  1182. X    /* Set the subject display width.
  1183. X     * If it is too long, truncate it to fit.
  1184. X     * If it is highlighted but not with the arrow  cursor,
  1185. X     * expand it to fit so that the reverse video bar extends
  1186. X     * aesthetically the full length of the line.
  1187. X     */
  1188. X    if ((highlight && !arrow_cursor)
  1189. X        || (COLUMNS-44 < (subj_width =strlen(entry->subject))))
  1190. X        subj_width = COLUMNS-44;
  1191. X
  1192. X    /* complete line with sender, length and subject. */
  1193. X    sprintf(buffer + strlen(buffer), "%-*.*s (%d) %s%-*.*s",
  1194. X        /* give max and min width parameters for 'from' */
  1195. X        who_width,
  1196. X        who_width,
  1197. X        from,
  1198. X
  1199. X        entry->lines, 
  1200. X        (entry->lines / 1000   > 0? ""   :    /* spacing the  */
  1201. X          entry->lines / 100   > 0? " "  :    /* same for the */
  1202. X            entry->lines / 10  > 0? "  " :    /* lines in ()  */
  1203. X                                    "   "),     /*   [wierd]    */
  1204. X
  1205. X        subj_width, subj_width, entry->subject);
  1206. X}
  1207. X
  1208. Xint
  1209. Xfix_header_page()
  1210. X{
  1211. X    /** this routine will check and ensure that the current header
  1212. X        page being displayed contains messages!  It will silently
  1213. X        fix 'header-page' if wrong.  Returns TRUE if changed.  **/
  1214. X
  1215. X    int last_page, old_header;
  1216. X
  1217. X    old_header = header_page;
  1218. X
  1219. X    last_page = (int) ((message_count-1) / headers_per_page);
  1220. X    if (header_page > last_page) 
  1221. X      header_page = last_page;
  1222. X    else if (header_page < 0) 
  1223. X          header_page = 0;
  1224. X
  1225. X    return(old_header != header_page);
  1226. X}
  1227. X
  1228. Xint
  1229. Xon_page(message)
  1230. Xint message;
  1231. X{
  1232. X    /** Returns true iff the specified message is on the displayed page. **/
  1233. X
  1234. X    if (selected) message = compute_visible(message);
  1235. X
  1236. X    if (message >= header_page * headers_per_page)
  1237. X      if (message < ((header_page+1) * headers_per_page))
  1238. X        return(TRUE);
  1239. X
  1240. X    return(FALSE);
  1241. X}
  1242. X
  1243. Xchar *show_status(status)
  1244. Xint status;
  1245. X{
  1246. X    /** This routine returns a pair of characters indicative of
  1247. X        the status of this message.  The first character represents
  1248. X        the interim status of the message (e.g. the status within 
  1249. X        the mail system):
  1250. X
  1251. X        E = Expired message
  1252. X        N = New message
  1253. X        O = Unread old message    dsi mailx emulation addition
  1254. X        D = Deleted message
  1255. X        _ = (space) default 
  1256. X
  1257. X        and the second represents the permanent attributes of the
  1258. X        message:
  1259. X
  1260. X        C = Company Confidential message
  1261. X            U = Urgent (or Priority) message
  1262. X        P = Private message
  1263. X        A = Action associated with message
  1264. X        F = Form letter
  1265. X        _ = (space) default
  1266. X    **/
  1267. X
  1268. X    static char mybuffer[3];
  1269. X
  1270. X    /** the first character, please **/
  1271. X
  1272. X         if (status & DELETED)    mybuffer[0] = 'D';
  1273. X    else if (status & EXPIRED)    mybuffer[0] = 'E';
  1274. X    else if (status & NEW)        mybuffer[0] = 'N';
  1275. X    else if (status & UNREAD)    mybuffer[0] = 'O';
  1276. X    else                            mybuffer[0] = ' ';
  1277. X
  1278. X    /** and the second... **/
  1279. X
  1280. X         if (status & CONFIDENTIAL) mybuffer[1] = 'C';
  1281. X    else if (status & URGENT)       mybuffer[1] = 'U';
  1282. X    else if (status & PRIVATE)      mybuffer[1] = 'P';
  1283. X    else if (status & ACTION)       mybuffer[1] = 'A';
  1284. X    else if (status & FORM_LETTER)  mybuffer[1] = 'F';
  1285. X    else                     mybuffer[1] = ' ';
  1286. X
  1287. X    mybuffer[2] = '\0';
  1288. X
  1289. X    return( (char *) mybuffer);
  1290. X}
  1291. SHAR_EOF
  1292. chmod 0444 src/screen.c || echo "restore of src/screen.c fails"
  1293. echo "x - extracting src/showmsg.c (Text)"
  1294. sed 's/^X//' << 'SHAR_EOF' > src/showmsg.c &&
  1295. X
  1296. Xstatic char rcsid[] = "@(#)$Id: showmsg.c,v 4.1 90/04/28 22:44:06 syd Exp $";
  1297. X
  1298. X/*******************************************************************************
  1299. X *  The Elm Mail System  -  $Revision: 4.1 $   $State: Exp $
  1300. X *
  1301. X *             Copyright (c) 1986, 1987 Dave Taylor
  1302. X *             Copyright (c) 1988, 1989, 1990 USENET Community Trust
  1303. X *******************************************************************************
  1304. X * Bug reports, patches, comments, suggestions should be sent to:
  1305. X *
  1306. X *    Syd Weinstein, Elm Coordinator
  1307. X *    elm@DSI.COM            dsinc!elm
  1308. X *
  1309. X *******************************************************************************
  1310. X * $Log:    showmsg.c,v $
  1311. X * Revision 4.1  90/04/28  22:44:06  syd
  1312. X * checkin of Elm 2.3 as of Release PL0
  1313. X * 
  1314. X *
  1315. X ******************************************************************************/
  1316. X
  1317. X/** This file contains all the routines needed to display the specified
  1318. X    message.
  1319. X**/
  1320. X
  1321. X#include "headers.h"
  1322. X#include <ctype.h>
  1323. X#include <errno.h>
  1324. X
  1325. X#ifdef BSD
  1326. X# include <sys/wait.h>
  1327. X# undef       tolower
  1328. X#endif
  1329. X
  1330. Xextern int errno;
  1331. X
  1332. Xchar *error_name(), *strcat(), *strcpy();
  1333. Xvoid   _exit();
  1334. X
  1335. Xint    memory_lock = FALSE;    /* is it available?? */
  1336. Xint    pipe_abort  = FALSE;    /* did we receive a SIGNAL(SIGPIPE)? */
  1337. X
  1338. XFILE *pipe_wr_fp;        /* file pointer to write to external pager */
  1339. Xextern int lines_displayed,    /* defined in "builtin" */    
  1340. X       lines_put_on_screen;    /*    ditto too!        */
  1341. X
  1342. Xint
  1343. Xshow_msg(number)
  1344. Xint number;
  1345. X{
  1346. X    /*** Display number'th message.  Get starting and ending lines
  1347. X         of message from headers data structure, then fly through
  1348. X         the file, displaying only those lines that are between the
  1349. X         two!
  1350. X
  1351. X         Return 0 to return to the index screen or a character entered
  1352. X         by the user to initiate a command without returning to
  1353. X         the index screen (to be processed via process_showmsg_cmd()).
  1354. X    ***/
  1355. X
  1356. X    char title1[SLEN], title2[SLEN], title3[SLEN], titlebuf[SLEN];
  1357. X    char who[LONG_STRING], buffer[VERY_LONG_STRING];
  1358. X#if defined(BSD) && !defined(WEXITSTATUS)
  1359. X    union wait wait_stat;
  1360. X#else
  1361. X    int wait_stat;
  1362. X#endif
  1363. X
  1364. X    int crypted = 0;            /* encryption */
  1365. X    int weed_header, weeding_out = 0;    /* weeding    */ 
  1366. X    int using_to,                /* misc use   */
  1367. X        pipe_fd[2],                /* pipe file descriptors */
  1368. X        new_pipe_fd,            /* dup'ed pipe fil des */
  1369. X        lines,                /* num lines in msg */
  1370. X        fork_ret,                /* fork return value */
  1371. X        wait_ret,                /* wait return value */
  1372. X        form_letter = FALSE,        /* Form ltr?  */
  1373. X        form_letter_section = 0,        /* section    */
  1374. X        padding = 0,            /*   counter  */
  1375. X        builtin = FALSE,            /* our pager? */
  1376. X        val = 0,                /* return val */
  1377. X        buf_len;                /* line length */
  1378. X    struct header_rec *current_header = headers[number-1];
  1379. X
  1380. X
  1381. X    lines = current_header->lines; 
  1382. X
  1383. X    dprint(4, (debugfile,"displaying %d lines from message %d using %s\n", 
  1384. X        lines, number, pager));
  1385. X
  1386. X    if (number > message_count || number < 1)
  1387. X      return(val);
  1388. X
  1389. X    if(ison(current_header->status, NEW)) {
  1390. X      clearit(current_header->status, NEW);   /* it's been read now! */
  1391. X      current_header->status_chgd = TRUE;
  1392. X    }
  1393. X    if(ison(current_header->status, UNREAD)) {
  1394. X      clearit(current_header->status, UNREAD);   /* it's been read now! */
  1395. X      current_header->status_chgd = TRUE;
  1396. X    }
  1397. X
  1398. X    memory_lock = FALSE;
  1399. X
  1400. X    /* some explanation for that last one - We COULD use memory locking
  1401. X       to speed up the paging, but the action of "ClearScreen" on a screen
  1402. X       with memory lock turned on seems to vary considerably (amazingly so)
  1403. X       so it's safer to only allow memory lock to be a viable bit of
  1404. X       trickery when dumping text to the screen in scroll mode.
  1405. X       Philosophical arguments should be forwarded to Bruce at the 
  1406. X       University of Walamazoo, Australia, via ACSNet  *wry chuckle* */
  1407. X
  1408. X    if (fseek(mailfile, current_header->offset, 0) == -1) {
  1409. X      dprint(1, (debugfile,
  1410. X          "Error: seek %d bytes into file, errno %s (show_message)\n",
  1411. X          current_header->offset, error_name(errno)));
  1412. X      error2("ELM failed seeking %d bytes into file (%s).",
  1413. X          current_header->offset, error_name(errno));    
  1414. X      return(val);
  1415. X    }
  1416. X    if(current_header->encrypted)
  1417. X      getkey(OFF);
  1418. X
  1419. X    if(builtin=(first_word(pager,"builtin")||first_word(pager,"internal")))
  1420. X
  1421. X      start_builtin(lines);
  1422. X
  1423. X    else {
  1424. X
  1425. X      /* put terminal out of raw mode so external pager has normal env */
  1426. X      Raw(OFF);
  1427. X
  1428. X      /* create pipe for external pager and fork */
  1429. X
  1430. X      if(pipe(pipe_fd) == -1) {
  1431. X        dprint(1, (debugfile, "Error: pipe failed, errno %s (show_msg)\n",
  1432. X          error_name(errno)));
  1433. X        error1("Could not prepare for external pager(pipe()-%s).",
  1434. X          error_name(errno));    
  1435. X        Raw(ON);
  1436. X        return(val);
  1437. X      }
  1438. X
  1439. X      if((fork_ret = fork()) == -1) {
  1440. X
  1441. X        dprint(1, (debugfile, "Error: fork failed, errno %s (show_msg)\n",
  1442. X          error_name(errno)));
  1443. X        error1("Could not prepare for external pager(fork()-%s).",
  1444. X          error_name(errno));    
  1445. X        Raw(ON);
  1446. X        return(val);
  1447. X
  1448. X      } else if (fork_ret == 0) {
  1449. X
  1450. X        /* child fork */
  1451. X
  1452. X        /* close write-only pipe fd and fit read-only pipe fd to stdin */
  1453. X
  1454. X        close(pipe_fd[1]);
  1455. X        close(fileno(stdin));
  1456. X        if((new_pipe_fd = dup(pipe_fd[0])) == -1) {
  1457. X          dprint(1, (debugfile, "Error: dup failed, errno %s (show_msg)\n",
  1458. X        error_name(errno)));
  1459. X          error1("Could not prepare for external pager(dup()-%s).",
  1460. X        error_name(errno));    
  1461. X          _exit(errno);
  1462. X        }
  1463. X        close(pipe_fd[0]);    /* original pipe fd no longer needed */
  1464. X
  1465. X        /* use stdio on new pipe fd */
  1466. X        if(fdopen(new_pipe_fd, "r") == NULL) {
  1467. X          dprint(1,
  1468. X        (debugfile, "Error: child fdopen failed, errno %s (show_msg)\n",
  1469. X        error_name(errno)));
  1470. X          error1("Could not prepare for external pager(child fdopen()-%s).",
  1471. X        error_name(errno));    
  1472. X          _exit(errno);
  1473. X        }
  1474. X
  1475. X        /* now execute pager and exit */
  1476. X        
  1477. X        /* system_call() will return user to user's normal permissions.
  1478. X         * This is what makes this pipe secure - user won't have elm's
  1479. X         * special setgid permissions (if so configured) and will only
  1480. X         * be able to execute a pager that user normally has permission
  1481. X         * to execute */
  1482. X
  1483. X        _exit(system_call(pager, SH, TRUE, TRUE));
  1484. X      
  1485. X      } /* else this is the parent fork */
  1486. X
  1487. X      /* close read-only pipe fd and do write-only with stdio */
  1488. X      close(pipe_fd[0]);
  1489. X
  1490. X      if((pipe_wr_fp = fdopen(pipe_fd[1], "w")) == NULL) {
  1491. X        dprint(1,
  1492. X          (debugfile, "Error: parent fdopen failed, errno %s (show_msg)\n",
  1493. X          error_name(errno)));
  1494. X        error1("Could not prepare for external pager(parent fdopen()-%s).",
  1495. X          error_name(errno));    
  1496. X
  1497. X        /* Failure - must close pipe and wait for child */
  1498. X        close(pipe_fd[1]);
  1499. X        while ((wait_ret = wait(&wait_stat)) != fork_ret && wait_ret!= -1)
  1500. X          ;
  1501. X
  1502. X        Raw(OFF);
  1503. X        return(val);    /* pager may have already touched the screen */
  1504. X      }
  1505. X
  1506. X      /* and that's it! */
  1507. X      lines_displayed = 0;
  1508. X    }
  1509. X
  1510. X    ClearScreen();
  1511. X
  1512. X    if (cursor_control) transmit_functions(OFF);
  1513. X
  1514. X    pipe_abort = FALSE;
  1515. X
  1516. X    if (form_letter = (current_header->status&FORM_LETTER)) {
  1517. X      if (filter)
  1518. X        form_letter_section = 1;    /* initialize to section 1 */
  1519. X    }
  1520. X
  1521. X    if (title_messages && filter) {
  1522. X
  1523. X      using_to =
  1524. X        tail_of(current_header->from, who, current_header->to);
  1525. X
  1526. X      sprintf(title1, "%s %d/%d  ",
  1527. X            headers[current-1]->status & DELETED ? "[deleted]" :
  1528. X            form_letter ? "Form": "Message",
  1529. X            number, message_count);
  1530. X      sprintf(title2, "%s %s", using_to? "To" : "From", who);
  1531. X      sprintf(title3, "  %s %s '%d at %s %s",
  1532. X                current_header->month, current_header->day, 
  1533. X               atoi(current_header->year), current_header->time,
  1534. X           current_header->time_zone);
  1535. X
  1536. X      /* truncate or pad title2 portion on the right
  1537. X       * so that line fits exactly */
  1538. X      padding =
  1539. X        COLUMNS -
  1540. X        (strlen(title1) + (buf_len=strlen(title2)) + strlen(title3));
  1541. X
  1542. X      sprintf(titlebuf, "%s%-*.*s%s\n", title1, buf_len+padding,
  1543. X          buf_len+padding, title2, title3);
  1544. X
  1545. X      if (builtin)
  1546. X        display_line(titlebuf);
  1547. X      else
  1548. X        fprintf(pipe_wr_fp, "%s", titlebuf);
  1549. X
  1550. X      /** if there's a subject, let's output it next,
  1551. X          centered if it fits on a single line.  **/
  1552. X
  1553. X      if ((buf_len = strlen(current_header->subject)) > 0 && 
  1554. X        matches_weedlist("Subject:")) {
  1555. X        padding = (buf_len < COLUMNS ? COLUMNS - buf_len : 0);
  1556. X        sprintf(buffer, "%*s%s\n", padding/2, "", current_header->subject);
  1557. X      } else
  1558. X        strcpy(buffer, "\n");
  1559. X
  1560. X      if (builtin)
  1561. X        display_line(buffer);
  1562. X      else
  1563. X        fprintf(pipe_wr_fp, "%s", buffer);
  1564. X      
  1565. X      /** was this message address to us?  if not, then to whom? **/
  1566. X
  1567. X      if (! using_to && matches_weedlist("To:") && filter &&
  1568. X          strcmp(current_header->to,username) != 0 &&
  1569. X          strlen(current_header->to) > 0) {
  1570. X        if (strlen(current_header->to) > 60)
  1571. X          sprintf(buffer, "%s(message addressed to %.60s)\n", 
  1572. X                strlen(current_header->subject) > 0 ? "\n" : "",
  1573. X            current_header->to);
  1574. X        else
  1575. X          sprintf(buffer, "%s(message addressed to %s)\n", 
  1576. X                strlen(current_header->subject) > 0 ? "\n" : "",
  1577. X            current_header->to);
  1578. X        if (builtin)
  1579. X          display_line(buffer);
  1580. X        else
  1581. X          fprintf(pipe_wr_fp, "%s", buffer);
  1582. X      }
  1583. X    
  1584. X      /** The test above is: if we didn't originally send the mail
  1585. X          (e.g. we're not reading "mail.sent") AND the user is currently
  1586. X          weeding out the "To:" line (otherwise they'll get it twice!)
  1587. X          AND the user is actually weeding out headers AND the message 
  1588. X          wasn't addressed to the user AND the 'to' address is non-zero 
  1589. X          (consider what happens when the message doesn't HAVE a "To:" 
  1590. X          line...the value is NULL but it doesn't match the username 
  1591. X          either.  We don't want to display something ugly like 
  1592. X          "(message addressed to )" which will just clutter up the 
  1593. X          screen!).
  1594. X
  1595. X          And you thought programming was EASY!!!!
  1596. X      **/
  1597. X
  1598. X      /** one more friendly thing - output a line indicating what sort
  1599. X          of status the message has (e.g. Urgent etc).  Mostly added
  1600. X          for X.400 support, this is nonetheless generally useful to
  1601. X          include...
  1602. X      **/
  1603. X
  1604. X      buffer[0] = '\0';
  1605. X
  1606. X      /* we want to flag Urgent, Confidential, Private and Expired tags */
  1607. X
  1608. X      if (current_header->status & PRIVATE)
  1609. X        strcpy(buffer, "\n(** This message is tagged Private");
  1610. X      else if (current_header->status & CONFIDENTIAL) 
  1611. X        strcpy(buffer, "\n(** This message is tagged Company Confidential");
  1612. X
  1613. X      if (current_header->status & URGENT) {
  1614. X        if (buffer[0] == '\0')
  1615. X          strcpy(buffer, "\n(** This message is tagged Urgent");
  1616. X        else if (current_header->status & EXPIRED)
  1617. X          strcat(buffer, ", Urgent");
  1618. X        else
  1619. X          strcat(buffer, " and Urgent");
  1620. X      }
  1621. X
  1622. X      if (current_header->status & EXPIRED) {
  1623. X        if (buffer[0] == '\0')
  1624. X          strcpy(buffer, "\n(** This message has Expired");
  1625. X        else
  1626. X          strcat(buffer, ", and has Expired");
  1627. X      }
  1628. X
  1629. X      if (buffer[0] != '\0') {
  1630. X        strcat(buffer, " **)\n");
  1631. X        if (builtin)
  1632. X          display_line(buffer);
  1633. X        else
  1634. X          fprintf(pipe_wr_fp, buffer);
  1635. X      }
  1636. X
  1637. X      if (builtin)            /* this is for a one-line blank    */
  1638. X        display_line("\n");        /*   separator between the title   */
  1639. X      else                /*   stuff and the actual message  */
  1640. X        fprintf(pipe_wr_fp, "\n");    /*   we're trying to display       */
  1641. X
  1642. X    }
  1643. X
  1644. X    weed_header = filter;    /* allow us to change it after header */
  1645. X
  1646. X    while (lines > 0 && pipe_abort == FALSE) {
  1647. X
  1648. X        if (fgets(buffer, VERY_LONG_STRING, mailfile) == NULL) {
  1649. X
  1650. X          dprint(1, (debugfile,
  1651. X        "Premature end of file! Lines left = %d msg = %s (show_msg)\n",
  1652. X        lines, number));
  1653. X
  1654. X          error("Premature end of file!");
  1655. SHAR_EOF
  1656. echo "End of part 21"
  1657. echo "File src/showmsg.c is continued in part 22"
  1658. echo "22" > s2_seq_.tmp
  1659. exit 0
  1660.  
  1661. exit 0 # Just in case...
  1662.