commit
1c168b6c51
8 changed files with 826 additions and 0 deletions
@ -0,0 +1,54 @@
|
||||
package main |
||||
|
||||
import ( |
||||
"fmt" |
||||
"os" |
||||
"strconv" |
||||
"strings" |
||||
) |
||||
|
||||
func Dir(in string) error { |
||||
p := ExpandedAbsFilepath(in) |
||||
return os.Chdir(p) |
||||
} |
||||
|
||||
func Up(in []string) error { |
||||
switch len(in) { |
||||
case 0: |
||||
return nil |
||||
case 1: |
||||
return os.Chdir("..") |
||||
case 2: |
||||
num, err := strconv.Atoi(in[1]) |
||||
if err != nil || num < 0 || num > 999 { |
||||
return fmt.Errorf("Invalid argument %q", in[1]) |
||||
} |
||||
return os.Chdir(strings.Repeat("../", num)) |
||||
default: |
||||
return fmt.Errorf("Too many arguments. Expected 1, got %d", len(in)-1) |
||||
} |
||||
} |
||||
|
||||
func MakeAlias(a []string) error { |
||||
if len(a) < 2 { |
||||
return fmt.Errorf("Invalida arguments, expected 2 got %d", len(a)) |
||||
} |
||||
cl := CommandLine{} |
||||
for _, i := range a[1:] { |
||||
ce := CommandElement{value: i} |
||||
ce.InferKind() |
||||
cl.value = append(cl.value, ce) |
||||
} |
||||
alias[a[0]] = cl |
||||
return nil |
||||
} |
||||
|
||||
func SetEnv(items []string) error { |
||||
return os.Setenv(items[0], strings.Join(items[1:], " ")) |
||||
} |
||||
|
||||
func UnsetEnv(item string) error { |
||||
return os.Unsetenv(item) |
||||
} |
||||
|
||||
|
@ -0,0 +1,410 @@
|
||||
package main |
||||
|
||||
import ( |
||||
"bytes" |
||||
"fmt" |
||||
"os" |
||||
"os/exec" |
||||
"strings" |
||||
"unicode" |
||||
) |
||||
|
||||
const ( |
||||
singleString int = iota |
||||
doubleString |
||||
comElement |
||||
sysPath |
||||
pipeLine |
||||
andJoin |
||||
fileWrite |
||||
fileAppend |
||||
) |
||||
|
||||
type CommandSession struct { |
||||
CommandList []CommandGroup |
||||
} |
||||
|
||||
func (cs CommandSession) Execute() { |
||||
var err error |
||||
for i, cg := range cs.CommandList { |
||||
if len(cg.commands) > 1 { |
||||
err = cg.ExecutePipes() |
||||
} else { |
||||
err = cg.Execute() |
||||
if err != nil { |
||||
fmt.Fprintf(os.Stderr, "%s\n", err.Error()) |
||||
} |
||||
} |
||||
if i < len(cs.CommandList) && err != nil { |
||||
break |
||||
} |
||||
} |
||||
} |
||||
|
||||
type CommandGroup struct { |
||||
hasFileRedirect bool |
||||
redirectType int |
||||
RedirectTo string |
||||
commands []*exec.Cmd |
||||
} |
||||
|
||||
func (cg *CommandGroup) AddCommand(c *exec.Cmd) { |
||||
if c == nil { |
||||
return |
||||
} |
||||
cg.commands = append(cg.commands, c) |
||||
} |
||||
|
||||
func (cg CommandGroup) ExecutePipes() error { |
||||
var out bytes.Buffer |
||||
var errOut bytes.Buffer |
||||
|
||||
if initialTerm != nil { |
||||
initialTerm.ApplyMode() |
||||
} |
||||
err := Execute(&out, &errOut, cg.hasFileRedirect, cg.commands...) |
||||
if linerTerm != nil { |
||||
linerTerm.ApplyMode() |
||||
} |
||||
if cg.hasFileRedirect { |
||||
var outfile *os.File |
||||
if cg.redirectType == fileAppend { |
||||
outfile, err = os.OpenFile(cg.RedirectTo, os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0644) |
||||
} else { |
||||
outfile, err = os.Create(cg.RedirectTo) |
||||
} |
||||
if err != nil { |
||||
return err |
||||
} |
||||
defer outfile.Close() |
||||
if out.Len() > 0 { |
||||
outfile.Write(out.Bytes()) |
||||
} |
||||
if errOut.Len() > 0 { |
||||
outfile.Write(errOut.Bytes()) |
||||
} |
||||
} else { |
||||
fmt.Print(out.String()) |
||||
e := errOut.String() |
||||
if len(e) > 0 { |
||||
fmt.Print(e) |
||||
} else if err != nil { |
||||
fmt.Print(err.Error()) |
||||
} |
||||
} |
||||
|
||||
return err |
||||
} |
||||
|
||||
func (cg CommandGroup) Execute() error { |
||||
if len(cg.commands) == 0 { |
||||
return fmt.Errorf("Empty command group") |
||||
} |
||||
var err error |
||||
|
||||
if cg.commands[0].Path == "slosh-special" { |
||||
if len(cg.commands[0].Args) > 2 { |
||||
switch cg.commands[0].Args[1] { |
||||
case "cd": |
||||
err = Dir(cg.commands[0].Args[2]) |
||||
case "up": |
||||
err = Dir(cg.commands[0].Args[2]) |
||||
case "alias": |
||||
err = MakeAlias(cg.commands[0].Args[2:]) |
||||
case "set": |
||||
err = SetEnv(cg.commands[0].Args[2:]) |
||||
case "unset": |
||||
err = UnsetEnv(cg.commands[0].Args[2]) |
||||
default: |
||||
err = fmt.Errorf("Unknown builtin call\n") |
||||
} |
||||
return err |
||||
} else { |
||||
return fmt.Errorf("Invalid call to builtin\n") |
||||
} |
||||
} |
||||
|
||||
if initialTerm != nil { |
||||
initialTerm.ApplyMode() |
||||
} |
||||
|
||||
cg.commands[0].Stdin = os.Stdin |
||||
if cg.hasFileRedirect && cg.RedirectTo != "" { |
||||
var outfile *os.File |
||||
if cg.redirectType == fileAppend { |
||||
outfile, err = os.OpenFile(cg.RedirectTo, os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0644) |
||||
} else { |
||||
outfile, err = os.Create(cg.RedirectTo) |
||||
} |
||||
if err != nil { |
||||
return err |
||||
} |
||||
defer outfile.Close() |
||||
cg.commands[0].Stdout = outfile |
||||
cg.commands[0].Stderr = outfile |
||||
} else { |
||||
cg.commands[0].Stdout = os.Stdout |
||||
cg.commands[0].Stderr = os.Stderr |
||||
} |
||||
err = cg.commands[0].Run() |
||||
if err != nil { |
||||
last_exit_code = 1 |
||||
} else { |
||||
last_exit_code = cg.commands[0].ProcessState.ExitCode() |
||||
} |
||||
if linerTerm != nil { |
||||
linerTerm.ApplyMode() |
||||
} |
||||
return err |
||||
} |
||||
|
||||
type CommandLine struct { |
||||
value []CommandElement |
||||
} |
||||
|
||||
func (cl CommandLine) String() string { |
||||
var out strings.Builder |
||||
for i, c := range cl.value { |
||||
out.WriteString(c.value) |
||||
if i < len(cl.value) - 1 { |
||||
out.WriteRune(' ') |
||||
} |
||||
} |
||||
return out.String() |
||||
} |
||||
|
||||
func (cl *CommandLine) ExpandVars() { |
||||
for i, c := range cl.value { |
||||
if c.kind != singleString { |
||||
cl.value[i].value = os.ExpandEnv(c.value) |
||||
} |
||||
} |
||||
} |
||||
|
||||
func (cl *CommandLine) ExpandPaths() { |
||||
for i := range cl.value { |
||||
if cl.value[i].kind == sysPath { |
||||
cl.value[i].value = ExpandedAbsFilepath(cl.value[i].value) |
||||
} |
||||
} |
||||
} |
||||
|
||||
func (cl CommandLine) Command() *exec.Cmd { |
||||
// Expand all of the paths before calling
|
||||
cl.ExpandPaths() |
||||
|
||||
// Expand all variables
|
||||
cl.ExpandVars() |
||||
|
||||
out := make([]string, 0, len(cl.value)) |
||||
for _, c := range cl.value { |
||||
out = append(out, c.value) |
||||
} |
||||
if len(out) == 0 { |
||||
return nil |
||||
} |
||||
if cl.value[0].kind == sysPath { |
||||
f, err := os.Stat(cl.value[0].value) |
||||
if err != nil || f.IsDir() || f.Mode()&0111 == 0 { |
||||
return exec.Command("slosh-special", "cd", out[0]) |
||||
} |
||||
} else if (cl.value[0].value == "cd" || cl.value[0].value == "dir") && len(cl.value[0].value) > 1 { |
||||
return exec.Command("slosh-special", "cd", out[1]) |
||||
} else if cl.value[0].value == "up" { |
||||
arg := "" |
||||
if len(out) > 1 { |
||||
arg = out[1] |
||||
} |
||||
return exec.Command("slosh-special", "up", arg) |
||||
} else if cl.value[0].value == "set" { |
||||
return exec.Command("slosh-special", out...) |
||||
} else if cl.value[0].value == "unset" { |
||||
return exec.Command("slosh-special", out...) |
||||
} else if cl.value[0].value == "alias" { |
||||
return exec.Command("slosh-special", out...) |
||||
} |
||||
|
||||
return exec.Command(out[0], out[1:]...) |
||||
} |
||||
|
||||
func ParseCommandLine(ln string) (CommandSession, []string) { |
||||
rawOut := make([]string, 0, 5) |
||||
|
||||
comSession := CommandSession{} |
||||
comGroup := CommandGroup{} |
||||
comLine := CommandLine{} |
||||
|
||||
var prev rune = 0 |
||||
var reader strings.Builder |
||||
skippingSpace := false |
||||
inDoubleString := false |
||||
inSingleString := false |
||||
expectingFile := false |
||||
|
||||
for _, ch := range ln { |
||||
if ch == '\\' { |
||||
prev = ch |
||||
continue |
||||
} |
||||
|
||||
if !skippingSpace && unicode.IsSpace(ch) && (!inSingleString && !inDoubleString) { |
||||
skippingSpace = true |
||||
if reader.Len() > 0 { |
||||
val := reader.String() |
||||
ce := CommandElement{value: val} |
||||
kind := ce.InferKind() |
||||
switch kind { |
||||
case andJoin: |
||||
if len(comLine.value) > 0 { |
||||
comGroup.AddCommand(comLine.Command()) |
||||
} |
||||
comSession.CommandList = append(comSession.CommandList, comGroup) |
||||
comGroup = CommandGroup{} |
||||
comLine = CommandLine{} |
||||
case pipeLine: |
||||
comGroup.AddCommand(comLine.Command()) |
||||
comLine = CommandLine{} |
||||
case fileWrite, fileAppend: |
||||
comGroup.AddCommand(comLine.Command()) |
||||
expectingFile = true |
||||
comGroup.hasFileRedirect = true |
||||
comGroup.redirectType = kind |
||||
comLine = CommandLine{} |
||||
default: |
||||
if expectingFile { |
||||
expectingFile = false |
||||
comGroup.RedirectTo = val |
||||
} else { |
||||
if val, ok := alias[ce.value]; ok { |
||||
comLine.value = append(comLine.value, val.value...) |
||||
fmt.Println(comLine) |
||||
} else { |
||||
comLine.value = append(comLine.value, ce) |
||||
} |
||||
} |
||||
} |
||||
rawOut = append(rawOut, val) |
||||
reader.Reset() |
||||
} |
||||
prev = ch |
||||
continue |
||||
} |
||||
|
||||
if skippingSpace && unicode.IsSpace(ch) && (!inSingleString && !inDoubleString) { |
||||
continue |
||||
} |
||||
|
||||
if unicode.IsSpace(ch) && (!inSingleString && !inDoubleString) && prev != '\\' { |
||||
continue |
||||
} |
||||
|
||||
if unicode.IsSpace(ch) && (!inSingleString && !inDoubleString) && prev == '\\' { |
||||
reader.WriteRune(' ') |
||||
} else if ch == '"' && (!inSingleString && !inDoubleString) { |
||||
inDoubleString = true |
||||
} else if ch == '\'' && (!inSingleString && !inDoubleString) { |
||||
inSingleString = true |
||||
} else if ch =='"' && prev == '\\' { |
||||
reader.WriteRune('"') |
||||
} else if ch =='\'' && prev == '\\' { |
||||
reader.WriteRune('\'') |
||||
} else if inDoubleString && ch == '"' { |
||||
s := reader.String() |
||||
rawOut = append(rawOut, s) |
||||
inDoubleString = false |
||||
comLine.value = append(comLine.value, CommandElement{doubleString, s}) |
||||
reader.Reset() |
||||
} else if inSingleString && ch == '\'' { |
||||
s := reader.String() |
||||
rawOut = append(rawOut, s) |
||||
inSingleString = false |
||||
comLine.value = append(comLine.value, CommandElement{singleString, s}) |
||||
reader.Reset() |
||||
} else { |
||||
skippingSpace = false |
||||
reader.WriteRune(ch) |
||||
} |
||||
|
||||
prev = ch |
||||
} |
||||
if reader.Len() > 0 { |
||||
val := reader.String() |
||||
ce := CommandElement{value: val} |
||||
var kind int |
||||
if inDoubleString { |
||||
kind = doubleString |
||||
} else if inSingleString { |
||||
kind = singleString |
||||
} else { |
||||
kind = ce.InferKind() |
||||
} |
||||
switch kind { |
||||
case andJoin: |
||||
if len(comLine.value) > 0 { |
||||
comGroup.AddCommand(comLine.Command()) |
||||
} |
||||
comSession.CommandList = append(comSession.CommandList, comGroup) |
||||
comGroup = CommandGroup{} |
||||
comLine = CommandLine{} |
||||
case pipeLine: |
||||
comGroup.AddCommand(comLine.Command()) |
||||
comLine = CommandLine{} |
||||
case fileWrite, fileAppend: |
||||
comGroup.AddCommand(comLine.Command()) |
||||
expectingFile = true |
||||
comGroup.hasFileRedirect = true |
||||
comGroup.redirectType = kind |
||||
comLine = CommandLine{} |
||||
default: |
||||
if expectingFile { |
||||
expectingFile = false |
||||
comGroup.RedirectTo = val |
||||
} else { |
||||
if val, ok := alias[ce.value]; ok { |
||||
comLine.value = append(comLine.value, val.value...) |
||||
fmt.Println(comLine) |
||||
} else { |
||||
comLine.value = append(comLine.value, ce) |
||||
} |
||||
} |
||||
comGroup.AddCommand(comLine.Command()) |
||||
} |
||||
rawOut = append(rawOut, val) |
||||
} else if len(comLine.value) > 0 { |
||||
comGroup.AddCommand(comLine.Command()) |
||||
} |
||||
reader.Reset() |
||||
|
||||
comSession.CommandList = append(comSession.CommandList, comGroup) |
||||
|
||||
return comSession, rawOut |
||||
} |
||||
|
||||
type CommandElement struct { |
||||
kind int |
||||
value string |
||||
} |
||||
|
||||
func (ce CommandElement) String() string { |
||||
return ce.value |
||||
} |
||||
|
||||
func (ce *CommandElement) InferKind() int { |
||||
if isFilepath(ce.value) { |
||||
ce.kind = sysPath |
||||
return ce.kind |
||||
} |
||||
switch ce.value { |
||||
case ">": |
||||
ce.kind = fileWrite |
||||
case ">>": |
||||
ce.kind = fileAppend |
||||
case "&&": |
||||
ce.kind = andJoin |
||||
case "|": |
||||
ce.kind = pipeLine |
||||
default: |
||||
ce.kind = comElement |
||||
} |
||||
return ce.kind |
||||
} |
@ -0,0 +1,5 @@
|
||||
module git.rawtext.club/slosh |
||||
|
||||
go 1.16 |
||||
|
||||
require github.com/peterh/liner v1.2.1 |
@ -0,0 +1,4 @@
|
||||
github.com/mattn/go-runewidth v0.0.3 h1:a+kO+98RDGEfo6asOGMmpodZq4FNtnGP54yps8BzLR4= |
||||
github.com/mattn/go-runewidth v0.0.3/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU= |
||||
github.com/peterh/liner v1.2.1 h1:O4BlKaq/LWu6VRWmol4ByWfzx6MfXc5Op5HETyIy5yg= |
||||
github.com/peterh/liner v1.2.1/go.mod h1:CRroGNssyjTd/qIG2FyxByd2S8JEAZXBl4qUrZf8GS0= |
@ -0,0 +1,188 @@
|
||||
package main |
||||
|
||||
import ( |
||||
"fmt" |
||||
"io/fs" |
||||
"io/ioutil" |
||||
"os" |
||||
"os/user" |
||||
"path/filepath" |
||||
"strings" |
||||
|
||||
ln "github.com/peterh/liner" |
||||
) |
||||
|
||||
const ( |
||||
History_Filename string = "~/.slosh_history" |
||||
) |
||||
|
||||
var ( |
||||
completion_names = map[string]bool{"cd":true,"exit":true,"up":true} |
||||
slosh_vars = map[string]string{} |
||||
alias = map[string]CommandLine{} |
||||
slosh_prompt string |
||||
last_exit_code int = 0 |
||||
initialTerm ln.ModeApplier = nil |
||||
linerTerm ln.ModeApplier = nil |
||||
) |
||||
|
||||
func setUpLineInput() *ln.State { |
||||
var err error |
||||
initialTerm, err = ln.TerminalMode() |
||||
if err != nil { |
||||
initialTerm = nil |
||||
} |
||||
l := ln.NewLiner() |
||||
linerTerm, err = ln.TerminalMode() |
||||
if err != nil { |
||||
linerTerm = nil |
||||
} |
||||
l.SetCtrlCAborts(true) |
||||
l.SetTabCompletionStyle(ln.TabPrints) |
||||
l.SetCompleter(func(line string) []string { |
||||
c := make([]string, 0, 5) |
||||
if line == "" { |
||||
return c |
||||
} |
||||
|
||||
// TODO add a search for local files here
|
||||
|
||||
_, l := ParseCommandLine(line) |
||||
current := l[len(l)-1] |
||||
|
||||
if isFilepath(current) { |
||||
p := current |
||||
if !strings.HasPrefix(p, "/") && !strings.HasPrefix(p, "~") { |
||||
wd, _ := os.Getwd() |
||||
p = filepath.Join(wd, current) |
||||
} |
||||
// TODO fix this to cycle through files in a folder
|
||||
// Currently the `/` isnt getting added to folders
|
||||
// on tab press, but otherwise it works
|
||||
var rootPath string |
||||
var match string |
||||
if strings.HasSuffix(p, "/") { |
||||
rootPath = ExpandedAbsFilepath(p) |
||||
match = "" |
||||
} else { |
||||
match = filepath.Base(p) |
||||
rootPath = ExpandedAbsFilepath(filepath.Dir(p)) |
||||
} |
||||
fInfo, err := ioutil.ReadDir(rootPath) |
||||
if err == nil { |
||||
for _, f := range fInfo { |
||||
if strings.HasPrefix(f.Name(), match) { |
||||
out := f.Name() |
||||
suffix := "" |
||||
if f.IsDir() { |
||||
suffix = "/" |
||||
} |
||||
suggestion := fmt.Sprintf( |
||||
"%s %s%s", |
||||
strings.TrimSpace(strings.Join(l[:len(l)-1], " ")), |
||||
filepath.Join(rootPath, out), |
||||
suffix, |
||||
) |
||||
c = append(c, suggestion) |
||||
} |
||||
} |
||||
} |
||||
} |
||||
|
||||
for key, _ := range completion_names { |
||||
if strings.HasPrefix(key, strings.ToLower(line)) { |
||||
c = append(c, key) |
||||
} |
||||
} |
||||
return c |
||||
}) |
||||
return l |
||||
} |
||||
|
||||
func setCompletionNames() { |
||||
pathvar := os.Getenv("PATH") |
||||
elmts := strings.Split(pathvar, ":") |
||||
for i := range elmts { |
||||
filepath.Walk(elmts[i], func(path string, info fs.FileInfo, err error) error { |
||||
if err != nil { |
||||
return err |
||||
} |
||||
if info.IsDir() { |
||||
return nil |
||||
} |
||||
b := filepath.Base(path) |
||||
if b == "." { |
||||
return nil |
||||
} |
||||
completion_names[b] = true |
||||
return nil |
||||
}, |
||||
) |
||||
} |
||||
} |
||||
|
||||
func prompt(ln *ln.State) string { |
||||
val, err := ln.Prompt(get_slosh_prompt()) |
||||
if err != nil { |
||||
panic("Input read error") |
||||
} |
||||
ln.AppendHistory(val) |
||||
return val |
||||
} |
||||
|
||||
func get_slosh_prompt() string { |
||||
p := os.Getenv("SLOSH_PROMPT") |
||||
wd, _ := os.Getwd() |
||||
if p == "" { |
||||
return wd + "# " |
||||
} |
||||
short := wd |
||||
if strings.Contains(p, "%d") { |
||||
s := strings.Split(short, "/") |
||||
if len(s) > 3 { |
||||
short = fmt.Sprintf("...%s", filepath.Join(s[len(s)-3:]...)) |
||||
} |
||||
} |
||||
p = strings.ReplaceAll(p, "%D", wd) |
||||
p = strings.ReplaceAll(p, "%d", short) |
||||
p = strings.ReplaceAll(p, "%c", filepath.Base(wd)) |
||||
u, err := user.Current() |
||||
if err != nil { |
||||
p = strings.ReplaceAll(p, "%u", "???") |
||||
} else { |
||||
p = strings.ReplaceAll(p, "%u", u.Name) |
||||
} |
||||
|
||||
p = strings.ReplaceAll(p, "\\n", "\n") |
||||
p = strings.ReplaceAll(p, "\\r", "\r") |
||||
return p |
||||
} |
||||
|
||||
func initShell() *ln.State { |
||||
setCompletionNames() |
||||
ParseSloshFile() |
||||
return setUpLineInput() |
||||
} |
||||
|
||||
|
||||
func main() { |
||||
ln := initShell() |
||||
defer ln.Close() |
||||
|
||||
if f, e := os.Open(ExpandedAbsFilepath(History_Filename)); e == nil { |
||||
ln.ReadHistory(f) |
||||
f.Close() |
||||
} |
||||
|
||||
// var err error
|
||||
for { |
||||
in := prompt(ln) |
||||
if len(in) == 0 { |
||||
continue |
||||
} else if in == "exit" { |
||||
break |
||||
} |
||||
entry, _ := ParseCommandLine(in) |
||||
entry.Execute() |
||||
} |
||||
} |
@ -0,0 +1,34 @@
|
||||
package main |
||||
|
||||
import ( |
||||
"fmt" |
||||
"io/ioutil" |
||||
"os" |
||||
"path/filepath" |
||||
"strings" |
||||
) |
||||
|
||||
func ParseSloshFile() { |
||||
parseRcLines(getRcLines()) |
||||
} |
||||
|
||||
func getRcLines() []string { |
||||
fdata, err := ioutil.ReadFile(filepath.Join(HomeDir(), ".slosh")) |
||||
if err != nil { |
||||
fmt.Fprintf(os.Stderr, "Unable to read ~/.slosh\n") |
||||
return []string{} |
||||
} |
||||
return strings.Split(string(fdata), "\n") |
||||
} |
||||
|
||||
func parseRcLines(lines []string) { |
||||
for _, line := range lines { |
||||
line = strings.TrimSpace(line) |
||||
if len(line) == 0 { |
||||
continue |
||||
} |
||||
command, _ := ParseCommandLine(line) |
||||
command.Execute() |
||||
} |
||||
} |
||||
|
@ -0,0 +1,129 @@
|
||||
package main |
||||
|
||||
import ( |
||||
"bytes" |
||||
"io" |
||||
"os" |
||||
"os/exec" |
||||
"os/user" |
||||
"path/filepath" |
||||
"strings" |
||||
) |
||||
|
||||
const ( |
||||
str int = iota |
||||
path |
||||
command |
||||
pipe |
||||
redirect |
||||
) |
||||
|
||||
type component struct { |
||||
value string |
||||
kind int |
||||
} |
||||
|
||||
|
||||
func HomeDir() string { |
||||
usr, _ := user.Current() |
||||
return usr.HomeDir |
||||
} |
||||
|
||||
func isFilepath(p string) bool { |
||||
if strings.HasPrefix(p, "./") || strings.HasPrefix(p, "~") || strings.HasPrefix(p, "../") || strings.HasPrefix(p, "/") { |
||||
return true |
||||
} |
||||
wd, _ := os.Getwd() |
||||
_, err := os.Stat(filepath.Join(wd, p)) |
||||
if err != nil { |
||||
return false |
||||
} |
||||
return true |
||||
} |
||||
|
||||
func ExpandedAbsFilepath(p string) string { |
||||
if strings.HasPrefix(p, "~") { |
||||
if p == "~" || strings.HasPrefix(p, "~/") { |
||||
homedir, _ := os.UserHomeDir() |
||||
if len(p) <= 2 { |
||||
p = homedir |
||||
} else if len(p) > 2 { |
||||
p = filepath.Join(homedir, p[2:]) |
||||
} |
||||
} else { |
||||
i := strings.IndexRune(p, '/') |
||||
var u string |
||||
if i < 0 { |
||||
u = p[1:] |
||||
} else { |
||||
u = p[1:i] |
||||
} |
||||
usr, err := user.Lookup(u) |
||||
if err != nil { |
||||
p = filepath.Join("/home", u, p[i:]) |
||||
} else { |
||||
p = filepath.Join(usr.HomeDir, p[i:]) |
||||
} |
||||
} |
||||
} else if !strings.HasPrefix(p, "/") { |
||||
wd, _ := os.Getwd() |
||||
p = filepath.Join(wd, p) |
||||
} |
||||
|
||||
path, _ := filepath.Abs(p) |
||||
return path |
||||
} |
||||
|
||||
func Contains(needle string, haystack []string) bool { |
||||
var out bool |
||||
for _, x := range haystack { |
||||
if x == needle { |
||||
out = true |
||||
break |
||||
} |
||||
} |
||||
return out |
||||
} |
||||
|
||||
// Make stderr work properly. It gets eaten up...
|
||||
func Execute(output_buffer, error_buffer *bytes.Buffer, capture_output bool, stack ...*exec.Cmd) (err error) { |
||||
pipe_stack := make([]*io.PipeWriter, len(stack)-1) |
||||
i := 0 |
||||
for ; i < len(stack)-1; i++ { |
||||
stdin_pipe, stdout_pipe := io.Pipe() |
||||
stack[i].Stdout = stdout_pipe |
||||
stack[i].Stderr = error_buffer |
||||
stack[i+1].Stdin = stdin_pipe |
||||
pipe_stack[i] = stdout_pipe |
||||
} |
||||
if capture_output { |
||||
stack[i].Stdout = output_buffer |
||||
stack[i].Stderr = error_buffer |
||||
} else { |
||||
stack[i].Stdout = os.Stdout |
||||
stack[i].Stderr = os.Stderr |
||||
|
||||
} |
||||
|
||||
return call(stack, pipe_stack) |
||||
} |
||||
|
||||
func call(stack []*exec.Cmd, pipes []*io.PipeWriter) (err error) { |
||||
if stack[0].Process == nil { |
||||
if err = stack[0].Start(); err != nil { |
||||
return err |
||||
} |
||||
} |
||||
if len(stack) > 1 { |
||||
if err = stack[1].Start(); err != nil { |
||||
return err |
||||
} |
||||
defer func() { |
||||
if err == nil { |
||||
pipes[0].Close() |
||||
err = call(stack[1:], pipes[1:]) |
||||
} |
||||
}() |
||||
} |
||||
return stack[0].Wait() |
||||
} |
Loading…
Reference in new issue