Add simple way to save with sudo if you forgot to open micro with sudo

If you are editing a read-only file and forgot to open micro with sudo
so you could write to it, when saving the file, micro will now give you
the option to save with sudo.

This little hack is used by vim users to achieve the same behavior, but
micro makes it nicer to use. Here is an explanation for how it works:
http://stackoverflow.com/questions/2600783/how-does-the-vim-write-with-sudo-trick-work

Fixes #158
This commit is contained in:
Zachary Yedidia
2016-06-02 13:01:13 -04:00
parent 931a895406
commit 1d52ef6c54
2 changed files with 74 additions and 3 deletions

View File

@@ -691,16 +691,29 @@ func (v *View) Save() bool {
v.Buf.Path = filename
v.Buf.Name = filename
} else {
return true
return false
}
}
err := v.Buf.Save()
if err != nil {
messenger.Error(err.Error())
if strings.HasSuffix(err.Error(), "permission denied") {
choice, _ := messenger.YesNoPrompt("Permission denied. Do you want to save this file using sudo? (y,n)")
if choice {
err = v.Buf.SaveWithSudo()
if err != nil {
messenger.Error(err.Error())
return false
}
}
messenger.Reset()
messenger.Clear()
} else {
messenger.Error(err.Error())
}
} else {
messenger.Message("Saved " + v.Buf.Path)
}
return true
return false
}
// Find opens a prompt and searches forward for the input

View File

@@ -1,9 +1,12 @@
package main
import (
"bytes"
"encoding/gob"
"io/ioutil"
"os"
"os/exec"
"os/signal"
"path/filepath"
"strings"
"time"
@@ -174,6 +177,11 @@ func (b *Buffer) Save() error {
return b.SaveAs(b.Path)
}
// SaveWithSudo saves the buffer to the default path with sudo
func (b *Buffer) SaveWithSudo() error {
return b.SaveAsWithSudo(b.Path)
}
// Serialize serializes the buffer to configDir/buffers
func (b *Buffer) Serialize() error {
if settings["savecursor"].(bool) || settings["saveundo"].(bool) {
@@ -210,6 +218,56 @@ func (b *Buffer) SaveAs(filename string) error {
return err
}
// SaveAsWithSudo is the same as SaveAs except it uses a neat trick
// with tee to use sudo so the user doesn't have to reopen micro with sudo
func (b *Buffer) SaveAsWithSudo(filename string) error {
b.UpdateRules()
b.Name = filename
b.Path = filename
// The user may have already used sudo in which case we won't need the password
// It's a bit nicer for them if they don't have to enter the password every time
_, err := RunShellCommand("sudo -v")
needPassword := err != nil
// If we need the password, we have to close the screen and ask using the shell
if needPassword {
// Shut down the screen because we're going to interact directly with the shell
screen.Fini()
screen = nil
}
// Set up everything for the command
cmd := exec.Command("sudo", "tee", filename)
cmd.Stdin = bytes.NewBufferString(b.String())
// This is a trap for Ctrl-C so that it doesn't kill micro
// Instead we trap Ctrl-C to kill the program we're running
c := make(chan os.Signal, 1)
signal.Notify(c, os.Interrupt)
go func() {
for range c {
cmd.Process.Kill()
}
}()
// Start the command
cmd.Start()
err = cmd.Wait()
// If we needed the password, we closed the screen, so we have to initialize it again
if needPassword {
// Start the screen back up
InitScreen()
}
if err == nil {
b.IsModified = false
b.ModTime, _ = GetModTime(filename)
b.Serialize()
}
return err
}
// This directly inserts value at idx, bypassing all undo/redo
func (b *Buffer) insert(idx int, value string) {
b.IsModified = true