<<< Date Index >>>     <<< Thread Index >>>

[PATCH] attachment-saving user interface



The attached patch fixes various aspects of the attachment-saving
user interface.  Changes include:

* When tagging and saving multiple attachments, you can now use the
  same target directory for multiple attachments.

* When you can't save an attachment, you get an opportunity to enter
  a new file name.

* The menu cursor will highlight the attachment that you are
  currently discussing with mutt.

* Various bug fixes.

This does, in particular, address #1619 (Debian#208430).

Comments welcome.
-- 
Thomas Roessler · Personal soap box at <http://log.does-not-exist.org/>.
? err
? patch-1.5.3-CVS.tlr.idna.1
? patch-1.5.4.nr.tag_prefix_cond
? patch-1.5.4.tlr.nodots.1
? patch-1.5.4.tlr.pgpsmimeautoselect.1
? patch-1.5.4.tlr.save_attachment.1
? patch-1.5.4.tlr.tag_prefix.1
? send.c.new
? sort.c.new
? stamp-h1
? typescript
Index: attach.h
===================================================================
RCS file: /cvs/mutt/mutt/attach.h,v
retrieving revision 3.1
diff -u -r3.1 attach.h
--- attach.h    11 Dec 2002 11:19:39 -0000      3.1
+++ attach.h    4 Oct 2003 20:30:15 -0000
@@ -24,7 +24,7 @@
                              int recv);
 
 
-void mutt_save_attachment_list (FILE *fp, int tag, BODY *top, HEADER *hdr);
+void mutt_save_attachment_list (FILE *fp, int tag, BODY *top, HEADER *hdr, 
MUTTMENU *menu);
 void mutt_pipe_attachment_list (FILE *fp, int tag, BODY *top, int filter);
 void mutt_print_attachment_list (FILE *fp, int tag, BODY *top);
 
Index: commands.c
===================================================================
RCS file: /cvs/mutt/mutt/commands.c,v
retrieving revision 3.19
diff -u -r3.19 commands.c
--- commands.c  5 Aug 2003 13:55:47 -0000       3.19
+++ commands.c  4 Oct 2003 20:30:16 -0000
@@ -761,7 +761,7 @@
   mutt_expand_path (buf, sizeof (buf));
 
   /* check to make sure that this file is really the one the user wants */
