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  }