github.com/teddydd/sh@v2.6.4+incompatible/interp/test.go (about) 1 // Copyright (c) 2017, Daniel Martà <mvdan@mvdan.cc> 2 // See LICENSE for licensing information 3 4 package interp 5 6 import ( 7 "context" 8 "fmt" 9 "os" 10 "os/exec" 11 "regexp" 12 13 "golang.org/x/crypto/ssh/terminal" 14 15 "mvdan.cc/sh/syntax" 16 ) 17 18 // non-empty string is true, empty string is false 19 func (r *Runner) bashTest(ctx context.Context, expr syntax.TestExpr, classic bool) string { 20 switch x := expr.(type) { 21 case *syntax.Word: 22 return r.document(x) 23 case *syntax.ParenTest: 24 return r.bashTest(ctx, x.X, classic) 25 case *syntax.BinaryTest: 26 switch x.Op { 27 case syntax.TsMatch, syntax.TsNoMatch: 28 str := r.literal(x.X.(*syntax.Word)) 29 yw := x.Y.(*syntax.Word) 30 if classic { // test, [ 31 lit := r.literal(yw) 32 if (str == lit) == (x.Op == syntax.TsMatch) { 33 return "1" 34 } 35 } else { // [[ 36 pattern := r.pattern(yw) 37 if match(pattern, str) == (x.Op == syntax.TsMatch) { 38 return "1" 39 } 40 } 41 return "" 42 } 43 if r.binTest(x.Op, r.bashTest(ctx, x.X, classic), r.bashTest(ctx, x.Y, classic)) { 44 return "1" 45 } 46 return "" 47 case *syntax.UnaryTest: 48 if r.unTest(ctx, x.Op, r.bashTest(ctx, x.X, classic)) { 49 return "1" 50 } 51 return "" 52 } 53 return "" 54 } 55 56 func (r *Runner) binTest(op syntax.BinTestOperator, x, y string) bool { 57 switch op { 58 case syntax.TsReMatch: 59 re, err := regexp.Compile(y) 60 if err != nil { 61 r.exit = 2 62 return false 63 } 64 return re.MatchString(x) 65 case syntax.TsNewer: 66 info1, err1 := r.stat(x) 67 info2, err2 := r.stat(y) 68 if err1 != nil || err2 != nil { 69 return false 70 } 71 return info1.ModTime().After(info2.ModTime()) 72 case syntax.TsOlder: 73 info1, err1 := r.stat(x) 74 info2, err2 := r.stat(y) 75 if err1 != nil || err2 != nil { 76 return false 77 } 78 return info1.ModTime().Before(info2.ModTime()) 79 case syntax.TsDevIno: 80 info1, err1 := r.stat(x) 81 info2, err2 := r.stat(y) 82 if err1 != nil || err2 != nil { 83 return false 84 } 85 return os.SameFile(info1, info2) 86 case syntax.TsEql: 87 return atoi(x) == atoi(y) 88 case syntax.TsNeq: 89 return atoi(x) != atoi(y) 90 case syntax.TsLeq: 91 return atoi(x) <= atoi(y) 92 case syntax.TsGeq: 93 return atoi(x) >= atoi(y) 94 case syntax.TsLss: 95 return atoi(x) < atoi(y) 96 case syntax.TsGtr: 97 return atoi(x) > atoi(y) 98 case syntax.AndTest: 99 return x != "" && y != "" 100 case syntax.OrTest: 101 return x != "" || y != "" 102 case syntax.TsBefore: 103 return x < y 104 default: // syntax.TsAfter 105 return x > y 106 } 107 } 108 109 func (r *Runner) statMode(name string, mode os.FileMode) bool { 110 info, err := r.stat(name) 111 return err == nil && info.Mode()&mode != 0 112 } 113 114 func (r *Runner) unTest(ctx context.Context, op syntax.UnTestOperator, x string) bool { 115 switch op { 116 case syntax.TsExists: 117 _, err := r.stat(x) 118 return err == nil 119 case syntax.TsRegFile: 120 info, err := r.stat(x) 121 return err == nil && info.Mode().IsRegular() 122 case syntax.TsDirect: 123 return r.statMode(x, os.ModeDir) 124 case syntax.TsCharSp: 125 return r.statMode(x, os.ModeCharDevice) 126 case syntax.TsBlckSp: 127 info, err := r.stat(x) 128 return err == nil && info.Mode()&os.ModeDevice != 0 && 129 info.Mode()&os.ModeCharDevice == 0 130 case syntax.TsNmPipe: 131 return r.statMode(x, os.ModeNamedPipe) 132 case syntax.TsSocket: 133 return r.statMode(x, os.ModeSocket) 134 case syntax.TsSmbLink: 135 info, err := os.Lstat(r.relPath(x)) 136 return err == nil && info.Mode()&os.ModeSymlink != 0 137 case syntax.TsSticky: 138 return r.statMode(x, os.ModeSticky) 139 case syntax.TsUIDSet: 140 return r.statMode(x, os.ModeSetuid) 141 case syntax.TsGIDSet: 142 return r.statMode(x, os.ModeSetgid) 143 //case syntax.TsGrpOwn: 144 //case syntax.TsUsrOwn: 145 //case syntax.TsModif: 146 case syntax.TsRead: 147 f, err := r.open(ctx, r.relPath(x), os.O_RDONLY, 0, false) 148 if err == nil { 149 f.Close() 150 } 151 return err == nil 152 case syntax.TsWrite: 153 f, err := r.open(ctx, r.relPath(x), os.O_WRONLY, 0, false) 154 if err == nil { 155 f.Close() 156 } 157 return err == nil 158 case syntax.TsExec: 159 _, err := exec.LookPath(r.relPath(x)) 160 return err == nil 161 case syntax.TsNoEmpty: 162 info, err := r.stat(x) 163 return err == nil && info.Size() > 0 164 case syntax.TsFdTerm: 165 return terminal.IsTerminal(atoi(x)) 166 case syntax.TsEmpStr: 167 return x == "" 168 case syntax.TsNempStr: 169 return x != "" 170 case syntax.TsOptSet: 171 if opt := r.optByName(x, false); opt != nil { 172 return *opt 173 } 174 return false 175 case syntax.TsVarSet: 176 return r.lookupVar(x).IsSet() 177 case syntax.TsRefVar: 178 return r.lookupVar(x).NameRef 179 case syntax.TsNot: 180 return x == "" 181 default: 182 panic(fmt.Sprintf("unhandled unary test op: %v", op)) 183 } 184 }