-  if (!mutt_save_confirm (buf, &st))
+  if (mutt_save_confirm (buf, &st) != 0)
     return -1;
 
   if (WithCrypto && need_passphrase && (decode || decrypt)
Index: compose.c
===================================================================
RCS file: /cvs/mutt/mutt/compose.c,v
retrieving revision 3.13
diff -u -r3.13 compose.c
--- compose.c   19 Sep 2003 13:03:25 -0000      3.13
+++ compose.c   4 Oct 2003 20:30:19 -0000
@@ -483,6 +483,7 @@
   idx[idxlen]->level = (idxlen > 0) ? idx[idxlen-1]->level : 0;
   if (idxlen)
     idx[idxlen - 1]->content->next = idx[idxlen]->content;
+  idx[idxlen]->content->aptr = idx[idxlen];
   menu->current = idxlen++;
   mutt_update_tree (idx, idxlen);
   menu->max = idxlen;
@@ -760,14 +761,7 @@
        idx[idxlen] = (ATTACHPTR *) safe_calloc (1, sizeof (ATTACHPTR));
        if ((idx[idxlen]->content = crypt_pgp_make_key_attachment(NULL)) != 
NULL)
        {
-         idx[idxlen]->level = (idxlen > 0) ? idx[idxlen-1]->level : 0;
-
-         if(idxlen)
-           idx[idxlen - 1]->content->next = idx[idxlen]->content;
-         
-         menu->current = idxlen++;
-         mutt_update_tree (idx, idxlen);
-         menu->max = idxlen;
+         update_idx (menu, idx, idxlen++);
          menu->redraw |= REDRAW_INDEX;
        }
        else
@@ -1187,14 +1181,7 @@
            mutt_error _("What we have here is a failure to make an 
attachment");
            continue;
          }
-         
-         idx[idxlen]->level = (idxlen > 0) ? idx[idxlen-1]->level : 0;
-         if (idxlen)
-           idx[idxlen - 1]->content->next = idx[idxlen]->content;
-         
-         menu->current = idxlen++;
-         mutt_update_tree (idx, idxlen);
-         menu->max = idxlen;
+         update_idx (menu, idx, idxlen++);
 
          idx[menu->current]->content->type = itype;
          mutt_str_replace (&idx[menu->current]->content->subtype, p);
@@ -1227,7 +1214,7 @@
 
       case OP_SAVE:
        CHECK_COUNT;
-       mutt_save_attachment_list (NULL, menu->tagprefix, menu->tagprefix ?  
msg->content : idx[menu->current]->content, NULL);
+       mutt_save_attachment_list (NULL, menu->tagprefix, menu->tagprefix ?  
msg->content : idx[menu->current]->content, NULL, menu);
        MAYBE_REDRAW (menu->redraw);
        break;
 
@@ -1392,7 +1379,10 @@
   {
     msg->content = idx[0]->content;
     for (i = 0; i < idxlen; i++)
+    {
+      idx[i]->content->aptr = NULL;
       FREE (&idx[i]);
+    }
   }
   else
     msg->content = NULL;
Index: keymap.h
===================================================================
RCS file: /cvs/mutt/mutt/keymap.h,v
retrieving revision 3.6
diff -u -r3.6 keymap.h
--- keymap.h    16 Jul 2003 11:17:31 -0000      3.6
+++ keymap.h    4 Oct 2003 20:30:20 -0000
@@ -19,6 +19,8 @@
 #ifndef KEYMAP_H
 #define KEYMAP_H
 
+#include "mapping.h"
+
 /* maximal length of a key binding sequence used for buffer in km_bindkey */
 #define MAX_SEQ 8
 
Index: lib.c
===================================================================
RCS file: /cvs/mutt/mutt/lib.c,v
retrieving revision 3.10
diff -u -r3.10 lib.c
--- lib.c       19 Sep 2003 13:03:25 -0000      3.10
+++ lib.c       4 Oct 2003 20:30:31 -0000
@@ -646,3 +646,10 @@
   snprintf (d, l, fmt, dir, fname);
   return d;
 }
+
+const char *mutt_basename (const char *f)
+{
+  const char *p = strrchr (f, '/');
+  if (p) ++p;
+  return p;
+}
Index: lib.h
===================================================================
RCS file: /cvs/mutt/mutt/lib.h,v
retrieving revision 3.7
diff -u -r3.7 lib.h
--- lib.h       19 Sep 2003 13:03:25 -0000      3.7
+++ lib.h       4 Oct 2003 20:30:31 -0000
@@ -118,6 +118,7 @@
 char *safe_strdup (const char *);
 
 const char *mutt_stristr (const char *, const char *);
+const char *mutt_basename (const char *);
 
 int mutt_copy_stream (FILE *, FILE *);
 int mutt_copy_bytes (FILE *, FILE *, size_t);
Index: menu.c
===================================================================
RCS file: /cvs/mutt/mutt/menu.c,v
retrieving revision 3.9
diff -u -r3.9 menu.c
--- menu.c      4 Oct 2003 16:41:04 -0000       3.9
+++ menu.c      4 Oct 2003 20:30:31 -0000
@@ -800,6 +800,34 @@
   }
 }
 
