about summary refs log tree commit diff stats
diff options
context:
space:
mode:
authorClayton Craft <clayton@craftyguy.net>2019-06-11 11:26:13 -0700
committerDrew DeVault <sir@cmpwn.com>2019-06-14 09:48:20 -0400
commite56ceb099e23cf170750758c67f00713bff1cce1 (patch)
treebe1704aa3d6034e63a7bc28c7e2ec7748925e405
parent61206c6ac6fdc3fc884626a5b1076354f8311c33 (diff)
downloadaerc-e56ceb099e23cf170750758c67f00713bff1cce1.tar.gz
Support directories in path to :save
This adds new functionality to :save in the message view for specifying
directories in the path. A new flag, -p, is also added to :save for
automatically creating any directories in the path that do not exist.

If the path ends in a / (e.g. "Downloads/mail/") or if the path is an
existing directory, the part's file name is the filename from the mail
header for the part. Otherwise, it uses the last element in the path as
the filename (e.g. 'blah.jpg' is the filename if the path is
'Downloads/mail/blah.jpg')
-rw-r--r--commands/msgview/save.go57
-rw-r--r--doc/aerc.1.scd4
2 files changed, 56 insertions, 5 deletions
diff --git a/commands/msgview/save.go b/commands/msgview/save.go
index 4dabd52..b15f42d 100644
--- a/commands/msgview/save.go
+++ b/commands/msgview/save.go
@@ -6,9 +6,11 @@ import (
 	"io"
 	"mime/quotedprintable"
 	"os"
+	"path/filepath"
 	"time"
 
 	"git.sr.ht/~sircmpwn/aerc/widgets"
+	"git.sr.ht/~sircmpwn/getopt"
 	"github.com/mitchellh/go-homedir"
 )
 
@@ -17,9 +19,24 @@ func init() {
 }
 
 func Save(aerc *widgets.Aerc, args []string) error {
-	if len(args) < 2 {
-		return errors.New("Usage: :save <path>")
+	opts, optind, err := getopt.Getopts(args, "p")
+	if err != nil {
+		return err
 	}
+	var (
+		mkdirs bool
+		path   string
+	)
+	for _, opt := range opts {
+		switch opt.Option {
+		case 'p':
+			mkdirs = true
+		}
+	}
+	if len(args) <= optind {
+		return errors.New("Usage: :save [-p] <path>")
+	}
+	path = args[optind]
 
 	mv := aerc.SelectedTab().(*widgets.MessageViewer)
 	p := mv.CurrentPart()
@@ -33,12 +50,44 @@ func Save(aerc *widgets.Aerc, args []string) error {
 			reader = quotedprintable.NewReader(reader)
 		}
 
-		target, err := homedir.Expand(args[1])
+		var pathIsDir bool
+		if path[len(path)-1:] == "/" {
+			pathIsDir = true
+		}
+		// Note: path expansion has to happen after test for trailing /,
+		// since it is stripped when path is expanded
+		path, err := homedir.Expand(path)
 		if err != nil {
 			aerc.PushError(" " + err.Error())
-			return
 		}
 
+		pathinfo, err := os.Stat(path)
+		if err == nil && pathinfo.IsDir() {
+			pathIsDir = true
+		} else if os.IsExist(err) && pathIsDir {
+			aerc.PushError("The given directory is an existing file")
+		}
+
+		// Use attachment name as filename if given path is a directory
+		save_file := filepath.Base(path)
+		save_dir := filepath.Dir(path)
+		if pathIsDir {
+			save_dir = path
+			if filename, ok := p.Part.DispositionParams["filename"]; ok {
+				save_file = filename
+			}
+		}
+		if _, err := os.Stat(save_dir); os.IsNotExist(err) {
+			if mkdirs {
+				os.MkdirAll(save_dir, 0755)
+			} else {
+				aerc.PushError("Target directory does not exist, use " +
+					":save with the -p option to create it")
+				return
+			}
+		}
+		target := filepath.Clean(filepath.Join(save_dir, save_file))
+
 		f, err := os.Create(target)
 		if err != nil {
 			aerc.PushError(" " + err.Error())
diff --git a/doc/aerc.1.scd b/doc/aerc.1.scd
index 229868f..7b09531 100644
--- a/doc/aerc.1.scd
+++ b/doc/aerc.1.scd
@@ -119,9 +119,11 @@ message list, the message in the message viewer, etc).
 	Downloads and pipes the current message part into the given shell command,
 	and opens a new terminal tab to show the result.
 
-*save* <path>
+*save* [-p] <path>
 	Saves the current message part to the given path.
 
+	*-p*: Make any directories in the path that do not exist
+
 *close*
 	Closes the message viewer.