1
0
mirror of https://github.com/go-task/task.git synced 2024-12-16 10:59:23 +02:00
task/vendor/github.com/mvdan/sh/interp/test.go
2017-04-24 09:47:10 -03:00

161 lines
3.5 KiB
Go

// Copyright (c) 2017, Daniel Martí <mvdan@mvdan.cc>
// See LICENSE for licensing information
package interp
import (
"os"
"os/exec"
"path"
"path/filepath"
"regexp"
"github.com/mvdan/sh/syntax"
)
// non-empty string is true, empty string is false
func (r *Runner) bashTest(expr syntax.TestExpr) string {
switch x := expr.(type) {
case *syntax.Word:
return r.loneWord(x)
case *syntax.ParenTest:
return r.bashTest(x.X)
case *syntax.BinaryTest:
if r.binTest(x.Op, r.bashTest(x.X), r.bashTest(x.Y)) {
return "1"
}
return ""
case *syntax.UnaryTest:
if r.unTest(x.Op, r.bashTest(x.X)) {
return "1"
}
return ""
}
return ""
}
func (r *Runner) binTest(op syntax.BinTestOperator, x, y string) bool {
switch op {
case syntax.TsReMatch:
re, err := regexp.Compile(y)
if err != nil {
r.exit = 2
return false
}
return re.MatchString(x)
case syntax.TsNewer:
i1, i2 := stat(x), stat(y)
if i1 == nil || i2 == nil {
return false
}
return i1.ModTime().After(i2.ModTime())
case syntax.TsOlder:
i1, i2 := stat(x), stat(y)
if i1 == nil || i2 == nil {
return false
}
return i1.ModTime().Before(i2.ModTime())
case syntax.TsDevIno:
i1, i2 := stat(x), stat(y)
return os.SameFile(i1, i2)
case syntax.TsEql:
return atoi(x) == atoi(y)
case syntax.TsNeq:
return atoi(x) != atoi(y)
case syntax.TsLeq:
return atoi(x) <= atoi(y)
case syntax.TsGeq:
return atoi(x) >= atoi(y)
case syntax.TsLss:
return atoi(x) < atoi(y)
case syntax.TsGtr:
return atoi(x) > atoi(y)
case syntax.AndTest:
return x != "" && y != ""
case syntax.OrTest:
return x != "" || y != ""
case syntax.TsEqual:
m, _ := path.Match(y, x)
return m
case syntax.TsNequal:
m, _ := path.Match(y, x)
return !m
case syntax.TsBefore:
return x < y
default: // syntax.TsAfter
return x > y
}
}
func stat(name string) os.FileInfo {
info, _ := os.Stat(name)
return info
}
func statMode(name string, mode os.FileMode) bool {
info := stat(name)
return info != nil && info.Mode()&mode != 0
}
func (r *Runner) unTest(op syntax.UnTestOperator, x string) bool {
switch op {
case syntax.TsExists:
return stat(x) != nil
case syntax.TsRegFile:
info := stat(x)
return info != nil && info.Mode().IsRegular()
case syntax.TsDirect:
return statMode(x, os.ModeDir)
//case syntax.TsCharSp:
//case syntax.TsBlckSp:
case syntax.TsNmPipe:
return statMode(x, os.ModeNamedPipe)
case syntax.TsSocket:
return statMode(x, os.ModeSocket)
case syntax.TsSmbLink:
info, _ := os.Lstat(x)
return info != nil && info.Mode()&os.ModeSymlink != 0
case syntax.TsSticky:
return statMode(x, os.ModeSticky)
case syntax.TsUIDSet:
return statMode(x, os.ModeSetuid)
case syntax.TsGIDSet:
return statMode(x, os.ModeSetgid)
//case syntax.TsGrpOwn:
//case syntax.TsUsrOwn:
//case syntax.TsModif:
case syntax.TsRead:
f, err := os.OpenFile(x, os.O_RDONLY, 0)
if err == nil {
f.Close()
}
return err == nil
case syntax.TsWrite:
f, err := os.OpenFile(x, os.O_WRONLY, 0)
if err == nil {
f.Close()
}
return err == nil
case syntax.TsExec:
// use an absolute path to not use $PATH
_, err := exec.LookPath(filepath.Join(r.Dir, x))
return err == nil
case syntax.TsNoEmpty:
info := stat(x)
return info != nil && info.Size() > 0
//case syntax.TsFdTerm:
case syntax.TsEmpStr:
return x == ""
case syntax.TsNempStr:
return x != ""
//case syntax.TsOptSet:
//case syntax.TsVarSet:
//case syntax.TsRefVar:
case syntax.TsNot:
return x == ""
default:
r.errf("unhandled unary test op: %v", op)
return false
}
}