+int menu_redraw (MUTTMENU *menu)
+{
+  /* See if all or part of the screen needs to be updated.  */
+  if (menu->redraw & REDRAW_FULL)
+  {
+    menu_redraw_full (menu);
+    /* allow the caller to do any local configuration */
+    return (OP_REDRAW);
+  }
+  
+  if (!menu->dialog)
+    menu_check_recenter (menu);
+  
+  if (menu->redraw & REDRAW_STATUS)
+    menu_redraw_status (menu);
+  if (menu->redraw & REDRAW_INDEX)
+    menu_redraw_index (menu);
+  else if (menu->redraw & (REDRAW_MOTION | REDRAW_MOTION_RESYNCH))
+    menu_redraw_motion (menu);
+  else if (menu->redraw == REDRAW_CURRENT)
+    menu_redraw_current (menu);
+  
+  if (menu->dialog)
+    menu_redraw_prompt (menu);
+  
+  return OP_NULL;
+}
+
 int mutt_menuLoop (MUTTMENU *menu)
 {
   int i = OP_NULL;
@@ -819,28 +847,8 @@
     imap_keepalive ();
 #endif
 
-    /* See if all or part of the screen needs to be updated.  */
-    if (menu->redraw & REDRAW_FULL)
-    {
-      menu_redraw_full (menu);
-      /* allow the caller to do any local configuration */
-      return (OP_REDRAW);
-    }
-
-    if (!menu->dialog)
-      menu_check_recenter (menu);
-
-    if (menu->redraw & REDRAW_STATUS)
-      menu_redraw_status (menu);
-    if (menu->redraw & REDRAW_INDEX)
-      menu_redraw_index (menu);
-    else if (menu->redraw & (REDRAW_MOTION | REDRAW_MOTION_RESYNCH))
-      menu_redraw_motion (menu);
-    else if (menu->redraw == REDRAW_CURRENT)
-      menu_redraw_current (menu);
-
-    if (menu->dialog)
-      menu_redraw_prompt (menu);
+    if (menu_redraw (menu) == OP_REDRAW)
+      return OP_REDRAW;
     
     menu->oldcurrent = menu->current;
 
Index: mutt.h
===================================================================
RCS file: /cvs/mutt/mutt/mutt.h,v
retrieving revision 3.20
diff -u -r3.20 mutt.h
--- mutt.h      16 Jul 2003 11:17:31 -0000      3.20
+++ mutt.h      4 Oct 2003 20:30:31 -0000
@@ -598,6 +598,8 @@
   struct body *parts;           /* parts of a multipart or message/rfc822 */
   struct header *hdr;          /* header information for message/rfc822 */
 
+  struct attachptr *aptr;      /* Menu information, used in recvattach.c */
+  
   time_t stamp;                        /* time stamp of last
                                 * encoding update.
                                 */
Index: mutt_menu.h
===================================================================
RCS file: /cvs/mutt/mutt/mutt_menu.h,v
retrieving revision 3.1
diff -u -r3.1 mutt_menu.h
--- mutt_menu.h 11 Dec 2002 11:19:40 -0000      3.1
+++ mutt_menu.h 4 Oct 2003 20:30:31 -0000
@@ -83,6 +83,7 @@
 void menu_redraw_status (MUTTMENU *);
 void menu_redraw_motion (MUTTMENU *);
 void menu_redraw_current (MUTTMENU *);
+int  menu_redraw (MUTTMENU *);
 void menu_first_entry (MUTTMENU *);
 void menu_last_entry (MUTTMENU *);
 void menu_top_page (MUTTMENU *);
Index: muttlib.c
===================================================================
RCS file: /cvs/mutt/mutt/muttlib.c,v
retrieving revision 3.16
diff -u -r3.16 muttlib.c
--- muttlib.c   24 Jul 2003 18:40:50 -0000      3.16
+++ muttlib.c   4 Oct 2003 20:30:31 -0000
@@ -814,10 +814,11 @@
   
 }
 
