diff --git a/pkg/exec/call.go b/pkg/exec/call.go deleted file mode 100644 index 8b23657..0000000 --- a/pkg/exec/call.go +++ /dev/null @@ -1,67 +0,0 @@ -package exec - -import ( - "strings" - - "gopkg.in/hedzr/errors.v3" -) - -// Call executes the command line via system (OS). -// -// DO NOT QUOTE: in 'cmd', A command line shouldn't has quoted parts. -// These are bad: -// -// cmd := "ls '/usr/bin'" -// cmd := `tar "c:/My Documents/"` -// -// Uses CallSlice if your args includes space (like 'c:/My Documents/') -func Call(cmd string, fn func(retCode int, stdoutText string)) (err error) { - a := strings.Split(cmd, " ") - err = internalCallImpl(a, fn, true) - return -} - -// CallQuiet executes the command line via system (OS) without error printing. -// -// DO NOT QUOTE: in 'cmd', A command line shouldn't has quoted parts. -// These are bad: -// -// cmd := "ls '/usr/bin'" -// cmd := `tar "c:/My Documents/"` -// -// Uses CallSliceQuiet if your args includes space (like 'c:/My Documents/') -func CallQuiet(cmd string, fn func(retCode int, stdoutText string)) (err error) { - a := strings.Split(cmd, " ") - err = internalCallImpl(a, fn, false) - return -} - -// CallSlice executes the command line via system (OS). -func CallSlice(cmd []string, fn func(retCode int, stdoutText string)) (err error) { - err = internalCallImpl(cmd, fn, true) - return -} - -// CallSliceQuiet executes the command line via system (OS) without error printing. -func CallSliceQuiet(cmd []string, fn func(retCode int, stdoutText string)) (err error) { - err = internalCallImpl(cmd, fn, false) - return -} - -// internalCallImpl executes the command line via system (OS) without error printing. -func internalCallImpl(cmd []string, fn func(retCode int, stdoutText string), autoErrReport bool) (err error) { - var ( - str string - rc int - ) - - _, str, err = RunWithOutput(cmd[0], cmd[1:]...) - if err != nil { - if autoErrReport { - err = errors.New("Error on launching '%v': %v", cmd, err) - } - return - } - fn(rc, str) - return -} diff --git a/pkg/exec/run.go b/pkg/exec/run.go deleted file mode 100644 index 74e6f6c..0000000 --- a/pkg/exec/run.go +++ /dev/null @@ -1,450 +0,0 @@ -package exec - -import ( - "bytes" - "context" - "fmt" - "io" - "io/ioutil" - "os" - "os/exec" - "strings" - "sync" - - "gopkg.in/hedzr/errors.v3" -) - -// New return a calling object to allow you to make the fluent call. -// -// Just like: -// -// exec.New().WithCommand("bash", "-c", "echo hello world!").Run() -// err = exec.New().WithCommand("bash", "-c", "echo hello world!").RunAndCheckError() -// -// Processing the invoke result: -// -// exec.New(). -// WithCommand("bash", "-c", "echo hello world!"). -// WithStdoutCaught(). -// WithOnOK(func(retCode int, stdoutText string) { }). -// WithStderrCaught(). -// WithOnError(func(err error, retCode int, stdoutText, stderrText string) { }). -// Run() -// -// Use context: -// -// exec.New(). -// WithCommandString("bash -c 'echo hello world!'", '\''). -// WithContext(context.TODO()). -// Run() -// // or double quote pieces -// exec.New(). -// WithCommandString("bash -c \"echo hello world!\""). -// Run() -// -// Auto Left-Padding if WithOnError / WithOnOK / -// WithStderrCaught / WithStdoutCaught specified -// (It's no effects when you caught stdout/stderr with -// the handlers above. In this case, do it with -// LeftPad manually). -// -// args := []string{"-al", "/usr/local/bin"} -// err := exec.New(). -// WithPadding(8). -// WithCommandArgs("ls", args...). -// RunAndCheckError() -func New(opts ...Opt) *calling { - c := &calling{} - - c.env = append(c.env, os.Environ()...) - - for _, opt := range opts { - opt(c) - } - return c -} - -func (c *calling) Run() { _ = c.run() } -func (c *calling) RunAndCheckError() error { return c.run() } - -func (c *calling) WithCommandArgs(cmd string, args ...string) *calling { - c.Cmd = exec.Command(cmd, args...) - return c -} - -// WithCommandString allows split command-line by quote -// characters (default is double-quote). -func (c *calling) WithCommandString(cmd string, quoteChars ...rune) *calling { - a := SplitCommandString(cmd, quoteChars...) - c.Cmd = exec.Command(a[0], a[1:]...) - return c -} - -func (c *calling) WithCommand(cmd ...interface{}) *calling { - var args []string - for _, a := range cmd[1:] { - if as, ok := a.([]string); ok { - args = append(args, as...) - } else if as, ok := a.(string); ok { - args = append(args, as) - } else { - args = append(args, toStringSimple(a)) - } - } - c.Cmd = exec.Command(toStringSimple(cmd[0]), args...) - return c -} - -func (c *calling) WithEnv(key, value string) *calling { - if key != "" { - chk := key + "=" - for i, kv := range c.env { - if strings.HasPrefix(kv, chk) { - c.env[i] = chk + value - return c - } - } - - c.env = append(c.env, chk+value) - } - return c -} - -func (c *calling) WithWorkDir(dir string) *calling { - c.Cmd.Dir = dir - return c -} - -func (c *calling) WithExtraFiles(files ...*os.File) *calling { - c.Cmd.ExtraFiles = files - return c -} - -func (c *calling) WithContext(ctx context.Context) *calling { - c.Cmd = exec.CommandContext(ctx, c.Cmd.Path, c.Cmd.Args[1:]...) - return c -} - -func (c *calling) WithStdoutCaught(writer ...io.Writer) *calling { - for _, w := range writer { - c.stdoutWriter = w - } - c.prepareStdoutPipe() - return c -} - -func (c *calling) WithOnOK(onOK func(retCode int, stdoutText string)) *calling { - c.onOK = onOK - return c -} - -func (c *calling) WithStderrCaught(writer ...io.Writer) *calling { - for _, w := range writer { - c.stderrWriter = w - } - c.prepareStderrPipe() - return c -} - -func (c *calling) WithOnError(onError func(err error, retCode int, stdoutText, stderrText string)) *calling { - c.onError = onError - return c -} - -// WithPadding apply left paddings to stdout/err if no -// WithOnError / WithOnOK / WithStderrCaught / WithStdoutCaught -// specified. -func (c *calling) WithPadding(leftPadding int) *calling { - c.leftPadding = leftPadding - return c -} - -func (c *calling) WithVerboseCommandLine(verbose bool) *calling { - c.verbose = verbose - return c -} - -// WithQuietOnError do NOT print error internally -func (c *calling) WithQuietOnError(quiet bool) *calling { - c.quiet = quiet - return c -} - -type calling struct { - *exec.Cmd - - err error - wg sync.WaitGroup - stdoutPiper io.ReadCloser - stderrPiper io.ReadCloser - stdoutWriter io.Writer - stderrWriter io.Writer - env []string - leftPadding int - verbose bool // print command-line in std-output dumping - - retCode int - output bytes.Buffer - slurp bytes.Buffer - - quiet bool - - onOK func(retCode int, stdoutText string) - onError func(err error, retCode int, stdoutText, stderrText string) -} - -func (c *calling) run() (err error) { - - err = c.runNow() - - var ok, er bool - if err == nil { - if c.onOK != nil { - c.onOK(c.retCode, c.output.String()) - ok = true - } - } - - if c.onError != nil && err != nil { - c.onError(err, c.retCode, c.output.String(), c.slurp.String()) - // er = true - return - } - - if !c.quiet { - if c.output.Len() > 0 && !ok { - if c.leftPadding > 0 { - fmt.Print(strings.Repeat(" ", c.leftPadding)) - } - if c.verbose { - fmt.Printf("OUTPUT // %v:\n", c.Cmd.Args) - } else { - fmt.Print("OUTPUT:\n") - } - _, _ = fmt.Fprintf(os.Stdout, "%v\n", leftPad(c.output.String(), c.leftPadding)) - } - if c.slurp.Len() > 0 && !er && c.retCode != 0 { - if c.leftPadding > 0 { - _, _ = fmt.Fprintf(os.Stderr, "%v", strings.Repeat(" ", c.leftPadding)) - } - if c.verbose { - fmt.Printf("SLURP // %v:\n", c.Cmd.Args) - } else { - fmt.Print("SLURP:\n") - } - _, _ = fmt.Fprintf(os.Stderr, "%v\n", leftPad(c.slurp.String(), c.leftPadding)) - } - if err != nil { - err = errors.New("system call failed (command-line: %q): %v", c.Args, err) - } - } - return -} - -func (c *calling) runNow() error { - if c.Cmd == nil { - return errors.New("WithCommand() hasn't called yet.") - } - - c.Cmd.Env = append(c.Cmd.Env, c.env...) - - // log.Debugf("ENV:\n%v", c.Cmd.Env) - - if (c.onOK != nil || c.leftPadding > 0) && c.stdoutPiper == nil { - c.prepareStdoutPipe() - } - - if c.stdoutPiper != nil { - defer c.stdoutPiper.Close() - c.wg.Add(1) - go func() { - defer c.wg.Done() - if c.stdoutWriter != nil { - if c.stdoutWriter == os.Stdout && c.leftPadding > 0 { - _, _ = io.Copy(&c.output, c.stdoutPiper) - b := []byte(leftPad(c.output.String(), c.leftPadding)) - _, _ = c.stdoutWriter.Write(b) - c.output.Reset() - } else { - _, _ = io.Copy(c.stdoutWriter, c.stderrPiper) - } - } else { - _, _ = io.Copy(&c.output, c.stdoutPiper) - } - }() - } else { - c.Cmd.Stdout = os.Stdout - } - - if (c.onError != nil || c.leftPadding > 0) && c.stderrPiper == nil { - c.prepareStderrPipe() - } - - if c.stderrPiper != nil { - defer c.stderrPiper.Close() - c.wg.Add(1) - go func() { - defer c.wg.Done() - if c.stderrWriter != nil { - if c.stderrWriter == os.Stderr && c.leftPadding > 0 { - _, _ = io.Copy(&c.slurp, c.stderrPiper) - b := []byte(leftPad(c.slurp.String(), c.leftPadding)) - _, _ = c.stderrWriter.Write(b) - c.slurp.Reset() - } else { - _, _ = io.Copy(c.stderrWriter, c.stderrPiper) - } - } else { - _, _ = io.Copy(&c.slurp, c.stderrPiper) - } - }() - } else { - c.Cmd.Stderr = os.Stderr - } - - if c.err = c.Cmd.Start(); c.err != nil { - // Problem while copying stdin, stdout, or stderr - c.err = fmt.Errorf("failed: %v, cmd: %q", c.err, c.Path) - return c.err - } - - // Zero exit status - // Darwin: launchctl can fail with a zero exit status, - // so check for emtpy stderr - if c.Path == "launchctl" { - slurpText, _ := ioutil.ReadAll(c.stderrPiper) - if len(slurpText) > 0 && !bytes.HasSuffix(slurpText, []byte("Operation now in progress\n")) { - c.err = fmt.Errorf("failed with stderr: %s, cmd: %q", slurpText, c.Path) - return c.err - } - } - - // slurp, _ := ioutil.ReadAll(stderr) - - c.wg.Wait() - - if c.err = c.Cmd.Wait(); c.err != nil { - exitStatus, ok := IsExitError(c.err) - if ok { - // CmdS didn't exit with a zero exit status. - c.retCode = exitStatus - c.err = errors.New("%q failed with stderr:\n%v\n ", c.Path, c.slurp.String()).WithErrors(c.err) - return c.err - } - - // An error occurred and there is no exit status. - // return 0, output, fmt.Errorf("%q failed: %v |\n stderr: %s", command, err.Error(), slurp) - c.err = errors.New("%q failed with stderr:\n%v\n ", c.Path, c.slurp.String()).WithErrors(c.err) - return c.err - } - - return nil -} - -func (c *calling) prepareStdoutPipe() { - c.stdoutPiper, c.err = c.Cmd.StdoutPipe() - if c.err != nil { - // Failed to connect pipe - c.err = fmt.Errorf("failed to connect stdout pipe: %v, cmd: %q", c.err, c.Path) - } -} - -func (c *calling) prepareStderrPipe() { - c.stderrPiper, c.err = c.Cmd.StderrPipe() - if c.err != nil { - // Failed to connect pipe - c.err = fmt.Errorf("failed to connect stderr pipe: %v, cmd: %q", c.err, c.Path) - } -} - -func (c *calling) OutputText() string { return c.output.String() } -func (c *calling) SlurpText() string { return c.slurp.String() } -func (c *calling) RetCode() int { return c.retCode } -func (c *calling) Error() error { return c.err } - -type Opt func(*calling) - -func WithCommandArgs(cmd string, args ...string) Opt { - return func(c *calling) { - c.WithCommandArgs(cmd, args...) - } -} - -func WithCommandString(cmd string) Opt { - return func(c *calling) { - c.WithCommandString(cmd) - } -} - -func WithCommand(cmd ...interface{}) Opt { - return func(c *calling) { - c.WithCommand(cmd...) - } -} - -func WithEnv(key, value string) Opt { - return func(c *calling) { - c.WithEnv(key, value) - } -} - -func WithWorkDir(dir string) Opt { - return func(c *calling) { - c.WithWorkDir(dir) - } -} - -func WithExtraFiles(files ...*os.File) Opt { - return func(c *calling) { - c.WithExtraFiles(files...) - } -} - -func WithContext(ctx context.Context) Opt { - return func(c *calling) { - c.WithContext(ctx) - } -} - -func WithStdoutCaught(writer ...io.Writer) Opt { - return func(c *calling) { - c.WithStdoutCaught(writer...) - } -} - -func WithOnOK(onOK func(retCode int, stdoutText string)) Opt { - return func(c *calling) { - c.WithOnOK(onOK) - } -} - -func WithStderrCaught(writer ...io.Writer) Opt { - return func(c *calling) { - c.WithStderrCaught(writer...) - } -} - -func WithOnError(onError func(err error, retCode int, stdoutText, stderrText string)) Opt { - return func(c *calling) { - c.WithOnError(onError) - } -} - -func WithPadding(leftPadding int) Opt { - return func(c *calling) { - c.WithPadding(leftPadding) - } -} - -func WithVerboseCommandLine(verbose bool) Opt { - return func(c *calling) { - c.WithVerboseCommandLine(verbose) - } -} - -// WithQuietOnError do NOT print error internally -func WithQuietOnError(quiet bool) Opt { - return func(c *calling) { - c.WithQuietOnError(quiet) - } -} diff --git a/pkg/exec/scripts_run.go b/pkg/exec/scripts_run.go deleted file mode 100644 index db243a9..0000000 --- a/pkg/exec/scripts_run.go +++ /dev/null @@ -1,155 +0,0 @@ -package exec - -import ( - "os" - "runtime" - "strings" -) - -// InvokeShellScripts invokes a shell script fragments with internal -// invoker (typically it's hedzr/log.exec.Run). -// -// InvokeShellScripts finds and prepares the proper shell (bash, or -// powershell, etc.), and call it by invoker. -// -// The invoker can be customized by yours, use WithScriptInvoker. -func InvokeShellScripts(scripts string, opts ...ISSOpt) (err error) { - return invokeShellScripts(scripts, opts...) -} - -type issCtx struct { - knownShell string - invoker func(command string, args ...string) (err error) - isFile bool - delayedOpts []func(c *issCtx) // not yet - expander func(source string) string - // cmd *CmdS - // args []string -} - -type ISSOpt func(c *issCtx) - -// WithScriptShell provides a predefined shell executable (short if it's -// in $PATH, or full-path by you risks). -// -// The knownShell looks like shebang. -// Such as: '/bin/bash', or '/usr/bin/env bash', ... -func WithScriptShell(knownShell string) ISSOpt { - return func(c *issCtx) { - c.knownShell = knownShell - } -} - -// // WithCmdrEnviron provides the current command hit with its args. -// // Its generally come from cmdr.CmdS.Action of a cmdr CmdS. -// func WithCmdrEnviron(cmd *CmdS, args []string) ISSOpt { -// return func(c *issCtx) { -// c.cmd, c.args = cmd, args -// } -// } - -// WithScriptInvoker provides a custom runner to run the shell and scripts. -// -// The default is exec.Run in hedzr/log package. -// -// For example: -// -// err = InvokeShellScripts("ls -l /", -// WithScriptShell("/bin/bash"), -// WithScriptIsFile(false), -// WithScriptInvoker(func(command string, args ...string) (err error) { -// err = exec.New(). -// WithCommandArgs(command, args...). -// WithOnOK(func(retCode int, stdoutText string) { -// t.Logf("%v", LeftPad(stdoutText, 4)) -// }). -// RunAndCheckError() -// return -// }), -// ) -// if err != nil { -// t.Errorf("%v", err) -// } -func WithScriptInvoker(invoker func(command string, args ...string) (err error)) ISSOpt { - return func(c *issCtx) { - c.invoker = invoker - } -} - -// WithScriptExpander providers a string expander for the given script. -// -// You may specify a special one (such as os.ExpandEnv) rather than -// internal default (a dummy functor to return the source directly). -func WithScriptExpander(expander func(source string) string) ISSOpt { - return func(c *issCtx) { - c.expander = expander - } -} - -// WithScriptIsFile provides a bool flag for flagging the given -// scripts is a shell scripts fragments or a file. -func WithScriptIsFile(isFile bool) ISSOpt { - return func(c *issCtx) { - c.isFile = isFile - } -} - -func invokeShellScripts(scripts string, opts ...ISSOpt) (err error) { - - var c issCtx - - for _, opt := range opts { - opt(&c) - } - - var a []string - - if c.knownShell == "" { - if runtime.GOOS == "windows" { - c.knownShell = "powershell.exe" - } else { - c.knownShell = "/bin/bash" - } - } else if strings.Contains(c.knownShell, "/env ") { - cc := strings.Split(c.knownShell, " ") - c.knownShell, a = cc[0], append(a, cc[1:]...) - } - - var scriptFragments string - // if c.cmd != nil { - // scriptFragments = internalGetWorker().expandTmplWithExecutiveEnv(scripts, c.cmd, c.args) - // } else { - // scriptFragments = scripts - // } - if c.expander == nil { - c.expander = os.ExpandEnv - } - scriptFragments = c.expander(scripts) - - for _, opt := range c.delayedOpts { - opt(&c) - } - - if strings.Contains(c.knownShell, "powershell") { - a = append(a, "-NoProfile", "-NonInteractive") - if c.isFile { - a = append(a, "-File", scriptFragments) - } else { - a = append(a, "-CmdS", scriptFragments) - } - } else { - if c.isFile { - a = append(a, scriptFragments) - } else { - a = append(a, "-c", scriptFragments) - } - } - - if c.invoker == nil { - c.invoker = Run - } - - err = c.invoker(c.knownShell, a...) - - return -} diff --git a/pkg/exec/scripts_run_test.go b/pkg/exec/scripts_run_test.go deleted file mode 100644 index 810cce2..0000000 --- a/pkg/exec/scripts_run_test.go +++ /dev/null @@ -1,42 +0,0 @@ -package exec - -import ( - "os" - "runtime" - "testing" -) - -func TestScriptsRun(t *testing.T) { - if runtime.GOOS == "linux" || runtime.GOOS == "darwin" { - - var err error - - err = InvokeShellScripts("ls -l /", - WithScriptShell("/bin/bash"), - WithScriptIsFile(false), - WithScriptInvoker(Run), - WithScriptExpander(os.ExpandEnv), - ) - if err != nil { - t.Errorf("%v", err) - } - - err = InvokeShellScripts("ls -l /", - WithScriptShell("/bin/bash"), - WithScriptIsFile(false), - WithScriptInvoker(func(command string, args ...string) (err error) { - err = New(). - WithCommandArgs(command, args...). - WithOnOK(func(retCode int, stdoutText string) { - t.Logf("%v", LeftPad(stdoutText, 4)) - }). - RunAndCheckError() - return - }), - ) - if err != nil { - t.Errorf("%v", err) - } - - } -} diff --git a/pkg/exec/sudo.go b/pkg/exec/sudo.go deleted file mode 100644 index 9d4a94c..0000000 --- a/pkg/exec/sudo.go +++ /dev/null @@ -1,153 +0,0 @@ -// Copyright © 2020 Hedzr Yeh. - -package exec - -import ( - "bytes" - "fmt" - "io" - "io/ioutil" - "os" - "os/exec" - "sync" - "syscall" - - "gopkg.in/hedzr/errors.v3" - - "github.com/hedzr/is" -) - -// Run runs an OS command -func Run(command string, arguments ...string) (err error) { - _, _, err = RunCommand(command, false, arguments...) - return -} - -// Sudo runs an OS command with sudo prefix -func Sudo(command string, arguments ...string) (retCode int, stdoutText string, err error) { - if is.Root() { - retCode, stdoutText, err = RunCommand(command, true, arguments...) - return - } - - var sudocmd string - sudocmd, err = exec.LookPath("sudo") - if err != nil { - return -1, "'sudo' not found", Run(command, arguments...) - } - - retCode, stdoutText, err = RunCommand(sudocmd, true, append([]string{command}, arguments...)...) - return -} - -// RunWithOutput runs an OS command and collect the result outputting -func RunWithOutput(command string, arguments ...string) (retCode int, stdoutText string, err error) { - return RunCommand(command, true, arguments...) -} - -// RunCommand runs an OS command and return outputs -func RunCommand(command string, readStdout bool, arguments ...string) (retCode int, stdoutText string, err error) { - var errText string - retCode, stdoutText, errText, err = RunCommandFull(command, readStdout, arguments...) - if errText != "" { - stdoutText += errText - } - return -} - -// RunCommandFull runs an OS command and return the all outputs -func RunCommandFull(command string, readStdout bool, arguments ...string) (retCode int, stdoutText, stderrText string, err error) { - cmd := exec.Command(command, arguments...) - - var stdout io.ReadCloser - var stderr io.ReadCloser - var output, slurp bytes.Buffer - var wg sync.WaitGroup - - if readStdout { - // Connect pipe to read Stdout - stdout, err = cmd.StdoutPipe() - if err != nil { - // Failed to connect pipe - return 0, "", "", fmt.Errorf("%q failed to connect stdout pipe: %v", command, err) - } - - defer stdout.Close() - wg.Add(1) - go func() { - defer wg.Done() - _, _ = io.Copy(&output, stdout) - }() - - } else { - cmd.Stdout = os.Stdout - // cmd.Stderr = os.Stderr - } - - // Connect pipe to read Stderr - stderr, err = cmd.StderrPipe() - if err != nil { - // Failed to connect pipe - return 0, "", "", fmt.Errorf("%q failed to connect stderr pipe: %v", command, err) - } - - defer stderr.Close() - - wg.Add(1) - go func() { - defer wg.Done() - _, _ = io.Copy(&slurp, stderr) - }() - - // Do not use cmd.Run() - if err = cmd.Start(); err != nil { - // Problem while copying stdin, stdout, or stderr - return 0, "", "", fmt.Errorf("%q failed: %v", command, err) - } - - // Zero exit status - // Darwin: launchctl can fail with a zero exit status, - // so check for emtpy stderr - if command == "launchctl" { - slurpText, _ := ioutil.ReadAll(stderr) - if len(slurpText) > 0 && !bytes.HasSuffix(slurpText, []byte("Operation now in progress\n")) { - return 0, "", "", fmt.Errorf("%q failed with stderr: %s", command, slurpText) - } - } - - // slurp, _ := ioutil.ReadAll(stderr) - - wg.Wait() - - if err = cmd.Wait(); err != nil { - exitStatus, ok := IsExitError(err) - if ok { - // CmdS didn't exit with a zero exit status. - return exitStatus, output.String(), slurp.String(), errors.New("%q failed with stderr:\n%v\n ", command, slurp.String()).WithErrors(err) - } - - // An error occurred and there is no exit status. - // return 0, output, fmt.Errorf("%q failed: %v |\n stderr: %s", command, err.Error(), slurp) - return 0, output.String(), slurp.String(), errors.New("%q failed with stderr:\n%v\n ", command, slurp.String()).WithErrors(err) - } - - // if readStdout { - // var out []byte - // out, err = ioutil.ReadAll(stdout) - // if err != nil { - // return 0, "", fmt.Errorf("%q failed while attempting to read stdout: %v", command, err) - // } else if len(out) > 0 { - // output = string(out) - // } - // } - - return 0, output.String(), slurp.String(), nil -} - -// IsEAccess detects whether err is a EACCESS errno or not -func IsEAccess(err error) bool { - if e, ok := err.(*os.PathError); ok && e.Err == syscall.EACCES { - return true - } - return false -} diff --git a/pkg/exec/sudo_nacl.go b/pkg/exec/sudo_nacl.go deleted file mode 100644 index 1e5d30c..0000000 --- a/pkg/exec/sudo_nacl.go +++ /dev/null @@ -1,19 +0,0 @@ -//go:build nacl -// +build nacl - -// Copyright © 2020 Hedzr Yeh. - -package exec - -import ( - "os/exec" -) - -// IsExitError checks the error object -func IsExitError(err error) (int, bool) { - if ee, ok := err.(*exec.ExitError); ok { - return ee.ExitCode(), true - } - - return 0, false -} diff --git a/pkg/exec/sudo_others.go b/pkg/exec/sudo_others.go deleted file mode 100644 index 788be5f..0000000 --- a/pkg/exec/sudo_others.go +++ /dev/null @@ -1,22 +0,0 @@ -//go:build !plan9 && !nacl -// +build !plan9,!nacl - -// Copyright © 2020 Hedzr Yeh. - -package exec - -import ( - "os/exec" - "syscall" -) - -// IsExitError checks the error object -func IsExitError(err error) (int, bool) { - if ee, ok := err.(*exec.ExitError); ok { - if status, ok := ee.Sys().(syscall.WaitStatus); ok { - return status.ExitStatus(), true - } - } - - return 0, false -} diff --git a/pkg/exec/sudo_plan9.go b/pkg/exec/sudo_plan9.go deleted file mode 100644 index fee9566..0000000 --- a/pkg/exec/sudo_plan9.go +++ /dev/null @@ -1,19 +0,0 @@ -//go:build plan9 -// +build plan9 - -// Copyright © 2020 Hedzr Yeh. - -package exec - -import ( - "os/exec" -) - -// IsExitError checks the error object -func IsExitError(err error) (int, bool) { - if ee, ok := err.(*exec.ExitError); ok { - return ee.ExitCode(), true - } - - return 0, false -} diff --git a/pkg/exec/sudo_test.go b/pkg/exec/sudo_test.go deleted file mode 100644 index 5904c1b..0000000 --- a/pkg/exec/sudo_test.go +++ /dev/null @@ -1,81 +0,0 @@ -// Copyright © 2020 Hedzr Yeh. - -package exec - -import ( - "io" - "io/ioutil" - "os" - "os/exec" - "runtime" - "testing" -) - -func TestRun(t *testing.T) { - _ = Run("ls", "-a") - _, _, _ = Sudo("ls", "-a") -} - -func TestRunWithOutput(t *testing.T) { - _, out, err := RunWithOutput("ls", "-la", "/not-exits") - t.Logf("stdout: %v", out) - t.Logf("stderr: %v", err) -} - -func TestRunCommand(t *testing.T) { - cmd := exec.Command("ls", "-lah", "/not-exits") - if runtime.GOOS == "windows" { - cmd = exec.Command("tasklist") - } - cmd.Stdout = os.Stdout - cmd.Stderr = os.Stderr - err := cmd.Run() - if err != nil { - t.Logf("cmd.Run() failed with %s\n", err) - } -} - -func TestCombineOutputs(t *testing.T) { - cmd := exec.Command("ls", "-lah") - out, err := cmd.CombinedOutput() - if err != nil { - t.Fatalf("cmd.Run() failed with %s\n%s", err, out) - } - t.Logf("combined out:\n%s\n", string(out)) -} - -func TestAlone(t *testing.T) { - var stdout io.ReadCloser - var stderr io.ReadCloser - var err error - cmd := exec.Command("ls", "-lah", "/not-exits") - stderr, err = cmd.StderrPipe() - if err != nil { - t.Fatal(err) - } - // var errBuffer bytes.Buffer - // cmd.Stderr = &errBuffer - stdout, err = cmd.StdoutPipe() - if err != nil { - t.Fatal(err) - } - err = cmd.Start() - if err != nil { - t.Fatal(err) - } - - slurp, _ := ioutil.ReadAll(stderr) - // fmt.Printf("%s\n", slurp) - - err = cmd.Wait() - if err != nil { - // var sb bytes.Buffer - // in := bufio.NewScanner(stderr) - // for in.Scan() { - // sb.Write(in.Bytes()) - // } - t.Logf("cmd.Run() failed with %s\n%s", err, slurp) - } - out, _ := ioutil.ReadAll(stdout) - t.Logf(" output:\n%s\n", string(out)) -} diff --git a/pkg/exec/tool.go b/pkg/exec/tool.go deleted file mode 100644 index b3d00f1..0000000 --- a/pkg/exec/tool.go +++ /dev/null @@ -1,218 +0,0 @@ -package exec - -import ( - "bufio" - "fmt" - "os/exec" - "strings" - "unicode/utf8" -) - -// LookPath searches for an executable named file in the -// directories named by the PATH environment variable. -// If file contains a slash, it is tried directly and the PATH is not consulted. -// The result may be an absolute path or a path relative to the current directory. -func LookPath(file string) (string, error) { - return exec.LookPath(file) -} - -func toStringSimple(i interface{}) string { - return fmt.Sprintf("%v", i) -} - -// StripLeftTabs removes the padding tabs at left margin. -// The least tab chars will be erased at left side of lines, and the -// tab chars beyond the least at left will be kept. -func StripLeftTabs(s string) string { return stripLeftTabs(s) } - -func stripLeftTabs(s string) string { - var lines []string - var tabs int = 1000 - var emptyLines []int - var sb strings.Builder - var line int - var noLastLF bool = !strings.HasSuffix(s, "\n") - - scanner := bufio.NewScanner(bufio.NewReader(strings.NewReader(s))) - for scanner.Scan() { - str := scanner.Text() - i, n, allTabs := 0, len(str), true - for ; i < n; i++ { - if str[i] != '\t' { - allTabs = false - if tabs > i && i > 0 { - tabs = i - break - } - } - } - if i == n && allTabs { - emptyLines = append(emptyLines, line) - } - lines = append(lines, str) - line++ - } - - pad := strings.Repeat("\t", tabs) - for i, str := range lines { - if strings.HasPrefix(str, pad) { - sb.WriteString(str[tabs:]) - } else if inIntSlice(i, emptyLines) { - } else { - sb.WriteString(str) - } - if noLastLF && i == len(lines)-1 { - break - } - sb.WriteRune('\n') - } - - return sb.String() -} - -func inIntSlice(i int, slice []int) bool { - for _, n := range slice { - if n == i { - return true - } - } - return false -} - -// LeftPad inserts spaces at beginning of each line -func LeftPad(s string, pad int) string { return leftPad(s, pad) } - -func leftPad(s string, pad int) string { - if pad <= 0 { - return s - } - - var sb strings.Builder - padStr := strings.Repeat(" ", pad) - scanner := bufio.NewScanner(bufio.NewReader(strings.NewReader(s))) - for scanner.Scan() { - sb.WriteString(padStr) - sb.WriteString(scanner.Text()) - sb.WriteRune('\n') - } - return sb.String() -} - -// StripHtmlTags aggressively strips HTML tags from a string. -// It will only keep anything between `>` and `<`. -func StripHtmlTags(s string) string { return stripHtmlTags(s) } - -const ( - htmlTagStart = 60 // Unicode `<` - htmlTagEnd = 62 // Unicode `>` -) - -// Aggressively strips HTML tags from a string. -// It will only keep anything between `>` and `<`. -func stripHtmlTags(s string) string { - // Setup a string builder and allocate enough memory for the new string. - var builder strings.Builder - builder.Grow(len(s) + utf8.UTFMax) - - in := false // True if we are inside an HTML tag. - start := 0 // The index of the previous start tag character `<` - end := 0 // The index of the previous end tag character `>` - - for i, c := range s { - // If this is the last character and we are not in an HTML tag, save it. - if (i+1) == len(s) && end >= start { - builder.WriteString(s[end:]) - } - - // Keep going if the character is not `<` or `>` - if c != htmlTagStart && c != htmlTagEnd { - continue - } - - if c == htmlTagStart { - // Only update the start if we are not in a tag. - // This make sure we strip out `<
` not just `
` - if !in { - start = i - } - in = true - - // Write the valid string between the close and start of the two tags. - builder.WriteString(s[end:start]) - continue - } - // else c == htmlTagEnd - in = false - end = i + 1 - } - s = builder.String() - return s -} - -// StripQuotes strips first and last quote char (double quote or single quote). -func StripQuotes(s string) string { return trimQuotes(s) } - -// TrimQuotes strips first and last quote char (double quote or single quote). -func TrimQuotes(s string) string { return trimQuotes(s) } - -// func trimQuotes(s string) string { -// if len(s) >= 2 { -// if c := s[len(s)-1]; s[0] == c && (c == '"' || c == '\'') { -// return s[1 : len(s)-1] -// } -// } -// return s -// } - -func trimQuotes(s string) string { - switch { - case s[0] == '\'': - if s[len(s)-1] == '\'' { - return s[1 : len(s)-1] - } - return s[1:] - case s[0] == '"': - if s[len(s)-1] == '"' { - return s[1 : len(s)-1] - } - return s[1:] - case s[len(s)-1] == '\'': - return s[0 : len(s)-1] - case s[len(s)-1] == '"': - return s[0 : len(s)-1] - } - return s -} - -// SplitCommandString allows split command-line by quote -// characters (default is double-quote). -// -// In: `bash -c 'echo hello world!'` -// Out: []string{ "bash", "-c", "echo hello world!"} -func SplitCommandString(s string, quoteChars ...rune) []string { - var qc rune = '"' - var m = map[rune]bool{qc: true} - for _, q := range quoteChars { - qc, m[q] = q, true //nolint:ineffassign - } - - quoted, ch := false, rune(0) - - a := strings.FieldsFunc(s, func(r rune) bool { - if ch == 0 { - if _, ok := m[r]; ok { - quoted, ch = !quoted, r - } - } else if ch == r { - quoted, ch = !quoted, r - } - return !quoted && r == ' ' - }) - - var b []string - for _, s := range a { - b = append(b, trimQuotes(s)) - } - - return b -} diff --git a/pkg/exec/tool_test.go b/pkg/exec/tool_test.go deleted file mode 100644 index 1eeabb9..0000000 --- a/pkg/exec/tool_test.go +++ /dev/null @@ -1,9 +0,0 @@ -package exec - -import "testing" - -func TestSplitCommandString(t *testing.T) { - in := `bash -c 'echo hello world!'` - out := SplitCommandString(in, '"', '\'') - t.Log(out) -}