From 533681e0f83ec4f69b3b8e9f1982ed9f089285b4 Mon Sep 17 00:00:00 2001 From: Clemens Fries Date: Tue, 1 Nov 2016 17:11:58 +0100 Subject: Initial commit --- cmd/create.go | 212 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 212 insertions(+) create mode 100644 cmd/create.go (limited to 'cmd/create.go') diff --git a/cmd/create.go b/cmd/create.go new file mode 100644 index 0000000..3d02bc7 --- /dev/null +++ b/cmd/create.go @@ -0,0 +1,212 @@ +/* create.go: help creating a new message + * + * Copyright (C) 2016 Clemens Fries + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package cmd + +import ( + "bufio" + "fmt" + "github.com/docopt/docopt.go" + . "github.com/githubert/clockrotz/common" + "io/ioutil" + "os" + "os/exec" + "path/filepath" + "strings" + "time" +) + +var usageCreate = +// tag::create[] +` +Usage: + clockrotz create [--draft=FILE] [options] + +Options: + --help Show this help. + --to=ADDR Destination address. + --from=ADDR Sender address. + --subject=ADDR Short subject. + --cc=ADDR Set "Cc". + --bcc=ADDR Set "Bcc". + --reply-to=ADDR Set "Reply-To". + --draft=FILE Use FILE from the drafts/ folder as template. +` // end::create[] + +func Create(argv []string, conf *Configuration) { + args, _ := docopt.Parse(usageCreate, argv, true, "", false) + + tmpFile, err := ioutil.TempFile("", "clockrotz") + + if err != nil { + fmt.Printf("Error while creating temporary file: %s\n", err.Error()) + os.Exit(1) + } + + // We close the file right away, because we need only its soulless + // shell, mwhaha. + tmpFile.Close() + + draftsDir := filepath.Join(conf.Get(CONF_WORKDIR), DIR_DRAFTS) + todoDir := filepath.Join(conf.Get(CONF_WORKDIR), DIR_TODO) + + defer os.Remove(tmpFile.Name()) + + message := NewMessage() + + if args["--draft"] != nil { + draft := filepath.Join(draftsDir, args["--draft"].(string)) + m, err := NewMessageFromFile(draft) + + if err != nil { + fmt.Printf("Error while reading draft: %s\n", err.Error()) + os.Exit(1) + } + + message = &m + } + + message.Conf.MergeWithDocOptArgs(CMD_USAGE, &args) + + // MergeWithDocOptArgs will also copy --draft and --help over, but we do not want + // that. + delete(message.Conf.Data, "draft") + delete(message.Conf.Data, "help") + + if message.Get("date") == "" { + // Add tomorrow's date. + message.Conf.Set("date", time.Now().AddDate(0, 0, 1).Format(DATE_FORMAT)) + } + + if message.Get("subject") == "" { + message.Conf.Set("subject", "Type subject here") + } + + if len(message.Body) == 0 { + message.Body = append(message.Body, "Add message to the world of tomorrow here.") + } + + message.WriteToFile(tmpFile.Name()) + + editor := editor() + + fmt.Printf("Opening %s using %s.\n", tmpFile.Name(), editor) + + cmd := exec.Command(editor, tmpFile.Name()) + cmd.Stdin = os.Stdin + cmd.Stdout = os.Stdout + cmd.Stderr = os.Stderr + cmd.Run() + + if cmd.ProcessState.Success() { + fmt.Printf("\nSave message? ([(y)es], (d)raft, (n)o): ") + reader := bufio.NewReader(os.Stdin) + response, err := reader.ReadString('\n') + + if err != nil { + fmt.Printf("Error when reading response: %s\n", err.Error()) + os.Exit(1) + } + + var dst string + + saved := false + + response = strings.TrimSpace(response) + + switch response { + case "y", "yes", "": + dst = filepath.Join(todoDir, filepath.Base(tmpFile.Name())+".msg") + dst, err = copyFile(tmpFile.Name(), dst, false) + saved = true + case "d", "draft": + dst = filepath.Join(draftsDir, filepath.Base(tmpFile.Name())+".msg") + dst, err = copyFile(tmpFile.Name(), dst, false) + saved = true + } + + if err != nil { + fmt.Printf("Error when saving %s: %s\n", dst, err) + } else if saved { + fmt.Printf("Saved as: %s\n", dst) + } else { + fmt.Println("Discarding message.") + } + } +} + +// Copy file from `src` to `dst`. If `overwrite` is false, then an alternative +// file name will be used and returned as string. +// TODO: Portability issues (cp) / https://github.com/golang/go/issues/8868 +func copyFile(src, dst string, overwrite bool) (string, error) { + if !overwrite { + var err error + + dst, err = nextFreeFilename(dst) + + if err != nil { + return "", err + } + } + + err := exec.Command("cp", "-f", src, dst).Run() + + return dst, err +} + +func nextFreeFilename(dst string) (string, error) { + _, e := os.Stat(dst) + + // If the file does not exist, we can use the name + if os.IsNotExist(e) { + return dst, nil + } + + alt := "" + + for i := 0; i < 255; i++ { + name := fmt.Sprintf("%s.%d", dst, i) + _, e := os.Stat(name) + + if os.IsNotExist(e) { + alt = name + break + } + } + + if alt == "" { + return "", fmt.Errorf("No suitable file name could be found.") + } + + return alt, nil +} + +func editor() string { + editor := os.Getenv("VISUAL") + + if editor != "" { + return editor + } + + editor = os.Getenv("EDITOR") + + if editor != "" { + return editor + } + + return "vi" +} -- cgit