-/* return 0 on success, -1 on error */
+/* return 0 on success, -1 on abort, 1 on error */
 int mutt_check_overwrite (const char *attname, const char *path,
-                               char *fname, size_t flen, int *append) 
+                               char *fname, size_t flen, int *append, char 
**directory) 
 {
+  int rc = 0;
   char tmp[_POSIX_PATH_MAX];
   struct stat st;
 
@@ -828,18 +829,38 @@
     return -1;
   if (S_ISDIR (st.st_mode))
   {
-    if (mutt_yesorno (_("File is a directory, save under it?"), M_YES) != 
M_YES) 
-      return (-1);
+    if (directory)
+    {
+      switch (mutt_multi_choice
+             (_("File is a directory, save under it? [(y)es, (n)o, (a)ll]"), 
_("yna")))
+      {
+       case 3:         /* all */
+         mutt_str_replace (directory, fname);
+         break;
+       case 1:         /* yes */
+         FREE (directory);
+         break;
+       case -1:        /* abort */
+         FREE (directory); 
+         return -1;
+       case  2:        /* no */
+         FREE (directory);
+         return 1;
+      }
+    }
+    else if ((rc = mutt_yesorno (_("File is a directory, save under it?"), 
M_YES)) != M_YES)
+      return (rc == M_NO) ? 1 : -1;
+
     if (!attname || !attname[0])
     {
       tmp[0] = 0;
       if (mutt_get_field (_("File under directory: "), tmp, sizeof (tmp),
                                      M_FILE | M_CLEAR) != 0 || !tmp[0])
        return (-1);
-      snprintf (fname, flen, "%s/%s", path, tmp);
+      mutt_concat_path (fname, path, tmp, flen);
     }
     else
-      snprintf (fname, flen, "%s/%s", path, attname);
+      mutt_concat_path (fname, path, mutt_basename (attname), flen);
   }
   
   if (*append == 0 && access (fname, F_OK) == 0)
@@ -848,8 +869,9 @@
            (_("File exists, (o)verwrite, (a)ppend, or (c)ancel?"), _("oac")))
     {
       case -1: /* abort */
+        return -1;
       case 3:  /* cancel */
-       return -1;
+       return 1;
 
       case 2: /* append */
         *append = M_SAVE_APPEND;
@@ -1133,11 +1155,12 @@
   return (f);
 }
 
-/* returns 1 if OK to proceed, 0 to abort */
+/* returns 0 if OK to proceed, -1 to abort, 1 to retry */
 int mutt_save_confirm (const char *s, struct stat *st)
 {
   char tmp[_POSIX_PATH_MAX];
-  int ret = 1;
+  int ret = 0;
+  int rc;
   int magic = 0;
 
   magic = mx_get_magic (s);
@@ -1146,7 +1169,7 @@
   if (magic == M_POP)
   {
     mutt_error _("Can't save message to POP mailbox.");
-    return 0;
+    return 1;
   }
 #endif
 
@@ -1155,14 +1178,16 @@
     if (magic == -1)
     {
       mutt_error (_("%s is not a mailbox!"), s);
-      return 0;
+      return 1;
     }
 
     if (option (OPTCONFIRMAPPEND))
     {
       snprintf (tmp, sizeof (tmp), _("Append messages to %s?"), s);
-      if (mutt_yesorno (tmp, M_YES) != M_YES)
-       ret = 0;
+      if ((rc = mutt_yesorno (tmp, M_YES)) == M_NO)
+       ret = 1;
+      else if (rc == -1)
+       ret = -1;
     }
   }
   else
@@ -1179,14 +1204,16 @@
        if (option (OPTCONFIRMCREATE))
        {
          snprintf (tmp, sizeof (tmp), _("Create %s?"), s);
-         if (mutt_yesorno (tmp, M_YES) != M_YES)
-           ret = 0;
+         if ((rc = mutt_yesorno (tmp, M_YES)) == M_NO)
+           ret = 1;
+         else if (rc == -1)
+           ret = -1;
        }
       }
       else
       {
        mutt_perror (s);
-       return 0;
+       return 1;
       }
     }
   }
Index: pager.c
===================================================================
RCS file: /cvs/mutt/mutt/pager.c,v
retrieving revision 3.11
diff -u -r3.11 pager.c
--- pager.c     19 Sep 2003 13:03:25 -0000      3.11
+++ pager.c     4 Oct 2003 20:30:38 -0000
@@ -2438,7 +2438,7 @@
       case OP_SAVE:
        if (IsAttach (extra))
        {
-         mutt_save_attachment_list (extra->fp, 0, extra->bdy, extra->hdr);
+         mutt_save_attachment_list (extra->fp, 0, extra->bdy, extra->hdr, 
NULL);
          break;
        }
        /* fall through */
Index: protos.h
===================================================================
RCS file: /cvs/mutt/mutt/protos.h,v
retrieving revision 3.17
diff -u -r3.17 protos.h
--- protos.h    5 Mar 2003 21:18:24 -0000       3.17
+++ protos.h    4 Oct 2003 20:30:38 -0000
@@ -248,7 +248,7 @@
 int mutt_check_menu (const char *);
 int mutt_check_mime_type (const char *);
 int mutt_check_month (const char *);
