From 02530e8c8ad94308458a33ac694e6ac9d3af4c87 Mon Sep 17 00:00:00 2001 From: xuri Date: Sat, 17 Oct 2020 00:57:12 +0800 Subject: [PATCH] Fix #701, init new formula function AND and OR, prevent formula lexer panic on retrieving the top token type --- calc.go | 296 ++++++++++++++++++++++++++++++++++++++++----------- calc_test.go | 95 +++++++++++++++++ go.mod | 8 +- go.sum | 27 ++--- 4 files changed, 337 insertions(+), 89 deletions(-) diff --git a/calc.go b/calc.go index 7b9785cc7f..111bc60c5f 100644 --- a/calc.go +++ b/calc.go @@ -93,21 +93,36 @@ type formulaArg struct { // formulaFuncs is the type of the formula functions. type formulaFuncs struct{} +// tokenPriority defined basic arithmetic operator priority. +var tokenPriority = map[string]int{ + "^": 5, + "*": 4, + "/": 4, + "+": 3, + "-": 3, + "=": 2, + "<": 2, + "<=": 2, + ">": 2, + ">=": 2, + "&": 1, +} + // CalcCellValue provides a function to get calculated cell value. This // feature is currently in working processing. Array formula, table formula // and some other formulas are not supported currently. // // Supported formulas: // -// ABS, ACOS, ACOSH, ACOT, ACOTH, ARABIC, ASIN, ASINH, ATAN2, ATANH, BASE, -// CEILING, CEILING.MATH, CEILING.PRECISE, COMBIN, COMBINA, COS, COSH, COT, -// COTH, COUNTA, CSC, CSCH, DECIMAL, DEGREES, EVEN, EXP, FACT, FACTDOUBLE, -// FLOOR, FLOOR.MATH, FLOOR.PRECISE, GCD, INT, ISBLANK, ISERR, ISERROR, -// ISEVEN, ISNA, ISNONTEXT, ISNUMBER, ISO.CEILING, ISODD, LCM, LN, LOG, -// LOG10, MDETERM, MEDIAN, MOD, MROUND, MULTINOMIAL, MUNIT, NA, ODD, PI, -// POWER, PRODUCT, QUOTIENT, RADIANS, RAND, RANDBETWEEN, ROUND, ROUNDDOWN, -// ROUNDUP, SEC, SECH, SIGN, SIN, SINH, SQRT, SQRTPI, SUM, SUMIF, SUMSQ, -// TAN, TANH, TRUNC +// ABS, ACOS, ACOSH, ACOT, ACOTH, AND, ARABIC, ASIN, ASINH, ATAN2, ATANH, +// BASE, CEILING, CEILING.MATH, CEILING.PRECISE, COMBIN, COMBINA, COS, +// COSH, COT, COTH, COUNTA, CSC, CSCH, DECIMAL, DEGREES, EVEN, EXP, FACT, +// FACTDOUBLE, FLOOR, FLOOR.MATH, FLOOR.PRECISE, GCD, INT, ISBLANK, ISERR, +// ISERROR, ISEVEN, ISNA, ISNONTEXT, ISNUMBER, ISO.CEILING, ISODD, LCM, +// LN, LOG, LOG10, MDETERM, MEDIAN, MOD, MROUND, MULTINOMIAL, MUNIT, NA, +// ODD, OR, PI, POWER, PRODUCT, QUOTIENT, RADIANS, RAND, RANDBETWEEN, +// ROUND, ROUNDDOWN, ROUNDUP, SEC, SECH, SIGN, SIN, SINH, SQRT, SQRTPI, +// SUM, SUMIF, SUMSQ, TAN, TANH, TRUNC // func (f *File) CalcCellValue(sheet, cell string) (result string, err error) { var ( @@ -131,15 +146,9 @@ func (f *File) CalcCellValue(sheet, cell string) (result string, err error) { // getPriority calculate arithmetic operator priority. func getPriority(token efp.Token) (pri int) { - var priority = map[string]int{ - "*": 2, - "/": 2, - "+": 1, - "-": 1, - } - pri, _ = priority[token.TValue] + pri, _ = tokenPriority[token.TValue] if token.TValue == "-" && token.TType == efp.TokenTypeOperatorPrefix { - pri = 3 + pri = 6 } if token.TSubType == efp.TokenSubTypeStart && token.TType == efp.TokenTypeSubexpression { // ( pri = 0 @@ -306,18 +315,96 @@ func (f *File) evalInfixExp(sheet string, tokens []efp.Token) (efp.Token, error) return opdStack.Peek().(efp.Token), err } -// calcAdd evaluate addition arithmetic operations. -func calcAdd(opdStack *Stack) error { - if opdStack.Len() < 2 { - return errors.New("formula not valid") +// calcPow evaluate exponentiation arithmetic operations. +func calcPow(rOpd, lOpd string, opdStack *Stack) error { + lOpdVal, err := strconv.ParseFloat(lOpd, 64) + if err != nil { + return err + } + rOpdVal, err := strconv.ParseFloat(rOpd, 64) + if err != nil { + return err + } + result := math.Pow(lOpdVal, rOpdVal) + opdStack.Push(efp.Token{TValue: fmt.Sprintf("%g", result), TType: efp.TokenTypeOperand, TSubType: efp.TokenSubTypeNumber}) + return nil +} + +// calcEq evaluate equal arithmetic operations. +func calcEq(rOpd, lOpd string, opdStack *Stack) error { + opdStack.Push(efp.Token{TValue: strings.ToUpper(strconv.FormatBool(rOpd == lOpd)), TType: efp.TokenTypeOperand, TSubType: efp.TokenSubTypeNumber}) + return nil +} + +// calcL evaluate less than arithmetic operations. +func calcL(rOpd, lOpd string, opdStack *Stack) error { + lOpdVal, err := strconv.ParseFloat(lOpd, 64) + if err != nil { + return err + } + rOpdVal, err := strconv.ParseFloat(rOpd, 64) + if err != nil { + return err + } + opdStack.Push(efp.Token{TValue: strings.ToUpper(strconv.FormatBool(rOpdVal > lOpdVal)), TType: efp.TokenTypeOperand, TSubType: efp.TokenSubTypeNumber}) + return nil +} + +// calcLe evaluate less than or equal arithmetic operations. +func calcLe(rOpd, lOpd string, opdStack *Stack) error { + lOpdVal, err := strconv.ParseFloat(lOpd, 64) + if err != nil { + return err + } + rOpdVal, err := strconv.ParseFloat(rOpd, 64) + if err != nil { + return err } - rOpd := opdStack.Pop().(efp.Token) - lOpd := opdStack.Pop().(efp.Token) - lOpdVal, err := strconv.ParseFloat(lOpd.TValue, 64) + opdStack.Push(efp.Token{TValue: strings.ToUpper(strconv.FormatBool(rOpdVal >= lOpdVal)), TType: efp.TokenTypeOperand, TSubType: efp.TokenSubTypeNumber}) + return nil +} + +// calcG evaluate greater than or equal arithmetic operations. +func calcG(rOpd, lOpd string, opdStack *Stack) error { + lOpdVal, err := strconv.ParseFloat(lOpd, 64) + if err != nil { + return err + } + rOpdVal, err := strconv.ParseFloat(rOpd, 64) + if err != nil { + return err + } + opdStack.Push(efp.Token{TValue: strings.ToUpper(strconv.FormatBool(rOpdVal < lOpdVal)), TType: efp.TokenTypeOperand, TSubType: efp.TokenSubTypeNumber}) + return nil +} + +// calcGe evaluate greater than or equal arithmetic operations. +func calcGe(rOpd, lOpd string, opdStack *Stack) error { + lOpdVal, err := strconv.ParseFloat(lOpd, 64) if err != nil { return err } - rOpdVal, err := strconv.ParseFloat(rOpd.TValue, 64) + rOpdVal, err := strconv.ParseFloat(rOpd, 64) + if err != nil { + return err + } + opdStack.Push(efp.Token{TValue: strings.ToUpper(strconv.FormatBool(rOpdVal <= lOpdVal)), TType: efp.TokenTypeOperand, TSubType: efp.TokenSubTypeNumber}) + return nil +} + +// calcSplice evaluate splice '&' operations. +func calcSplice(rOpd, lOpd string, opdStack *Stack) error { + opdStack.Push(efp.Token{TValue: lOpd + rOpd, TType: efp.TokenTypeOperand, TSubType: efp.TokenSubTypeNumber}) + return nil +} + +// calcAdd evaluate addition arithmetic operations. +func calcAdd(rOpd, lOpd string, opdStack *Stack) error { + lOpdVal, err := strconv.ParseFloat(lOpd, 64) + if err != nil { + return err + } + rOpdVal, err := strconv.ParseFloat(rOpd, 64) if err != nil { return err } @@ -327,17 +414,12 @@ func calcAdd(opdStack *Stack) error { } // calcSubtract evaluate subtraction arithmetic operations. -func calcSubtract(opdStack *Stack) error { - if opdStack.Len() < 2 { - return errors.New("formula not valid") - } - rOpd := opdStack.Pop().(efp.Token) - lOpd := opdStack.Pop().(efp.Token) - lOpdVal, err := strconv.ParseFloat(lOpd.TValue, 64) +func calcSubtract(rOpd, lOpd string, opdStack *Stack) error { + lOpdVal, err := strconv.ParseFloat(lOpd, 64) if err != nil { return err } - rOpdVal, err := strconv.ParseFloat(rOpd.TValue, 64) + rOpdVal, err := strconv.ParseFloat(rOpd, 64) if err != nil { return err } @@ -347,17 +429,12 @@ func calcSubtract(opdStack *Stack) error { } // calcMultiply evaluate multiplication arithmetic operations. -func calcMultiply(opdStack *Stack) error { - if opdStack.Len() < 2 { - return errors.New("formula not valid") - } - rOpd := opdStack.Pop().(efp.Token) - lOpd := opdStack.Pop().(efp.Token) - lOpdVal, err := strconv.ParseFloat(lOpd.TValue, 64) +func calcMultiply(rOpd, lOpd string, opdStack *Stack) error { + lOpdVal, err := strconv.ParseFloat(lOpd, 64) if err != nil { return err } - rOpdVal, err := strconv.ParseFloat(rOpd.TValue, 64) + rOpdVal, err := strconv.ParseFloat(rOpd, 64) if err != nil { return err } @@ -366,18 +443,13 @@ func calcMultiply(opdStack *Stack) error { return nil } -// calcDivide evaluate division arithmetic operations. -func calcDivide(opdStack *Stack) error { - if opdStack.Len() < 2 { - return errors.New("formula not valid") - } - rOpd := opdStack.Pop().(efp.Token) - lOpd := opdStack.Pop().(efp.Token) - lOpdVal, err := strconv.ParseFloat(lOpd.TValue, 64) +// calcDiv evaluate division arithmetic operations. +func calcDiv(rOpd, lOpd string, opdStack *Stack) error { + lOpdVal, err := strconv.ParseFloat(lOpd, 64) if err != nil { return err } - rOpdVal, err := strconv.ParseFloat(rOpd.TValue, 64) + rOpdVal, err := strconv.ParseFloat(rOpd, 64) if err != nil { return err } @@ -403,24 +475,36 @@ func calculate(opdStack *Stack, opt efp.Token) error { result := 0 - opdVal opdStack.Push(efp.Token{TValue: fmt.Sprintf("%g", result), TType: efp.TokenTypeOperand, TSubType: efp.TokenSubTypeNumber}) } - - if opt.TValue == "+" { - if err := calcAdd(opdStack); err != nil { - return err - } + tokenCalcFunc := map[string]func(rOpd, lOpd string, opdStack *Stack) error{ + "^": calcPow, + "*": calcMultiply, + "/": calcDiv, + "+": calcAdd, + "=": calcEq, + "<": calcL, + "<=": calcLe, + ">": calcG, + ">=": calcGe, + "&": calcSplice, } if opt.TValue == "-" && opt.TType == efp.TokenTypeOperatorInfix { - if err := calcSubtract(opdStack); err != nil { - return err + if opdStack.Len() < 2 { + return errors.New("formula not valid") } - } - if opt.TValue == "*" { - if err := calcMultiply(opdStack); err != nil { + rOpd := opdStack.Pop().(efp.Token) + lOpd := opdStack.Pop().(efp.Token) + if err := calcSubtract(rOpd.TValue, lOpd.TValue, opdStack); err != nil { return err } } - if opt.TValue == "/" { - if err := calcDivide(opdStack); err != nil { + fn, ok := tokenCalcFunc[opt.TValue] + if ok { + if opdStack.Len() < 2 { + return errors.New("formula not valid") + } + rOpd := opdStack.Pop().(efp.Token) + lOpd := opdStack.Pop().(efp.Token) + if err := fn(rOpd.TValue, lOpd.TValue, opdStack); err != nil { return err } } @@ -459,8 +543,8 @@ func (f *File) parseOperatorPrefixToken(optStack, opdStack *Stack, token efp.Tok // isOperatorPrefixToken determine if the token is parse operator prefix // token. func isOperatorPrefixToken(token efp.Token) bool { - if (token.TValue == "-" && token.TType == efp.TokenTypeOperatorPrefix) || - token.TValue == "+" || token.TValue == "-" || token.TValue == "*" || token.TValue == "/" { + _, ok := tokenPriority[token.TValue] + if (token.TValue == "-" && token.TType == efp.TokenTypeOperatorPrefix) || ok { return true } return false @@ -3140,3 +3224,87 @@ func (fn *formulaFuncs) NA(argsList *list.List) (result string, err error) { result = formulaErrorNA return } + +// Logical Functions + +// AND function tests a number of supplied conditions and returns TRUE or +// FALSE. +func (fn *formulaFuncs) AND(argsList *list.List) (result string, err error) { + if argsList.Len() == 0 { + err = errors.New("AND requires at least 1 argument") + return + } + if argsList.Len() > 30 { + err = errors.New("AND accepts at most 30 arguments") + return + } + var and = true + var val float64 + for arg := argsList.Front(); arg != nil; arg = arg.Next() { + token := arg.Value.(formulaArg) + switch token.Type { + case ArgUnknown: + continue + case ArgString: + if token.String == "TRUE" { + continue + } + if token.String == "FALSE" { + result = token.String + return + } + if val, err = strconv.ParseFloat(token.String, 64); err != nil { + err = errors.New(formulaErrorVALUE) + return + } + and = and && (val != 0) + case ArgMatrix: + // TODO + err = errors.New(formulaErrorVALUE) + return + } + } + result = strings.ToUpper(strconv.FormatBool(and)) + return +} + +// OR function tests a number of supplied conditions and returns either TRUE +// or FALSE. +func (fn *formulaFuncs) OR(argsList *list.List) (result string, err error) { + if argsList.Len() == 0 { + err = errors.New("OR requires at least 1 argument") + return + } + if argsList.Len() > 30 { + err = errors.New("OR accepts at most 30 arguments") + return + } + var or bool + var val float64 + for arg := argsList.Front(); arg != nil; arg = arg.Next() { + token := arg.Value.(formulaArg) + switch token.Type { + case ArgUnknown: + continue + case ArgString: + if token.String == "FALSE" { + continue + } + if token.String == "TRUE" { + or = true + continue + } + if val, err = strconv.ParseFloat(token.String, 64); err != nil { + err = errors.New(formulaErrorVALUE) + return + } + or = val != 0 + case ArgMatrix: + // TODO + err = errors.New(formulaErrorVALUE) + return + } + } + result = strings.ToUpper(strconv.FormatBool(or)) + return +} diff --git a/calc_test.go b/calc_test.go index 7d7b886e43..02b51610a1 100644 --- a/calc_test.go +++ b/calc_test.go @@ -1,7 +1,9 @@ package excelize import ( + "container/list" "path/filepath" + "strings" "testing" "github.com/stretchr/testify/assert" @@ -31,6 +33,18 @@ func TestCalcCellValue(t *testing.T) { } mathCalc := map[string]string{ + "=2^3": "8", + "=1=1": "TRUE", + "=1=2": "FALSE", + "=1<2": "TRUE", + "=3<2": "FALSE", + "=2<=3": "TRUE", + "=2<=1": "FALSE", + "=2>1": "TRUE", + "=2>3": "FALSE", + "=2>=1": "TRUE", + "=2>=3": "FALSE", + "=1&2": "12", // ABS "=ABS(-1)": "1", "=ABS(-6.5)": "6.5", @@ -429,6 +443,20 @@ func TestCalcCellValue(t *testing.T) { "=ISODD(A2)": "FALSE", // NA "=NA()": "#N/A", + // AND + "=AND(0)": "FALSE", + "=AND(1)": "TRUE", + "=AND(1,0)": "FALSE", + "=AND(0,1)": "FALSE", + "=AND(1=1)": "TRUE", + "=AND(1<2)": "TRUE", + "=AND(1>2,2<3,2>0,3>1)": "FALSE", + "=AND(1=1),1=1": "TRUE", + // OR + "=OR(1)": "TRUE", + "=OR(0)": "FALSE", + "=OR(1=2,2=2)": "TRUE", + "=OR(1=2,2=3)": "FALSE", } for formula, expected := range mathCalc { f := prepareData() @@ -728,6 +756,16 @@ func TestCalcCellValue(t *testing.T) { `=ISODD("text")`: "#VALUE!", // NA "=NA(1)": "NA accepts no arguments", + // AND + `=AND("text")`: "#VALUE!", + `=AND(A1:B1)`: "#VALUE!", + "=AND()": "AND requires at least 1 argument", + "=AND(1" + strings.Repeat(",1", 30) + ")": "AND accepts at most 30 arguments", + // OR + `=OR("text")`: "#VALUE!", + `=OR(A1:B1)`: "#VALUE!", + "=OR()": "OR requires at least 1 argument", + "=OR(1" + strings.Repeat(",1", 30) + ")": "OR accepts at most 30 arguments", } for formula, expected := range mathCalcError { f := prepareData() @@ -829,6 +867,63 @@ func TestCalcCellValueWithDefinedName(t *testing.T) { assert.Equal(t, "B1 value", result, "=defined_name1") } +func TestCalcPow(t *testing.T) { + err := `strconv.ParseFloat: parsing "text": invalid syntax` + assert.EqualError(t, calcPow("1", "text", nil), err) + assert.EqualError(t, calcPow("text", "1", nil), err) + assert.EqualError(t, calcL("1", "text", nil), err) + assert.EqualError(t, calcL("text", "1", nil), err) + assert.EqualError(t, calcLe("1", "text", nil), err) + assert.EqualError(t, calcLe("text", "1", nil), err) + assert.EqualError(t, calcG("1", "text", nil), err) + assert.EqualError(t, calcG("text", "1", nil), err) + assert.EqualError(t, calcGe("1", "text", nil), err) + assert.EqualError(t, calcGe("text", "1", nil), err) + assert.EqualError(t, calcAdd("1", "text", nil), err) + assert.EqualError(t, calcAdd("text", "1", nil), err) + assert.EqualError(t, calcAdd("1", "text", nil), err) + assert.EqualError(t, calcAdd("text", "1", nil), err) + assert.EqualError(t, calcSubtract("1", "text", nil), err) + assert.EqualError(t, calcSubtract("text", "1", nil), err) + assert.EqualError(t, calcMultiply("1", "text", nil), err) + assert.EqualError(t, calcMultiply("text", "1", nil), err) + assert.EqualError(t, calcDiv("1", "text", nil), err) + assert.EqualError(t, calcDiv("text", "1", nil), err) +} + +func TestISBLANK(t *testing.T) { + argsList := list.New() + argsList.PushBack(formulaArg{ + Type: ArgUnknown, + }) + fn := formulaFuncs{} + result, err := fn.ISBLANK(argsList) + assert.Equal(t, result, "TRUE") + assert.NoError(t, err) +} + +func TestAND(t *testing.T) { + argsList := list.New() + argsList.PushBack(formulaArg{ + Type: ArgUnknown, + }) + fn := formulaFuncs{} + result, err := fn.AND(argsList) + assert.Equal(t, result, "TRUE") + assert.NoError(t, err) +} + +func TestOR(t *testing.T) { + argsList := list.New() + argsList.PushBack(formulaArg{ + Type: ArgUnknown, + }) + fn := formulaFuncs{} + result, err := fn.OR(argsList) + assert.Equal(t, result, "FALSE") + assert.NoError(t, err) +} + func TestDet(t *testing.T) { assert.Equal(t, det([][]float64{ {1, 2, 3, 4}, diff --git a/go.mod b/go.mod index 4b6e77801f..773f0a3914 100644 --- a/go.mod +++ b/go.mod @@ -5,10 +5,8 @@ go 1.11 require ( github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826 github.com/richardlehane/mscfb v1.0.3 - github.com/stretchr/testify v1.6.1 - github.com/xuri/efp v0.0.0-20200605144744-ba689101faaf - golang.org/x/crypto v0.0.0-20200820211705-5c72a883971a - golang.org/x/image v0.0.0-20200922025426-e59bae62ef32 - golang.org/x/net v0.0.0-20200904194848-62affa334b73 + github.com/xuri/efp v0.0.0-20201016154823-031c29024257 + golang.org/x/crypto v0.0.0-20201012173705-84dcc777aaee + golang.org/x/net v0.0.0-20201016165138-7b1cca2348c0 golang.org/x/text v0.3.3 ) diff --git a/go.sum b/go.sum index e082e86359..17e16a5a58 100644 --- a/go.sum +++ b/go.sum @@ -1,35 +1,22 @@ -github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8= -github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826 h1:RWengNIwukTxcDr9M+97sNutRR1RKhG96O6jWumTTnw= github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826/go.mod h1:TaXosZuwdSHYgviHp1DAtfrULt5eUgsSMsZf+YrPgl8= -github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= -github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/richardlehane/mscfb v1.0.3 h1:rD8TBkYWkObWO0oLDFCbwMeZ4KoalxQy+QgniCj3nKI= github.com/richardlehane/mscfb v1.0.3/go.mod h1:YzVpcZg9czvAuhk9T+a3avCpcFPMUWm7gK3DypaEsUk= github.com/richardlehane/msoleps v1.0.1 h1:RfrALnSNXzmXLbGct/P2b4xkFz4e8Gmj/0Vj9M9xC1o= github.com/richardlehane/msoleps v1.0.1/go.mod h1:BWev5JBpU9Ko2WAgmZEuiz4/u3ZYTKbjLycmwiWUfWg= -github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= -github.com/stretchr/testify v1.6.1 h1:hDPOHmpOpP40lSULcqw7IrRb/u7w6RpDC9399XyoNd0= -github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= -github.com/xuri/efp v0.0.0-20200605144744-ba689101faaf h1:spotWVWg9DP470pPFQ7LaYtUqDpWEOS/BUrSmwFZE4k= -github.com/xuri/efp v0.0.0-20200605144744-ba689101faaf/go.mod h1:uBiSUepVYMhGTfDeBKKasV4GpgBlzJ46gXUBAqV8qLk= +github.com/xuri/efp v0.0.0-20201016154823-031c29024257 h1:6ldmGEJXtsRMwdR2KuS3esk9wjVJNvgk05/YY2XmOj0= +github.com/xuri/efp v0.0.0-20201016154823-031c29024257/go.mod h1:uBiSUepVYMhGTfDeBKKasV4GpgBlzJ46gXUBAqV8qLk= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= -golang.org/x/crypto v0.0.0-20200820211705-5c72a883971a h1:vclmkQCjlDX5OydZ9wv8rBCcS0QyQY66Mpf/7BZbInM= -golang.org/x/crypto v0.0.0-20200820211705-5c72a883971a/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= -golang.org/x/image v0.0.0-20200922025426-e59bae62ef32 h1:E+SEVulmY8U4+i6vSB88YSc2OKAFfvbHPU/uDTdQu7M= -golang.org/x/image v0.0.0-20200922025426-e59bae62ef32/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= +golang.org/x/crypto v0.0.0-20201012173705-84dcc777aaee h1:4yd7jl+vXjalO5ztz6Vc1VADv+S/80LGJmyl1ROJ2AI= +golang.org/x/crypto v0.0.0-20201012173705-84dcc777aaee/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= -golang.org/x/net v0.0.0-20200904194848-62affa334b73 h1:MXfv8rhZWmFeqX3GNZRsd6vOLoaCHjYEX3qkRo3YBUA= -golang.org/x/net v0.0.0-20200904194848-62affa334b73/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= +golang.org/x/net v0.0.0-20201016165138-7b1cca2348c0 h1:5kGOVHlq0euqwzgTC9Vu15p6fV1Wi0ArVi8da2urnVg= +golang.org/x/net v0.0.0-20201016165138-7b1cca2348c0/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.3 h1:cokOdA+Jmi5PJGXLlLllQSgYigAEfHXJAERHVMaCc2k= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= -gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= -gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo= -gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=