home *** CD-ROM | disk | FTP | other *** search
/ ftp.freefriends.org / ftp.freefriends.org.tar / ftp.freefriends.org / arnold / Source / mush.rstevens.tar.gz / mush.tar / msgs.c < prev    next >
C/C++ Source or Header  |  1992-10-30  |  30KB  |  1,056 lines

  1. /* @(#)msgs.c    (c) copyright 10/18/86 (Dan Heller) */
  2.  
  3. #include "mush.h"
  4.  
  5. void
  6. display_msg(n, flg)
  7. register int n;
  8. u_long flg;
  9. {
  10.     char buf[32], *pager = NULL;
  11.     int intro = TRUE;
  12.  
  13.     if (ison(msg[n].m_flags, DELETE) && !do_set(set_options, "show_deleted")) {
  14.     print("Message %d deleted; ", n+1);
  15. #ifdef SUNTOOL
  16.     if (istool)
  17.         wprint("Select UNDELETE to read.\n");
  18.     else
  19. #endif /* SUNTOOL */
  20.     if (iscurses)
  21.         print_more("Type 'u' to undelete.");
  22.     else
  23.         wprint("Type 'undelete %d' to undelete\n", n+1);
  24.     return;
  25.     }
  26.     set_isread(n);
  27.     if (ison(flg, M_TOP)) {
  28.     turnon(flg, NO_HEADER);
  29.     print("Top of "), turnon(glob_flags, CONT_PRNT);
  30.     }
  31.  
  32. #ifdef MSG_SEPARATOR
  33.     turnon(flg, NO_SEPARATOR);
  34. #endif /* MMDF */
  35.     if (ison(msg[n].m_flags, METAMAIL) && isoff(flg, NO_PAGE) &&
  36.         (pager = do_set(set_options, "metamail"))) {
  37.     intro = FALSE;
  38.     turnoff(flg, NO_HEADER);
  39.     turnoff(flg, M_TOP);
  40.     turnon(flg, NO_IGNORE);
  41.     } else if (!istool && isoff(flg, NO_PAGE) &&
  42.         crt < msg[n].m_lines && isoff(flg, M_TOP)) {
  43.     if (!(pager = do_set(set_options, "pager")))
  44.         pager = DEF_PAGER;
  45.     if (!*pager || !strcmp(pager, "internal"))
  46.         pager = NULL; /* default to internal pager if pager set to "" */
  47.     }
  48.     (void) do_pager(pager, intro? 1 : -1); /* start pager */
  49.     if (intro)
  50.     (void) do_pager(sprintf(buf, "Message #%d (%d lines)\n",
  51.                  n+1, msg[n].m_lines), FALSE);
  52.     (void) copy_msg(n, NULL_FILE, flg, NULL);
  53.     (void) do_pager(NULL, FALSE); /* end pager */
  54. }
  55.  
  56. /*
  57.  * copy message 'n' to file "fp" according to various flag arguments
  58.  * return number of lines copied or -1 if system error on fputs.
  59.  * If "fp" is null, send to internal pager.  This can only happen from
  60.  * display_msg above.
  61.  */
  62. copy_msg(n, fp, flags, pattern)
  63. register int n;
  64. register FILE *fp;
  65. u_long flags;
  66. char *pattern;
  67. {
  68.     register int  ignoring = 0, lines = 0;
  69.     register char *indent_str, *p, *end_pat = NULL;
  70.     int          on_hdr = 1, top, squeeze = 0;
  71.     long      still_more = 0;
  72.     int          pat_len, pat_seek;
  73.     char       line[BUFSIZ], *show_hdrs = NULL;
  74.  
  75.     if (ison(flags, M_TOP)) {
  76.     p = do_set(set_options, "toplines");
  77.     top = (p)? atoi(p) : crt;
  78.     }
  79.     /* When updating to a folder, always write all headers! */
  80.     if (ison(flags, UPDATE_STATUS))
  81.     turnon(flags, NO_IGNORE);
  82.     else if (ison(flags, NO_IGNORE) &&
  83.         (p = do_set(set_options, "alwaysignore")) && !*p)
  84.     turnoff(flags, NO_IGNORE);    /* preserve historic behavior */
  85.     if (isoff(flags, NO_IGNORE)) {
  86.     if (do_set(set_options, "squeeze"))
  87.         squeeze = 1;
  88.     show_hdrs = do_set(set_options, "show_hdrs");
  89.     }
  90.     if (pattern && *pattern == '/' && (end_pat = index(pattern+1, '/'))) {
  91.     if (end_pat[1] == ',') {
  92.         pattern++;
  93.         *end_pat++ = 0;
  94.     } else
  95.         end_pat = NULL;
  96.     }
  97.     pat_len = pattern? strlen(pattern) : 0;
  98.     pat_seek = !!pat_len;
  99.  
  100. #ifdef SUNTOOL
  101.     xfree(more_prompt), more_prompt = NULL;
  102. #endif /* SUNTOOL */
  103.  
  104.     if (ison(flags, INDENT)) {
  105.     if ((indent_str = do_set(set_options, "pre_indent_str"))) {
  106.         fputs(format_hdr(n, indent_str, FALSE) + 9, fp); /* magic 9 !! */
  107.         fputc('\n', fp);
  108.     }
  109.     if (!(indent_str = do_set(set_options, "indent_str")))
  110.         indent_str = DEF_INDENT_STR;
  111.     indent_str = format_hdr(n, indent_str, FALSE) + 9; /* magic 9 !! */
  112.     }
  113.     /* "line" used as dummy here, since 0 bytes read */
  114.     if (!msg_get(n, line, 0)) {
  115.     error("Unable to find msg %d", n+1);
  116.     return -1;
  117.     }
  118.     while (still_more < msg[n].m_size && fgets(line, sizeof (line), tmpf)) {
  119.     still_more += strlen(line);
  120. #ifdef MSG_SEPARATOR
  121.     if (ison(flags, NO_SEPARATOR)) {
  122. #ifdef MMDF
  123.         if (!strncmp(line, MSG_SEPARATOR, 4))
  124. #else /* !MMDF */
  125.         if (!strncmp(line, MSG_SEPARATOR, strlen(MSG_SEPARATOR)))
  126. #endif /* MMDF */
  127.         continue;
  128.     }
  129. #endif /* MMDF */
  130.     /*
  131.      * If squeeze is one, all blanks lines squeeze down to one blank line.
  132.      * If squeeze is two, squeezing is in progress so wait for the next \n.
  133.      */
  134.     if (*line == '\n') {
  135.         if (on_hdr) {  /* blank line -- end of header */
  136.         on_hdr = 0;
  137.         if (ison(flags, NO_HEADER))
  138.             continue;
  139.         }
  140.         if (squeeze > 1 || pat_len && pat_seek)
  141.         continue;
  142.         else if (squeeze)
  143.         squeeze = 2;
  144.     } else {
  145.         if (squeeze > 1)
  146.         squeeze = 1;
  147.         if (pat_len && (!on_hdr || isoff(flags, NO_HEADER))) {
  148.         /* If we're looking for a pattern for mush-pipe, then
  149.          * continue if this line doesn't match the pattern.
  150.          */
  151.         if (pat_len == 0)
  152.             continue;
  153.         Debug("Seeking (%s) in (%s)", pattern, line);
  154.         if (strncmp(line, pattern, pat_len)) {
  155.             if (pat_seek)
  156.             continue;
  157.         } else if (end_pat && *end_pat++ == ',') {
  158.             pattern = end_pat;
  159.             if (*pattern == '/') {
  160.             pattern++;
  161.             if (end_pat = index(pattern, '/'))
  162.                 *end_pat++ = 0;
  163.             }
  164.             pat_len = pattern? strlen(pattern) : 0;
  165.             pat_seek = !pat_seek;
  166.         } else {
  167.             pat_len = 0;
  168.             pat_seek = !pat_seek;
  169.         }
  170.         }
  171.     }
  172.  
  173.     if (ison(flags, UPDATE_STATUS))
  174.         if (!strncmp(line, "Status:", 7) || !strncmp(line, "Priority:", 9))
  175.         continue; /* ignore "Status" and "Priority" lines */
  176.         else if (!on_hdr) {
  177.         int i, write_priority = 0;
  178.         p = line;
  179.         p += Strcpy(p, "Priority:");
  180.         for (i = 0; i < MAX_PRIORITY; i++)
  181.             if (ison(msg[n].m_flags, M_PRIORITY(i + 1))) {
  182.             write_priority = 1;
  183.             *p++ = ' ';
  184.             *p++ = i + 'A';
  185.             }
  186.         if (write_priority) {
  187.             *p++ = '\n', *p = 0;
  188.             (void) fputs(line, fp);
  189.         }
  190.         /* PRESERVE here avoids changing new message status */
  191.         if (isoff(flags, PRESERVE) || /* NOT msg[n].m_flags */
  192.             ison(msg[n].m_flags, OLD) ||
  193.             isoff(msg[n].m_flags, UNREAD)) {
  194.             p = line;
  195.             p += Strcpy(p, "Status: O");
  196.             if (isoff(msg[n].m_flags, UNREAD))
  197.             *p++ = 'R';
  198.             if (ison(msg[n].m_flags, SAVED))
  199.             *p++ = 'S';
  200.             if (ison(msg[n].m_flags, REPLIED))
  201.             *p++ = 'r';
  202.             if (ison(msg[n].m_flags, PRINTED))
  203.             *p++ = 'p';
  204.             if (ison(msg[n].m_flags, FORWARD))
  205.             *p++ = 'f';
  206.             *p++ = '\n', *p = 0;
  207.             (void) fputs(line, fp);
  208.         }
  209.         turnoff(flags, UPDATE_STATUS);
  210.         line[0] = '\n', line[1] = '\0';
  211.         }
  212.     if (on_hdr && (isoff(flags, NO_IGNORE) || ison(flags, FORWARD))) {
  213.         p = any(line, " \t:");
  214.         if (!p)
  215.         ignoring = 0, on_hdr = 0;
  216.         else if (ignoring)
  217.         if (*p != ':') {
  218.             Debug("Ignoring: %s", line);
  219.             continue;
  220.         } else
  221.             ignoring = 0;
  222.         if (p && *p == ':') {
  223.         *p = 0;
  224.         ignoring = 0;
  225.         if (ison(flags, FORWARD)) {
  226.             if (chk_two_lists(line, IGNORE_ON_FWD, ":, \t"))
  227.             ignoring = 1;
  228.         } else if (show_hdrs) {
  229.             if (!chk_two_lists(line, show_hdrs, ":, \t"))
  230.             ignoring = 1;
  231.         } else {
  232.             register struct options *opts;
  233.             for (opts = ignore_hdr; opts; opts = opts->next)
  234.             if (!lcase_strncmp(opts->option, line, -1)) {
  235.                 ignoring = 1;
  236.                 break;
  237.             }
  238.         }
  239.         *p = ':';
  240.         if (ignoring) {
  241.             Debug("Ignoring: %s", line);
  242.             continue;
  243.         }
  244.         }
  245.     }
  246.     if (!on_hdr && ison(flags, M_TOP) && !--top)
  247.         break;
  248.     if (!on_hdr && (still_more < msg[n].m_size || line[0] != '\n') ||
  249.         isoff(flags, NO_HEADER)) {
  250.         /* note that function returns the number of lines */
  251.         lines++;
  252.         if (ison(flags, INDENT))
  253.         (void) fputs(indent_str, fp);
  254.         if (!fp) {
  255.         if (do_pager(line, FALSE) == EOF)
  256.             return -1;
  257.         } else if (fputs(line, fp) == EOF)
  258.         /* Pipe broken, out of file space, etc */
  259.         return -1;
  260.     }
  261.     if (pat_seek && !pat_len)
  262.         break; /* Skip the rest */
  263.     }
  264.     if (ison(flags, INDENT) &&
  265.     (indent_str = do_set(set_options, "post_indent_str")) && *indent_str) {
  266.     (void) fprintf(fp, "%s\n", format_hdr(n, indent_str, FALSE)+9);
  267.     }
  268.     if (fp && fflush(fp) == EOF)
  269.     return -1;    /* Write failure? */
  270.     return lines;
  271. }
  272.  
  273. /*
  274.  * copy tempfile back to folder.
  275.  * Return 1 on success, 0 on failure.
  276.  */
  277. copyback(prompt, final)
  278. char *prompt;
  279. int final;    /* Are we exiting or updating? */
  280. {
  281.     register int    i = 0, held = 0, saved = 0;
  282.     register u_long    flg = 0;
  283.     register FILE    *mbox = NULL_FILE, *mail_fp = NULL_FILE;
  284. #ifdef SYSV
  285.     FILE         *save_mail_fp = NULL_FILE;
  286. #endif /* SYSV */
  287.     char        *mbox_file, action = 0;
  288.     int         hold = 0, delete_it = 0, dont_unlink = !final;
  289.     int            isspool, keepsave, write_err = FALSE;
  290.     static int        first = 1;
  291.  
  292.     /*
  293.      * if there is new mail in this folder, the user is notified and
  294.      * prompted if he really wants to update the folder.  He's either
  295.      * quitting or changing folders, so let him read the new mail first.
  296.      */
  297.     if (!first && mail_size()) {
  298. lost_lock:
  299.     if ((ison(glob_flags, CORRUPTED) || get_new_mail(TRUE)) &&
  300.         prompt && isoff(glob_flags, REDIRECT) && show_new_mail()) {
  301.         char buf[80];
  302.         if (iscurses)
  303.         putchar('\n'), turnon(glob_flags, CNTD_CMD);
  304.         if (!istool)
  305.         print("%s [n] ", prompt);
  306.         buf[0] = 0;
  307. #ifdef SUNTOOL
  308.         if (istool) {
  309.         (void) sprintf(buf, "%s -- %s",
  310.             ison(glob_flags, CORRUPTED) ? "Error" : "New mail",
  311.             prompt);
  312.         if (ask(buf) != TRUE)
  313.             return 0;
  314.         } else
  315. #endif /* SUNTOOL */
  316.         if (!Getstr(buf, sizeof (buf), 0) || lower(*buf) != 'y')
  317.             return 0;
  318.         turnoff(glob_flags, CORRUPTED); /* User says go ahead */
  319.     }
  320.     }
  321.     first = 0;
  322.  
  323.     /* If the user hasn't changed anything, just return true */
  324.     if (isoff(glob_flags, DO_UPDATE) || ison(glob_flags, CORRUPTED))
  325.     return 1;
  326.     if (ison(glob_flags, READ_ONLY)) {
  327.     print("Unable to update %s: read only\n", mailfile);
  328.     return 0; /* user should use "exit" instead of "quit". */
  329.     }
  330.     if (!msg_cnt) /* prevent unnecessary overwrite */
  331.     return 1;
  332.  
  333. #ifdef SUNTOOL
  334.     if (istool) {
  335.     (void) notify_set_itimer_func(tool, do_check,
  336.         ITIMER_REAL, (struct itimerval *) 0, (struct itimerval *) 0);
  337.     }
  338. #endif /* SUNTOOL */
  339.  
  340.     /* We can't lock a file unless we have an fd, but "w+" will zero
  341.      * the file.  If the lock later failed for any reason (possible
  342.      * race condition with an MTA), we would lose all current mail.
  343.      * So, open read/write (if possible) and truncate later.
  344.      */
  345.     if (!(mail_fp = lock_fopen(mailfile, "r+"))) {
  346.     error("WARNING: unable to lock %s -- update aborted", mailfile);
  347. #ifdef SUNTOOL
  348.     if (istool) {
  349.         write_err = 1;    /* forces return 0; below */
  350.         goto resume_timer;    /* blecch */
  351.     }
  352. #endif /* SUNTOOL */
  353.     return 0;
  354.     }
  355.     /* Make sure no mail arrived between the last check and when we
  356.      * got the lock.  If it did, release the lock and try again.
  357.      */
  358.     if (mail_size()) {
  359.     (void) close_lock(mailfile, mail_fp);
  360.     goto lost_lock;
  361.     }
  362.  
  363.     /* open mbox if: "autodelete" AND "hold" are NOT set. */
  364.     if (!strcmp(mailfile, spoolfile)
  365.         && !(delete_it = !!do_set(set_options, "autodelete"))
  366.         && !(hold = !!do_set(set_options, "hold"))) {
  367.     register char *p;
  368.     int x = 1; /* tell getpath to ignore "ENOENT" if file not found */
  369.  
  370.     if (!(p = do_set(set_options, "mbox")))
  371.         p = DEF_MBOX;
  372.     mbox_file = getpath(p, &x); /* static data -- copy? */
  373.     if (x) {
  374.         if (x > 0)
  375.         print("%s is a directory.\n", mbox_file);
  376.         else
  377.         print("Unable to open %s: %s\n", p, mbox_file);
  378.         mbox = NULL_FILE;
  379.     } else {
  380.         if (Access(mbox_file, F_OK) == -1) /* does it exist? */
  381.         mbox = lock_fopen(mbox_file, "w");
  382.         else
  383.         mbox = lock_fopen(mbox_file, "a");
  384.         if (!mbox)
  385.         error("Unable to write to %s", mbox_file);
  386.     }
  387.     }
  388.  
  389.     /* ignore signals before truncating */
  390.     turnon(glob_flags, IGN_SIGS);
  391. #ifdef SYSV
  392.     /* SysV can't truncate a file in the middle, so we can't just
  393.      * write to mail_fp and close.  Instead, we save the mail_fp
  394.      * and reopen for writing, ignoring our own lock.  After updating,
  395.      * we can safely fclose both file pointers.
  396.      */
  397.     save_mail_fp = mail_fp;
  398.     /* This could fail if we run out of file descriptors */
  399.     if (!(mail_fp = fopen(mailfile, "w"))) {
  400.     error("WARNING: unable to reopen %s for update", mailfile);
  401.     if (save_mail_fp)
  402.         (void) close_lock(mailfile, save_mail_fp);
  403.     if (mbox)
  404.         (void) close_lock(mbox_file, mbox);
  405.     turnoff(glob_flags, IGN_SIGS);
  406.     return 0;
  407.     }
  408. #endif /* SYSV */
  409.  
  410.     print("Updating \"%s\"", mailfile);
  411.  
  412.     turnon(flg, UPDATE_STATUS);
  413.     /* Don't set OLD for new messages on update. */
  414.     if (!final)
  415.     turnon(flg, PRESERVE);
  416.  
  417.     keepsave = !!do_set(set_options, "keepsave");
  418.     isspool = !strcmp(mailfile, spoolfile);
  419.  
  420.     for (i = 0; i < msg_cnt; i++) {
  421.     /* Maintain the current message across update; if this turns out
  422.      * to be unnecessary (changing folders), folder() will reset it.
  423.      */
  424.     if (current_msg == i)
  425.         current_msg = held;
  426.     /* Check to see if message is marked for deletion or, if read and not
  427.      * preserved, delete it if autodelete is set.  Otherwise, if hold is
  428.      * set save the message in the spool file.  If all fails, save in mbox.
  429.      */
  430.     if (ison(msg[i].m_flags, DELETE)
  431.     ||  ison(msg[i].m_flags, SAVED) && !keepsave &&
  432.         isoff(msg[i].m_flags, PRESERVE) && isspool
  433.     ||  isoff(msg[i].m_flags, UNREAD) && isoff(msg[i].m_flags, PRESERVE) 
  434.         && delete_it) {
  435.         Debug("%s %d",
  436.         (action!='d')? "\ndeleting message:" : "", i+1), action = 'd';
  437.         continue;
  438.     } else if (isoff(msg[i].m_flags, DO_UPDATE) || hold || !mbox ||
  439.         ison(msg[i].m_flags, UNREAD) ||
  440.         ison(msg[i].m_flags, PRESERVE)) {
  441.         Debug("%s %d",
  442.         (action!='s')? "\nsaving in spool:" : "", i+1), action = 's';
  443.         if (copy_msg(i, mail_fp, flg, NULL) == -1) {
  444.         error("WARNING: unable to write back to spool");
  445.         print_more("ALL mail left in %s\n", tempfile);
  446.         print_more("Spool mailbox may be corrupted.\n");
  447.         dont_unlink = TRUE;
  448.         write_err = TRUE;
  449.         break;
  450.         }
  451.         held++;
  452.     } else if (isspool) {   /* copy back to mbox */
  453.         if (copy_msg(i, mbox, flg, NULL) == -1) {
  454.         error("WARNING: unable to write to mbox");
  455.         print_more("Unresolved mail left in %s\n", tempfile);
  456.         dont_unlink = TRUE;
  457.         write_err = TRUE;
  458.         break;
  459.         }
  460.         saved++;
  461.         Debug("%s %d",
  462.         (action!='m')? "\nsaving in mbox:" : "", i+1), action = 'm';
  463.     }
  464.     }
  465.     if (write_err)
  466.     current_msg = 0;
  467.     else if (current_msg == held)
  468.     current_msg--;    /* Don't point to a message that got deleted */
  469.     Debug("\n%s", mailfile);
  470.  
  471. #ifdef SYSV
  472.     /* Close the write file pointer first */
  473.     (void) fclose(mail_fp);
  474.     mail_fp = save_mail_fp;
  475. #else /* !SYSV */
  476.     /* Truncate the file at the end of what we just wrote.
  477.      * If you aren't SYSV and you still can't ftruncate(),
  478.      * you're out of luck?
  479.      */
  480.     (void) ftruncate(fileno(mail_fp), ftell(mail_fp));
  481. #endif /* SYSV */
  482.  
  483.     /* some users like to have zero length folders for frequent usage */
  484.     if (mbox && close_lock(mbox_file, mbox) == EOF) {
  485.     error("WARNING: unable to close mbox");
  486.     print_more("Unresolved mail left in %s\n", tempfile);
  487.     dont_unlink = TRUE;
  488.     write_err = TRUE;
  489.     }
  490.     if (held) {
  491.     print_more(": saved %d message%s\n", held, (held==1)? NO_STRING: "s");
  492.     } else
  493. #ifdef HOMEMAIL
  494.     if (!dont_unlink && !do_set(set_options, "save_empty"))
  495. #else /* HOMEMAIL */
  496.     if (strcmp(mailfile, spoolfile) && !dont_unlink &&
  497.     !do_set(set_options, "save_empty"))
  498. #endif /* HOMEMAIL */
  499.     if (unlink(mailfile))
  500.         turnon(glob_flags, CONT_PRNT), error(": cannot remove");
  501.     else {
  502.         print_more(": removed\n");
  503.         held = -1;
  504.     }
  505.     else
  506.     print_more(": empty\n");
  507.     if (saved)
  508.     print("saved %d message%s in %s\n",
  509.                 saved,(saved==1)? NO_STRING:"s", mbox_file);
  510.  
  511.     if (held > 0) {
  512.     /* Reset the access time of the spool file to prevent
  513.      * bogus "new mail" messages from the shell.
  514.      */
  515. #ifdef POSIX_UTIME
  516.     struct utimbuf times[1];
  517.     (void) fflush(mail_fp); /* just in case */
  518.     times[0].modtime = time(×[0].actime) - 2;
  519.     times[0].ausec = times[0].modusec = 0;
  520. #else /* !POSIX_UTIME */
  521.     long times[2];
  522.     (void) fflush(mail_fp); /* just in case */
  523.     times[1] = time(×[0]) - (long)2;
  524. #endif /* POSIX_UTIME */
  525.     if (!strcmp(mailfile, spoolfile) && utime(mailfile, times))
  526.         error("utime");
  527.     }
  528.  
  529.     if (close_lock(mailfile, mail_fp) == EOF) {
  530.     error("WARNING: unable to close spool");
  531.     print_more("ALL mail left in %s\n", tempfile);
  532.     print_more("Spool mailbox may be corrupted.\n");
  533.     write_err = TRUE;
  534.     }
  535.  
  536. #ifdef SUNTOOL
  537.     if (istool) {
  538. resume_timer:
  539.     mail_timer.it_value.tv_sec = time_out;
  540.     mail_timer.it_interval.tv_sec = time_out;
  541.     (void) notify_set_itimer_func(tool, do_check,
  542.         ITIMER_REAL, &mail_timer, (struct itimerval *) 0);
  543.     }
  544. #endif /* SUNTOOL */
  545.  
  546.     turnoff(glob_flags, IGN_SIGS);
  547.  
  548.     /* Return nonzero for success, -1 if file removed */
  549.     if (write_err)
  550.     return 0;
  551.     else if (held < 0)
  552.     return -1;
  553.     else
  554.     return 1;
  555. }
  556.  
  557. /*
  558.  * check the sizes of the current folder (file) and the spool file.
  559.  * spool_size is the size in bytes of the user's main mailbox.
  560.  * last_size is the size of the _current_ folder the last time we checked.
  561.  * return true if the current folder has new mail.  check_new_mail() checks
  562.  * for new mail in the system mailbox since it checks against last_spool_size.
  563.  */
  564. mail_size()
  565. {
  566.     struct stat buf;
  567.  
  568.     if (!stat(spoolfile, &buf))
  569.     spool_size = buf.st_size;
  570.     else if (!strcmp(mailfile, spoolfile))
  571.     return 0;
  572.     if (!is_shell || ison(glob_flags, IS_SENDING))
  573.     return 0;
  574.     if (strcmp(mailfile, spoolfile) && stat(mailfile, &buf)) {
  575.     if (errno != ENOENT)
  576.         error("Unable to stat %s", mailfile);
  577.     return 0;
  578.     }
  579.     if (buf.st_size != last_size) {
  580.     last_size = buf.st_size;
  581.     return 1;
  582.     }
  583.     return 0;
  584. }
  585.  
  586. static
  587. struct mailstat {
  588.     int new, unread, deleted;
  589. } mail_stat;
  590.  
  591. void
  592. mail_status(as_prompt)
  593. {
  594.     char buf[MAXPATHLEN];
  595.     register int cnt;
  596.  
  597.     mail_stat.new = mail_stat.unread = mail_stat.deleted = 0;
  598.  
  599.     for (cnt = 0; cnt < msg_cnt; cnt++) {
  600.     if (ison(msg[cnt].m_flags, UNREAD))
  601.         mail_stat.unread++;
  602.     if (ison(msg[cnt].m_flags, DELETE))
  603.         mail_stat.deleted++;
  604.     if (isoff(msg[cnt].m_flags, OLD))
  605.         mail_stat.new++;
  606.     }
  607.     if (as_prompt) {
  608.     /* use %s in case prompt has any %'s in it */
  609.     print("%s", format_prompt(current_msg, prompt));
  610.     return;
  611.     }
  612.     (void) sprintf(buf,"\"%s\"%s: %d message%s, %d new, %d unread",
  613.     trim_filename(mailfile),
  614.     ison(glob_flags, READ_ONLY)? " [read only]" : "",
  615.     msg_cnt, (msg_cnt != 1)? "s": NO_STRING,
  616.     mail_stat.new, mail_stat.unread);
  617.     if (istool || iscurses)
  618.     (void) sprintf(buf+strlen(buf), ", %d deleted", mail_stat.deleted);
  619. #ifdef SUNTOOL
  620.     if (istool) {
  621.     static char ic_text[4];
  622.     char *lbl;
  623.     Icon icon;
  624.     extern struct pixrect mail_icon_image1, mail_icon_image2;
  625.     (void) sprintf(ic_text, "%3d", msg_cnt);
  626.     if (!(lbl = (char *)window_get(tool, FRAME_LABEL)) || strcmp(lbl, buf))
  627.         (void) window_set(tool, FRAME_LABEL, buf, NULL);
  628.     icon = (Icon) window_get(tool, FRAME_ICON);
  629.     (void) icon_set(icon,
  630.         ICON_IMAGE, ison(glob_flags, NEW_MAIL)?
  631.                 &mail_icon_image2 : &mail_icon_image1,
  632.         NULL);
  633.     if (!chk_option("quiet", "iconlabel"))
  634.         (void) icon_set(icon, ICON_LABEL, ic_text, NULL);
  635.     else
  636.         (void) icon_set(icon, ICON_LABEL, NO_STRING, NULL);
  637.     (void) window_set(tool, FRAME_ICON, icon, NULL);
  638.     } else
  639. #endif /* SUNTOOL */
  640.  
  641. #ifdef CURSES
  642.     if (iscurses) {
  643.         move (0, 0);
  644.         printw("%-3d %-.*s",
  645.         ((msg_cnt)? current_msg+1 : 0), COLS-5, buf), clrtoeol();
  646.     } else
  647. #endif /* CURSES */
  648.         puts(buf);
  649.     return;
  650. }
  651.  
  652. /*
  653.  * Construct a prompt for the given message number using the given format
  654.  */
  655. char *
  656. format_prompt(num, fmt)
  657. int num;
  658. char *fmt;
  659. {
  660.     static char buf[MAXPATHLEN];
  661.     register char *p, *b = buf, *mf;
  662.  
  663.     if (is_shell)
  664.     mf = mailfile;
  665.     else
  666.     mf = "[no folder]";
  667.  
  668.     for (p = fmt; *p; p++)
  669.     if (*p == '\\')
  670.         switch (*++p) {
  671.         case 'n': case 'r': *b++ = '\n';
  672.         when 't': *b++ = '\t';
  673.         otherwise: *b++ = *p;
  674.         }
  675.     else if (*p == '%')
  676.         switch (*++p) {
  677.         case 'm':
  678.             b += strlen(sprintf(b,"%d",(msg_cnt)? num + 1 : 0));
  679.         when 't':
  680.             b += strlen(sprintf(b, "%d", msg_cnt));
  681.         when 'd':
  682.             b += strlen(sprintf(b, "%d", mail_stat.deleted));
  683.         when 'u':
  684.             b += strlen(sprintf(b, "%d", mail_stat.unread));
  685.         when 'n':
  686.             b += strlen(sprintf(b, "%d", mail_stat.new));
  687.         when 'f':
  688.         {
  689.             char *tail = rindex(mf, '/'); 
  690.             if (tail && tail[1])
  691.             b += Strcpy(b, tail+1);
  692.             else {
  693.             /* Fall through */
  694.         case 'F':
  695.             b += Strcpy(b, mf);
  696.             }
  697.             if (ison(glob_flags, READ_ONLY))
  698.             b += Strcpy(b, " [read-only]");
  699.         }
  700.         when 'T': case 'D': case 'Y': case 'y':
  701.         case 'M': case 'N': case 'W':
  702.             b += Strcpy(b, Time(p, (long)0));
  703.         when '$':
  704.         {
  705.             struct expand var;
  706.             var.orig = p;
  707.             if (varexp(&var)) {
  708.             b += Strcpy(b, var.exp);
  709.             xfree(var.exp);
  710.             p = var.rest - 1;
  711.             }
  712.         }
  713.         otherwise: *b++ = *p;
  714.         }
  715.     else if (*p == '!')
  716.         b += strlen(sprintf(b, "%d", hist_no+1));
  717.     else
  718.         *b++ = *p;
  719.     *b = 0;
  720.     return buf;
  721. }
  722.  
  723. /*
  724.  *  For uucp mailers that use >From lines with "remote from <path>":
  725.  * (where "path" is a hostname or pathnames)
  726.  *
  727.  *  a. Set the return_path to the empty string.
  728.  *  b. For each From_ or >From_ line:
  729.  *  c. Save the username (second token).
  730.  *  d. Save the date (3-7 tokens).
  731.  *  e. If it has a "remote from" then append the remote host
  732.  *    (last token) followed by a "!" to the return_path.
  733.  *  f. If the saved username has a '@' but no '!' then convert it
  734.  *    to UUCP path form.
  735.  *  g. Append the saved username to return_path.
  736.  */
  737. parse_from(fp, path)
  738. FILE *fp;
  739. char path[];
  740. {
  741.     char user[256], buf[256]; /* max size for each line in a mail file */
  742.     register char *p;
  743.     long save_offset = ftell(fp);
  744.  
  745.     path[0] = '\0';
  746.     while (fgets(buf, sizeof buf, fp)) {
  747.     if (strncmp(buf, ">From ", 6))
  748.         break;
  749.     p = buf + 6;
  750.  
  751.     (void) sscanf(p, "%s", user);
  752.  
  753.     while (p = index(p+1, 'r')) {
  754.         if (!strncmp(p, "remote from ", 12)) {
  755.         char *p2 = path+strlen(path);
  756.         skipspaces(12);
  757.         (void) sscanf(p, "%s", p2); /* add the new machine to current path */
  758.         (void) strcat(p2, "!");
  759.         break;
  760.         }
  761.     }
  762.  
  763.     if (p)
  764.         (void) bang_form(path + strlen(path), user);
  765.     save_offset = ftell(fp);
  766.     }
  767.     (void) fseek(fp, save_offset, L_SET);
  768. }
  769.  
  770. /*
  771.  * Scan a file and select messages from it and append them to the current folder
  772.  *
  773.  * If "append" is 1, start where we left off (held in msg[cnt].m_offset)
  774.  * and scan for messages.  Append all messages found until EOF.
  775.  *
  776.  * If "append" is 2, we're merging in a new file, so start at the end of
  777.  * the present folder and append all messages found until EOF.
  778.  *
  779.  * If "append" is 0, then the message separator must exist once and
  780.  * only once.  All extra occurrences of the separator is preceded by a '>'.
  781.  * The list argument will be the message number to replace in the current
  782.  * folder with the message read in from other filename.
  783.  */
  784. load_folder(file, append, list)
  785. char *file, *list;
  786. int append;
  787. {
  788.     char    buf[BUFSIZ];
  789.     int        lines = 0, msg_found = 0, had_error = 1;
  790.     int        get_status = 1, cnt;
  791.     long    bytes, ftell();
  792.     struct msg  old;
  793.     char    *p, date[64];
  794.     FILE    *fp;
  795.     int         warn = ison(glob_flags, WARNING);
  796. #ifdef MMDF
  797.     int        begin_sep = 0; /* track beginning vs ending separators */
  798. #endif /* MMDF */
  799.  
  800.     if (!(fp = lock_fopen(file, "r"))) {
  801.     error("Unable to open %s", file);
  802.     return -1;
  803.     }
  804.  
  805.     if (append) {
  806.     cnt = msg_cnt;
  807.     (void) fseek(fp, append == 1 ? msg[cnt].m_offset : 0L, L_SET);
  808.     } else {
  809.     cnt = (int)list;
  810.     old = msg[cnt];
  811.     }
  812.  
  813.     if (isoff(glob_flags, READ_ONLY)) {
  814.     if (tmpf)
  815.         (void) fclose(tmpf);
  816.     if (!(tmpf = mask_fopen(tempfile, "a"))) {
  817.         error("Unable to open %s for appending", tempfile);
  818.         close_lock(file, fp);
  819.         return -1;
  820.     }
  821.     (void) fseek(tmpf, 0L, 2); /* assure we're at the end of the file */
  822.     } else if (append == 2) {
  823.     /* you can't merge in a folder to a read-only folder */
  824.     close_lock(file, fp);
  825.     return -1;
  826.     }
  827.  
  828. #ifdef MMDF
  829.     if (!append) {
  830.     (void) strcpy(buf, MSG_SEPARATOR);
  831.     goto do_headers;
  832.     }
  833. #endif /* MMDF */
  834.     buf[0] = 0;
  835.     while (fgets(buf, sizeof (buf), fp)) {
  836. #ifndef MSG_SEPARATOR
  837.     turnoff(glob_flags, WARNING);
  838.     if (!strncmp(buf, "From ", 5)) {
  839.         /* skip the address to find the date */
  840.         p = buf + 5;    /* skip "From " */
  841.         skipspaces(0);
  842.         if ((p = any(p, " \t")) && (p = parse_date(p + 1)) ||
  843.             /* Try once more the hard way */
  844.             (p = get_name_n_addr(buf + 5, NULL, NULL)) &&
  845.             (p = parse_date(p + 1)))
  846.         (void) strcpy(date, p);
  847.     } else
  848.         p = NULL;
  849.     if (p)
  850. #else /* MSG_SEPARATOR */
  851.     if (!strncmp(buf, MSG_SEPARATOR, strlen(MSG_SEPARATOR)))
  852. #endif /* MSG_SEPARATOR */
  853.     {
  854. #ifdef MMDF
  855.         if (!append)
  856.         (void) fputc('>', tmpf);
  857.         else if (begin_sep = !begin_sep)
  858. do_headers:
  859. #else /* MMDF */
  860.         if (!append && msg_found)
  861.         (void) fputc('>', tmpf);
  862.         else
  863. #endif /* MMDF */
  864.         {
  865.         msg_found++;
  866.         had_error = 0;
  867.         if (append && cnt == MAXMSGS-append) {
  868.             wprint("WARNING: exceeded %d messages.\n", MAXMSGS-append);
  869.             wprint("Not all messages have been loaded.\n");
  870.             msg_cnt--;
  871.             had_error++;
  872.             break;
  873.         }
  874.         if (ison(glob_flags, READ_ONLY))
  875.             bytes = ftell(fp) - strlen(buf);
  876.         else {
  877.             char path[256];
  878.             parse_from(fp, path);
  879.             if (path[0])
  880.             (void)sprintf(buf,"From %s %s", path,
  881.                         date_to_ctime(date));
  882.             bytes = ftell(tmpf);
  883.         }
  884.         /* finish up message structure from previous message.
  885.          * if this is incorporating new mail, check "lines" to
  886.          * see if previous message has already been set!
  887.          */
  888.         if (cnt && lines) {
  889.             msg[cnt-1].m_size = bytes - msg[cnt-1].m_offset;
  890.             msg[cnt-1].m_lines = lines;
  891.         }
  892.         if (isoff(glob_flags, READ_ONLY) && fputs(buf, tmpf) == -1) {
  893.             error(tempfile);
  894.             had_error++;
  895.             break;
  896.         }
  897.         msg[cnt].m_offset = bytes;
  898.         msg[cnt].m_flags = 0L;
  899. #ifdef MSG_SEPARATOR
  900.         lines = 0;
  901. #else /* MSG_SEPARATOR */
  902.         lines = 1; /* count the From_ line */
  903.         if (warn)
  904.             turnon(glob_flags, WARNING);
  905.         strdup(msg[cnt].m_date_recv, date);
  906. #endif /* MSG_SEPARATOR */
  907.         turnon(msg[cnt].m_flags, UNREAD); /* initialize */
  908.         /* we've read the "From " line(s), now read the rest of
  909.          * the message headers till we get to a blank line.
  910.          */
  911.         while (fgets(buf, sizeof (buf), fp) && (*buf != '\n')) {
  912.             p = buf;
  913. #ifdef MMDF
  914.             /* MMDF might keep the From_ line, so check for it */
  915.             if (!msg[cnt].m_date_recv && !strncmp(buf, "From ", 5)) {
  916.             p = buf + 5;    /* skip "From " */
  917.             skipspaces(0);
  918.             if ((p = any(p, " \t")) && (p = parse_date(p + 1)) ||
  919.                 /* Try once more the hard way */
  920.                 (p = get_name_n_addr(buf + 5, NULL, NULL)) &&
  921.                 (p = parse_date(p + 1)))
  922.                 strdup(msg[cnt].m_date_recv, p);
  923.             } else
  924. #endif /* MMDF */
  925.             if (!strncmp(buf, "Date:", 5))
  926.             strdup(msg[cnt].m_date_sent, parse_date(p+5));
  927.             else if (!msg[cnt].m_date_sent &&
  928.                 !strncmp(buf, "Resent-Date:", 12))
  929.             msg[cnt].m_date_sent = savestr(parse_date(p+12));
  930.             else if (!strncmp(buf, "Content-Type:", 13))
  931.             turnon(msg[cnt].m_flags, METAMAIL);
  932.             else if (!strncmp(buf, "Priority:", 9)) {
  933.             for (p += 9 ; *p != '\n'; p++) {
  934.                 if (!isalpha(*p) || upper(*p) > 'A' + MAX_PRIORITY)
  935.                 continue;
  936.                 turnon(msg[cnt].m_flags,
  937.                 M_PRIORITY(upper(*p) - 'A' + 1));
  938.             }
  939.             } else if (get_status &&
  940.                 !(get_status = strncmp(p, "Status:", 7))) {
  941.             /* new mail should not have a Status: field! */
  942.             turnon(msg[cnt].m_flags, OLD);
  943.             for (p += 7 ; *p != '\n'; p++) {
  944.                 if (isspace(*p))
  945.                 continue;
  946.                 switch(*p) {
  947.                 case 'R': turnoff(msg[cnt].m_flags, UNREAD);
  948.                 when 'P': turnon(msg[cnt].m_flags, UNREAD);
  949.                 when 'N': turnon(msg[cnt].m_flags, UNREAD);
  950.                       turnoff(msg[cnt].m_flags, OLD);
  951.                 when 'S': turnon(msg[cnt].m_flags, SAVED);
  952.                 when 'r': turnon(msg[cnt].m_flags, REPLIED);
  953.                 when 'O': ; /* do nothing */
  954.                 when 'f': turnon(msg[cnt].m_flags, FORWARD);
  955.                 when 'p': turnon(msg[cnt].m_flags, PRINTED);
  956.                 otherwise :
  957.                     if (ison(glob_flags, WARNING))
  958.                     print("unknown msg status flag: %c\n",
  959.                         *p);
  960.                 }
  961.             }
  962.             }
  963.             if (isoff(glob_flags,READ_ONLY) && fputs(buf, tmpf) == -1) {
  964.             error(tempfile);
  965.             had_error++;
  966.             break;
  967.             }
  968.             lines++;
  969.         }
  970.         if (!msg[cnt].m_date_sent || !*msg[cnt].m_date_sent)
  971.             if (!msg[cnt].m_date_recv || !*msg[cnt].m_date_recv) {
  972.             wprint("Message %d has *no* date!?\n", cnt+1);
  973.             msg[cnt].m_date_sent = msg[cnt].m_date_recv =
  974.                 "0000000000XXX";
  975.             } else
  976.             strdup(msg[cnt].m_date_sent, msg[cnt].m_date_recv);
  977.         else if (!msg[cnt].m_date_recv || !*msg[cnt].m_date_recv)
  978.             strdup(msg[cnt].m_date_recv, msg[cnt].m_date_sent);
  979.         if (had_error)
  980.             break;
  981.         if (append && list)
  982.             set_msg_bit(list, cnt);
  983.         if (append)
  984.             cnt = ++msg_cnt;
  985.         get_status = 1;
  986.         }
  987.     } else if (!msg_found && buf[0] != '\n') {
  988.         /* Allow leading blank lines, but anything else is wrong */
  989.         lines++;
  990.         had_error++;
  991.         break;
  992.     }
  993.     if (msg_found) {
  994.         lines++;
  995.         if (isoff(glob_flags, READ_ONLY) && fputs(buf, tmpf) == -1) {
  996.         error(tempfile);
  997.         had_error++;
  998.         break;
  999.         }
  1000.     }
  1001.     }
  1002. #ifndef MSG_SEPARATOR
  1003.     if (warn)
  1004.     turnon(glob_flags, WARNING);
  1005. #endif /* !MSG_SEPARATOR */
  1006.     if (msg_found && append != 1)
  1007.     turnon(glob_flags, DO_UPDATE);
  1008. #ifdef MMDF
  1009.     if (!append)
  1010.     (void) fputs(END_MSG_SEP, tmpf);
  1011. #endif /* MMDF */
  1012.     if (had_error) {
  1013.     if (!append)
  1014.         msg[cnt] = old;
  1015.     if (!msg_found) {
  1016.         if (!append)
  1017.         print("File not left in correct message format.\n");
  1018.         else if (cnt == 0) {
  1019.         if (buf[0]) 
  1020.             print("\"%s\" does not seem to be a folder\n", file);
  1021.         else
  1022.             had_error = 0;    /* empty files are OK */
  1023.         }
  1024.     }
  1025.     } else {
  1026.     if (append)
  1027.         cnt--;
  1028.     if (isoff(glob_flags, READ_ONLY))
  1029.         msg[cnt].m_size = ftell(tmpf) - msg[cnt].m_offset;
  1030.     else
  1031.         msg[cnt].m_size = ftell(fp) - msg[cnt].m_offset;
  1032.     msg[cnt].m_lines = lines;
  1033.     /* remember where we were to seek to for when we append new mail */ 
  1034.     if (append)
  1035.         cnt++;
  1036.     }
  1037.     if (append == 1) { /* merge_folders takes care of this for append == 2 */
  1038.     (void) fseek(fp, 0L, 2); /* Position at end of file */
  1039.     msg[msg_cnt].m_offset = ftell(fp);
  1040.     }
  1041.     close_lock(file, fp);
  1042.     if (isoff(glob_flags, READ_ONLY)) {
  1043.     if (had_error && msg_found && append == 1 && cnt == MAXMSGS-append) {
  1044.         wprint("Using read-only mode.\n");
  1045.         turnon(glob_flags, READ_ONLY);
  1046.         had_error = 0;    /* return successfully anyway */
  1047.     }
  1048.     (void) fclose(tmpf);
  1049.     if (!(tmpf = fopen(tempfile, "r"))) {
  1050.         error("Unable to open %s for reading", tempfile);
  1051.         return -1;
  1052.     }
  1053.     }
  1054.     return !had_error;
  1055. }
  1056.