-int mutt_check_overwrite (const char *, const char *, char *, size_t, int *);
+int mutt_check_overwrite (const char *, const char *, char *, size_t, int *, 
char **);
 int mutt_check_traditional_pgp (HEADER *, int *);
 int mutt_command_complete (char *, size_t, int, int);
 int mutt_var_value_complete (char *, size_t, int);
Index: recvattach.c
===================================================================
RCS file: /cvs/mutt/mutt/recvattach.c,v
retrieving revision 3.11
diff -u -r3.11 recvattach.c
--- recvattach.c        19 Sep 2003 13:03:26 -0000      3.11
+++ recvattach.c        4 Oct 2003 20:30:38 -0000
@@ -129,6 +129,7 @@
 
       new = idx[(*idxlen)++];
       new->content = m;
+      m->aptr = new;
       new->parent_type = parent_type;
       new->level = level;
 
@@ -363,14 +364,21 @@
   return (ascii_strcasecmp (subtype, "rfc822") == 0 || ascii_strcasecmp 
(subtype, "news") == 0);
 }
 
-static int mutt_query_save_attachment (FILE *fp, BODY *body, HEADER *hdr)
+static int mutt_query_save_attachment (FILE *fp, BODY *body, HEADER *hdr, char 
**directory)
 {
+  char *prompt;
   char buf[_POSIX_PATH_MAX], tfile[_POSIX_PATH_MAX];
   int is_message;
   int append = 0;
-
-  if (body->filename)
-    strfcpy (buf, body->filename, sizeof (buf));
+  int rc;
+  
+  if (body->filename) 
+  {
+    if (directory && *directory)
+      mutt_concat_path (buf, *directory, mutt_basename (body->filename), 
sizeof (buf));
+    else
+      strfcpy (buf, body->filename, sizeof (buf));
+  }
   else if(body->hdr &&
          body->encoding != ENCBASE64 &&
          body->encoding != ENCQUOTEDPRINTABLE &&
@@ -379,45 +387,68 @@
   else
     buf[0] = 0;
 
-  if (mutt_get_field (_("Save to file: "), buf, sizeof (buf), M_FILE | 
M_CLEAR) != 0
-      || !buf[0])
-    return -1;
-
-  mutt_expand_path (buf, sizeof (buf));
-
-  is_message = (fp && 
-      body->hdr && 
-      body->encoding != ENCBASE64 && 
-      body->encoding != ENCQUOTEDPRINTABLE && 
-      mutt_is_message_type (body->type, body->subtype));
-  
-  if (is_message)
+  prompt = _("Save to file: ");
+  while (prompt)
   {
-    struct stat st;
-    
-    /* check to make sure that this file is really the one the user wants */
-    if (!mutt_save_confirm (buf, &st))
-      return -1;
-    strfcpy(tfile, buf, sizeof(tfile));
-  }
-  else
-    if (mutt_check_overwrite (body->filename, buf, tfile, sizeof (tfile), 
&append))
+    if (mutt_get_field (prompt, buf, sizeof (buf), M_FILE | M_CLEAR) != 0
+       || !buf[0])
       return -1;
-
-  mutt_message _("Saving...");
-  if (mutt_save_attachment (fp, body, tfile, append, (hdr || !is_message) ? 
hdr : body->hdr) == 0)
-  {
-    mutt_message _("Attachment saved.");
-    return 0;
+    
+    prompt = NULL;
+    mutt_expand_path (buf, sizeof (buf));
+    
+    is_message = (fp && 
+                 body->hdr && 
+                 body->encoding != ENCBASE64 && 
+                 body->encoding != ENCQUOTEDPRINTABLE && 
+                 mutt_is_message_type (body->type, body->subtype));
+    
+    if (is_message)
+    {
+      struct stat st;
+      
+      /* check to make sure that this file is really the one the user wants */
+      if ((rc = mutt_save_confirm (buf, &st)) == 1)
+      {
+       prompt = _("Save to file: ");
+       continue;
+      } 
+      else if (rc == -1)
+       return -1;
+      strfcpy(tfile, buf, sizeof(tfile));
+    }
+    else
+    {
+      if ((rc = mutt_check_overwrite (body->filename, buf, tfile, sizeof 
(tfile), &append, directory)) == -1)
+       return -1;
+      else if (rc == 1)
+      {
+       prompt = _("Save to file: ");
+       continue;
+      }
+    }
+    
+    mutt_message _("Saving...");
+    if (mutt_save_attachment (fp, body, tfile, append, (hdr || !is_message) ? 
hdr : body->hdr) == 0)
+    {
+      mutt_message _("Attachment saved.");
+      return 0;
+    }
+    else
+    {
+      prompt = _("Save to file: ");
+      continue;
+    }
   }
-  else
-    return -1;
+  return 0;
 }
-
-void mutt_save_attachment_list (FILE *fp, int tag, BODY *top, HEADER *hdr)
+    
+void mutt_save_attachment_list (FILE *fp, int tag, BODY *top, HEADER *hdr, 
MUTTMENU *menu)
 {
   char buf[_POSIX_PATH_MAX], tfile[_POSIX_PATH_MAX];
+  char *directory = NULL;
   int rc = 1;
+  int last = menu ? menu->current : -1;
   FILE *fpout;
 
   buf[0] = 0;
@@ -438,7 +469,7 @@
            return;
          mutt_expand_path (buf, sizeof (buf));
          if (mutt_check_overwrite (top->filename, buf, tfile,
-                                   sizeof (tfile), &append))
+                                   sizeof (tfile), &append, NULL))
            return;
          rc = mutt_save_attachment (fp, top, tfile, append, hdr);
          if (rc == 0 && AttachSep && (fpout = fopen (tfile,"a")) != NULL)
@@ -457,15 +488,37 @@
          }
        }
       }
-      else
-       mutt_query_save_attachment (fp, top, hdr);
+      else 
+      {
+       if (tag && menu && top->aptr)
+       {
+         menu->oldcurrent = menu->current;
+         menu->current = top->aptr->num;
+         menu_check_recenter (menu);
+         menu->redraw |= REDRAW_MOTION;
+
+         menu_redraw (menu);
+       }
+       if (mutt_query_save_attachment (fp, top, hdr, &directory) == -1)
+         break;
+      }
     }
     else if (top->parts)
-      mutt_save_attachment_list (fp, 1, top->parts, hdr);
+      mutt_save_attachment_list (fp, 1, top->parts, hdr, menu);
     if (!tag)
-      return;
+      break;
   }
 
+  FREE (&directory);
+
+  if (tag && menu)
+  {
+    menu->oldcurrent = menu->current;
+    menu->current = last;
+    menu_check_recenter (menu);
+    menu->redraw |= REDRAW_MOTION;
+  }
+  
   if (!option (OPTATTACHSPLIT) && (rc == 0))
     mutt_message _("Attachment saved.");
 }
@@ -977,9 +1030,9 @@
 
       case OP_SAVE:
        mutt_save_attachment_list (fp, menu->tagprefix, 
-                 menu->tagprefix ?  cur : idx[menu->current]->content, hdr);
+                 menu->tagprefix ?  cur : idx[menu->current]->content, hdr, 
menu);
 
-        if (option (OPTRESOLVE) && menu->current < menu->max - 1)
+        if (!menu->tagprefix && option (OPTRESOLVE) && menu->current < 
menu->max - 1)
          menu->current++;
       
         menu->redraw = REDRAW_MOTION_RESYNCH | REDRAW_FULL;
@@ -1120,6 +1173,8 @@
            continue;
          if (idx[idxmax]->content && idx[idxmax]->content->deleted)
            hdr->attach_del = 1;
+         if (idx[idxmax]->content)
+           idx[idxmax]->content->aptr = NULL;
          FREE (&idx[idxmax]->tree);
          FREE (&idx[idxmax]);
        }

Attachment: pgpkB9IXNGdXN.pgp
Description: PGP signature