From 860f3d4612a8c897c944d998a0ebccea33b9e5d9 Mon Sep 17 00:00:00 2001 From: harisabdullah Date: Mon, 24 Jul 2023 11:48:23 +0500 Subject: [PATCH 01/50] Implement IP to Integer Conversion as Part of Issue #108 This commit is a part of Issue #108, which involves implementing the IP calculator. Specifically, it introduces the 'ip2n' command to convert IP addresses to their corresponding integers. The 'ip2n' subcommand now supports both IPv4 and IPv6 addresses. Usage: 'my_command ip2n ' Examples: - 'ipinfo ip2n "190.87.89.1"' - 'ipinfo ip2n "2001:0db8:85a3:0000:0000:8a2e:0370:7334"' - 'ipinfo ip2n "2001:0db8:85a3::8a2e:0370:7334"' - 'ipinfo ip2n "::7334"' - 'ipinfo ip2n "7334::"' Fixes: #108 --- ipinfo/cmd_ip2n.go | 120 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 120 insertions(+) create mode 100644 ipinfo/cmd_ip2n.go diff --git a/ipinfo/cmd_ip2n.go b/ipinfo/cmd_ip2n.go new file mode 100644 index 00000000..51a85299 --- /dev/null +++ b/ipinfo/cmd_ip2n.go @@ -0,0 +1,120 @@ +package main + +import ( + "errors" + "fmt" + "net" + "os" + "strconv" + "strings" + + "github.com/fatih/color" + "github.com/ipinfo/cli/lib/complete" + "github.com/ipinfo/cli/lib/complete/predict" + "github.com/spf13/pflag" +) + +var completionsIP2n = &complete.Command{ + Flags: map[string]complete.Predictor{ + "--nocolor": predict.Nothing, + "-h": predict.Nothing, + "--help": predict.Nothing, + "-f": predict.Set(predictReadFmts), + "--format": predict.Set(predictReadFmts), + }, +} + +func printHelpIp2n() { + + fmt.Printf( + `Usage: %s ip2n + +Example: + %s ip2n "190.87.89.1" + %s ip2n "2001:0db8:85a3:0000:0000:8a2e:0370:7334 + %s ip2n "2001:0db8:85a3::8a2e:0370:7334 + %s ip2n "::7334 + %s ip2n "7334::"" + + +Options: + General: + --nocolor + disable colored output. + --help, -h + show help. +`, progBase, progBase, progBase, progBase, progBase, progBase) +} + +func ip2nHelp() (err error) { + pflag.BoolVarP(&fHelp, "help", "h", false, "show help.") + pflag.BoolVar(&fNoColor, "nocolor", false, "disable colored output.") + pflag.Parse() + + if fNoColor { + color.NoColor = true + } + + if fHelp { + printHelpDefault() + return nil + } + + // currently we do nothing by default. + printHelpIp2n() + return nil +} + +func cmdIP2n() error { + var err error + var res string + cmd := "" + if len(os.Args) > 2 { + cmd = os.Args[2] + } + + if strings.TrimSpace(cmd) == "" { + err := ip2nHelp() + if err != nil { + return err + } + return nil + } + + res, err = calcIP2n(cmd) + + if err != nil { + fmt.Fprintf(os.Stderr, "err: %v\n", err) + err := ip2nHelp() + if err != nil { + return err + } + return nil + } + + fmt.Println(res) + + return nil +} + +func calcIP2n(strIP string) (string, error) { + if isIPv6Address(strIP) { + ip := net.ParseIP(strIP) + if ip == nil { + fmt.Println("Invalid IPv6 address") + return "", errors.New("invalid IPv6 address: '" + strIP + "'") + } + + decimalIP := IP6toSInt(ip) + return decimalIP.String(), nil + } + if isIPv4Address(strIP) { + ip := net.ParseIP(strIP) + if ip == nil { + return "", errors.New("invalid IPv4 address: '" + strIP + "'") + } + return strconv.FormatInt(IP4toInt(ip), 10), nil + } else { + return "", errors.New("invalid IP address: '" + strIP + "'") + } +} From b0ceb39a9019caf42aba035079f1f4af1a5b8bea Mon Sep 17 00:00:00 2001 From: harisabdullah Date: Mon, 24 Jul 2023 11:50:02 +0500 Subject: [PATCH 02/50] This commit is a part of Issue #108, which involves implementing the IP calculator. Specifically, it introduces the 'n2ip' command to convert integers to their corresponding IP addresses. The 'n2ip' command supports both IPv4 and IPv6 addresses. Usage: 'ipinfo n2ip [] ' Examples: 'ipinfo n2ip "2*2828-1"' 'ipinfo n2ip "190.87.89.1*2"' 'ipinfo n2ip "2001:0db8:85a3:0000:0000:8a2e:0370:7334*6"' Additionally, the '--ipv6' option, when specified, forces conversion to IPv6 address even if the result is within the IPv4 range. This implementation includes helper functions to validate expressions and convert integers to IP addresses. Fixes: #108 --- ipinfo/cmd_n2ip.go | 163 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 163 insertions(+) create mode 100644 ipinfo/cmd_n2ip.go diff --git a/ipinfo/cmd_n2ip.go b/ipinfo/cmd_n2ip.go new file mode 100644 index 00000000..d0fda830 --- /dev/null +++ b/ipinfo/cmd_n2ip.go @@ -0,0 +1,163 @@ +package main + +import ( + "errors" + "fmt" + "math/big" + "net" + "os" + "strings" + + "github.com/fatih/color" + "github.com/ipinfo/cli/lib/complete" + "github.com/ipinfo/cli/lib/complete/predict" + "github.com/spf13/pflag" +) + +var forceIpv6 bool + +var completionsN2IP = &complete.Command{ + Flags: map[string]complete.Predictor{ + "--nocolor": predict.Nothing, + "-h": predict.Nothing, + "--help": predict.Nothing, + "-f": predict.Set(predictReadFmts), + "--format": predict.Set(predictReadFmts), + "-6": predict.Set(predictReadFmts), + "--ipv6": predict.Set(predictReadFmts), + }, +} + +func printHelpN2IP() { + + fmt.Printf( + `Usage: %s n2ip [] + +Example: + %s n2ip "2*2828-1" + %s n2ip "190.87.89.1*2" + %s n2ip "2001:0db8:85a3:0000:0000:8a2e:0370:7334*6" + +Options: + General: + --nocolor + disable colored output. + --help, -h + show help. + --ipv6, -6 + force conversion to IPv6 address +`, progBase, progBase, progBase, progBase) +} + +func n2ipHelp() (err error) { + pflag.BoolVarP(&fHelp, "help", "h", false, "show help.") + pflag.BoolVar(&fNoColor, "nocolor", false, "disable colored output.") + + if fNoColor { + color.NoColor = true + } + + if fHelp { + printHelpDefault() + return nil + } + + // currently we do nothing by default. + printHelpN2IP() + return nil +} + +func cmdN2IP() error { + pflag.BoolVarP(&forceIpv6, "ipv6", "6", false, "force conversion to IPv6 address") + pflag.Parse() + var err error + + cmd := "" + + fmt.Println(os.Args) + + // Reading input from the command line + if forceIpv6 && len(os.Args) > 3 { + cmd = os.Args[3] + } else if !forceIpv6 && len(os.Args) > 2 { + cmd = os.Args[2] + } else { + err := n2ipHelp() + if err != nil { + return err + } + return nil + } + + // Validate the input + if strings.TrimSpace(cmd) == "" { + err := n2ipHelp() + if err != nil { + return err + } + return nil + } + + if isInvalid(cmd) { + return errors.New("invalid expression") + } + + // Tokenize + tokens, err := tokeinzeExp(cmd) + + if err != nil { + return err + } + + // Convert to postfix + // If it is a single number and not an expression + // The tokenization ad evaluation would have no effect on the number + postfix := infixToPostfix(tokens) + + // Evaluate the postfix expression + result, err := evaluatePostfix(postfix) + + if err != nil { + return err + } + + // Convert to IP + res := decimalToIP(result.String(), forceIpv6) + + if err != nil { + fmt.Fprintf(os.Stderr, "err: %v\n", err) + err := n2ipHelp() + if err != nil { + return err + } + return nil + } + + fmt.Println(res) + + return nil +} + +func decimalToIP(decimal string, forceIPv6 bool) net.IP { + // Create a new big.Int with a value of 'decimal' + num := new(big.Int) + num, success := num.SetString(decimal, 10) + if !success { + fmt.Fprintf(os.Stderr, "Error parsing the decimal string: %v\n", success) + return nil + } + + // Convert to IPv4 if not forcing IPv6 and 'num' is within the IPv4 range + if !forceIPv6 && num.Cmp(big.NewInt(4294967295)) <= 0 { + ip := make(net.IP, 4) + b := num.Bytes() + copy(ip[4-len(b):], b) + return ip + } + + // Convert to IPv6 + ip := make(net.IP, 16) + b := num.Bytes() + copy(ip[16-len(b):], b) + return ip +} From 7fdd0be1595bad3c5bf2972f2ae0bd514e7562f7 Mon Sep 17 00:00:00 2001 From: harisabdullah Date: Mon, 24 Jul 2023 11:56:53 +0500 Subject: [PATCH 03/50] This commit is a part of Issue #108, which involves implementing the IP calculator. Specifically, it introduces the 'n2ip6' command to convert decimal numbers to IPv6 addresses. Usage: 'ipinfo n2ip6 [] ' Examples: 'ipinfo n2ip6 "12*121"' 'ipinfo n2ip6 "::2*2' 'ipinfo n2ip6 "192.132.12.32*2"' 'ipinfo n2ip6 "::2+4::"' Fixes: #108 --- ipinfo/cmd_n2ip6.go | 113 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 113 insertions(+) create mode 100644 ipinfo/cmd_n2ip6.go diff --git a/ipinfo/cmd_n2ip6.go b/ipinfo/cmd_n2ip6.go new file mode 100644 index 00000000..1a2c19e7 --- /dev/null +++ b/ipinfo/cmd_n2ip6.go @@ -0,0 +1,113 @@ +package main + +import ( + "errors" + "fmt" + "github.com/fatih/color" + "github.com/ipinfo/cli/lib/complete" + "github.com/ipinfo/cli/lib/complete/predict" + "github.com/spf13/pflag" + "os" + "strings" +) + +var completionsN2IP6 = &complete.Command{ + Flags: map[string]complete.Predictor{ + "--nocolor": predict.Nothing, + "-h": predict.Nothing, + "--help": predict.Nothing, + "-f": predict.Set(predictReadFmts), + "--format": predict.Set(predictReadFmts), + }, +} + +func printHelpN2IP6() { + + fmt.Printf( + `Usage: %s n2ip6 [] + +Example: + %s n2ip6 "190.87.89.1" + %s n2ip6 "2001:0db8:85a3:0000:0000:8a2e:0370:7334 + %s n2ip6 "2001:0db8:85a3::8a2e:0370:7334 + %s n2ip6 "::7334 + %s n2ip6 "7334::"" + + +Options: + General: + --nocolor + disable colored output. + --help, -h + show help. +`, progBase, progBase, progBase, progBase, progBase, progBase) +} + +func n2ip6Help() (err error) { + pflag.BoolVarP(&fHelp, "help", "h", false, "show help.") + pflag.BoolVar(&fNoColor, "nocolor", false, "disable colored output.") + pflag.Parse() + + if fNoColor { + color.NoColor = true + } + + if fHelp { + printHelpDefault() + return nil + } + + // currently we do nothing by default. + printHelpN2IP6() + return nil +} + +func cmdN2IP6() error { + var err error + + cmd := "" + + if len(os.Args) > 2 { + cmd = os.Args[2] + } + + if strings.TrimSpace(cmd) == "" { + err := n2ip6Help() + if err != nil { + return err + } + return nil + } + + if isInvalid(cmd) { + return errors.New("invalid expression") + } + tokens, err := tokeinzeExp(cmd) + + if err != nil { + return err + } + + postfix := infixToPostfix(tokens) + + result, err := evaluatePostfix(postfix) + + if err != nil { + return err + } + + res := decimalToIP(result.String(), true) + + if err != nil { + fmt.Fprintf(os.Stderr, "err: %v\n", err) + err := n2ip6Help() + if err != nil { + return err + } + return nil + } + + fmt.Println(res) + + return nil +} From a184ba531ca432338ec281b827ea6c7852d54ce7 Mon Sep 17 00:00:00 2001 From: harisabdullah Date: Mon, 24 Jul 2023 12:00:50 +0500 Subject: [PATCH 04/50] This commit is a part of Issue #108, which involves implementing the IP calculator. Specifically, it introduces the 'calc' command to perform arithmetic on IP addresses and numbers Usage: 'ipinfo calc ' Examples: 'ipinfo calc "12*121-(2::*4)"' 'ipinfo calc "::2*2' 'ipinfo calc "192.132.12.32*2"' 'ipinfo calc "::2+4::"' Fixes: #108 --- ipinfo/cmd_calc.go | 86 +++++++++ ipinfo/cmd_calc_infix.go | 378 +++++++++++++++++++++++++++++++++++++++ 2 files changed, 464 insertions(+) create mode 100644 ipinfo/cmd_calc.go create mode 100644 ipinfo/cmd_calc_infix.go diff --git a/ipinfo/cmd_calc.go b/ipinfo/cmd_calc.go new file mode 100644 index 00000000..90b9af55 --- /dev/null +++ b/ipinfo/cmd_calc.go @@ -0,0 +1,86 @@ +package main + +import ( + "fmt" + "github.com/fatih/color" + "github.com/ipinfo/cli/lib/complete" + "github.com/ipinfo/cli/lib/complete/predict" + "github.com/spf13/pflag" + "os" +) + +var completionsCalc = &complete.Command{ + Flags: map[string]complete.Predictor{ + "-h": predict.Nothing, + "--help": predict.Nothing, + }, +} + +func printHelpCalc() { + + fmt.Printf( + `Usage: %s calc [] + +calc + Evaluate a mathematical expression and print the result. + + +Example: + %s calc "2*2828-1" + %s calc "190.87.89.1*2" + %s calc "2001:0db8:85a3:0000:0000:8a2e:0370:7334*6" + +Options: + General: + --nocolor + disable colored output. + --help, -h + show help. +`, progBase, progBase, progBase, progBase) +} + +func calcHelp() (err error) { + pflag.BoolVarP(&fHelp, "help", "h", false, "show help.") + pflag.BoolVar(&fNoColor, "nocolor", false, "disable colored output.") + pflag.Parse() + + if fNoColor { + color.NoColor = true + } + + if fHelp { + printHelpDefault() + return nil + } + + // currently we do nothing by default. + printHelpCalc() + return nil +} + +func cmdCalc() error { + var err error + var res string + cmd := "" + if len(os.Args) > 2 { + cmd = os.Args[2] + } + + switch { + case cmd != "": + res, err = cmdCalcInfix() + default: + err = calcHelp() + } + + if err != nil { + _, err := fmt.Fprintf(os.Stderr, "err: %v\n", err) + if err != nil { + return err + } + } + + fmt.Println(res) + + return nil +} diff --git a/ipinfo/cmd_calc_infix.go b/ipinfo/cmd_calc_infix.go new file mode 100644 index 00000000..dc830048 --- /dev/null +++ b/ipinfo/cmd_calc_infix.go @@ -0,0 +1,378 @@ +package main + +import ( + "errors" + "fmt" + "math" + "math/big" + "net" + "os" + "regexp" + "strconv" +) + +type Stack []string + +// IsEmpty check if stack is empty +func (st *Stack) IsEmpty() bool { + return len(*st) == 0 +} + +// Push a new value onto the stack +func (st *Stack) Push(str string) { + *st = append(*st, str) //Simply append the new value to the end of the stack +} + +// Pop Remove top element of stack. Return false if stack is empty. +func (st *Stack) Pop() bool { + if st.IsEmpty() { + return false + } else { + index := len(*st) - 1 // Get the index of top most element. + *st = (*st)[:index] // Remove it from the stack by slicing it off. + return true + } +} + +// Top Return top element of stack. Return false if stack is empty. +func (st *Stack) Top() string { + if st.IsEmpty() { + return "" + } else { + index := len(*st) - 1 // Get the index of top most element. + element := (*st)[index] // Index onto the slice and obtain the element. + return element + } +} + +// Function to return precedence of operators +func prec(s string) int { + if s == "^" { + return 3 + } else if (s == "/") || (s == "*") { + return 2 + } else if (s == "+") || (s == "-") { + return 1 + } else { + return -1 + } +} + +func isFloat(str string) bool { + pattern := `^[-+]?\d+(\.\d+)?$` + + // Compile the regular expression pattern. + regex := regexp.MustCompile(pattern) + return regex.MatchString(str) +} + +func infixToPostfix(infix []string) []string { + var sta Stack + var postfix []string + + for _, token := range infix { + if isOperator(token) { + for !sta.IsEmpty() && prec(token) <= prec(sta.Top()) { + // postfix = postfix + sta.Top() + postfix = append(postfix, sta.Top()) + sta.Pop() + } + sta.Push(token) + } else if token == "(" { + sta.Push(token) + } else if token == ")" { + for sta.Top() != "(" { + // postfix = postfix + sta.Top() + postfix = append(postfix, sta.Top()) + sta.Pop() + } + sta.Pop() + } else { + // postfix = postfix + token + postfix = append(postfix, token) + } + } + // Pop all the remaining elements from the stack + for !sta.IsEmpty() { + // postfix = postfix + sta.Top() + postfix = append(postfix, sta.Top()) + sta.Pop() + } + return postfix +} + +func evaluatePostfix(postfix []string) (*big.Float, error) { + var sta Stack + for _, el := range postfix { + //fmt.Println("stack:", sta) + // if operand, push it onto the stack. + if el == "" { + continue + } + if isFloat(el) || isIPv4Address(el) || isIPv6Address(el) { + sta.Push(el) + continue + } + + // if operator + // pop two elements off of the stack. + var num1 big.Float + strNum1 := sta.Top() + //fmt.Println("strNum1:", strNum1) + _, success := num1.SetString(strNum1) + + if !success { + fmt.Println("Error: Failed to convert the num1 to big.Int") + return big.NewFloat(0), nil + } + sta.Pop() + + var num2 big.Float + strNum2 := sta.Top() + //fmt.Println("strNum2:", strNum2) + _, success = num2.SetString(strNum2) + + if !success { + fmt.Println("Error: Failed to convert the num2 to big.Int:", strNum2) + return big.NewFloat(0), nil + } + sta.Pop() + operator := el + + // Perform addition + result := new(big.Float) + + // Convert the result back to a string + + switch { + case operator == "+": + //fmt.Println("Adding") + result = result.Add(&num2, &num1) + + case operator == "-": + //fmt.Println("Subtracting") + result = result.Sub(&num2, &num1) + + case operator == "*": + //fmt.Println("Multiplying") + result = result.Mul(&num2, &num1) + case operator == "/": + //fmt.Println("Dividing") + result = new(big.Float).Quo(&num2, &num1) + + case operator == "^": + //fmt.Println("Exponent") + // Using logs to calculate exponent + //logRes := new(big.Int) + + result1, _ := num1.Float64() + result2, _ := num2.Float64() + + res := math.Pow(result2, result1) + result = new(big.Float).SetPrec(64).SetFloat64(res) + + default: + fmt.Println("invalid operator: ", operator) + } + + strResult := result.String() + sta.Push(strResult) + + //fmt.Println(strNum1, operator, strNum2, " = ", strResult) + } + + strTop := sta.Top() + sta.Pop() + + var top = new(big.Float) + _, success := top.SetString(strTop) + + if !success { + fmt.Println("Error: Failed to convert the string to big.Int") + return big.NewFloat(0), nil + } + return top, nil +} + +func isOperator(token string) bool { + operators := map[string]bool{"+": true, "-": true, "*": true, "/": true, "^": true /* add other operators here */} + _, isOperator := operators[token] + return isOperator +} + +//func isPostfix(expression string) bool { +// bytes := []byte(expression) +// +// // Get the last character (last byte) from the slice +// lastChar := string(bytes[len(bytes)-1]) +// return isOperator(lastChar) +//} + +// fmt.Println("->", tempToken, " | char:", string(char)) + +func translateToken(tempToken string, tokens []string) ([]string, error) { + var err error = nil + + if tempToken == "" { + return tokens, nil + } + + if isFloat(tempToken) { + tokens = append(tokens, tempToken) + } else if isIPv4Address(tempToken) { + // convert ipv4 to decimal then append to tokens + ip := net.ParseIP(tempToken) + if ip == nil { + err = errors.New("invalid IPv4 address: '" + tempToken + "'") + } + decimalIP := IP4toInt(ip) + res := strconv.FormatInt(decimalIP, 10) + tokens = append(tokens, res) + + } else if isIPv6Address(tempToken) { + ip := net.ParseIP(tempToken) + if ip == nil { + fmt.Println("Invalid IPv6 address") + err = errors.New("invalid IPv6 address: '" + tempToken + "'") + } + decimalIP := IP6toSInt(ip) + tokens = append(tokens, decimalIP.String()) + } else { + err = errors.New("invalid expression") + } + return tokens, err +} + +func tokeinzeExp(expression string) ([]string, error) { + var tokens []string + var err error + + expression = "(" + expression + ")" + tempToken := "" + for _, char := range expression { + //fmt.Println("Is IPv6:", tempToken, isIPv6Address(tempToken)) + opchar := string(char) + if isFloat(opchar) || opchar == "." || opchar == ":" { + tempToken = tempToken + opchar + } else if char == '(' || char == ')' || isOperator(opchar) { + tokens, err = translateToken(tempToken, tokens) + if err != nil { + return []string{}, err + } + tokens = append(tokens, opchar) + tempToken = "" + } + } + tokens = append(tokens, tempToken) + return tokens, nil +} + +func IP6toSInt(IPv6Address net.IP) *big.Int { + IPv6Int := big.NewInt(0) + IPv6Int.SetBytes(IPv6Address) + return IPv6Int +} +func IP4toInt(IPv4Address net.IP) int64 { + IPv4Int := big.NewInt(0) + IPv4Int.SetBytes(IPv4Address.To4()) + return IPv4Int.Int64() +} + +func isIPv4Address(expression string) bool { + // Define the regular expression pattern for matching IPv4 addresses + ipV4Pattern := `^(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$` + + // Compile the regular expression + ipV4Regex := regexp.MustCompile(ipV4Pattern) + + // Use the MatchString function to check if the expression matches the IPv4 pattern + return ipV4Regex.MatchString(expression) +} + +func isIPv6Address(expression string) bool { + // Define the regular expression pattern for matching IPv6 addresses + ipV6Pattern := `^(([0-9a-fA-F]{1,4}:){7,7}[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,7}:|([0-9a-fA-F]{1,4}:){1,6}:[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,5}(:[0-9a-fA-F]{1,4}){1,2}|([0-9a-fA-F]{1,4}:){1,4}(:[0-9a-fA-F]{1,4}){1,3}|([0-9a-fA-F]{1,4}:){1,3}(:[0-9a-fA-F]{1,4}){1,4}|([0-9a-fA-F]{1,4}:){1,2}(:[0-9a-fA-F]{1,4}){1,5}|[0-9a-fA-F]{1,4}:((:[0-9a-fA-F]{1,4}){1,6})|:((:[0-9a-fA-F]{1,4}){1,7}|:)|fe80:(:[0-9a-fA-F]{0,4}){0,4}%[0-9a-zA-Z]{1,}|::(ffff(:0{1,4}){0,1}:){0,1}((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])\.){3,3}(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])|([0-9a-fA-F]{1,4}:){1,4}:((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])\.){3,3}(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9]))$` + + // Compile the regular expression + ipV6Regex := regexp.MustCompile(ipV6Pattern) + + // Use the MatchString function to check if the expression matches the IPv6 pattern + return ipV6Regex.MatchString(expression) +} + +func isInvalid(expression string) bool { + validChars := `^[0-9:\.\+\-\*\^\(\)\/ ]*$` + validCharsRegx := regexp.MustCompile(validChars) + + var PrevChar rune + var colonCount int + for _, char := range expression { + if isOperator(string(char)) && isOperator(string(PrevChar)) || + char == ')' && isOperator(string(PrevChar)) { + return true + } + if char == '.' && PrevChar == '.' { + return true + } + if char == ':' { + colonCount++ + if colonCount > 2 { + return true + } + } else { + colonCount = 0 + } + PrevChar = char + } + + if isOperator(string(PrevChar)) || PrevChar == '.' { + return true + } + + return !validCharsRegx.MatchString(expression) || !isBalanced(expression) + +} + +// Function to check if parentheses are balanced +func isBalanced(input string) bool { + var sta Stack + for _, char := range input { + if char == '(' { + sta.Push("(") + } else if char == ')' { + if sta.IsEmpty() { + return false + } + sta.Pop() + } + } + return sta.IsEmpty() +} + +func cmdCalcInfix() (string, error) { + // infix := "2+3*(2^3-5)^(2+1*2)-4" //abcd^e-fgh*+^*+i- + cmd := "" + if len(os.Args) > 2 { + cmd = os.Args[2] + } + + if isInvalid(cmd) { + return "", errors.New("invalid expression") + } + + tokens, err := tokeinzeExp(cmd) + + if err != nil { + return "", err + } + + postfix := infixToPostfix(tokens) + + result, err := evaluatePostfix(postfix) + + if err != nil { + return "", err + } + + return result.Text('f', 0), nil +} From 17af6020313a11740fed5b82bcb1e2fb3136d41b Mon Sep 17 00:00:00 2001 From: harisabdullah Date: Mon, 24 Jul 2023 12:04:31 +0500 Subject: [PATCH 05/50] This commit is a part of Issue #108, which involves implementing the IP calculator. Specifically, it adds entries for 'calc', 'ip2n', 'n2ip', and 'n2ip6' commands in the default help message --- ipinfo/cmd_default.go | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/ipinfo/cmd_default.go b/ipinfo/cmd_default.go index 9926dc50..99dc238e 100644 --- a/ipinfo/cmd_default.go +++ b/ipinfo/cmd_default.go @@ -20,6 +20,10 @@ Commands: look up details for an IP address, e.g. 8.8.8.8. look up details for an ASN, e.g. AS123 or as123. myip get details for your IP. + calc evaluates a mathematical expression that may contain IP addresses. + ip2n convert an IPv4 or IPv6 address to its decimal representation. + n2ip evaluates an expression and converts it to IPv4 or IPv6 address. + n2ip6 evaluates an expression and converts it to IPv6 address. bulk get details for multiple IPs in bulk. summarize get summarized data for a group of IPs. map open a URL to a map showing the locations of a group of IPs. From 27e310e02c9f3eb5f1a6b92b01cda62097727bcc Mon Sep 17 00:00:00 2001 From: harisabdullah Date: Mon, 24 Jul 2023 12:05:00 +0500 Subject: [PATCH 06/50] This commit is a part of Issue #108, which involves implementing the IP calculator. Specifically, it adds entries for 'calc', 'ip2n', 'n2ip', and 'n2ip6' commands in the completions. --- ipinfo/completions.go | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/ipinfo/completions.go b/ipinfo/completions.go index 0287d836..e58a25ee 100644 --- a/ipinfo/completions.go +++ b/ipinfo/completions.go @@ -32,6 +32,10 @@ var completions = &complete.Command{ "config": completionsConfig, "completion": completionsCompletion, "version": completionsVersion, + "calc": completionsCalc, + "ip2n": completionsIP2n, + "n2ip": completionsN2IP, + "n2ip6": completionsN2IP6, }, Flags: map[string]complete.Predictor{ "-v": predict.Nothing, From bdabecf174404c59cf6f8124efc973c4a73448cc Mon Sep 17 00:00:00 2001 From: harisabdullah Date: Mon, 24 Jul 2023 12:05:47 +0500 Subject: [PATCH 07/50] This commit is a part of Issue #108, which involves implementing the IP calculator. Specifically, it adds cases for 'calc', 'ip2n', 'n2ip', and 'n2ip6' commands in the switch block. --- ipinfo/main.go | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/ipinfo/main.go b/ipinfo/main.go index c8426d59..1acc343f 100644 --- a/ipinfo/main.go +++ b/ipinfo/main.go @@ -65,6 +65,14 @@ func main() { err = cmdRandIP() case cmd == "splitcidr": err = cmdSplitCIDR() + case cmd == "calc": + err = cmdCalc() + case cmd == "ip2n": + err = cmdIP2n() + case cmd == "n2ip": + err = cmdN2IP() + case cmd == "n2ip6": + err = cmdN2IP6() case cmd == "mmdb": err = cmdMmdb() case cmd == "download": From edd3de9ec1a1b8ed40ba93eb44747c384964ecad Mon Sep 17 00:00:00 2001 From: harisabdullah Date: Mon, 24 Jul 2023 12:13:13 +0500 Subject: [PATCH 08/50] refactoring --- ipinfo/cmd_calc.go | 1 - ipinfo/cmd_calc_infix.go | 36 ++++-------------------------------- ipinfo/cmd_ip2n.go | 2 +- 3 files changed, 5 insertions(+), 34 deletions(-) diff --git a/ipinfo/cmd_calc.go b/ipinfo/cmd_calc.go index 90b9af55..9dcf9c02 100644 --- a/ipinfo/cmd_calc.go +++ b/ipinfo/cmd_calc.go @@ -24,7 +24,6 @@ func printHelpCalc() { calc Evaluate a mathematical expression and print the result. - Example: %s calc "2*2828-1" %s calc "190.87.89.1*2" diff --git a/ipinfo/cmd_calc_infix.go b/ipinfo/cmd_calc_infix.go index dc830048..e2723ee6 100644 --- a/ipinfo/cmd_calc_infix.go +++ b/ipinfo/cmd_calc_infix.go @@ -73,7 +73,6 @@ func infixToPostfix(infix []string) []string { for _, token := range infix { if isOperator(token) { for !sta.IsEmpty() && prec(token) <= prec(sta.Top()) { - // postfix = postfix + sta.Top() postfix = append(postfix, sta.Top()) sta.Pop() } @@ -82,19 +81,16 @@ func infixToPostfix(infix []string) []string { sta.Push(token) } else if token == ")" { for sta.Top() != "(" { - // postfix = postfix + sta.Top() postfix = append(postfix, sta.Top()) sta.Pop() } sta.Pop() } else { - // postfix = postfix + token postfix = append(postfix, token) } } // Pop all the remaining elements from the stack for !sta.IsEmpty() { - // postfix = postfix + sta.Top() postfix = append(postfix, sta.Top()) sta.Pop() } @@ -104,7 +100,6 @@ func infixToPostfix(infix []string) []string { func evaluatePostfix(postfix []string) (*big.Float, error) { var sta Stack for _, el := range postfix { - //fmt.Println("stack:", sta) // if operand, push it onto the stack. if el == "" { continue @@ -114,11 +109,9 @@ func evaluatePostfix(postfix []string) (*big.Float, error) { continue } - // if operator - // pop two elements off of the stack. + // if operator pop two elements off of the stack. var num1 big.Float strNum1 := sta.Top() - //fmt.Println("strNum1:", strNum1) _, success := num1.SetString(strNum1) if !success { @@ -129,7 +122,6 @@ func evaluatePostfix(postfix []string) (*big.Float, error) { var num2 big.Float strNum2 := sta.Top() - //fmt.Println("strNum2:", strNum2) _, success = num2.SetString(strNum2) if !success { @@ -139,11 +131,8 @@ func evaluatePostfix(postfix []string) (*big.Float, error) { sta.Pop() operator := el - // Perform addition result := new(big.Float) - // Convert the result back to a string - switch { case operator == "+": //fmt.Println("Adding") @@ -161,10 +150,6 @@ func evaluatePostfix(postfix []string) (*big.Float, error) { result = new(big.Float).Quo(&num2, &num1) case operator == "^": - //fmt.Println("Exponent") - // Using logs to calculate exponent - //logRes := new(big.Int) - result1, _ := num1.Float64() result2, _ := num2.Float64() @@ -177,8 +162,6 @@ func evaluatePostfix(postfix []string) (*big.Float, error) { strResult := result.String() sta.Push(strResult) - - //fmt.Println(strNum1, operator, strNum2, " = ", strResult) } strTop := sta.Top() @@ -200,16 +183,6 @@ func isOperator(token string) bool { return isOperator } -//func isPostfix(expression string) bool { -// bytes := []byte(expression) -// -// // Get the last character (last byte) from the slice -// lastChar := string(bytes[len(bytes)-1]) -// return isOperator(lastChar) -//} - -// fmt.Println("->", tempToken, " | char:", string(char)) - func translateToken(tempToken string, tokens []string) ([]string, error) { var err error = nil @@ -235,7 +208,7 @@ func translateToken(tempToken string, tokens []string) ([]string, error) { fmt.Println("Invalid IPv6 address") err = errors.New("invalid IPv6 address: '" + tempToken + "'") } - decimalIP := IP6toSInt(ip) + decimalIP := IP6toInt(ip) tokens = append(tokens, decimalIP.String()) } else { err = errors.New("invalid expression") @@ -250,7 +223,6 @@ func tokeinzeExp(expression string) ([]string, error) { expression = "(" + expression + ")" tempToken := "" for _, char := range expression { - //fmt.Println("Is IPv6:", tempToken, isIPv6Address(tempToken)) opchar := string(char) if isFloat(opchar) || opchar == "." || opchar == ":" { tempToken = tempToken + opchar @@ -267,7 +239,7 @@ func tokeinzeExp(expression string) ([]string, error) { return tokens, nil } -func IP6toSInt(IPv6Address net.IP) *big.Int { +func IP6toInt(IPv6Address net.IP) *big.Int { IPv6Int := big.NewInt(0) IPv6Int.SetBytes(IPv6Address) return IPv6Int @@ -350,7 +322,7 @@ func isBalanced(input string) bool { } func cmdCalcInfix() (string, error) { - // infix := "2+3*(2^3-5)^(2+1*2)-4" //abcd^e-fgh*+^*+i- + // infix := "2+3*(2^3-5)^(2+1*2)-4" cmd := "" if len(os.Args) > 2 { cmd = os.Args[2] diff --git a/ipinfo/cmd_ip2n.go b/ipinfo/cmd_ip2n.go index 51a85299..a7fc417e 100644 --- a/ipinfo/cmd_ip2n.go +++ b/ipinfo/cmd_ip2n.go @@ -105,7 +105,7 @@ func calcIP2n(strIP string) (string, error) { return "", errors.New("invalid IPv6 address: '" + strIP + "'") } - decimalIP := IP6toSInt(ip) + decimalIP := IP6toInt(ip) return decimalIP.String(), nil } if isIPv4Address(strIP) { From 08801ddc96329115fbc252ac781ca31308b79cf3 Mon Sep 17 00:00:00 2001 From: Haris Date: Mon, 24 Jul 2023 14:07:13 +0500 Subject: [PATCH 09/50] Update ipinfo/cmd_calc.go Co-authored-by: awaismslm <109338796+awaismslm@users.noreply.github.com> --- ipinfo/cmd_calc.go | 1 - 1 file changed, 1 deletion(-) diff --git a/ipinfo/cmd_calc.go b/ipinfo/cmd_calc.go index 9dcf9c02..8e9fd1f1 100644 --- a/ipinfo/cmd_calc.go +++ b/ipinfo/cmd_calc.go @@ -17,7 +17,6 @@ var completionsCalc = &complete.Command{ } func printHelpCalc() { - fmt.Printf( `Usage: %s calc [] From 530454ed4619cdc839b0af46753449ec03d75616 Mon Sep 17 00:00:00 2001 From: Haris Date: Mon, 24 Jul 2023 14:07:31 +0500 Subject: [PATCH 10/50] Update ipinfo/cmd_calc.go Co-authored-by: awaismslm <109338796+awaismslm@users.noreply.github.com> --- ipinfo/cmd_calc.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ipinfo/cmd_calc.go b/ipinfo/cmd_calc.go index 8e9fd1f1..36a2ea25 100644 --- a/ipinfo/cmd_calc.go +++ b/ipinfo/cmd_calc.go @@ -34,7 +34,7 @@ Options: disable colored output. --help, -h show help. -`, progBase, progBase, progBase, progBase) +`, progBase) } func calcHelp() (err error) { From 323b2a443b8325c7b3fa6ffe45ceae118b0c9f51 Mon Sep 17 00:00:00 2001 From: Haris Date: Mon, 24 Jul 2023 14:07:42 +0500 Subject: [PATCH 11/50] Update ipinfo/cmd_calc.go Co-authored-by: awaismslm <109338796+awaismslm@users.noreply.github.com> --- ipinfo/cmd_calc.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ipinfo/cmd_calc.go b/ipinfo/cmd_calc.go index 36a2ea25..cb5f4040 100644 --- a/ipinfo/cmd_calc.go +++ b/ipinfo/cmd_calc.go @@ -51,7 +51,7 @@ func calcHelp() (err error) { return nil } - // currently we do nothing by default. + // Currently we do nothing by default. printHelpCalc() return nil } From 2f5139480d590e795198b749cdeb0dc43ed8a5b4 Mon Sep 17 00:00:00 2001 From: Haris Date: Mon, 24 Jul 2023 14:07:51 +0500 Subject: [PATCH 12/50] Update ipinfo/cmd_calc.go Co-authored-by: awaismslm <109338796+awaismslm@users.noreply.github.com> --- ipinfo/cmd_calc.go | 1 - 1 file changed, 1 deletion(-) diff --git a/ipinfo/cmd_calc.go b/ipinfo/cmd_calc.go index cb5f4040..1e93ae86 100644 --- a/ipinfo/cmd_calc.go +++ b/ipinfo/cmd_calc.go @@ -79,6 +79,5 @@ func cmdCalc() error { } fmt.Println(res) - return nil } From 5fa615aded4eecebeca42f3ad798c474ce91d66f Mon Sep 17 00:00:00 2001 From: Haris Date: Mon, 24 Jul 2023 14:08:03 +0500 Subject: [PATCH 13/50] Update ipinfo/cmd_calc_infix.go Co-authored-by: awaismslm <109338796+awaismslm@users.noreply.github.com> --- ipinfo/cmd_calc_infix.go | 1 - 1 file changed, 1 deletion(-) diff --git a/ipinfo/cmd_calc_infix.go b/ipinfo/cmd_calc_infix.go index e2723ee6..ffaf0d09 100644 --- a/ipinfo/cmd_calc_infix.go +++ b/ipinfo/cmd_calc_infix.go @@ -130,7 +130,6 @@ func evaluatePostfix(postfix []string) (*big.Float, error) { } sta.Pop() operator := el - result := new(big.Float) switch { From 444621e1fd00c154759fb12da3ded1de0378d594 Mon Sep 17 00:00:00 2001 From: Haris Date: Mon, 24 Jul 2023 14:08:14 +0500 Subject: [PATCH 14/50] Update ipinfo/cmd_calc_infix.go Co-authored-by: awaismslm <109338796+awaismslm@users.noreply.github.com> --- ipinfo/cmd_calc_infix.go | 1 - 1 file changed, 1 deletion(-) diff --git a/ipinfo/cmd_calc_infix.go b/ipinfo/cmd_calc_infix.go index ffaf0d09..d18fcdf2 100644 --- a/ipinfo/cmd_calc_infix.go +++ b/ipinfo/cmd_calc_infix.go @@ -136,7 +136,6 @@ func evaluatePostfix(postfix []string) (*big.Float, error) { case operator == "+": //fmt.Println("Adding") result = result.Add(&num2, &num1) - case operator == "-": //fmt.Println("Subtracting") result = result.Sub(&num2, &num1) From dfd3ef84de045a9058ae3c0194b0ab4342a460a5 Mon Sep 17 00:00:00 2001 From: Haris Date: Mon, 24 Jul 2023 14:08:35 +0500 Subject: [PATCH 15/50] Update ipinfo/cmd_calc_infix.go Co-authored-by: awaismslm <109338796+awaismslm@users.noreply.github.com> --- ipinfo/cmd_calc_infix.go | 1 - 1 file changed, 1 deletion(-) diff --git a/ipinfo/cmd_calc_infix.go b/ipinfo/cmd_calc_infix.go index d18fcdf2..91b356cc 100644 --- a/ipinfo/cmd_calc_infix.go +++ b/ipinfo/cmd_calc_infix.go @@ -300,7 +300,6 @@ func isInvalid(expression string) bool { } return !validCharsRegx.MatchString(expression) || !isBalanced(expression) - } // Function to check if parentheses are balanced From ba19d995876181a9e48ad3e897fa6b421dbaee60 Mon Sep 17 00:00:00 2001 From: harisabdullah Date: Mon, 24 Jul 2023 14:14:37 +0500 Subject: [PATCH 16/50] Moving 'calc', 'n2ip', 'n2ip6', and 'ip2n' in hep message --- ipinfo/cmd_default.go | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/ipinfo/cmd_default.go b/ipinfo/cmd_default.go index 99dc238e..8c6ef6f7 100644 --- a/ipinfo/cmd_default.go +++ b/ipinfo/cmd_default.go @@ -20,10 +20,6 @@ Commands: look up details for an IP address, e.g. 8.8.8.8. look up details for an ASN, e.g. AS123 or as123. myip get details for your IP. - calc evaluates a mathematical expression that may contain IP addresses. - ip2n convert an IPv4 or IPv6 address to its decimal representation. - n2ip evaluates an expression and converts it to IPv4 or IPv6 address. - n2ip6 evaluates an expression and converts it to IPv6 address. bulk get details for multiple IPs in bulk. summarize get summarized data for a group of IPs. map open a URL to a map showing the locations of a group of IPs. @@ -36,6 +32,10 @@ Commands: randip Generates random IPs. splitcidr splits a larger CIDR into smaller CIDRs. mmdb read, import and export mmdb files. + calc evaluates a mathematical expression that may contain IP addresses. + ip2n convert an IPv4 or IPv6 address to its decimal representation. + n2ip evaluates an expression and converts it to IPv4 or IPv6 address. + n2ip6 evaluates an expression and converts it to IPv6 address. tool misc. tools related to IPs, IP ranges and CIDRs. download download free ipinfo database files. cache manage the cache. From 8ce85047be1e6471914fb212d7e11d0505ac12b1 Mon Sep 17 00:00:00 2001 From: Haris Date: Mon, 24 Jul 2023 14:18:27 +0500 Subject: [PATCH 17/50] Update ipinfo/cmd_ip2n.go Co-authored-by: awaismslm <109338796+awaismslm@users.noreply.github.com> --- ipinfo/cmd_ip2n.go | 1 - 1 file changed, 1 deletion(-) diff --git a/ipinfo/cmd_ip2n.go b/ipinfo/cmd_ip2n.go index a7fc417e..fda2f887 100644 --- a/ipinfo/cmd_ip2n.go +++ b/ipinfo/cmd_ip2n.go @@ -25,7 +25,6 @@ var completionsIP2n = &complete.Command{ } func printHelpIp2n() { - fmt.Printf( `Usage: %s ip2n From 82f406c9b8e1d23239f888426fff8295dd068d86 Mon Sep 17 00:00:00 2001 From: Haris Date: Mon, 24 Jul 2023 14:19:05 +0500 Subject: [PATCH 18/50] Update ipinfo/cmd_ip2n.go Co-authored-by: awaismslm <109338796+awaismslm@users.noreply.github.com> --- ipinfo/cmd_ip2n.go | 1 - 1 file changed, 1 deletion(-) diff --git a/ipinfo/cmd_ip2n.go b/ipinfo/cmd_ip2n.go index fda2f887..2344291a 100644 --- a/ipinfo/cmd_ip2n.go +++ b/ipinfo/cmd_ip2n.go @@ -92,7 +92,6 @@ func cmdIP2n() error { } fmt.Println(res) - return nil } From 92068c95a15a89056158c4dc518b6157cafa2763 Mon Sep 17 00:00:00 2001 From: Haris Date: Mon, 24 Jul 2023 14:19:19 +0500 Subject: [PATCH 19/50] Update ipinfo/cmd_n2ip.go Co-authored-by: awaismslm <109338796+awaismslm@users.noreply.github.com> --- ipinfo/cmd_n2ip.go | 1 - 1 file changed, 1 deletion(-) diff --git a/ipinfo/cmd_n2ip.go b/ipinfo/cmd_n2ip.go index d0fda830..8bb1bc4a 100644 --- a/ipinfo/cmd_n2ip.go +++ b/ipinfo/cmd_n2ip.go @@ -7,7 +7,6 @@ import ( "net" "os" "strings" - "github.com/fatih/color" "github.com/ipinfo/cli/lib/complete" "github.com/ipinfo/cli/lib/complete/predict" From 1a8a3b14b66aa3e45114ca2aeee76a5df34e3e29 Mon Sep 17 00:00:00 2001 From: harisabdullah Date: Mon, 24 Jul 2023 15:19:48 +0500 Subject: [PATCH 20/50] Fixed repetition of progbase, and removed unused flags --- ipinfo/cmd_calc.go | 6 +++--- ipinfo/cmd_ip2n.go | 16 ++++++---------- ipinfo/cmd_n2ip.go | 16 +++++----------- ipinfo/cmd_n2ip6.go | 21 +++++++-------------- 4 files changed, 21 insertions(+), 38 deletions(-) diff --git a/ipinfo/cmd_calc.go b/ipinfo/cmd_calc.go index 1e93ae86..6ff25871 100644 --- a/ipinfo/cmd_calc.go +++ b/ipinfo/cmd_calc.go @@ -24,9 +24,9 @@ calc Evaluate a mathematical expression and print the result. Example: - %s calc "2*2828-1" - %s calc "190.87.89.1*2" - %s calc "2001:0db8:85a3:0000:0000:8a2e:0370:7334*6" + %[1]s calc "2*2828-1" + %[1]s calc "190.87.89.1*2" + %[1]s calc "2001:0db8:85a3:0000:0000:8a2e:0370:7334*6" Options: General: diff --git a/ipinfo/cmd_ip2n.go b/ipinfo/cmd_ip2n.go index a7fc417e..7155ad58 100644 --- a/ipinfo/cmd_ip2n.go +++ b/ipinfo/cmd_ip2n.go @@ -19,22 +19,19 @@ var completionsIP2n = &complete.Command{ "--nocolor": predict.Nothing, "-h": predict.Nothing, "--help": predict.Nothing, - "-f": predict.Set(predictReadFmts), - "--format": predict.Set(predictReadFmts), }, } func printHelpIp2n() { - fmt.Printf( `Usage: %s ip2n Example: - %s ip2n "190.87.89.1" - %s ip2n "2001:0db8:85a3:0000:0000:8a2e:0370:7334 - %s ip2n "2001:0db8:85a3::8a2e:0370:7334 - %s ip2n "::7334 - %s ip2n "7334::"" + %[1]s ip2n "190.87.89.1" + %[1]s ip2n "2001:0db8:85a3:0000:0000:8a2e:0370:7334 + %[1]s ip2n "2001:0db8:85a3::8a2e:0370:7334 + %[1]s ip2n "::7334 + %[1]s ip2n "7334::"" Options: @@ -43,7 +40,7 @@ Options: disable colored output. --help, -h show help. -`, progBase, progBase, progBase, progBase, progBase, progBase) +`, progBase) } func ip2nHelp() (err error) { @@ -101,7 +98,6 @@ func calcIP2n(strIP string) (string, error) { if isIPv6Address(strIP) { ip := net.ParseIP(strIP) if ip == nil { - fmt.Println("Invalid IPv6 address") return "", errors.New("invalid IPv6 address: '" + strIP + "'") } diff --git a/ipinfo/cmd_n2ip.go b/ipinfo/cmd_n2ip.go index d0fda830..87425acb 100644 --- a/ipinfo/cmd_n2ip.go +++ b/ipinfo/cmd_n2ip.go @@ -21,22 +21,19 @@ var completionsN2IP = &complete.Command{ "--nocolor": predict.Nothing, "-h": predict.Nothing, "--help": predict.Nothing, - "-f": predict.Set(predictReadFmts), - "--format": predict.Set(predictReadFmts), "-6": predict.Set(predictReadFmts), "--ipv6": predict.Set(predictReadFmts), }, } func printHelpN2IP() { - fmt.Printf( `Usage: %s n2ip [] Example: - %s n2ip "2*2828-1" - %s n2ip "190.87.89.1*2" - %s n2ip "2001:0db8:85a3:0000:0000:8a2e:0370:7334*6" + %[1]s n2ip "2*2828-1" + %[1]s n2ip "190.87.89.1*2" + %[1]s n2ip "2001:0db8:85a3:0000:0000:8a2e:0370:7334*6" Options: General: @@ -46,7 +43,7 @@ Options: show help. --ipv6, -6 force conversion to IPv6 address -`, progBase, progBase, progBase, progBase) +`, progBase) } func n2ipHelp() (err error) { @@ -73,9 +70,6 @@ func cmdN2IP() error { var err error cmd := "" - - fmt.Println(os.Args) - // Reading input from the command line if forceIpv6 && len(os.Args) > 3 { cmd = os.Args[3] @@ -111,7 +105,7 @@ func cmdN2IP() error { // Convert to postfix // If it is a single number and not an expression - // The tokenization ad evaluation would have no effect on the number + // The tokenization and evaluation would have no effect on the number postfix := infixToPostfix(tokens) // Evaluate the postfix expression diff --git a/ipinfo/cmd_n2ip6.go b/ipinfo/cmd_n2ip6.go index 1a2c19e7..f0f6e326 100644 --- a/ipinfo/cmd_n2ip6.go +++ b/ipinfo/cmd_n2ip6.go @@ -16,22 +16,19 @@ var completionsN2IP6 = &complete.Command{ "--nocolor": predict.Nothing, "-h": predict.Nothing, "--help": predict.Nothing, - "-f": predict.Set(predictReadFmts), - "--format": predict.Set(predictReadFmts), }, } func printHelpN2IP6() { - fmt.Printf( `Usage: %s n2ip6 [] Example: - %s n2ip6 "190.87.89.1" - %s n2ip6 "2001:0db8:85a3:0000:0000:8a2e:0370:7334 - %s n2ip6 "2001:0db8:85a3::8a2e:0370:7334 - %s n2ip6 "::7334 - %s n2ip6 "7334::"" + %[1]s n2ip6 "190.87.89.1" + %[1]s n2ip6 "2001:0db8:85a3:0000:0000:8a2e:0370:7334 + %[1]s n2ip6 "2001:0db8:85a3::8a2e:0370:7334 + %[1]s n2ip6 "::7334 + %[1]s n2ip6 "7334::"" Options: @@ -40,7 +37,7 @@ Options: disable colored output. --help, -h show help. -`, progBase, progBase, progBase, progBase, progBase, progBase) +`, progBase) } func n2ip6Help() (err error) { @@ -66,11 +63,11 @@ func cmdN2IP6() error { var err error cmd := "" - if len(os.Args) > 2 { cmd = os.Args[2] } + // If no argument is given, print help. if strings.TrimSpace(cmd) == "" { err := n2ip6Help() if err != nil { @@ -83,7 +80,6 @@ func cmdN2IP6() error { return errors.New("invalid expression") } tokens, err := tokeinzeExp(cmd) - if err != nil { return err } @@ -91,13 +87,11 @@ func cmdN2IP6() error { postfix := infixToPostfix(tokens) result, err := evaluatePostfix(postfix) - if err != nil { return err } res := decimalToIP(result.String(), true) - if err != nil { fmt.Fprintf(os.Stderr, "err: %v\n", err) err := n2ip6Help() @@ -108,6 +102,5 @@ func cmdN2IP6() error { } fmt.Println(res) - return nil } From fb5dd9dfe59b1e68ea9f96ec06291d46598f70bf Mon Sep 17 00:00:00 2001 From: harisabdullah Date: Mon, 24 Jul 2023 15:20:26 +0500 Subject: [PATCH 21/50] Relocated 'calc', 'ip2n', 'n2ip', and 'n2ip6' --- ipinfo/completions.go | 8 ++++---- ipinfo/main.go | 4 ++-- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/ipinfo/completions.go b/ipinfo/completions.go index e58a25ee..80752d73 100644 --- a/ipinfo/completions.go +++ b/ipinfo/completions.go @@ -23,6 +23,10 @@ var completions = &complete.Command{ "randip": completionsRandIP, "splitcidr": completionsSplitCIDR, "mmdb": completionsMmdb, + "calc": completionsCalc, + "ip2n": completionsIP2n, + "n2ip": completionsN2IP, + "n2ip6": completionsN2IP6, "tool": completionsTool, "download": completionsDownload, "cache": completionsCache, @@ -32,10 +36,6 @@ var completions = &complete.Command{ "config": completionsConfig, "completion": completionsCompletion, "version": completionsVersion, - "calc": completionsCalc, - "ip2n": completionsIP2n, - "n2ip": completionsN2IP, - "n2ip6": completionsN2IP6, }, Flags: map[string]complete.Predictor{ "-v": predict.Nothing, diff --git a/ipinfo/main.go b/ipinfo/main.go index 1acc343f..87a103a4 100644 --- a/ipinfo/main.go +++ b/ipinfo/main.go @@ -65,6 +65,8 @@ func main() { err = cmdRandIP() case cmd == "splitcidr": err = cmdSplitCIDR() + case cmd == "mmdb": + err = cmdMmdb() case cmd == "calc": err = cmdCalc() case cmd == "ip2n": @@ -73,8 +75,6 @@ func main() { err = cmdN2IP() case cmd == "n2ip6": err = cmdN2IP6() - case cmd == "mmdb": - err = cmdMmdb() case cmd == "download": err = cmdDownload() case cmd == "tool": From 462a1b5c15af741e30f735dfff8005cbc71418c0 Mon Sep 17 00:00:00 2001 From: harisabdullah Date: Mon, 24 Jul 2023 15:50:03 +0500 Subject: [PATCH 22/50] making flags work --- ipinfo/cmd_calc.go | 12 ++++-------- ipinfo/cmd_ip2n.go | 19 +++---------------- ipinfo/cmd_n2ip.go | 28 ++++++---------------------- ipinfo/cmd_n2ip6.go | 18 +++--------------- 4 files changed, 16 insertions(+), 61 deletions(-) diff --git a/ipinfo/cmd_calc.go b/ipinfo/cmd_calc.go index 6ff25871..340c4ecc 100644 --- a/ipinfo/cmd_calc.go +++ b/ipinfo/cmd_calc.go @@ -37,7 +37,7 @@ Options: `, progBase) } -func calcHelp() (err error) { +func cmdCalc() error { pflag.BoolVarP(&fHelp, "help", "h", false, "show help.") pflag.BoolVar(&fNoColor, "nocolor", false, "disable colored output.") pflag.Parse() @@ -51,12 +51,6 @@ func calcHelp() (err error) { return nil } - // Currently we do nothing by default. - printHelpCalc() - return nil -} - -func cmdCalc() error { var err error var res string cmd := "" @@ -68,7 +62,7 @@ func cmdCalc() error { case cmd != "": res, err = cmdCalcInfix() default: - err = calcHelp() + printHelpCalc() } if err != nil { @@ -76,6 +70,8 @@ func cmdCalc() error { if err != nil { return err } + + printHelpCalc() } fmt.Println(res) diff --git a/ipinfo/cmd_ip2n.go b/ipinfo/cmd_ip2n.go index 7155ad58..510c4caf 100644 --- a/ipinfo/cmd_ip2n.go +++ b/ipinfo/cmd_ip2n.go @@ -43,7 +43,7 @@ Options: `, progBase) } -func ip2nHelp() (err error) { +func cmdIP2n() error { pflag.BoolVarP(&fHelp, "help", "h", false, "show help.") pflag.BoolVar(&fNoColor, "nocolor", false, "disable colored output.") pflag.Parse() @@ -57,12 +57,6 @@ func ip2nHelp() (err error) { return nil } - // currently we do nothing by default. - printHelpIp2n() - return nil -} - -func cmdIP2n() error { var err error var res string cmd := "" @@ -71,10 +65,7 @@ func cmdIP2n() error { } if strings.TrimSpace(cmd) == "" { - err := ip2nHelp() - if err != nil { - return err - } + printHelpIp2n() return nil } @@ -82,15 +73,11 @@ func cmdIP2n() error { if err != nil { fmt.Fprintf(os.Stderr, "err: %v\n", err) - err := ip2nHelp() - if err != nil { - return err - } + printHelpIp2n() return nil } fmt.Println(res) - return nil } diff --git a/ipinfo/cmd_n2ip.go b/ipinfo/cmd_n2ip.go index 87425acb..33f0e836 100644 --- a/ipinfo/cmd_n2ip.go +++ b/ipinfo/cmd_n2ip.go @@ -46,9 +46,11 @@ Options: `, progBase) } -func n2ipHelp() (err error) { +func cmdN2IP() error { pflag.BoolVarP(&fHelp, "help", "h", false, "show help.") pflag.BoolVar(&fNoColor, "nocolor", false, "disable colored output.") + pflag.BoolVarP(&forceIpv6, "ipv6", "6", false, "force conversion to IPv6 address") + pflag.Parse() if fNoColor { color.NoColor = true @@ -59,14 +61,6 @@ func n2ipHelp() (err error) { return nil } - // currently we do nothing by default. - printHelpN2IP() - return nil -} - -func cmdN2IP() error { - pflag.BoolVarP(&forceIpv6, "ipv6", "6", false, "force conversion to IPv6 address") - pflag.Parse() var err error cmd := "" @@ -76,19 +70,13 @@ func cmdN2IP() error { } else if !forceIpv6 && len(os.Args) > 2 { cmd = os.Args[2] } else { - err := n2ipHelp() - if err != nil { - return err - } + printHelpN2IP() return nil } // Validate the input if strings.TrimSpace(cmd) == "" { - err := n2ipHelp() - if err != nil { - return err - } + printHelpN2IP() return nil } @@ -120,15 +108,11 @@ func cmdN2IP() error { if err != nil { fmt.Fprintf(os.Stderr, "err: %v\n", err) - err := n2ipHelp() - if err != nil { - return err - } + printHelpN2IP() return nil } fmt.Println(res) - return nil } diff --git a/ipinfo/cmd_n2ip6.go b/ipinfo/cmd_n2ip6.go index f0f6e326..91e67fff 100644 --- a/ipinfo/cmd_n2ip6.go +++ b/ipinfo/cmd_n2ip6.go @@ -40,7 +40,7 @@ Options: `, progBase) } -func n2ip6Help() (err error) { +func cmdN2IP6() error { pflag.BoolVarP(&fHelp, "help", "h", false, "show help.") pflag.BoolVar(&fNoColor, "nocolor", false, "disable colored output.") pflag.Parse() @@ -54,12 +54,6 @@ func n2ip6Help() (err error) { return nil } - // currently we do nothing by default. - printHelpN2IP6() - return nil -} - -func cmdN2IP6() error { var err error cmd := "" @@ -69,10 +63,7 @@ func cmdN2IP6() error { // If no argument is given, print help. if strings.TrimSpace(cmd) == "" { - err := n2ip6Help() - if err != nil { - return err - } + printHelpN2IP6() return nil } @@ -94,10 +85,7 @@ func cmdN2IP6() error { res := decimalToIP(result.String(), true) if err != nil { fmt.Fprintf(os.Stderr, "err: %v\n", err) - err := n2ip6Help() - if err != nil { - return err - } + printHelpN2IP6() return nil } From a7d31296ae249399544a29e540fdd299edf966b2 Mon Sep 17 00:00:00 2001 From: harisabdullah Date: Mon, 24 Jul 2023 16:38:38 +0500 Subject: [PATCH 23/50] Moved Logic to lib --- ipinfo/cmd_ip2n.go | 27 +--------- ipinfo/cmd_n2ip.go | 38 +++---------- ipinfo/cmd_n2ip6.go | 11 ++-- {ipinfo => lib}/cmd_calc_infix.go | 90 +++++++++++++++---------------- lib/ip_conversions.go | 55 +++++++++++++++++++ 5 files changed, 115 insertions(+), 106 deletions(-) rename {ipinfo => lib}/cmd_calc_infix.go (82%) create mode 100644 lib/ip_conversions.go diff --git a/ipinfo/cmd_ip2n.go b/ipinfo/cmd_ip2n.go index 510c4caf..415bc5a1 100644 --- a/ipinfo/cmd_ip2n.go +++ b/ipinfo/cmd_ip2n.go @@ -1,11 +1,9 @@ package main import ( - "errors" "fmt" - "net" + "github.com/ipinfo/cli/lib" "os" - "strconv" "strings" "github.com/fatih/color" @@ -69,7 +67,7 @@ func cmdIP2n() error { return nil } - res, err = calcIP2n(cmd) + res, err = lib.CalcIP2n(cmd) if err != nil { fmt.Fprintf(os.Stderr, "err: %v\n", err) @@ -80,24 +78,3 @@ func cmdIP2n() error { fmt.Println(res) return nil } - -func calcIP2n(strIP string) (string, error) { - if isIPv6Address(strIP) { - ip := net.ParseIP(strIP) - if ip == nil { - return "", errors.New("invalid IPv6 address: '" + strIP + "'") - } - - decimalIP := IP6toInt(ip) - return decimalIP.String(), nil - } - if isIPv4Address(strIP) { - ip := net.ParseIP(strIP) - if ip == nil { - return "", errors.New("invalid IPv4 address: '" + strIP + "'") - } - return strconv.FormatInt(IP4toInt(ip), 10), nil - } else { - return "", errors.New("invalid IP address: '" + strIP + "'") - } -} diff --git a/ipinfo/cmd_n2ip.go b/ipinfo/cmd_n2ip.go index f209f16e..e9f42e20 100644 --- a/ipinfo/cmd_n2ip.go +++ b/ipinfo/cmd_n2ip.go @@ -3,10 +3,10 @@ package main import ( "errors" "fmt" - "math/big" - "net" + "github.com/ipinfo/cli/lib" "os" "strings" + "github.com/fatih/color" "github.com/ipinfo/cli/lib/complete" "github.com/ipinfo/cli/lib/complete/predict" @@ -79,12 +79,12 @@ func cmdN2IP() error { return nil } - if isInvalid(cmd) { + if lib.IsInvalid(cmd) { return errors.New("invalid expression") } // Tokenize - tokens, err := tokeinzeExp(cmd) + tokens, err := lib.TokeinzeExp(cmd) if err != nil { return err @@ -93,17 +93,17 @@ func cmdN2IP() error { // Convert to postfix // If it is a single number and not an expression // The tokenization and evaluation would have no effect on the number - postfix := infixToPostfix(tokens) + postfix := lib.InfixToPostfix(tokens) // Evaluate the postfix expression - result, err := evaluatePostfix(postfix) + result, err := lib.EvaluatePostfix(postfix) if err != nil { return err } // Convert to IP - res := decimalToIP(result.String(), forceIpv6) + res := lib.DecimalToIP(result.String(), forceIpv6) if err != nil { fmt.Fprintf(os.Stderr, "err: %v\n", err) @@ -114,27 +114,3 @@ func cmdN2IP() error { fmt.Println(res) return nil } - -func decimalToIP(decimal string, forceIPv6 bool) net.IP { - // Create a new big.Int with a value of 'decimal' - num := new(big.Int) - num, success := num.SetString(decimal, 10) - if !success { - fmt.Fprintf(os.Stderr, "Error parsing the decimal string: %v\n", success) - return nil - } - - // Convert to IPv4 if not forcing IPv6 and 'num' is within the IPv4 range - if !forceIPv6 && num.Cmp(big.NewInt(4294967295)) <= 0 { - ip := make(net.IP, 4) - b := num.Bytes() - copy(ip[4-len(b):], b) - return ip - } - - // Convert to IPv6 - ip := make(net.IP, 16) - b := num.Bytes() - copy(ip[16-len(b):], b) - return ip -} diff --git a/ipinfo/cmd_n2ip6.go b/ipinfo/cmd_n2ip6.go index 91e67fff..ff0ec596 100644 --- a/ipinfo/cmd_n2ip6.go +++ b/ipinfo/cmd_n2ip6.go @@ -4,6 +4,7 @@ import ( "errors" "fmt" "github.com/fatih/color" + "github.com/ipinfo/cli/lib" "github.com/ipinfo/cli/lib/complete" "github.com/ipinfo/cli/lib/complete/predict" "github.com/spf13/pflag" @@ -67,22 +68,22 @@ func cmdN2IP6() error { return nil } - if isInvalid(cmd) { + if lib.IsInvalid(cmd) { return errors.New("invalid expression") } - tokens, err := tokeinzeExp(cmd) + tokens, err := lib.TokeinzeExp(cmd) if err != nil { return err } - postfix := infixToPostfix(tokens) + postfix := lib.InfixToPostfix(tokens) - result, err := evaluatePostfix(postfix) + result, err := lib.EvaluatePostfix(postfix) if err != nil { return err } - res := decimalToIP(result.String(), true) + res := lib.DecimalToIP(result.String(), true) if err != nil { fmt.Fprintf(os.Stderr, "err: %v\n", err) printHelpN2IP6() diff --git a/ipinfo/cmd_calc_infix.go b/lib/cmd_calc_infix.go similarity index 82% rename from ipinfo/cmd_calc_infix.go rename to lib/cmd_calc_infix.go index 91b356cc..a453f83a 100644 --- a/ipinfo/cmd_calc_infix.go +++ b/lib/cmd_calc_infix.go @@ -1,4 +1,4 @@ -package main +package lib import ( "errors" @@ -66,69 +66,69 @@ func isFloat(str string) bool { return regex.MatchString(str) } -func infixToPostfix(infix []string) []string { - var sta Stack +func InfixToPostfix(infix []string) []string { + var postfixStack Stack var postfix []string for _, token := range infix { if isOperator(token) { - for !sta.IsEmpty() && prec(token) <= prec(sta.Top()) { - postfix = append(postfix, sta.Top()) - sta.Pop() + for !postfixStack.IsEmpty() && prec(token) <= prec(postfixStack.Top()) { + postfix = append(postfix, postfixStack.Top()) + postfixStack.Pop() } - sta.Push(token) + postfixStack.Push(token) } else if token == "(" { - sta.Push(token) + postfixStack.Push(token) } else if token == ")" { - for sta.Top() != "(" { - postfix = append(postfix, sta.Top()) - sta.Pop() + for postfixStack.Top() != "(" { + postfix = append(postfix, postfixStack.Top()) + postfixStack.Pop() } - sta.Pop() + postfixStack.Pop() } else { postfix = append(postfix, token) } } // Pop all the remaining elements from the stack - for !sta.IsEmpty() { - postfix = append(postfix, sta.Top()) - sta.Pop() + for !postfixStack.IsEmpty() { + postfix = append(postfix, postfixStack.Top()) + postfixStack.Pop() } return postfix } -func evaluatePostfix(postfix []string) (*big.Float, error) { - var sta Stack +func EvaluatePostfix(postfix []string) (*big.Float, error) { + var postfixStack Stack for _, el := range postfix { // if operand, push it onto the stack. if el == "" { continue } - if isFloat(el) || isIPv4Address(el) || isIPv6Address(el) { - sta.Push(el) + if isFloat(el) || IsIPv4Address(el) || IsIPv6Address(el) { + postfixStack.Push(el) continue } // if operator pop two elements off of the stack. var num1 big.Float - strNum1 := sta.Top() + strNum1 := postfixStack.Top() _, success := num1.SetString(strNum1) if !success { fmt.Println("Error: Failed to convert the num1 to big.Int") return big.NewFloat(0), nil } - sta.Pop() + postfixStack.Pop() var num2 big.Float - strNum2 := sta.Top() + strNum2 := postfixStack.Top() _, success = num2.SetString(strNum2) if !success { fmt.Println("Error: Failed to convert the num2 to big.Int:", strNum2) return big.NewFloat(0), nil } - sta.Pop() + postfixStack.Pop() operator := el result := new(big.Float) @@ -148,10 +148,10 @@ func evaluatePostfix(postfix []string) (*big.Float, error) { result = new(big.Float).Quo(&num2, &num1) case operator == "^": - result1, _ := num1.Float64() - result2, _ := num2.Float64() + num1F64, _ := num1.Float64() + num2F64, _ := num2.Float64() - res := math.Pow(result2, result1) + res := math.Pow(num1F64, num2F64) result = new(big.Float).SetPrec(64).SetFloat64(res) default: @@ -159,11 +159,11 @@ func evaluatePostfix(postfix []string) (*big.Float, error) { } strResult := result.String() - sta.Push(strResult) + postfixStack.Push(strResult) } - strTop := sta.Top() - sta.Pop() + strTop := postfixStack.Top() + postfixStack.Pop() var top = new(big.Float) _, success := top.SetString(strTop) @@ -190,7 +190,7 @@ func translateToken(tempToken string, tokens []string) ([]string, error) { if isFloat(tempToken) { tokens = append(tokens, tempToken) - } else if isIPv4Address(tempToken) { + } else if IsIPv4Address(tempToken) { // convert ipv4 to decimal then append to tokens ip := net.ParseIP(tempToken) if ip == nil { @@ -200,7 +200,7 @@ func translateToken(tempToken string, tokens []string) ([]string, error) { res := strconv.FormatInt(decimalIP, 10) tokens = append(tokens, res) - } else if isIPv6Address(tempToken) { + } else if IsIPv6Address(tempToken) { ip := net.ParseIP(tempToken) if ip == nil { fmt.Println("Invalid IPv6 address") @@ -214,7 +214,7 @@ func translateToken(tempToken string, tokens []string) ([]string, error) { return tokens, err } -func tokeinzeExp(expression string) ([]string, error) { +func TokeinzeExp(expression string) ([]string, error) { var tokens []string var err error @@ -248,7 +248,7 @@ func IP4toInt(IPv4Address net.IP) int64 { return IPv4Int.Int64() } -func isIPv4Address(expression string) bool { +func IsIPv4Address(expression string) bool { // Define the regular expression pattern for matching IPv4 addresses ipV4Pattern := `^(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$` @@ -259,7 +259,7 @@ func isIPv4Address(expression string) bool { return ipV4Regex.MatchString(expression) } -func isIPv6Address(expression string) bool { +func IsIPv6Address(expression string) bool { // Define the regular expression pattern for matching IPv6 addresses ipV6Pattern := `^(([0-9a-fA-F]{1,4}:){7,7}[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,7}:|([0-9a-fA-F]{1,4}:){1,6}:[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,5}(:[0-9a-fA-F]{1,4}){1,2}|([0-9a-fA-F]{1,4}:){1,4}(:[0-9a-fA-F]{1,4}){1,3}|([0-9a-fA-F]{1,4}:){1,3}(:[0-9a-fA-F]{1,4}){1,4}|([0-9a-fA-F]{1,4}:){1,2}(:[0-9a-fA-F]{1,4}){1,5}|[0-9a-fA-F]{1,4}:((:[0-9a-fA-F]{1,4}){1,6})|:((:[0-9a-fA-F]{1,4}){1,7}|:)|fe80:(:[0-9a-fA-F]{0,4}){0,4}%[0-9a-zA-Z]{1,}|::(ffff(:0{1,4}){0,1}:){0,1}((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])\.){3,3}(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])|([0-9a-fA-F]{1,4}:){1,4}:((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])\.){3,3}(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9]))$` @@ -270,7 +270,7 @@ func isIPv6Address(expression string) bool { return ipV6Regex.MatchString(expression) } -func isInvalid(expression string) bool { +func IsInvalid(expression string) bool { validChars := `^[0-9:\.\+\-\*\^\(\)\/ ]*$` validCharsRegx := regexp.MustCompile(validChars) @@ -304,40 +304,40 @@ func isInvalid(expression string) bool { // Function to check if parentheses are balanced func isBalanced(input string) bool { - var sta Stack + var postfixStack Stack for _, char := range input { if char == '(' { - sta.Push("(") + postfixStack.Push("(") } else if char == ')' { - if sta.IsEmpty() { + if postfixStack.IsEmpty() { return false } - sta.Pop() + postfixStack.Pop() } } - return sta.IsEmpty() + return postfixStack.IsEmpty() } -func cmdCalcInfix() (string, error) { +func CmdCalcInfix() (string, error) { // infix := "2+3*(2^3-5)^(2+1*2)-4" cmd := "" if len(os.Args) > 2 { cmd = os.Args[2] } - if isInvalid(cmd) { + if IsInvalid(cmd) { return "", errors.New("invalid expression") } - tokens, err := tokeinzeExp(cmd) + tokens, err := TokeinzeExp(cmd) if err != nil { return "", err } - postfix := infixToPostfix(tokens) + postfix := InfixToPostfix(tokens) - result, err := evaluatePostfix(postfix) + result, err := EvaluatePostfix(postfix) if err != nil { return "", err diff --git a/lib/ip_conversions.go b/lib/ip_conversions.go new file mode 100644 index 00000000..51df43cc --- /dev/null +++ b/lib/ip_conversions.go @@ -0,0 +1,55 @@ +package lib + +import ( + "errors" + "fmt" + "math/big" + "net" + "os" + "strconv" +) + +func CalcIP2n(strIP string) (string, error) { + if IsIPv6Address(strIP) { + ip := net.ParseIP(strIP) + if ip == nil { + return "", errors.New("invalid IPv6 address: '" + strIP + "'") + } + + decimalIP := IP6toInt(ip) + return decimalIP.String(), nil + } + if IsIPv4Address(strIP) { + ip := net.ParseIP(strIP) + if ip == nil { + return "", errors.New("invalid IPv4 address: '" + strIP + "'") + } + return strconv.FormatInt(IP4toInt(ip), 10), nil + } else { + return "", errors.New("invalid IP address: '" + strIP + "'") + } +} + +func DecimalToIP(decimal string, forceIPv6 bool) net.IP { + // Create a new big.Int with a value of 'decimal' + num := new(big.Int) + num, success := num.SetString(decimal, 10) + if !success { + fmt.Fprintf(os.Stderr, "Error parsing the decimal string: %v\n", success) + return nil + } + + // Convert to IPv4 if not forcing IPv6 and 'num' is within the IPv4 range + if !forceIPv6 && num.Cmp(big.NewInt(4294967295)) <= 0 { + ip := make(net.IP, 4) + b := num.Bytes() + copy(ip[4-len(b):], b) + return ip + } + + // Convert to IPv6 + ip := make(net.IP, 16) + b := num.Bytes() + copy(ip[16-len(b):], b) + return ip +} From 6129a414eac5946ed035a4ed32a18493e48f4219 Mon Sep 17 00:00:00 2001 From: harisabdullah Date: Mon, 24 Jul 2023 16:43:37 +0500 Subject: [PATCH 24/50] Moved Logic to lib --- ipinfo/cmd_calc.go | 3 +- ipinfo/cmd_ip2n.go | 2 +- lib/{cmd_calc_infix.go => calc_infix.go} | 33 ---------------------- lib/ip_conversions.go | 36 +++++++++++++++++++++++- 4 files changed, 38 insertions(+), 36 deletions(-) rename lib/{cmd_calc_infix.go => calc_infix.go} (80%) diff --git a/ipinfo/cmd_calc.go b/ipinfo/cmd_calc.go index 340c4ecc..fba80346 100644 --- a/ipinfo/cmd_calc.go +++ b/ipinfo/cmd_calc.go @@ -3,6 +3,7 @@ package main import ( "fmt" "github.com/fatih/color" + "github.com/ipinfo/cli/lib" "github.com/ipinfo/cli/lib/complete" "github.com/ipinfo/cli/lib/complete/predict" "github.com/spf13/pflag" @@ -60,7 +61,7 @@ func cmdCalc() error { switch { case cmd != "": - res, err = cmdCalcInfix() + res, err = lib.CmdCalcInfix() default: printHelpCalc() } diff --git a/ipinfo/cmd_ip2n.go b/ipinfo/cmd_ip2n.go index 415bc5a1..eeb5bb28 100644 --- a/ipinfo/cmd_ip2n.go +++ b/ipinfo/cmd_ip2n.go @@ -67,7 +67,7 @@ func cmdIP2n() error { return nil } - res, err = lib.CalcIP2n(cmd) + res, err = lib.IPtoDecimal(cmd) if err != nil { fmt.Fprintf(os.Stderr, "err: %v\n", err) diff --git a/lib/cmd_calc_infix.go b/lib/calc_infix.go similarity index 80% rename from lib/cmd_calc_infix.go rename to lib/calc_infix.go index a453f83a..28e68ea8 100644 --- a/lib/cmd_calc_infix.go +++ b/lib/calc_infix.go @@ -237,39 +237,6 @@ func TokeinzeExp(expression string) ([]string, error) { return tokens, nil } -func IP6toInt(IPv6Address net.IP) *big.Int { - IPv6Int := big.NewInt(0) - IPv6Int.SetBytes(IPv6Address) - return IPv6Int -} -func IP4toInt(IPv4Address net.IP) int64 { - IPv4Int := big.NewInt(0) - IPv4Int.SetBytes(IPv4Address.To4()) - return IPv4Int.Int64() -} - -func IsIPv4Address(expression string) bool { - // Define the regular expression pattern for matching IPv4 addresses - ipV4Pattern := `^(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$` - - // Compile the regular expression - ipV4Regex := regexp.MustCompile(ipV4Pattern) - - // Use the MatchString function to check if the expression matches the IPv4 pattern - return ipV4Regex.MatchString(expression) -} - -func IsIPv6Address(expression string) bool { - // Define the regular expression pattern for matching IPv6 addresses - ipV6Pattern := `^(([0-9a-fA-F]{1,4}:){7,7}[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,7}:|([0-9a-fA-F]{1,4}:){1,6}:[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,5}(:[0-9a-fA-F]{1,4}){1,2}|([0-9a-fA-F]{1,4}:){1,4}(:[0-9a-fA-F]{1,4}){1,3}|([0-9a-fA-F]{1,4}:){1,3}(:[0-9a-fA-F]{1,4}){1,4}|([0-9a-fA-F]{1,4}:){1,2}(:[0-9a-fA-F]{1,4}){1,5}|[0-9a-fA-F]{1,4}:((:[0-9a-fA-F]{1,4}){1,6})|:((:[0-9a-fA-F]{1,4}){1,7}|:)|fe80:(:[0-9a-fA-F]{0,4}){0,4}%[0-9a-zA-Z]{1,}|::(ffff(:0{1,4}){0,1}:){0,1}((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])\.){3,3}(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])|([0-9a-fA-F]{1,4}:){1,4}:((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])\.){3,3}(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9]))$` - - // Compile the regular expression - ipV6Regex := regexp.MustCompile(ipV6Pattern) - - // Use the MatchString function to check if the expression matches the IPv6 pattern - return ipV6Regex.MatchString(expression) -} - func IsInvalid(expression string) bool { validChars := `^[0-9:\.\+\-\*\^\(\)\/ ]*$` validCharsRegx := regexp.MustCompile(validChars) diff --git a/lib/ip_conversions.go b/lib/ip_conversions.go index 51df43cc..0831fdaa 100644 --- a/lib/ip_conversions.go +++ b/lib/ip_conversions.go @@ -6,10 +6,11 @@ import ( "math/big" "net" "os" + "regexp" "strconv" ) -func CalcIP2n(strIP string) (string, error) { +func IPtoDecimal(strIP string) (string, error) { if IsIPv6Address(strIP) { ip := net.ParseIP(strIP) if ip == nil { @@ -53,3 +54,36 @@ func DecimalToIP(decimal string, forceIPv6 bool) net.IP { copy(ip[16-len(b):], b) return ip } + +func IP6toInt(IPv6Address net.IP) *big.Int { + IPv6Int := big.NewInt(0) + IPv6Int.SetBytes(IPv6Address) + return IPv6Int +} +func IP4toInt(IPv4Address net.IP) int64 { + IPv4Int := big.NewInt(0) + IPv4Int.SetBytes(IPv4Address.To4()) + return IPv4Int.Int64() +} + +func IsIPv4Address(expression string) bool { + // Define the regular expression pattern for matching IPv4 addresses + ipV4Pattern := `^(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$` + + // Compile the regular expression + ipV4Regex := regexp.MustCompile(ipV4Pattern) + + // Use the MatchString function to check if the expression matches the IPv4 pattern + return ipV4Regex.MatchString(expression) +} + +func IsIPv6Address(expression string) bool { + // Define the regular expression pattern for matching IPv6 addresses + ipV6Pattern := `^(([0-9a-fA-F]{1,4}:){7,7}[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,7}:|([0-9a-fA-F]{1,4}:){1,6}:[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,5}(:[0-9a-fA-F]{1,4}){1,2}|([0-9a-fA-F]{1,4}:){1,4}(:[0-9a-fA-F]{1,4}){1,3}|([0-9a-fA-F]{1,4}:){1,3}(:[0-9a-fA-F]{1,4}){1,4}|([0-9a-fA-F]{1,4}:){1,2}(:[0-9a-fA-F]{1,4}){1,5}|[0-9a-fA-F]{1,4}:((:[0-9a-fA-F]{1,4}){1,6})|:((:[0-9a-fA-F]{1,4}){1,7}|:)|fe80:(:[0-9a-fA-F]{0,4}){0,4}%[0-9a-zA-Z]{1,}|::(ffff(:0{1,4}){0,1}:){0,1}((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])\.){3,3}(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])|([0-9a-fA-F]{1,4}:){1,4}:((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])\.){3,3}(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9]))$` + + // Compile the regular expression + ipV6Regex := regexp.MustCompile(ipV6Pattern) + + // Use the MatchString function to check if the expression matches the IPv6 pattern + return ipV6Regex.MatchString(expression) +} From 114eeb1f35559f5cea89e5502a264865accd541f Mon Sep 17 00:00:00 2001 From: harisabdullah Date: Mon, 24 Jul 2023 16:49:22 +0500 Subject: [PATCH 25/50] sorting imports --- ipinfo/cmd_ip2n.go | 7 +++---- ipinfo/cmd_n2ip.go | 7 +++---- 2 files changed, 6 insertions(+), 8 deletions(-) diff --git a/ipinfo/cmd_ip2n.go b/ipinfo/cmd_ip2n.go index eeb5bb28..422d42d2 100644 --- a/ipinfo/cmd_ip2n.go +++ b/ipinfo/cmd_ip2n.go @@ -2,14 +2,13 @@ package main import ( "fmt" - "github.com/ipinfo/cli/lib" - "os" - "strings" - "github.com/fatih/color" + "github.com/ipinfo/cli/lib" "github.com/ipinfo/cli/lib/complete" "github.com/ipinfo/cli/lib/complete/predict" "github.com/spf13/pflag" + "os" + "strings" ) var completionsIP2n = &complete.Command{ diff --git a/ipinfo/cmd_n2ip.go b/ipinfo/cmd_n2ip.go index e9f42e20..434c4e2c 100644 --- a/ipinfo/cmd_n2ip.go +++ b/ipinfo/cmd_n2ip.go @@ -3,14 +3,13 @@ package main import ( "errors" "fmt" - "github.com/ipinfo/cli/lib" - "os" - "strings" - "github.com/fatih/color" + "github.com/ipinfo/cli/lib" "github.com/ipinfo/cli/lib/complete" "github.com/ipinfo/cli/lib/complete/predict" "github.com/spf13/pflag" + "os" + "strings" ) var forceIpv6 bool From efffdd4016ae9182e1504cb7dd7de883884bf97e Mon Sep 17 00:00:00 2001 From: harisabdullah Date: Tue, 25 Jul 2023 16:31:38 +0500 Subject: [PATCH 26/50] 1) Moving logic to ```lib/``` 2) Standardization of errors 3) Improved naming conventions and useful comments 4) Fixed a parsing from string to float which was causing information loss --- ipinfo/cmd_calc.go | 4 +- ipinfo/cmd_ip2n.go | 11 ++--- ipinfo/cmd_n2ip.go | 41 +++------------- ipinfo/cmd_n2ip6.go | 27 ++--------- lib/calc_infix.go | 108 +++++++++++++++++++----------------------- lib/cmd_ip2n.go | 10 ++++ lib/cmd_n2ip.go | 37 +++++++++++++++ lib/cmd_n2ip6.go | 37 +++++++++++++++ lib/ip_conversions.go | 42 ++++++++++------ 9 files changed, 175 insertions(+), 142 deletions(-) create mode 100644 lib/cmd_ip2n.go create mode 100644 lib/cmd_n2ip.go create mode 100644 lib/cmd_n2ip6.go diff --git a/ipinfo/cmd_calc.go b/ipinfo/cmd_calc.go index fba80346..a7e3eb18 100644 --- a/ipinfo/cmd_calc.go +++ b/ipinfo/cmd_calc.go @@ -61,7 +61,7 @@ func cmdCalc() error { switch { case cmd != "": - res, err = lib.CmdCalcInfix() + res, err = lib.CmdCalcInfix(cmd) default: printHelpCalc() } @@ -71,8 +71,6 @@ func cmdCalc() error { if err != nil { return err } - - printHelpCalc() } fmt.Println(res) diff --git a/ipinfo/cmd_ip2n.go b/ipinfo/cmd_ip2n.go index 422d42d2..9c5a0c5a 100644 --- a/ipinfo/cmd_ip2n.go +++ b/ipinfo/cmd_ip2n.go @@ -51,11 +51,8 @@ func cmdIP2n() error { if fHelp { printHelpDefault() - return nil } - var err error - var res string cmd := "" if len(os.Args) > 2 { cmd = os.Args[2] @@ -66,14 +63,12 @@ func cmdIP2n() error { return nil } - res, err = lib.IPtoDecimal(cmd) - + res, err := lib.CmdIP2n(cmd) if err != nil { - fmt.Fprintf(os.Stderr, "err: %v\n", err) - printHelpIp2n() - return nil + return err } fmt.Println(res) + return nil } diff --git a/ipinfo/cmd_n2ip.go b/ipinfo/cmd_n2ip.go index 434c4e2c..a879fcab 100644 --- a/ipinfo/cmd_n2ip.go +++ b/ipinfo/cmd_n2ip.go @@ -1,7 +1,6 @@ package main import ( - "errors" "fmt" "github.com/fatih/color" "github.com/ipinfo/cli/lib" @@ -12,8 +11,6 @@ import ( "strings" ) -var forceIpv6 bool - var completionsN2IP = &complete.Command{ Flags: map[string]complete.Predictor{ "--nocolor": predict.Nothing, @@ -45,6 +42,7 @@ Options: } func cmdN2IP() error { + var forceIpv6 bool pflag.BoolVarP(&fHelp, "help", "h", false, "show help.") pflag.BoolVar(&fNoColor, "nocolor", false, "disable colored output.") pflag.BoolVarP(&forceIpv6, "ipv6", "6", false, "force conversion to IPv6 address") @@ -53,14 +51,11 @@ func cmdN2IP() error { if fNoColor { color.NoColor = true } - if fHelp { printHelpDefault() return nil } - var err error - cmd := "" // Reading input from the command line if forceIpv6 && len(os.Args) > 3 { @@ -78,36 +73,12 @@ func cmdN2IP() error { return nil } - if lib.IsInvalid(cmd) { - return errors.New("invalid expression") - } - - // Tokenize - tokens, err := lib.TokeinzeExp(cmd) - + res, err := lib.CmdN2IP(cmd, forceIpv6) if err != nil { - return err - } - - // Convert to postfix - // If it is a single number and not an expression - // The tokenization and evaluation would have no effect on the number - postfix := lib.InfixToPostfix(tokens) - - // Evaluate the postfix expression - result, err := lib.EvaluatePostfix(postfix) - - if err != nil { - return err - } - - // Convert to IP - res := lib.DecimalToIP(result.String(), forceIpv6) - - if err != nil { - fmt.Fprintf(os.Stderr, "err: %v\n", err) - printHelpN2IP() - return nil + _, err := fmt.Fprintf(os.Stderr, "err: %v\n", err) + if err != nil { + return err + } } fmt.Println(res) diff --git a/ipinfo/cmd_n2ip6.go b/ipinfo/cmd_n2ip6.go index ff0ec596..c7c33e24 100644 --- a/ipinfo/cmd_n2ip6.go +++ b/ipinfo/cmd_n2ip6.go @@ -1,7 +1,6 @@ package main import ( - "errors" "fmt" "github.com/fatih/color" "github.com/ipinfo/cli/lib" @@ -55,8 +54,6 @@ func cmdN2IP6() error { return nil } - var err error - cmd := "" if len(os.Args) > 2 { cmd = os.Args[2] @@ -68,26 +65,12 @@ func cmdN2IP6() error { return nil } - if lib.IsInvalid(cmd) { - return errors.New("invalid expression") - } - tokens, err := lib.TokeinzeExp(cmd) - if err != nil { - return err - } - - postfix := lib.InfixToPostfix(tokens) - - result, err := lib.EvaluatePostfix(postfix) + res, err := lib.CmdN2IP6(cmd) if err != nil { - return err - } - - res := lib.DecimalToIP(result.String(), true) - if err != nil { - fmt.Fprintf(os.Stderr, "err: %v\n", err) - printHelpN2IP6() - return nil + _, err := fmt.Fprintf(os.Stderr, "err: %v\n", err) + if err != nil { + return err + } } fmt.Println(res) diff --git a/lib/calc_infix.go b/lib/calc_infix.go index 28e68ea8..4e895153 100644 --- a/lib/calc_infix.go +++ b/lib/calc_infix.go @@ -1,12 +1,10 @@ package lib import ( - "errors" "fmt" "math" "math/big" "net" - "os" "regexp" "strconv" ) @@ -45,7 +43,7 @@ func (st *Stack) Top() string { } } -// Function to return precedence of operators +// prec Function to return precedence of operators func prec(s string) int { if s == "^" { return 3 @@ -58,6 +56,7 @@ func prec(s string) int { } } +// isFloat Function to check if string is a float func isFloat(str string) bool { pattern := `^[-+]?\d+(\.\d+)?$` @@ -66,6 +65,7 @@ func isFloat(str string) bool { return regex.MatchString(str) } +// InfixToPostfix Function to convert infix expression to postfix expression using a stack based algorithm func InfixToPostfix(infix []string) []string { var postfixStack Stack var postfix []string @@ -97,6 +97,7 @@ func InfixToPostfix(infix []string) []string { return postfix } +// EvaluatePostfix Function to evaluate postfix expression using a stack based algorithm func EvaluatePostfix(postfix []string) (*big.Float, error) { var postfixStack Stack for _, el := range postfix { @@ -110,55 +111,56 @@ func EvaluatePostfix(postfix []string) (*big.Float, error) { } // if operator pop two elements off of the stack. - var num1 big.Float strNum1 := postfixStack.Top() - _, success := num1.SetString(strNum1) - - if !success { - fmt.Println("Error: Failed to convert the num1 to big.Int") - return big.NewFloat(0), nil - } postfixStack.Pop() + num1, _, err := big.ParseFloat(strNum1, 10, uint(10000), big.ToZero) + if err != nil { + return big.NewFloat(0), ErrInvalidInput + } - var num2 big.Float strNum2 := postfixStack.Top() - _, success = num2.SetString(strNum2) - - if !success { - fmt.Println("Error: Failed to convert the num2 to big.Int:", strNum2) - return big.NewFloat(0), nil - } postfixStack.Pop() + num2, _, err := big.ParseFloat(strNum2, 10, uint(10000), big.ToZero) + if err != nil { + return big.NewFloat(0), ErrInvalidInput + } + operator := el result := new(big.Float) switch { case operator == "+": - //fmt.Println("Adding") - result = result.Add(&num2, &num1) + result = result.Add(num2, num1) case operator == "-": - //fmt.Println("Subtracting") - result = result.Sub(&num2, &num1) + result = result.Sub(num2, num1) case operator == "*": - //fmt.Println("Multiplying") - result = result.Mul(&num2, &num1) + result = result.Mul(num2, num1) + case operator == "/": - //fmt.Println("Dividing") - result = new(big.Float).Quo(&num2, &num1) + // Check for division by zero + if num1.Cmp(big.NewFloat(0)) == 0 { + return big.NewFloat(0), ErrInvalidInput + } + + result = new(big.Float).Quo(num2, num1) case operator == "^": + // Using Float64() to convert big.Float to float64 + // because big.Float does not have a equivalent function + // for math.Pow() which accepts big.Float + // also, it can support around 194 digits before decimal num1F64, _ := num1.Float64() num2F64, _ := num2.Float64() - res := math.Pow(num1F64, num2F64) - result = new(big.Float).SetPrec(64).SetFloat64(res) + res := math.Pow(num2F64, num1F64) + result = new(big.Float).SetPrec(uint(10000)).SetFloat64(res) default: - fmt.Println("invalid operator: ", operator) + return big.NewFloat(0), ErrInvalidInput } - strResult := result.String() + strResult := result.Text('f', 50) postfixStack.Push(strResult) } @@ -172,18 +174,19 @@ func EvaluatePostfix(postfix []string) (*big.Float, error) { fmt.Println("Error: Failed to convert the string to big.Int") return big.NewFloat(0), nil } + return top, nil } +// isOperator Function to check if token is an operator func isOperator(token string) bool { operators := map[string]bool{"+": true, "-": true, "*": true, "/": true, "^": true /* add other operators here */} _, isOperator := operators[token] return isOperator } +// translateToken Function to translate token to decimal i.e. convert ipv4, ipv6 to decimal func translateToken(tempToken string, tokens []string) ([]string, error) { - var err error = nil - if tempToken == "" { return tokens, nil } @@ -191,36 +194,30 @@ func translateToken(tempToken string, tokens []string) ([]string, error) { if isFloat(tempToken) { tokens = append(tokens, tempToken) } else if IsIPv4Address(tempToken) { - // convert ipv4 to decimal then append to tokens + // Convert ipv4 to decimal then append to tokens ip := net.ParseIP(tempToken) - if ip == nil { - err = errors.New("invalid IPv4 address: '" + tempToken + "'") - } decimalIP := IP4toInt(ip) res := strconv.FormatInt(decimalIP, 10) tokens = append(tokens, res) } else if IsIPv6Address(tempToken) { ip := net.ParseIP(tempToken) - if ip == nil { - fmt.Println("Invalid IPv6 address") - err = errors.New("invalid IPv6 address: '" + tempToken + "'") - } decimalIP := IP6toInt(ip) tokens = append(tokens, decimalIP.String()) } else { - err = errors.New("invalid expression") + return []string{}, ErrInvalidInput } - return tokens, err + return tokens, nil } -func TokeinzeExp(expression string) ([]string, error) { +// TokenizeInfix Function to tokenize infix expression +func TokenizeInfix(infix string) ([]string, error) { var tokens []string var err error - expression = "(" + expression + ")" + infix = "(" + infix + ")" tempToken := "" - for _, char := range expression { + for _, char := range infix { opchar := string(char) if isFloat(opchar) || opchar == "." || opchar == ":" { tempToken = tempToken + opchar @@ -237,7 +234,8 @@ func TokeinzeExp(expression string) ([]string, error) { return tokens, nil } -func IsInvalid(expression string) bool { +// IsInvalidInfix Function to check if infix expression is valid +func IsInvalidInfix(expression string) bool { validChars := `^[0-9:\.\+\-\*\^\(\)\/ ]*$` validCharsRegx := regexp.MustCompile(validChars) @@ -269,7 +267,7 @@ func IsInvalid(expression string) bool { return !validCharsRegx.MatchString(expression) || !isBalanced(expression) } -// Function to check if parentheses are balanced +// isBalanced Function to check if parentheses are balanced func isBalanced(input string) bool { var postfixStack Stack for _, char := range input { @@ -285,19 +283,12 @@ func isBalanced(input string) bool { return postfixStack.IsEmpty() } -func CmdCalcInfix() (string, error) { - // infix := "2+3*(2^3-5)^(2+1*2)-4" - cmd := "" - if len(os.Args) > 2 { - cmd = os.Args[2] - } - - if IsInvalid(cmd) { - return "", errors.New("invalid expression") +func CmdCalcInfix(infix string) (string, error) { + if IsInvalidInfix(infix) { + return "", ErrInvalidInput } - tokens, err := TokeinzeExp(cmd) - + tokens, err := TokenizeInfix(infix) if err != nil { return "", err } @@ -305,10 +296,9 @@ func CmdCalcInfix() (string, error) { postfix := InfixToPostfix(tokens) result, err := EvaluatePostfix(postfix) - if err != nil { return "", err } - return result.Text('f', 0), nil + return result.Text('f', 10), nil } diff --git a/lib/cmd_ip2n.go b/lib/cmd_ip2n.go new file mode 100644 index 00000000..60fe795e --- /dev/null +++ b/lib/cmd_ip2n.go @@ -0,0 +1,10 @@ +package lib + +func CmdIP2n(cmd string) (string, error) { + res, err := IPtoDecimalStr(cmd) + if err != nil { + return "", err + } + + return res, err +} diff --git a/lib/cmd_n2ip.go b/lib/cmd_n2ip.go new file mode 100644 index 00000000..553580d9 --- /dev/null +++ b/lib/cmd_n2ip.go @@ -0,0 +1,37 @@ +package lib + +func CmdN2IP(expression string, forceIpv6 bool) (string, error) { + if IsInvalidInfix(expression) { + return "", ErrInvalidInput + } + + // n2ip also accepts an expression which is why the following + // Steps are being done + // Convert to postfix + // If it is a single number and not an expression + // The tokenization and evaluation would have no effect on the number + + // Tokenize the expression + tokens, err := TokenizeInfix(expression) + if err != nil { + return "", err + } + + postfix := InfixToPostfix(tokens) + + // Evaluate the postfix expression + result, err := EvaluatePostfix(postfix) + if err != nil { + return "", err + } + + // Convert to IP + // Precision should be 0 i.e. number of digits after decimal + // as ip cannot be derived from a float + res, err := DecimalStrToIP(result.Text('f', 0), forceIpv6) + if err != nil { + return "", err + } + + return res.String(), nil +} diff --git a/lib/cmd_n2ip6.go b/lib/cmd_n2ip6.go new file mode 100644 index 00000000..e1613ab6 --- /dev/null +++ b/lib/cmd_n2ip6.go @@ -0,0 +1,37 @@ +package lib + +func CmdN2IP6(expression string) (string, error) { + if IsInvalidInfix(expression) { + return "", ErrInvalidInput + } + + // n2ip6 also accepts an expression which is why the following + // Steps are being done + // Convert to postfix + // If it is a single number and not an expression + // The tokenization and evaluation would have no effect on the number + + // Tokenize + tokens, err := TokenizeInfix(expression) + if err != nil { + return "", err + } + + postfix := InfixToPostfix(tokens) + + // Evaluate the postfix expression + result, err := EvaluatePostfix(postfix) + if err != nil { + return "", err + } + + // Convert to IP + // Precision should be 0 i.e. number of digits after decimal + // as ip cannot be derived from a float + res, err := DecimalStrToIP(result.Text('f', 0), true) + if err != nil { + return "", err + } + + return res.String(), nil +} diff --git a/lib/ip_conversions.go b/lib/ip_conversions.go index 0831fdaa..ae7d84c4 100644 --- a/lib/ip_conversions.go +++ b/lib/ip_conversions.go @@ -1,20 +1,19 @@ package lib import ( - "errors" "fmt" "math/big" "net" - "os" "regexp" "strconv" ) -func IPtoDecimal(strIP string) (string, error) { +// IPtoDecimalStr converts an IP address to a decimal string +func IPtoDecimalStr(strIP string) (string, error) { if IsIPv6Address(strIP) { ip := net.ParseIP(strIP) if ip == nil { - return "", errors.New("invalid IPv6 address: '" + strIP + "'") + return "", ErrNotIP } decimalIP := IP6toInt(ip) @@ -23,21 +22,23 @@ func IPtoDecimal(strIP string) (string, error) { if IsIPv4Address(strIP) { ip := net.ParseIP(strIP) if ip == nil { - return "", errors.New("invalid IPv4 address: '" + strIP + "'") + return "", ErrNotIP } return strconv.FormatInt(IP4toInt(ip), 10), nil } else { - return "", errors.New("invalid IP address: '" + strIP + "'") + return "", ErrInvalidInput } } -func DecimalToIP(decimal string, forceIPv6 bool) net.IP { +// DecimalStrToIP converts a decimal string to an IP address +func DecimalStrToIP(decimal string, forceIPv6 bool) (net.IP, error) { // Create a new big.Int with a value of 'decimal' num := new(big.Int) num, success := num.SetString(decimal, 10) + if !success { - fmt.Fprintf(os.Stderr, "Error parsing the decimal string: %v\n", success) - return nil + fmt.Print(decimal) + return nil, ErrInvalidInput } // Convert to IPv4 if not forcing IPv6 and 'num' is within the IPv4 range @@ -45,27 +46,37 @@ func DecimalToIP(decimal string, forceIPv6 bool) net.IP { ip := make(net.IP, 4) b := num.Bytes() copy(ip[4-len(b):], b) - return ip + return ip, nil } - // Convert to IPv6 - ip := make(net.IP, 16) - b := num.Bytes() - copy(ip[16-len(b):], b) - return ip + // Convert to IPv6 if 'num' is within the IPv6 range + maxIpv6 := new(big.Int) + maxIpv6.SetString("340282366920938463463374607431768211455", 10) + if num.Cmp(maxIpv6) <= 0 { + ip := make(net.IP, 16) + b := num.Bytes() + copy(ip[16-len(b):], b) + return ip, nil + } + + return nil, ErrInvalidInput } +// IP6toInt converts an IPv6 address to a big.Int func IP6toInt(IPv6Address net.IP) *big.Int { IPv6Int := big.NewInt(0) IPv6Int.SetBytes(IPv6Address) return IPv6Int } + +// IP4toInt converts an IPv4 address to a big.Int func IP4toInt(IPv4Address net.IP) int64 { IPv4Int := big.NewInt(0) IPv4Int.SetBytes(IPv4Address.To4()) return IPv4Int.Int64() } +// IsIPv4Address checks if the given string is an IPv4 address func IsIPv4Address(expression string) bool { // Define the regular expression pattern for matching IPv4 addresses ipV4Pattern := `^(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$` @@ -77,6 +88,7 @@ func IsIPv4Address(expression string) bool { return ipV4Regex.MatchString(expression) } +// IsIPv6Address checks if the given string is an IPv6 address func IsIPv6Address(expression string) bool { // Define the regular expression pattern for matching IPv6 addresses ipV6Pattern := `^(([0-9a-fA-F]{1,4}:){7,7}[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,7}:|([0-9a-fA-F]{1,4}:){1,6}:[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,5}(:[0-9a-fA-F]{1,4}){1,2}|([0-9a-fA-F]{1,4}:){1,4}(:[0-9a-fA-F]{1,4}){1,3}|([0-9a-fA-F]{1,4}:){1,3}(:[0-9a-fA-F]{1,4}){1,4}|([0-9a-fA-F]{1,4}:){1,2}(:[0-9a-fA-F]{1,4}){1,5}|[0-9a-fA-F]{1,4}:((:[0-9a-fA-F]{1,4}){1,6})|:((:[0-9a-fA-F]{1,4}){1,7}|:)|fe80:(:[0-9a-fA-F]{0,4}){0,4}%[0-9a-zA-Z]{1,}|::(ffff(:0{1,4}){0,1}:){0,1}((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])\.){3,3}(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])|([0-9a-fA-F]{1,4}:){1,4}:((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])\.){3,3}(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9]))$` From c5dc29938ebb46a69baefc472b7a716ee9cca181 Mon Sep 17 00:00:00 2001 From: harisabdullah Date: Tue, 25 Jul 2023 17:04:14 +0500 Subject: [PATCH 27/50] 1) Fixed a parsing from string to float which was causing information loss --- lib/calc_infix.go | 32 +++++++++++++++++++++++++------- 1 file changed, 25 insertions(+), 7 deletions(-) diff --git a/lib/calc_infix.go b/lib/calc_infix.go index 4e895153..eccc3d69 100644 --- a/lib/calc_infix.go +++ b/lib/calc_infix.go @@ -7,6 +7,7 @@ import ( "net" "regexp" "strconv" + "strings" ) type Stack []string @@ -161,18 +162,16 @@ func EvaluatePostfix(postfix []string) (*big.Float, error) { } strResult := result.Text('f', 50) + fmt.Println(strResult) postfixStack.Push(strResult) } strTop := postfixStack.Top() postfixStack.Pop() - var top = new(big.Float) - _, success := top.SetString(strTop) - - if !success { - fmt.Println("Error: Failed to convert the string to big.Int") - return big.NewFloat(0), nil + top, _, err := big.ParseFloat(strTop, 10, uint(10000), big.ToZero) + if err != nil { + return big.NewFloat(0), ErrInvalidInput } return top, nil @@ -300,5 +299,24 @@ func CmdCalcInfix(infix string) (string, error) { return "", err } - return result.Text('f', 10), nil + precision := digitsAfterDecimal(*result) + fmt.Println(precision) + resultStr := result.Text('f', precision) + return resultStr, nil +} + +func digitsAfterDecimal(float big.Float) int { + str := float.Text('f', 100) + decimalIndex := strings.Index(str, ".") + // Start counting the digits after the decimal point. + count := 0 + for i := len(str) - 1; i > decimalIndex; i-- { + if str[i] == '0' { + count++ + } else { + break + } + } + + return len(str) - (decimalIndex + 1) - count } From 095ffd2b8035449fb808410ac83bbfcfcf7d1780 Mon Sep 17 00:00:00 2001 From: harisabdullah Date: Tue, 25 Jul 2023 17:06:08 +0500 Subject: [PATCH 28/50] removed print statement --- lib/calc_infix.go | 3 --- 1 file changed, 3 deletions(-) diff --git a/lib/calc_infix.go b/lib/calc_infix.go index eccc3d69..bde3d54d 100644 --- a/lib/calc_infix.go +++ b/lib/calc_infix.go @@ -1,7 +1,6 @@ package lib import ( - "fmt" "math" "math/big" "net" @@ -162,7 +161,6 @@ func EvaluatePostfix(postfix []string) (*big.Float, error) { } strResult := result.Text('f', 50) - fmt.Println(strResult) postfixStack.Push(strResult) } @@ -300,7 +298,6 @@ func CmdCalcInfix(infix string) (string, error) { } precision := digitsAfterDecimal(*result) - fmt.Println(precision) resultStr := result.Text('f', precision) return resultStr, nil } From 992a32bd0d29683777fca99d88a4b933c9b06d08 Mon Sep 17 00:00:00 2001 From: harisabdullah Date: Tue, 25 Jul 2023 17:35:10 +0500 Subject: [PATCH 29/50] Fixed a bug where calc was not accepting alphabets for ipv6 --- lib/calc_infix.go | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/lib/calc_infix.go b/lib/calc_infix.go index bde3d54d..c8d85c50 100644 --- a/lib/calc_infix.go +++ b/lib/calc_infix.go @@ -207,6 +207,12 @@ func translateToken(tempToken string, tokens []string) ([]string, error) { return tokens, nil } +func isValidPartOfToken(char rune) bool { + validChars := `^[0-9a-fA-F:\.]*$` + validCharsRegx := regexp.MustCompile(validChars) + return validCharsRegx.MatchString(string(char)) +} + // TokenizeInfix Function to tokenize infix expression func TokenizeInfix(infix string) ([]string, error) { var tokens []string @@ -216,7 +222,7 @@ func TokenizeInfix(infix string) ([]string, error) { tempToken := "" for _, char := range infix { opchar := string(char) - if isFloat(opchar) || opchar == "." || opchar == ":" { + if isValidPartOfToken(char) { tempToken = tempToken + opchar } else if char == '(' || char == ')' || isOperator(opchar) { tokens, err = translateToken(tempToken, tokens) @@ -233,7 +239,7 @@ func TokenizeInfix(infix string) ([]string, error) { // IsInvalidInfix Function to check if infix expression is valid func IsInvalidInfix(expression string) bool { - validChars := `^[0-9:\.\+\-\*\^\(\)\/ ]*$` + validChars := `^[0-9a-fA-F:\.\+\-\*\^\(\)\/ ]*$` validCharsRegx := regexp.MustCompile(validChars) var PrevChar rune From b5346c92fb08f5e241b9069514248ffcf1b3d7fa Mon Sep 17 00:00:00 2001 From: harisabdullah Date: Wed, 26 Jul 2023 11:28:51 +0500 Subject: [PATCH 30/50] Updated Comments --- lib/calc_infix.go | 1 - 1 file changed, 1 deletion(-) diff --git a/lib/calc_infix.go b/lib/calc_infix.go index c8d85c50..d8d32488 100644 --- a/lib/calc_infix.go +++ b/lib/calc_infix.go @@ -149,7 +149,6 @@ func EvaluatePostfix(postfix []string) (*big.Float, error) { // Using Float64() to convert big.Float to float64 // because big.Float does not have a equivalent function // for math.Pow() which accepts big.Float - // also, it can support around 194 digits before decimal num1F64, _ := num1.Float64() num2F64, _ := num2.Float64() From 5f2b34deb4d31230cb23fb6bde557c3379cb7530 Mon Sep 17 00:00:00 2001 From: harisabdullah Date: Wed, 26 Jul 2023 17:40:59 +0500 Subject: [PATCH 31/50] Refactor: Moved logic to ```lib/``` --- ipinfo/cmd_calc.go | 41 ++++------------------- ipinfo/cmd_ip2n.go | 36 ++++----------------- ipinfo/cmd_n2ip.go | 49 +++++----------------------- ipinfo/cmd_n2ip6.go | 41 ++++------------------- lib/calc_infix.go | 79 +++++++++++++++++++++++++++++++++------------ lib/cmd_ip2n.go | 47 ++++++++++++++++++++++++--- lib/cmd_n2ip.go | 61 +++++++++++++++++++++++++++++----- lib/cmd_n2ip6.go | 58 +++++++++++++++++++++++++++------ 8 files changed, 232 insertions(+), 180 deletions(-) diff --git a/ipinfo/cmd_calc.go b/ipinfo/cmd_calc.go index a7e3eb18..3f04e1b0 100644 --- a/ipinfo/cmd_calc.go +++ b/ipinfo/cmd_calc.go @@ -2,12 +2,10 @@ package main import ( "fmt" - "github.com/fatih/color" "github.com/ipinfo/cli/lib" "github.com/ipinfo/cli/lib/complete" "github.com/ipinfo/cli/lib/complete/predict" "github.com/spf13/pflag" - "os" ) var completionsCalc = &complete.Command{ @@ -17,6 +15,7 @@ var completionsCalc = &complete.Command{ }, } +// printHelpCalc prints the help message for the "calc" command. func printHelpCalc() { fmt.Printf( `Usage: %s calc [] @@ -38,41 +37,15 @@ Options: `, progBase) } +// cmdCalc is the handler for the "calc" command. func cmdCalc() error { - pflag.BoolVarP(&fHelp, "help", "h", false, "show help.") - pflag.BoolVar(&fNoColor, "nocolor", false, "disable colored output.") + f := lib.CmdCalcFlags{} + f.Init() pflag.Parse() - if fNoColor { - color.NoColor = true + if pflag.NArg() <= 1 && pflag.NFlag() == 0 { + f.Help = true } - if fHelp { - printHelpDefault() - return nil - } - - var err error - var res string - cmd := "" - if len(os.Args) > 2 { - cmd = os.Args[2] - } - - switch { - case cmd != "": - res, err = lib.CmdCalcInfix(cmd) - default: - printHelpCalc() - } - - if err != nil { - _, err := fmt.Fprintf(os.Stderr, "err: %v\n", err) - if err != nil { - return err - } - } - - fmt.Println(res) - return nil + return lib.CmdCalcInfix(f, pflag.Args()[1:], printHelpCalc) } diff --git a/ipinfo/cmd_ip2n.go b/ipinfo/cmd_ip2n.go index 9c5a0c5a..795659f8 100644 --- a/ipinfo/cmd_ip2n.go +++ b/ipinfo/cmd_ip2n.go @@ -2,13 +2,10 @@ package main import ( "fmt" - "github.com/fatih/color" "github.com/ipinfo/cli/lib" "github.com/ipinfo/cli/lib/complete" "github.com/ipinfo/cli/lib/complete/predict" "github.com/spf13/pflag" - "os" - "strings" ) var completionsIP2n = &complete.Command{ @@ -19,6 +16,7 @@ var completionsIP2n = &complete.Command{ }, } +// printHelpIp2n prints the help message for the "ip2n" command. func printHelpIp2n() { fmt.Printf( `Usage: %s ip2n @@ -40,35 +38,15 @@ Options: `, progBase) } +// cmdIP2n is the handler for the "ip2n" command. func cmdIP2n() error { - pflag.BoolVarP(&fHelp, "help", "h", false, "show help.") - pflag.BoolVar(&fNoColor, "nocolor", false, "disable colored output.") + f := lib.CmdIP2nFlags{} + f.Init() pflag.Parse() - if fNoColor { - color.NoColor = true + if pflag.NArg() <= 1 && pflag.NFlag() == 0 { + f.Help = true } - if fHelp { - printHelpDefault() - } - - cmd := "" - if len(os.Args) > 2 { - cmd = os.Args[2] - } - - if strings.TrimSpace(cmd) == "" { - printHelpIp2n() - return nil - } - - res, err := lib.CmdIP2n(cmd) - if err != nil { - return err - } - - fmt.Println(res) - - return nil + return lib.CmdIP2n(f, pflag.Args()[1:], printHelpIp2n) } diff --git a/ipinfo/cmd_n2ip.go b/ipinfo/cmd_n2ip.go index a879fcab..eb1ec38c 100644 --- a/ipinfo/cmd_n2ip.go +++ b/ipinfo/cmd_n2ip.go @@ -2,15 +2,13 @@ package main import ( "fmt" - "github.com/fatih/color" "github.com/ipinfo/cli/lib" "github.com/ipinfo/cli/lib/complete" "github.com/ipinfo/cli/lib/complete/predict" "github.com/spf13/pflag" - "os" - "strings" ) +// cmdN2IP is the handler for the "n2ip" command. var completionsN2IP = &complete.Command{ Flags: map[string]complete.Predictor{ "--nocolor": predict.Nothing, @@ -21,6 +19,7 @@ var completionsN2IP = &complete.Command{ }, } +// printHelpN2IP prints the help message for the "n2ip" command. func printHelpN2IP() { fmt.Printf( `Usage: %s n2ip [] @@ -41,46 +40,14 @@ Options: `, progBase) } +// cmdN2IP is the handler for the "n2ip" command. func cmdN2IP() error { - var forceIpv6 bool - pflag.BoolVarP(&fHelp, "help", "h", false, "show help.") - pflag.BoolVar(&fNoColor, "nocolor", false, "disable colored output.") - pflag.BoolVarP(&forceIpv6, "ipv6", "6", false, "force conversion to IPv6 address") + f := lib.CmdN2IPFlags{} + f.Init() pflag.Parse() - - if fNoColor { - color.NoColor = true - } - if fHelp { - printHelpDefault() - return nil - } - - cmd := "" - // Reading input from the command line - if forceIpv6 && len(os.Args) > 3 { - cmd = os.Args[3] - } else if !forceIpv6 && len(os.Args) > 2 { - cmd = os.Args[2] - } else { - printHelpN2IP() - return nil - } - - // Validate the input - if strings.TrimSpace(cmd) == "" { - printHelpN2IP() - return nil - } - - res, err := lib.CmdN2IP(cmd, forceIpv6) - if err != nil { - _, err := fmt.Fprintf(os.Stderr, "err: %v\n", err) - if err != nil { - return err - } + if pflag.NArg() <= 1 && pflag.NFlag() == 0 { + f.Help = true } - fmt.Println(res) - return nil + return lib.CmdN2IP(f, pflag.Args()[1:], printHelpN2IP) } diff --git a/ipinfo/cmd_n2ip6.go b/ipinfo/cmd_n2ip6.go index c7c33e24..36c86a39 100644 --- a/ipinfo/cmd_n2ip6.go +++ b/ipinfo/cmd_n2ip6.go @@ -2,13 +2,10 @@ package main import ( "fmt" - "github.com/fatih/color" "github.com/ipinfo/cli/lib" "github.com/ipinfo/cli/lib/complete" "github.com/ipinfo/cli/lib/complete/predict" "github.com/spf13/pflag" - "os" - "strings" ) var completionsN2IP6 = &complete.Command{ @@ -19,6 +16,7 @@ var completionsN2IP6 = &complete.Command{ }, } +// printHelpN2IP6 prints the help message for the "n2ip6" command. func printHelpN2IP6() { fmt.Printf( `Usage: %s n2ip6 [] @@ -40,39 +38,14 @@ Options: `, progBase) } +// cmdN2IP6 is the handler for the "n2ip6" command. func cmdN2IP6() error { - pflag.BoolVarP(&fHelp, "help", "h", false, "show help.") - pflag.BoolVar(&fNoColor, "nocolor", false, "disable colored output.") + f := lib.CmdN2IP6Flags{} + f.Init() pflag.Parse() - - if fNoColor { - color.NoColor = true - } - - if fHelp { - printHelpDefault() - return nil - } - - cmd := "" - if len(os.Args) > 2 { - cmd = os.Args[2] - } - - // If no argument is given, print help. - if strings.TrimSpace(cmd) == "" { - printHelpN2IP6() - return nil - } - - res, err := lib.CmdN2IP6(cmd) - if err != nil { - _, err := fmt.Fprintf(os.Stderr, "err: %v\n", err) - if err != nil { - return err - } + if pflag.NArg() <= 1 && pflag.NFlag() == 0 { + f.Help = true } - fmt.Println(res) - return nil + return lib.CmdN2IP6(f, pflag.Args()[1:], printHelpN2IP6) } diff --git a/lib/calc_infix.go b/lib/calc_infix.go index d8d32488..e9552e70 100644 --- a/lib/calc_infix.go +++ b/lib/calc_infix.go @@ -1,6 +1,9 @@ package lib import ( + "fmt" + "github.com/fatih/color" + "github.com/spf13/pflag" "math" "math/big" "net" @@ -9,6 +12,28 @@ import ( "strings" ) +// CmdCalcFlags are flags expected by CmdCalcInfix +type CmdCalcFlags struct { + Help bool + NoColor bool +} + +// Init initializes the common flags available to CmdCalcInfix with sensible +func (f *CmdCalcFlags) Init() { + _h := "see description in --help" + pflag.BoolVarP( + &f.Help, + "help", "h", false, + "show help.", + ) + pflag.BoolVar( + &f.NoColor, + "nocolor", false, + _h, + ) +} + +// Stack type type Stack []string // IsEmpty check if stack is empty @@ -285,40 +310,52 @@ func isBalanced(input string) bool { return postfixStack.IsEmpty() } -func CmdCalcInfix(infix string) (string, error) { +// digitsAfterDecimal Function to count the number of non-zero digits after the decimal point +func digitsAfterDecimal(float big.Float) int { + str := float.Text('f', 100) + decimalIndex := strings.Index(str, ".") + // Start counting the digits after the decimal point. + count := 0 + for i := len(str) - 1; i > decimalIndex; i-- { + if str[i] == '0' { + count++ + } else { + break + } + } + + return len(str) - (decimalIndex + 1) - count +} + +// CmdCalcInfix Function is the handler for the "calc" command. +func CmdCalcInfix(f CmdCalcFlags, args []string, printHelp func()) error { + if f.NoColor { + color.NoColor = true + } + + if f.Help { + printHelp() + return nil + } + infix := args[0] if IsInvalidInfix(infix) { - return "", ErrInvalidInput + return ErrInvalidInput } tokens, err := TokenizeInfix(infix) if err != nil { - return "", err + return err } postfix := InfixToPostfix(tokens) result, err := EvaluatePostfix(postfix) if err != nil { - return "", err + return err } precision := digitsAfterDecimal(*result) resultStr := result.Text('f', precision) - return resultStr, nil -} - -func digitsAfterDecimal(float big.Float) int { - str := float.Text('f', 100) - decimalIndex := strings.Index(str, ".") - // Start counting the digits after the decimal point. - count := 0 - for i := len(str) - 1; i > decimalIndex; i-- { - if str[i] == '0' { - count++ - } else { - break - } - } - - return len(str) - (decimalIndex + 1) - count + fmt.Println(resultStr) + return nil } diff --git a/lib/cmd_ip2n.go b/lib/cmd_ip2n.go index 60fe795e..0fe39292 100644 --- a/lib/cmd_ip2n.go +++ b/lib/cmd_ip2n.go @@ -1,10 +1,49 @@ package lib -func CmdIP2n(cmd string) (string, error) { - res, err := IPtoDecimalStr(cmd) +import ( + "fmt" + "github.com/fatih/color" + "github.com/spf13/pflag" +) + +// CmdIP2nFlags are flags expected by CmdIP2n +type CmdIP2nFlags struct { + Help bool + NoColor bool +} + +// Init initializes the common flags available to CmdIP2n with sensible +func (f *CmdIP2nFlags) Init() { + _h := "see description in --help" + pflag.BoolVarP( + &f.Help, + "help", "h", false, + "show help.", + ) + pflag.BoolVar( + &f.NoColor, + "nocolor", false, + _h, + ) +} + +// CmdIP2n converts an IP address to a number +func CmdIP2n(f CmdIP2nFlags, args []string, printHelp func()) error { + if f.NoColor { + color.NoColor = true + } + + if f.Help { + printHelp() + return nil + } + + ipString := args[0] + res, err := IPtoDecimalStr(ipString) if err != nil { - return "", err + return err } - return res, err + fmt.Println(res) + return nil } diff --git a/lib/cmd_n2ip.go b/lib/cmd_n2ip.go index 553580d9..f2260628 100644 --- a/lib/cmd_n2ip.go +++ b/lib/cmd_n2ip.go @@ -1,8 +1,52 @@ package lib -func CmdN2IP(expression string, forceIpv6 bool) (string, error) { +import ( + "fmt" + "github.com/fatih/color" + "github.com/spf13/pflag" +) + +// CmdN2IPFlags are flags expected by CmdN2IP +type CmdN2IPFlags struct { + Help bool + NoColor bool + ipv6 bool +} + +// Init initializes the common flags available to CmdN2IP with sensible +func (f *CmdN2IPFlags) Init() { + _h := "see description in --help" + pflag.BoolVarP( + &f.Help, + "help", "h", false, + "show help.", + ) + pflag.BoolVar( + &f.NoColor, + "nocolor", false, + _h, + ) + pflag.BoolVarP( + &f.ipv6, + "ipv6", "6", false, + _h, + ) +} + +// CmdN2IP converts a number to an IP address +func CmdN2IP(f CmdN2IPFlags, args []string, printHelp func()) error { + if f.NoColor { + color.NoColor = true + } + + if f.Help { + printHelp() + return nil + } + + expression := args[0] if IsInvalidInfix(expression) { - return "", ErrInvalidInput + return ErrInvalidInput } // n2ip also accepts an expression which is why the following @@ -14,24 +58,25 @@ func CmdN2IP(expression string, forceIpv6 bool) (string, error) { // Tokenize the expression tokens, err := TokenizeInfix(expression) if err != nil { - return "", err + return err } postfix := InfixToPostfix(tokens) - + // // Evaluate the postfix expression result, err := EvaluatePostfix(postfix) if err != nil { - return "", err + return err } // Convert to IP // Precision should be 0 i.e. number of digits after decimal // as ip cannot be derived from a float - res, err := DecimalStrToIP(result.Text('f', 0), forceIpv6) + res, err := DecimalStrToIP(result.Text('f', 0), f.ipv6) if err != nil { - return "", err + return err } - return res.String(), nil + fmt.Println(res.String()) + return nil } diff --git a/lib/cmd_n2ip6.go b/lib/cmd_n2ip6.go index e1613ab6..77571867 100644 --- a/lib/cmd_n2ip6.go +++ b/lib/cmd_n2ip6.go @@ -1,28 +1,67 @@ package lib -func CmdN2IP6(expression string) (string, error) { +import ( + "fmt" + "github.com/fatih/color" + "github.com/spf13/pflag" +) + +// CmdN2IP6Flags are flags expected by CmdN2IP6 +type CmdN2IP6Flags struct { + Help bool + NoColor bool +} + +// Init initializes the common flags available to CmdN2IP6 with sensible +func (f *CmdN2IP6Flags) Init() { + _h := "see description in --help" + pflag.BoolVarP( + &f.Help, + "help", "h", false, + "show help.", + ) + pflag.BoolVar( + &f.NoColor, + "nocolor", false, + _h, + ) +} + +// CmdN2IP6 converts a number to an IPv6 address +func CmdN2IP6(f CmdN2IP6Flags, args []string, printHelp func()) error { + if f.NoColor { + color.NoColor = true + } + + if f.Help { + printHelp() + return nil + } + + expression := args[0] + if IsInvalidInfix(expression) { - return "", ErrInvalidInput + return ErrInvalidInput } - // n2ip6 also accepts an expression which is why the following + // n2ip also accepts an expression which is why the following // Steps are being done // Convert to postfix // If it is a single number and not an expression // The tokenization and evaluation would have no effect on the number - // Tokenize + // Tokenize the expression tokens, err := TokenizeInfix(expression) if err != nil { - return "", err + return err } postfix := InfixToPostfix(tokens) - + // // Evaluate the postfix expression result, err := EvaluatePostfix(postfix) if err != nil { - return "", err + return err } // Convert to IP @@ -30,8 +69,9 @@ func CmdN2IP6(expression string) (string, error) { // as ip cannot be derived from a float res, err := DecimalStrToIP(result.Text('f', 0), true) if err != nil { - return "", err + return err } - return res.String(), nil + fmt.Println(res.String()) + return nil } From 92bb8f244d10ca009e87116e4454a8e67102b61b Mon Sep 17 00:00:00 2001 From: harisabdullah Date: Thu, 27 Jul 2023 10:17:21 +0500 Subject: [PATCH 32/50] - Declared constant for precision while parsing string to float - Added comments --- lib/calc_infix.go | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/lib/calc_infix.go b/lib/calc_infix.go index e9552e70..00f256d4 100644 --- a/lib/calc_infix.go +++ b/lib/calc_infix.go @@ -124,6 +124,8 @@ func InfixToPostfix(infix []string) []string { // EvaluatePostfix Function to evaluate postfix expression using a stack based algorithm func EvaluatePostfix(postfix []string) (*big.Float, error) { + // Precision for parsing string to big.Float + var precision uint = 10000 var postfixStack Stack for _, el := range postfix { // if operand, push it onto the stack. @@ -138,14 +140,14 @@ func EvaluatePostfix(postfix []string) (*big.Float, error) { // if operator pop two elements off of the stack. strNum1 := postfixStack.Top() postfixStack.Pop() - num1, _, err := big.ParseFloat(strNum1, 10, uint(10000), big.ToZero) + num1, _, err := big.ParseFloat(strNum1, 10, precision, big.ToZero) if err != nil { return big.NewFloat(0), ErrInvalidInput } strNum2 := postfixStack.Top() postfixStack.Pop() - num2, _, err := big.ParseFloat(strNum2, 10, uint(10000), big.ToZero) + num2, _, err := big.ParseFloat(strNum2, 10, precision, big.ToZero) if err != nil { return big.NewFloat(0), ErrInvalidInput } @@ -178,7 +180,7 @@ func EvaluatePostfix(postfix []string) (*big.Float, error) { num2F64, _ := num2.Float64() res := math.Pow(num2F64, num1F64) - result = new(big.Float).SetPrec(uint(10000)).SetFloat64(res) + result = new(big.Float).SetPrec(precision).SetFloat64(res) default: return big.NewFloat(0), ErrInvalidInput @@ -191,7 +193,7 @@ func EvaluatePostfix(postfix []string) (*big.Float, error) { strTop := postfixStack.Top() postfixStack.Pop() - top, _, err := big.ParseFloat(strTop, 10, uint(10000), big.ToZero) + top, _, err := big.ParseFloat(strTop, 10, precision, big.ToZero) if err != nil { return big.NewFloat(0), ErrInvalidInput } @@ -312,8 +314,10 @@ func isBalanced(input string) bool { // digitsAfterDecimal Function to count the number of non-zero digits after the decimal point func digitsAfterDecimal(float big.Float) int { + // Initially allowing 100 digits after decimal str := float.Text('f', 100) decimalIndex := strings.Index(str, ".") + // Start counting the digits after the decimal point. count := 0 for i := len(str) - 1; i > decimalIndex; i-- { From 378ac6388345afa0778d25fb2e2796d6bc90761c Mon Sep 17 00:00:00 2001 From: harisabdullah Date: Mon, 31 Jul 2023 09:54:23 +0500 Subject: [PATCH 33/50] - Renamed ```lib/calc_infix.go``` to ```lib/cmd_calc.go``` - Removed unnecessary arguments check in handler functions - Removed whitespaces --- ipinfo/cmd_calc.go | 6 +----- ipinfo/cmd_ip2n.go | 4 ---- ipinfo/cmd_n2ip.go | 3 --- ipinfo/cmd_n2ip6.go | 3 --- lib/{calc_infix.go => cmd_calc.go} | 17 +++++++++-------- lib/cmd_ip2n.go | 2 +- lib/cmd_n2ip.go | 4 ++-- lib/cmd_n2ip6.go | 5 ++--- 8 files changed, 15 insertions(+), 29 deletions(-) rename lib/{calc_infix.go => cmd_calc.go} (96%) diff --git a/ipinfo/cmd_calc.go b/ipinfo/cmd_calc.go index 3f04e1b0..a7670f78 100644 --- a/ipinfo/cmd_calc.go +++ b/ipinfo/cmd_calc.go @@ -43,9 +43,5 @@ func cmdCalc() error { f.Init() pflag.Parse() - if pflag.NArg() <= 1 && pflag.NFlag() == 0 { - f.Help = true - } - - return lib.CmdCalcInfix(f, pflag.Args()[1:], printHelpCalc) + return lib.CmdCalc(f, pflag.Args()[1:], printHelpCalc) } diff --git a/ipinfo/cmd_ip2n.go b/ipinfo/cmd_ip2n.go index 795659f8..992af2be 100644 --- a/ipinfo/cmd_ip2n.go +++ b/ipinfo/cmd_ip2n.go @@ -44,9 +44,5 @@ func cmdIP2n() error { f.Init() pflag.Parse() - if pflag.NArg() <= 1 && pflag.NFlag() == 0 { - f.Help = true - } - return lib.CmdIP2n(f, pflag.Args()[1:], printHelpIp2n) } diff --git a/ipinfo/cmd_n2ip.go b/ipinfo/cmd_n2ip.go index eb1ec38c..f02d748f 100644 --- a/ipinfo/cmd_n2ip.go +++ b/ipinfo/cmd_n2ip.go @@ -45,9 +45,6 @@ func cmdN2IP() error { f := lib.CmdN2IPFlags{} f.Init() pflag.Parse() - if pflag.NArg() <= 1 && pflag.NFlag() == 0 { - f.Help = true - } return lib.CmdN2IP(f, pflag.Args()[1:], printHelpN2IP) } diff --git a/ipinfo/cmd_n2ip6.go b/ipinfo/cmd_n2ip6.go index 36c86a39..a67af479 100644 --- a/ipinfo/cmd_n2ip6.go +++ b/ipinfo/cmd_n2ip6.go @@ -43,9 +43,6 @@ func cmdN2IP6() error { f := lib.CmdN2IP6Flags{} f.Init() pflag.Parse() - if pflag.NArg() <= 1 && pflag.NFlag() == 0 { - f.Help = true - } return lib.CmdN2IP6(f, pflag.Args()[1:], printHelpN2IP6) } diff --git a/lib/calc_infix.go b/lib/cmd_calc.go similarity index 96% rename from lib/calc_infix.go rename to lib/cmd_calc.go index 00f256d4..5aae5d26 100644 --- a/lib/calc_infix.go +++ b/lib/cmd_calc.go @@ -12,13 +12,13 @@ import ( "strings" ) -// CmdCalcFlags are flags expected by CmdCalcInfix +// CmdCalcFlags are flags expected by CmdCalc type CmdCalcFlags struct { Help bool NoColor bool } -// Init initializes the common flags available to CmdCalcInfix with sensible +// Init initializes the common flags available to CmdCalc with sensible func (f *CmdCalcFlags) Init() { _h := "see description in --help" pflag.BoolVarP( @@ -331,16 +331,17 @@ func digitsAfterDecimal(float big.Float) int { return len(str) - (decimalIndex + 1) - count } -// CmdCalcInfix Function is the handler for the "calc" command. -func CmdCalcInfix(f CmdCalcFlags, args []string, printHelp func()) error { +// CmdCalc Function is the handler for the "calc" command. +func CmdCalc(f CmdCalcFlags, args []string, printHelp func()) error { + if len(args) == 0 { + printHelp() + return nil + } + if f.NoColor { color.NoColor = true } - if f.Help { - printHelp() - return nil - } infix := args[0] if IsInvalidInfix(infix) { return ErrInvalidInput diff --git a/lib/cmd_ip2n.go b/lib/cmd_ip2n.go index 0fe39292..82f1cf3c 100644 --- a/lib/cmd_ip2n.go +++ b/lib/cmd_ip2n.go @@ -33,7 +33,7 @@ func CmdIP2n(f CmdIP2nFlags, args []string, printHelp func()) error { color.NoColor = true } - if f.Help { + if len(args) == 0 { printHelp() return nil } diff --git a/lib/cmd_n2ip.go b/lib/cmd_n2ip.go index f2260628..12559bb1 100644 --- a/lib/cmd_n2ip.go +++ b/lib/cmd_n2ip.go @@ -39,7 +39,7 @@ func CmdN2IP(f CmdN2IPFlags, args []string, printHelp func()) error { color.NoColor = true } - if f.Help { + if len(args) == 0 { printHelp() return nil } @@ -62,7 +62,7 @@ func CmdN2IP(f CmdN2IPFlags, args []string, printHelp func()) error { } postfix := InfixToPostfix(tokens) - // + // Evaluate the postfix expression result, err := EvaluatePostfix(postfix) if err != nil { diff --git a/lib/cmd_n2ip6.go b/lib/cmd_n2ip6.go index 77571867..990794e8 100644 --- a/lib/cmd_n2ip6.go +++ b/lib/cmd_n2ip6.go @@ -33,13 +33,12 @@ func CmdN2IP6(f CmdN2IP6Flags, args []string, printHelp func()) error { color.NoColor = true } - if f.Help { + if len(args) == 0 { printHelp() return nil } expression := args[0] - if IsInvalidInfix(expression) { return ErrInvalidInput } @@ -57,7 +56,7 @@ func CmdN2IP6(f CmdN2IP6Flags, args []string, printHelp func()) error { } postfix := InfixToPostfix(tokens) - // + // Evaluate the postfix expression result, err := EvaluatePostfix(postfix) if err != nil { From 50a58ac34e5b7ea232ce993351d04c0939ba2b47 Mon Sep 17 00:00:00 2001 From: harisabdullah Date: Mon, 31 Jul 2023 11:18:56 +0500 Subject: [PATCH 34/50] Moved ```StrIsIPv4Str``` and ```StrIsIPv6Str``` to ```lib/ip_str.go``` --- ipinfo/cmd_bulk.go | 1 + ipinfo/cmd_ip.go | 2 ++ lib/cmd_calc.go | 6 +++--- lib/ip_conversions.go | 29 ++--------------------------- lib/ip_str.go | 25 +++++++++++++++++++++++++ 5 files changed, 33 insertions(+), 30 deletions(-) diff --git a/ipinfo/cmd_bulk.go b/ipinfo/cmd_bulk.go index e3e04e08..21db6df8 100644 --- a/ipinfo/cmd_bulk.go +++ b/ipinfo/cmd_bulk.go @@ -112,6 +112,7 @@ func cmdBulk() (err error) { } ips, err = lib.IPListFromAllSrcs(pflag.Args()[1:]) + fmt.Println(ips) if err != nil { return err } diff --git a/ipinfo/cmd_ip.go b/ipinfo/cmd_ip.go index 863b2f58..ffa7c307 100644 --- a/ipinfo/cmd_ip.go +++ b/ipinfo/cmd_ip.go @@ -95,6 +95,7 @@ func cmdIP(ipStr string) error { ip := net.ParseIP(ipStr) ii = prepareIpinfoClient(fTok) data, err := ii.GetIPInfo(ip) + //fmt.Println(outputYAML(data)) if err != nil { return err } @@ -104,6 +105,7 @@ func cmdIP(ipStr string) error { d[ipStr] = data return outputFieldBatchCore(d, fField, false, false) } + if fJSON { return outputJSON(data) } diff --git a/lib/cmd_calc.go b/lib/cmd_calc.go index 5aae5d26..bcc7a206 100644 --- a/lib/cmd_calc.go +++ b/lib/cmd_calc.go @@ -132,7 +132,7 @@ func EvaluatePostfix(postfix []string) (*big.Float, error) { if el == "" { continue } - if isFloat(el) || IsIPv4Address(el) || IsIPv6Address(el) { + if isFloat(el) || StrIsIPv4Str(el) || StrIsIPv6Str(el) { postfixStack.Push(el) continue } @@ -216,14 +216,14 @@ func translateToken(tempToken string, tokens []string) ([]string, error) { if isFloat(tempToken) { tokens = append(tokens, tempToken) - } else if IsIPv4Address(tempToken) { + } else if StrIsIPv4Str(tempToken) { // Convert ipv4 to decimal then append to tokens ip := net.ParseIP(tempToken) decimalIP := IP4toInt(ip) res := strconv.FormatInt(decimalIP, 10) tokens = append(tokens, res) - } else if IsIPv6Address(tempToken) { + } else if StrIsIPv6Str(tempToken) { ip := net.ParseIP(tempToken) decimalIP := IP6toInt(ip) tokens = append(tokens, decimalIP.String()) diff --git a/lib/ip_conversions.go b/lib/ip_conversions.go index ae7d84c4..c587a4db 100644 --- a/lib/ip_conversions.go +++ b/lib/ip_conversions.go @@ -4,13 +4,12 @@ import ( "fmt" "math/big" "net" - "regexp" "strconv" ) // IPtoDecimalStr converts an IP address to a decimal string func IPtoDecimalStr(strIP string) (string, error) { - if IsIPv6Address(strIP) { + if StrIsIPv6Str(strIP) { ip := net.ParseIP(strIP) if ip == nil { return "", ErrNotIP @@ -19,7 +18,7 @@ func IPtoDecimalStr(strIP string) (string, error) { decimalIP := IP6toInt(ip) return decimalIP.String(), nil } - if IsIPv4Address(strIP) { + if StrIsIPv4Str(strIP) { ip := net.ParseIP(strIP) if ip == nil { return "", ErrNotIP @@ -75,27 +74,3 @@ func IP4toInt(IPv4Address net.IP) int64 { IPv4Int.SetBytes(IPv4Address.To4()) return IPv4Int.Int64() } - -// IsIPv4Address checks if the given string is an IPv4 address -func IsIPv4Address(expression string) bool { - // Define the regular expression pattern for matching IPv4 addresses - ipV4Pattern := `^(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$` - - // Compile the regular expression - ipV4Regex := regexp.MustCompile(ipV4Pattern) - - // Use the MatchString function to check if the expression matches the IPv4 pattern - return ipV4Regex.MatchString(expression) -} - -// IsIPv6Address checks if the given string is an IPv6 address -func IsIPv6Address(expression string) bool { - // Define the regular expression pattern for matching IPv6 addresses - ipV6Pattern := `^(([0-9a-fA-F]{1,4}:){7,7}[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,7}:|([0-9a-fA-F]{1,4}:){1,6}:[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,5}(:[0-9a-fA-F]{1,4}){1,2}|([0-9a-fA-F]{1,4}:){1,4}(:[0-9a-fA-F]{1,4}){1,3}|([0-9a-fA-F]{1,4}:){1,3}(:[0-9a-fA-F]{1,4}){1,4}|([0-9a-fA-F]{1,4}:){1,2}(:[0-9a-fA-F]{1,4}){1,5}|[0-9a-fA-F]{1,4}:((:[0-9a-fA-F]{1,4}){1,6})|:((:[0-9a-fA-F]{1,4}){1,7}|:)|fe80:(:[0-9a-fA-F]{0,4}){0,4}%[0-9a-zA-Z]{1,}|::(ffff(:0{1,4}){0,1}:){0,1}((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])\.){3,3}(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])|([0-9a-fA-F]{1,4}:){1,4}:((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])\.){3,3}(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9]))$` - - // Compile the regular expression - ipV6Regex := regexp.MustCompile(ipV6Pattern) - - // Use the MatchString function to check if the expression matches the IPv6 pattern - return ipV6Regex.MatchString(expression) -} diff --git a/lib/ip_str.go b/lib/ip_str.go index 7dcc24a8..f331a290 100644 --- a/lib/ip_str.go +++ b/lib/ip_str.go @@ -2,9 +2,34 @@ package lib import ( "net" + "regexp" ) // StrIsIPStr checks whether a string is an IP. func StrIsIPStr(ipStr string) bool { return net.ParseIP(ipStr) != nil } + +// StrIsIPv4Str checks if the given string is an IPv4 address +func StrIsIPv4Str(expression string) bool { + // Define the regular expression pattern for matching IPv4 addresses + ipV4Pattern := `^(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$` + + // Compile the regular expression + ipV4Regex := regexp.MustCompile(ipV4Pattern) + + // Use the MatchString function to check if the expression matches the IPv4 pattern + return ipV4Regex.MatchString(expression) +} + +// StrIsIPv6Str checks if the given string is an IPv6 address +func StrIsIPv6Str(expression string) bool { + // Define the regular expression pattern for matching IPv6 addresses + ipV6Pattern := `^(([0-9a-fA-F]{1,4}:){7,7}[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,7}:|([0-9a-fA-F]{1,4}:){1,6}:[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,5}(:[0-9a-fA-F]{1,4}){1,2}|([0-9a-fA-F]{1,4}:){1,4}(:[0-9a-fA-F]{1,4}){1,3}|([0-9a-fA-F]{1,4}:){1,3}(:[0-9a-fA-F]{1,4}){1,4}|([0-9a-fA-F]{1,4}:){1,2}(:[0-9a-fA-F]{1,4}){1,5}|[0-9a-fA-F]{1,4}:((:[0-9a-fA-F]{1,4}){1,6})|:((:[0-9a-fA-F]{1,4}){1,7}|:)|fe80:(:[0-9a-fA-F]{0,4}){0,4}%[0-9a-zA-Z]{1,}|::(ffff(:0{1,4}){0,1}:){0,1}((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])\.){3,3}(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])|([0-9a-fA-F]{1,4}:){1,4}:((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])\.){3,3}(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9]))$` + + // Compile the regular expression + ipV6Regex := regexp.MustCompile(ipV6Pattern) + + // Use the MatchString function to check if the expression matches the IPv6 pattern + return ipV6Regex.MatchString(expression) +} From a28331d2b1744fbe2e92edc801016937c7b79450 Mon Sep 17 00:00:00 2001 From: harisabdullah Date: Mon, 31 Jul 2023 11:22:17 +0500 Subject: [PATCH 35/50] Moved ```StrIsIPv4Str``` and ```StrIsIPv6Str``` to ```lib/ip_str.go``` --- ipinfo/cmd_bulk.go | 1 - ipinfo/cmd_ip.go | 2 -- 2 files changed, 3 deletions(-) diff --git a/ipinfo/cmd_bulk.go b/ipinfo/cmd_bulk.go index 21db6df8..e3e04e08 100644 --- a/ipinfo/cmd_bulk.go +++ b/ipinfo/cmd_bulk.go @@ -112,7 +112,6 @@ func cmdBulk() (err error) { } ips, err = lib.IPListFromAllSrcs(pflag.Args()[1:]) - fmt.Println(ips) if err != nil { return err } diff --git a/ipinfo/cmd_ip.go b/ipinfo/cmd_ip.go index ffa7c307..863b2f58 100644 --- a/ipinfo/cmd_ip.go +++ b/ipinfo/cmd_ip.go @@ -95,7 +95,6 @@ func cmdIP(ipStr string) error { ip := net.ParseIP(ipStr) ii = prepareIpinfoClient(fTok) data, err := ii.GetIPInfo(ip) - //fmt.Println(outputYAML(data)) if err != nil { return err } @@ -105,7 +104,6 @@ func cmdIP(ipStr string) error { d[ipStr] = data return outputFieldBatchCore(d, fField, false, false) } - if fJSON { return outputJSON(data) } From 7e652c13ac2e551d06e84eed370d18f38e76ebcb Mon Sep 17 00:00:00 2001 From: Haris Date: Mon, 31 Jul 2023 13:13:47 +0500 Subject: [PATCH 36/50] Update lib/cmd_calc.go Co-authored-by: awaismslm <109338796+awaismslm@users.noreply.github.com> --- lib/cmd_calc.go | 1 - 1 file changed, 1 deletion(-) diff --git a/lib/cmd_calc.go b/lib/cmd_calc.go index bcc7a206..02f47c37 100644 --- a/lib/cmd_calc.go +++ b/lib/cmd_calc.go @@ -178,7 +178,6 @@ func EvaluatePostfix(postfix []string) (*big.Float, error) { // for math.Pow() which accepts big.Float num1F64, _ := num1.Float64() num2F64, _ := num2.Float64() - res := math.Pow(num2F64, num1F64) result = new(big.Float).SetPrec(precision).SetFloat64(res) From 52a91f161044e30a710fd83b0e094cf1264d94cc Mon Sep 17 00:00:00 2001 From: Haris Date: Mon, 31 Jul 2023 13:18:07 +0500 Subject: [PATCH 37/50] Update lib/cmd_calc.go Co-authored-by: awaismslm <109338796+awaismslm@users.noreply.github.com> --- lib/cmd_calc.go | 1 - 1 file changed, 1 deletion(-) diff --git a/lib/cmd_calc.go b/lib/cmd_calc.go index 02f47c37..e5fb6a6b 100644 --- a/lib/cmd_calc.go +++ b/lib/cmd_calc.go @@ -169,7 +169,6 @@ func EvaluatePostfix(postfix []string) (*big.Float, error) { if num1.Cmp(big.NewFloat(0)) == 0 { return big.NewFloat(0), ErrInvalidInput } - result = new(big.Float).Quo(num2, num1) case operator == "^": From 6ac24da37b3796171bfff29eefe3db46d21cc443 Mon Sep 17 00:00:00 2001 From: Haris Date: Mon, 31 Jul 2023 13:18:25 +0500 Subject: [PATCH 38/50] Update lib/cmd_calc.go Co-authored-by: awaismslm <109338796+awaismslm@users.noreply.github.com> --- lib/cmd_calc.go | 1 - 1 file changed, 1 deletion(-) diff --git a/lib/cmd_calc.go b/lib/cmd_calc.go index e5fb6a6b..e37e78c4 100644 --- a/lib/cmd_calc.go +++ b/lib/cmd_calc.go @@ -170,7 +170,6 @@ func EvaluatePostfix(postfix []string) (*big.Float, error) { return big.NewFloat(0), ErrInvalidInput } result = new(big.Float).Quo(num2, num1) - case operator == "^": // Using Float64() to convert big.Float to float64 // because big.Float does not have a equivalent function From c012c9302bf78e2b03029fabc8c9656915badd18 Mon Sep 17 00:00:00 2001 From: Haris Date: Mon, 31 Jul 2023 13:18:38 +0500 Subject: [PATCH 39/50] Update lib/cmd_calc.go Co-authored-by: awaismslm <109338796+awaismslm@users.noreply.github.com> --- lib/cmd_calc.go | 1 - 1 file changed, 1 deletion(-) diff --git a/lib/cmd_calc.go b/lib/cmd_calc.go index e37e78c4..9da3dacf 100644 --- a/lib/cmd_calc.go +++ b/lib/cmd_calc.go @@ -178,7 +178,6 @@ func EvaluatePostfix(postfix []string) (*big.Float, error) { num2F64, _ := num2.Float64() res := math.Pow(num2F64, num1F64) result = new(big.Float).SetPrec(precision).SetFloat64(res) - default: return big.NewFloat(0), ErrInvalidInput } From addd2dd9b3272509b2442b0d777ff17659215d22 Mon Sep 17 00:00:00 2001 From: Haris Date: Mon, 31 Jul 2023 13:18:49 +0500 Subject: [PATCH 40/50] Update lib/cmd_calc.go Co-authored-by: awaismslm <109338796+awaismslm@users.noreply.github.com> --- lib/cmd_calc.go | 1 - 1 file changed, 1 deletion(-) diff --git a/lib/cmd_calc.go b/lib/cmd_calc.go index 9da3dacf..7c2bfec9 100644 --- a/lib/cmd_calc.go +++ b/lib/cmd_calc.go @@ -160,7 +160,6 @@ func EvaluatePostfix(postfix []string) (*big.Float, error) { result = result.Add(num2, num1) case operator == "-": result = result.Sub(num2, num1) - case operator == "*": result = result.Mul(num2, num1) From 2f77e43b7a941661976b4d056382386e1a9347e4 Mon Sep 17 00:00:00 2001 From: Haris Date: Mon, 31 Jul 2023 13:19:02 +0500 Subject: [PATCH 41/50] Update lib/cmd_calc.go Co-authored-by: awaismslm <109338796+awaismslm@users.noreply.github.com> --- lib/cmd_calc.go | 1 - 1 file changed, 1 deletion(-) diff --git a/lib/cmd_calc.go b/lib/cmd_calc.go index 7c2bfec9..001ce2eb 100644 --- a/lib/cmd_calc.go +++ b/lib/cmd_calc.go @@ -162,7 +162,6 @@ func EvaluatePostfix(postfix []string) (*big.Float, error) { result = result.Sub(num2, num1) case operator == "*": result = result.Mul(num2, num1) - case operator == "/": // Check for division by zero if num1.Cmp(big.NewFloat(0)) == 0 { From 5e55a8905e33ad147dc51da591deab57fd43c90f Mon Sep 17 00:00:00 2001 From: Haris Date: Mon, 31 Jul 2023 13:19:16 +0500 Subject: [PATCH 42/50] Update lib/cmd_calc.go Co-authored-by: awaismslm <109338796+awaismslm@users.noreply.github.com> --- lib/cmd_calc.go | 1 - 1 file changed, 1 deletion(-) diff --git a/lib/cmd_calc.go b/lib/cmd_calc.go index 001ce2eb..19c92721 100644 --- a/lib/cmd_calc.go +++ b/lib/cmd_calc.go @@ -179,7 +179,6 @@ func EvaluatePostfix(postfix []string) (*big.Float, error) { default: return big.NewFloat(0), ErrInvalidInput } - strResult := result.Text('f', 50) postfixStack.Push(strResult) } From aa50cae5fca3caf4343b5ad06dbb2a41d3522e2f Mon Sep 17 00:00:00 2001 From: harisabdullah Date: Mon, 31 Jul 2023 13:24:16 +0500 Subject: [PATCH 43/50] Suggested changes applied --- lib/cmd_calc.go | 38 ++------------------------------------ lib/cmd_n2ip.go | 3 +-- lib/cmd_n2ip6.go | 3 +-- lib/stack.go | 36 ++++++++++++++++++++++++++++++++++++ 4 files changed, 40 insertions(+), 40 deletions(-) create mode 100644 lib/stack.go diff --git a/lib/cmd_calc.go b/lib/cmd_calc.go index 19c92721..40ddfb74 100644 --- a/lib/cmd_calc.go +++ b/lib/cmd_calc.go @@ -33,41 +33,6 @@ func (f *CmdCalcFlags) Init() { ) } -// Stack type -type Stack []string - -// IsEmpty check if stack is empty -func (st *Stack) IsEmpty() bool { - return len(*st) == 0 -} - -// Push a new value onto the stack -func (st *Stack) Push(str string) { - *st = append(*st, str) //Simply append the new value to the end of the stack -} - -// Pop Remove top element of stack. Return false if stack is empty. -func (st *Stack) Pop() bool { - if st.IsEmpty() { - return false - } else { - index := len(*st) - 1 // Get the index of top most element. - *st = (*st)[:index] // Remove it from the stack by slicing it off. - return true - } -} - -// Top Return top element of stack. Return false if stack is empty. -func (st *Stack) Top() string { - if st.IsEmpty() { - return "" - } else { - index := len(*st) - 1 // Get the index of top most element. - element := (*st)[index] // Index onto the slice and obtain the element. - return element - } -} - // prec Function to return precedence of operators func prec(s string) int { if s == "^" { @@ -114,6 +79,7 @@ func InfixToPostfix(infix []string) []string { postfix = append(postfix, token) } } + // Pop all the remaining elements from the stack for !postfixStack.IsEmpty() { postfix = append(postfix, postfixStack.Top()) @@ -196,7 +162,7 @@ func EvaluatePostfix(postfix []string) (*big.Float, error) { // isOperator Function to check if token is an operator func isOperator(token string) bool { - operators := map[string]bool{"+": true, "-": true, "*": true, "/": true, "^": true /* add other operators here */} + operators := map[string]bool{"+": true, "-": true, "*": true, "/": true, "^": true} _, isOperator := operators[token] return isOperator } diff --git a/lib/cmd_n2ip.go b/lib/cmd_n2ip.go index 12559bb1..835594e1 100644 --- a/lib/cmd_n2ip.go +++ b/lib/cmd_n2ip.go @@ -61,9 +61,8 @@ func CmdN2IP(f CmdN2IPFlags, args []string, printHelp func()) error { return err } - postfix := InfixToPostfix(tokens) - // Evaluate the postfix expression + postfix := InfixToPostfix(tokens) result, err := EvaluatePostfix(postfix) if err != nil { return err diff --git a/lib/cmd_n2ip6.go b/lib/cmd_n2ip6.go index 990794e8..4ca2575d 100644 --- a/lib/cmd_n2ip6.go +++ b/lib/cmd_n2ip6.go @@ -55,9 +55,8 @@ func CmdN2IP6(f CmdN2IP6Flags, args []string, printHelp func()) error { return err } - postfix := InfixToPostfix(tokens) - // Evaluate the postfix expression + postfix := InfixToPostfix(tokens) result, err := EvaluatePostfix(postfix) if err != nil { return err diff --git a/lib/stack.go b/lib/stack.go new file mode 100644 index 00000000..a70060d9 --- /dev/null +++ b/lib/stack.go @@ -0,0 +1,36 @@ +package lib + +// Stack type +type Stack []string + +// IsEmpty check if stack is empty +func (st *Stack) IsEmpty() bool { + return len(*st) == 0 +} + +// Push a new value onto the stack +func (st *Stack) Push(str string) { + *st = append(*st, str) //Simply append the new value to the end of the stack +} + +// Pop Remove top element of stack. Return false if stack is empty. +func (st *Stack) Pop() bool { + if st.IsEmpty() { + return false + } else { + index := len(*st) - 1 // Get the index of top most element. + *st = (*st)[:index] // Remove it from the stack by slicing it off. + return true + } +} + +// Top Return top element of stack. Return false if stack is empty. +func (st *Stack) Top() string { + if st.IsEmpty() { + return "" + } else { + index := len(*st) - 1 // Get the index of top most element. + element := (*st)[index] // Index onto the slice and obtain the element. + return element + } +} From 8586480fafd9d2a8fd4711f758ddda49545b1090 Mon Sep 17 00:00:00 2001 From: Haris Date: Mon, 31 Jul 2023 15:52:06 +0500 Subject: [PATCH 44/50] Update lib/cmd_calc.go Co-authored-by: awaismslm <109338796+awaismslm@users.noreply.github.com> --- lib/cmd_calc.go | 1 - 1 file changed, 1 deletion(-) diff --git a/lib/cmd_calc.go b/lib/cmd_calc.go index 40ddfb74..644b0199 100644 --- a/lib/cmd_calc.go +++ b/lib/cmd_calc.go @@ -312,7 +312,6 @@ func CmdCalc(f CmdCalcFlags, args []string, printHelp func()) error { } postfix := InfixToPostfix(tokens) - result, err := EvaluatePostfix(postfix) if err != nil { return err From 360c610ac5e08845327a598dcd542fb8b0a30e87 Mon Sep 17 00:00:00 2001 From: harisabdullah Date: Tue, 1 Aug 2023 14:18:07 +0500 Subject: [PATCH 45/50] * Nested `n2ip, n2ip6, and ip2n` sub commands inside `tool` sub command * Centralized regex patterns for `ipv4` and `ipv6` addresses in one place at `regex.go` for better code organization and maintainability. * Improved documentation * Found and solved a bug in `calc` where 2(2) would not work as 2*(2) * Improved Implementation of `Pop` inside `stack.go` and better documentation * Applied other suggestions from the review. --- ipinfo/cmd_calc.go | 6 +- ipinfo/cmd_default.go | 3 - ipinfo/cmd_ip2n.go | 48 ---------------- ipinfo/cmd_n2ip.go | 50 ----------------- ipinfo/cmd_n2ip6.go | 48 ---------------- ipinfo/cmd_tool.go | 12 ++++ ipinfo/cmd_tool_ip2n.go | 47 ++++++++++++++++ ipinfo/cmd_tool_n2ip.go | 52 +++++++++++++++++ ipinfo/cmd_tool_n2ip6.go | 47 ++++++++++++++++ ipinfo/completions.go | 3 - ipinfo/main.go | 6 -- lib/cmd_calc.go | 75 ++++++++++++++++--------- lib/cmd_grepip.go | 8 +-- lib/{cmd_ip2n.go => cmd_tool_ip2n.go} | 12 ++-- lib/{cmd_n2ip.go => cmd_tool_n2ip.go} | 12 ++-- lib/{cmd_n2ip6.go => cmd_tool_n2ip6.go} | 12 ++-- lib/init.go | 2 + lib/ip_str.go | 10 +--- lib/regex.go | 21 +++++++ lib/stack.go | 33 +++++------ 20 files changed, 269 insertions(+), 238 deletions(-) delete mode 100644 ipinfo/cmd_ip2n.go delete mode 100644 ipinfo/cmd_n2ip.go delete mode 100644 ipinfo/cmd_n2ip6.go create mode 100644 ipinfo/cmd_tool_ip2n.go create mode 100644 ipinfo/cmd_tool_n2ip.go create mode 100644 ipinfo/cmd_tool_n2ip6.go rename lib/{cmd_ip2n.go => cmd_tool_ip2n.go} (60%) rename lib/{cmd_n2ip.go => cmd_tool_n2ip.go} (79%) rename lib/{cmd_n2ip6.go => cmd_tool_n2ip6.go} (78%) create mode 100644 lib/regex.go diff --git a/ipinfo/cmd_calc.go b/ipinfo/cmd_calc.go index a7670f78..3b46c844 100644 --- a/ipinfo/cmd_calc.go +++ b/ipinfo/cmd_calc.go @@ -20,18 +20,16 @@ func printHelpCalc() { fmt.Printf( `Usage: %s calc [] -calc +Description: Evaluate a mathematical expression and print the result. -Example: +Examples: %[1]s calc "2*2828-1" %[1]s calc "190.87.89.1*2" %[1]s calc "2001:0db8:85a3:0000:0000:8a2e:0370:7334*6" Options: General: - --nocolor - disable colored output. --help, -h show help. `, progBase) diff --git a/ipinfo/cmd_default.go b/ipinfo/cmd_default.go index 8c6ef6f7..6db4e7fe 100644 --- a/ipinfo/cmd_default.go +++ b/ipinfo/cmd_default.go @@ -33,9 +33,6 @@ Commands: splitcidr splits a larger CIDR into smaller CIDRs. mmdb read, import and export mmdb files. calc evaluates a mathematical expression that may contain IP addresses. - ip2n convert an IPv4 or IPv6 address to its decimal representation. - n2ip evaluates an expression and converts it to IPv4 or IPv6 address. - n2ip6 evaluates an expression and converts it to IPv6 address. tool misc. tools related to IPs, IP ranges and CIDRs. download download free ipinfo database files. cache manage the cache. diff --git a/ipinfo/cmd_ip2n.go b/ipinfo/cmd_ip2n.go deleted file mode 100644 index 992af2be..00000000 --- a/ipinfo/cmd_ip2n.go +++ /dev/null @@ -1,48 +0,0 @@ -package main - -import ( - "fmt" - "github.com/ipinfo/cli/lib" - "github.com/ipinfo/cli/lib/complete" - "github.com/ipinfo/cli/lib/complete/predict" - "github.com/spf13/pflag" -) - -var completionsIP2n = &complete.Command{ - Flags: map[string]complete.Predictor{ - "--nocolor": predict.Nothing, - "-h": predict.Nothing, - "--help": predict.Nothing, - }, -} - -// printHelpIp2n prints the help message for the "ip2n" command. -func printHelpIp2n() { - fmt.Printf( - `Usage: %s ip2n - -Example: - %[1]s ip2n "190.87.89.1" - %[1]s ip2n "2001:0db8:85a3:0000:0000:8a2e:0370:7334 - %[1]s ip2n "2001:0db8:85a3::8a2e:0370:7334 - %[1]s ip2n "::7334 - %[1]s ip2n "7334::"" - - -Options: - General: - --nocolor - disable colored output. - --help, -h - show help. -`, progBase) -} - -// cmdIP2n is the handler for the "ip2n" command. -func cmdIP2n() error { - f := lib.CmdIP2nFlags{} - f.Init() - pflag.Parse() - - return lib.CmdIP2n(f, pflag.Args()[1:], printHelpIp2n) -} diff --git a/ipinfo/cmd_n2ip.go b/ipinfo/cmd_n2ip.go deleted file mode 100644 index f02d748f..00000000 --- a/ipinfo/cmd_n2ip.go +++ /dev/null @@ -1,50 +0,0 @@ -package main - -import ( - "fmt" - "github.com/ipinfo/cli/lib" - "github.com/ipinfo/cli/lib/complete" - "github.com/ipinfo/cli/lib/complete/predict" - "github.com/spf13/pflag" -) - -// cmdN2IP is the handler for the "n2ip" command. -var completionsN2IP = &complete.Command{ - Flags: map[string]complete.Predictor{ - "--nocolor": predict.Nothing, - "-h": predict.Nothing, - "--help": predict.Nothing, - "-6": predict.Set(predictReadFmts), - "--ipv6": predict.Set(predictReadFmts), - }, -} - -// printHelpN2IP prints the help message for the "n2ip" command. -func printHelpN2IP() { - fmt.Printf( - `Usage: %s n2ip [] - -Example: - %[1]s n2ip "2*2828-1" - %[1]s n2ip "190.87.89.1*2" - %[1]s n2ip "2001:0db8:85a3:0000:0000:8a2e:0370:7334*6" - -Options: - General: - --nocolor - disable colored output. - --help, -h - show help. - --ipv6, -6 - force conversion to IPv6 address -`, progBase) -} - -// cmdN2IP is the handler for the "n2ip" command. -func cmdN2IP() error { - f := lib.CmdN2IPFlags{} - f.Init() - pflag.Parse() - - return lib.CmdN2IP(f, pflag.Args()[1:], printHelpN2IP) -} diff --git a/ipinfo/cmd_n2ip6.go b/ipinfo/cmd_n2ip6.go deleted file mode 100644 index a67af479..00000000 --- a/ipinfo/cmd_n2ip6.go +++ /dev/null @@ -1,48 +0,0 @@ -package main - -import ( - "fmt" - "github.com/ipinfo/cli/lib" - "github.com/ipinfo/cli/lib/complete" - "github.com/ipinfo/cli/lib/complete/predict" - "github.com/spf13/pflag" -) - -var completionsN2IP6 = &complete.Command{ - Flags: map[string]complete.Predictor{ - "--nocolor": predict.Nothing, - "-h": predict.Nothing, - "--help": predict.Nothing, - }, -} - -// printHelpN2IP6 prints the help message for the "n2ip6" command. -func printHelpN2IP6() { - fmt.Printf( - `Usage: %s n2ip6 [] - -Example: - %[1]s n2ip6 "190.87.89.1" - %[1]s n2ip6 "2001:0db8:85a3:0000:0000:8a2e:0370:7334 - %[1]s n2ip6 "2001:0db8:85a3::8a2e:0370:7334 - %[1]s n2ip6 "::7334 - %[1]s n2ip6 "7334::"" - - -Options: - General: - --nocolor - disable colored output. - --help, -h - show help. -`, progBase) -} - -// cmdN2IP6 is the handler for the "n2ip6" command. -func cmdN2IP6() error { - f := lib.CmdN2IP6Flags{} - f.Init() - pflag.Parse() - - return lib.CmdN2IP6(f, pflag.Args()[1:], printHelpN2IP6) -} diff --git a/ipinfo/cmd_tool.go b/ipinfo/cmd_tool.go index dfd7cedd..3dc8dc4b 100644 --- a/ipinfo/cmd_tool.go +++ b/ipinfo/cmd_tool.go @@ -12,6 +12,9 @@ import ( var completionsTool = &complete.Command{ Sub: map[string]*complete.Command{ "aggregate": completionsToolAggregate, + "ip2n": completionsToolIP2n, + "n2ip": completionsToolN2IP, + "n2ip6": completionsToolN2IP6, }, Flags: map[string]complete.Predictor{ "-h": predict.Nothing, @@ -26,6 +29,9 @@ func printHelpTool() { Commands: aggregate aggregate IPs, IP ranges, and CIDRs. + ip2n converts an IPv4 or IPv6 address to its decimal representation. + n2ip evaluates a mathematical expression and converts it to an IPv4 or IPv6. + n2ip6 evaluates a mathematical expression and converts it to an IPv6. Options: --help, -h @@ -56,6 +62,12 @@ func cmdTool() error { switch { case cmd == "aggregate": err = cmdToolAggregate() + case cmd == "ip2n": + err = cmdToolIP2n() + case cmd == "n2ip": + err = cmdToolN2IP() + case cmd == "n2ip6": + err = cmdToolN2IP6() default: err = toolHelp() } diff --git a/ipinfo/cmd_tool_ip2n.go b/ipinfo/cmd_tool_ip2n.go new file mode 100644 index 00000000..eb2a57e8 --- /dev/null +++ b/ipinfo/cmd_tool_ip2n.go @@ -0,0 +1,47 @@ +package main + +import ( + "fmt" + "github.com/ipinfo/cli/lib" + "github.com/ipinfo/cli/lib/complete" + "github.com/ipinfo/cli/lib/complete/predict" + "github.com/spf13/pflag" +) + +var completionsToolIP2n = &complete.Command{ + Flags: map[string]complete.Predictor{ + "-h": predict.Nothing, + "--help": predict.Nothing, + }, +} + +// printHelpToolIp2n prints the help message for the "ip2n" command. +func printHelpToolIp2n() { + fmt.Printf( + `Usage: %s tool ip2n + +Description: + Converts an IPv4 or IPv6 address to its decimal representation. + +Examples: + %[1]s ip2n "190.87.89.1" + %[1]s ip2n "2001:0db8:85a3:0000:0000:8a2e:0370:7334" + %[1]s ip2n "2001:0db8:85a3::8a2e:0370:7334" + %[1]s ip2n "::7334" + %[1]s ip2n "7334::" + +Options: + General: + --help, -h + show help. +`, progBase) +} + +// cmdToolIP2n is the handler for the "ip2n" command. +func cmdToolIP2n() error { + f := lib.CmdToolIP2nFlags{} + f.Init() + pflag.Parse() + + return lib.CmdToolIP2n(f, pflag.Args()[2:], printHelpToolIp2n) +} diff --git a/ipinfo/cmd_tool_n2ip.go b/ipinfo/cmd_tool_n2ip.go new file mode 100644 index 00000000..67dd81c9 --- /dev/null +++ b/ipinfo/cmd_tool_n2ip.go @@ -0,0 +1,52 @@ +package main + +import ( + "fmt" + "github.com/ipinfo/cli/lib" + "github.com/ipinfo/cli/lib/complete" + "github.com/ipinfo/cli/lib/complete/predict" + "github.com/spf13/pflag" +) + +// cmdToolN2IP is the handler for the "n2ip" command. +var completionsToolN2IP = &complete.Command{ + Flags: map[string]complete.Predictor{ + "-h": predict.Nothing, + "--help": predict.Nothing, + "-6": predict.Set(predictReadFmts), + "--ipv6": predict.Set(predictReadFmts), + }, +} + +// printHelpToolN2IP prints the help message for the "n2ip" command. +func printHelpToolN2IP() { + fmt.Printf( + `Usage: %s n2ip tool [] + +Description: + Converts a given numeric representation to its corresponding IPv4 or IPv6 address, and can also evaluate a mathematical expression for conversion. + +Examples: + %[1]s n2ip "4294967295 + 87" + %[1]s n2ip "4294967295" --ipv6 + %[1]s n2ip -6 "201523715" + %[1]s n2ip "51922968585348276285304963292200960" + %[1]s n2ip "a:: - 4294967295" + +Options: + General: + --help, -h + show help. + --ipv6, -6 + force conversion to IPv6 address +`, progBase) +} + +// cmdToolN2IP is the handler for the "n2ip" command. +func cmdToolN2IP() error { + f := lib.CmdToolN2IPFlags{} + f.Init() + pflag.Parse() + + return lib.CmdToolN2IP(f, pflag.Args()[2:], printHelpToolN2IP) +} diff --git a/ipinfo/cmd_tool_n2ip6.go b/ipinfo/cmd_tool_n2ip6.go new file mode 100644 index 00000000..4f861d5b --- /dev/null +++ b/ipinfo/cmd_tool_n2ip6.go @@ -0,0 +1,47 @@ +package main + +import ( + "fmt" + "github.com/ipinfo/cli/lib" + "github.com/ipinfo/cli/lib/complete" + "github.com/ipinfo/cli/lib/complete/predict" + "github.com/spf13/pflag" +) + +var completionsToolN2IP6 = &complete.Command{ + Flags: map[string]complete.Predictor{ + "-h": predict.Nothing, + "--help": predict.Nothing, + }, +} + +// printHelpToolN2IP6 prints the help message for the "n2ip6" command. +func printHelpToolN2IP6() { + fmt.Printf( + `Usage: %s tool n2ip6 [] + +Description: + Converts a given numeric representation to its corresponding IPv6 address, and can also evaluate a mathematical expression for conversion. + +Examples: + %[1]s n2ip "4294967295 + 87" + %[1]s n2ip "4294967295" + %[1]s n2ip "201523715" + %[1]s n2ip "51922968585348276285304963292200960" + %[1]s n2ip "a:: - 4294967295" + +Options: + General: + --help, -h + show help. +`, progBase) +} + +// cmdToolN2IP6 is the handler for the "n2ip6" command. +func cmdToolN2IP6() error { + f := lib.CmdToolN2IP6Flags{} + f.Init() + pflag.Parse() + + return lib.CmdToolN2IP6(f, pflag.Args()[2:], printHelpToolN2IP6) +} diff --git a/ipinfo/completions.go b/ipinfo/completions.go index 80752d73..b1d06a06 100644 --- a/ipinfo/completions.go +++ b/ipinfo/completions.go @@ -24,9 +24,6 @@ var completions = &complete.Command{ "splitcidr": completionsSplitCIDR, "mmdb": completionsMmdb, "calc": completionsCalc, - "ip2n": completionsIP2n, - "n2ip": completionsN2IP, - "n2ip6": completionsN2IP6, "tool": completionsTool, "download": completionsDownload, "cache": completionsCache, diff --git a/ipinfo/main.go b/ipinfo/main.go index 87a103a4..9bb766c6 100644 --- a/ipinfo/main.go +++ b/ipinfo/main.go @@ -69,12 +69,6 @@ func main() { err = cmdMmdb() case cmd == "calc": err = cmdCalc() - case cmd == "ip2n": - err = cmdIP2n() - case cmd == "n2ip": - err = cmdN2IP() - case cmd == "n2ip6": - err = cmdN2IP6() case cmd == "download": err = cmdDownload() case cmd == "tool": diff --git a/lib/cmd_calc.go b/lib/cmd_calc.go index 40ddfb74..5d31c9d4 100644 --- a/lib/cmd_calc.go +++ b/lib/cmd_calc.go @@ -62,28 +62,35 @@ func InfixToPostfix(infix []string) []string { for _, token := range infix { if isOperator(token) { - for !postfixStack.IsEmpty() && prec(token) <= prec(postfixStack.Top()) { - postfix = append(postfix, postfixStack.Top()) - postfixStack.Pop() + for { + topOfStack, isEmpty := postfixStack.Pop() + if isEmpty || prec(token) > prec(topOfStack) { + postfixStack.Push(topOfStack) + break + } + postfix = append(postfix, topOfStack) } postfixStack.Push(token) } else if token == "(" { postfixStack.Push(token) } else if token == ")" { - for postfixStack.Top() != "(" { - postfix = append(postfix, postfixStack.Top()) - postfixStack.Pop() + for { + topOfStack, _ := postfixStack.Pop() + if topOfStack == "(" { + break + } + postfix = append(postfix, topOfStack) } - postfixStack.Pop() } else { postfix = append(postfix, token) } } // Pop all the remaining elements from the stack - for !postfixStack.IsEmpty() { - postfix = append(postfix, postfixStack.Top()) - postfixStack.Pop() + topOfStack, isEmpty := postfixStack.Pop() + for !isEmpty { + postfix = append(postfix, topOfStack) + topOfStack, isEmpty = postfixStack.Pop() } return postfix } @@ -104,19 +111,17 @@ func EvaluatePostfix(postfix []string) (*big.Float, error) { } // if operator pop two elements off of the stack. - strNum1 := postfixStack.Top() - postfixStack.Pop() - num1, _, err := big.ParseFloat(strNum1, 10, precision, big.ToZero) - if err != nil { + strNum1, isEmpty := postfixStack.Pop() + if isEmpty { return big.NewFloat(0), ErrInvalidInput } + num1, _, _ := big.ParseFloat(strNum1, 10, precision, big.ToZero) - strNum2 := postfixStack.Top() - postfixStack.Pop() - num2, _, err := big.ParseFloat(strNum2, 10, precision, big.ToZero) - if err != nil { + strNum2, isEmpty := postfixStack.Pop() + if isEmpty { return big.NewFloat(0), ErrInvalidInput } + num2, _, _ := big.ParseFloat(strNum2, 10, precision, big.ToZero) operator := el result := new(big.Float) @@ -149,13 +154,8 @@ func EvaluatePostfix(postfix []string) (*big.Float, error) { postfixStack.Push(strResult) } - strTop := postfixStack.Top() - postfixStack.Pop() - - top, _, err := big.ParseFloat(strTop, 10, precision, big.ToZero) - if err != nil { - return big.NewFloat(0), ErrInvalidInput - } + strTop, _ := postfixStack.Pop() + top, _, _ := big.ParseFloat(strTop, 10, precision, big.ToZero) return top, nil } @@ -192,7 +192,7 @@ func translateToken(tempToken string, tokens []string) ([]string, error) { return tokens, nil } -func isValidPartOfToken(char rune) bool { +func isValidPartOfOperand(char rune) bool { validChars := `^[0-9a-fA-F:\.]*$` validCharsRegx := regexp.MustCompile(validChars) return validCharsRegx.MatchString(string(char)) @@ -202,20 +202,41 @@ func isValidPartOfToken(char rune) bool { func TokenizeInfix(infix string) ([]string, error) { var tokens []string var err error + var prevCharIsPartOfOperand bool + var prevCharIsClosingBracket bool infix = "(" + infix + ")" tempToken := "" for _, char := range infix { opchar := string(char) - if isValidPartOfToken(char) { + if isValidPartOfOperand(char) { tempToken = tempToken + opchar + prevCharIsPartOfOperand = true + // If previous character was closing bracket and current character is part of operand + // then insert a '*' between them + // For example: (3+4)2 should be (3+4)*2 + if prevCharIsClosingBracket { + tokens = append(tokens, "*") + } + prevCharIsClosingBracket = false + } else if char == '(' || char == ')' || isOperator(opchar) { tokens, err = translateToken(tempToken, tokens) if err != nil { return []string{}, err } + // If previous character was part of operand and current character is '(' + // then insert a '*' between them + // For example: 2(3+4) should be 2*(3+4) + if prevCharIsPartOfOperand && char == '(' { + tokens = append(tokens, "*") + } + tokens = append(tokens, opchar) tempToken = "" + + prevCharIsPartOfOperand = false + prevCharIsClosingBracket = char == ')' } } tokens = append(tokens, tempToken) diff --git a/lib/cmd_grepip.go b/lib/cmd_grepip.go index 13cf3f1c..8a806cee 100644 --- a/lib/cmd_grepip.go +++ b/lib/cmd_grepip.go @@ -121,14 +121,12 @@ func CmdGrepIP( // prepare regexp var rexp *regexp.Regexp - rexp4 := `((25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)` - rexp6 := `(([0-9a-fA-F]{1,4}:){7,7}[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,7}:|([0-9a-fA-F]{1,4}:){1,6}:[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,5}(:[0-9a-fA-F]{1,4}){1,2}|([0-9a-fA-F]{1,4}:){1,4}(:[0-9a-fA-F]{1,4}){1,3}|([0-9a-fA-F]{1,4}:){1,3}(:[0-9a-fA-F]{1,4}){1,4}|([0-9a-fA-F]{1,4}:){1,2}(:[0-9a-fA-F]{1,4}){1,5}|[0-9a-fA-F]{1,4}:((:[0-9a-fA-F]{1,4}){1,6})|:((:[0-9a-fA-F]{1,4}){1,7}|:)|fe80:(:[0-9a-fA-F]{0,4}){0,4}%[0-9a-zA-Z]{1,}|::(ffff(:0{1,4}){0,1}:){0,1}((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])\.){3,3}(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])|([0-9a-fA-F]{1,4}:){1,4}:((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])\.){3,3}(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9]))` if ipv == 4 { - rexp = regexp.MustCompilePOSIX(rexp4) + rexp = regexp.MustCompilePOSIX(ipV4RgxPattern) } else if ipv == 6 { - rexp = regexp.MustCompilePOSIX(rexp6) + rexp = regexp.MustCompilePOSIX(ipV4RgxPattern) } else { - rexp = regexp.MustCompilePOSIX(rexp4 + "|" + rexp6) + rexp = regexp.MustCompilePOSIX(ipV4RgxPattern + "|" + ipV6RgxPattern) } fmtSrc := color.New(color.FgMagenta) diff --git a/lib/cmd_ip2n.go b/lib/cmd_tool_ip2n.go similarity index 60% rename from lib/cmd_ip2n.go rename to lib/cmd_tool_ip2n.go index 82f1cf3c..cd393368 100644 --- a/lib/cmd_ip2n.go +++ b/lib/cmd_tool_ip2n.go @@ -6,14 +6,14 @@ import ( "github.com/spf13/pflag" ) -// CmdIP2nFlags are flags expected by CmdIP2n -type CmdIP2nFlags struct { +// CmdToolIP2nFlags are flags expected by CmdToolIP2n +type CmdToolIP2nFlags struct { Help bool NoColor bool } -// Init initializes the common flags available to CmdIP2n with sensible -func (f *CmdIP2nFlags) Init() { +// Init initializes the common flags available to CmdToolIP2n with sensible +func (f *CmdToolIP2nFlags) Init() { _h := "see description in --help" pflag.BoolVarP( &f.Help, @@ -27,8 +27,8 @@ func (f *CmdIP2nFlags) Init() { ) } -// CmdIP2n converts an IP address to a number -func CmdIP2n(f CmdIP2nFlags, args []string, printHelp func()) error { +// CmdToolIP2n converts an IP address to a number +func CmdToolIP2n(f CmdToolIP2nFlags, args []string, printHelp func()) error { if f.NoColor { color.NoColor = true } diff --git a/lib/cmd_n2ip.go b/lib/cmd_tool_n2ip.go similarity index 79% rename from lib/cmd_n2ip.go rename to lib/cmd_tool_n2ip.go index 835594e1..35f9d5f8 100644 --- a/lib/cmd_n2ip.go +++ b/lib/cmd_tool_n2ip.go @@ -6,15 +6,15 @@ import ( "github.com/spf13/pflag" ) -// CmdN2IPFlags are flags expected by CmdN2IP -type CmdN2IPFlags struct { +// CmdToolN2IPFlags are flags expected by CmdToolN2IP +type CmdToolN2IPFlags struct { Help bool NoColor bool ipv6 bool } -// Init initializes the common flags available to CmdN2IP with sensible -func (f *CmdN2IPFlags) Init() { +// Init initializes the common flags available to CmdToolN2IP with sensible +func (f *CmdToolN2IPFlags) Init() { _h := "see description in --help" pflag.BoolVarP( &f.Help, @@ -33,8 +33,8 @@ func (f *CmdN2IPFlags) Init() { ) } -// CmdN2IP converts a number to an IP address -func CmdN2IP(f CmdN2IPFlags, args []string, printHelp func()) error { +// CmdToolN2IP converts a number to an IP address +func CmdToolN2IP(f CmdToolN2IPFlags, args []string, printHelp func()) error { if f.NoColor { color.NoColor = true } diff --git a/lib/cmd_n2ip6.go b/lib/cmd_tool_n2ip6.go similarity index 78% rename from lib/cmd_n2ip6.go rename to lib/cmd_tool_n2ip6.go index 4ca2575d..2ea9dc6b 100644 --- a/lib/cmd_n2ip6.go +++ b/lib/cmd_tool_n2ip6.go @@ -6,14 +6,14 @@ import ( "github.com/spf13/pflag" ) -// CmdN2IP6Flags are flags expected by CmdN2IP6 -type CmdN2IP6Flags struct { +// CmdToolN2IP6Flags are flags expected by CmdToolN2IP6 +type CmdToolN2IP6Flags struct { Help bool NoColor bool } -// Init initializes the common flags available to CmdN2IP6 with sensible -func (f *CmdN2IP6Flags) Init() { +// Init initializes the common flags available to CmdToolN2IP6 with sensible +func (f *CmdToolN2IP6Flags) Init() { _h := "see description in --help" pflag.BoolVarP( &f.Help, @@ -27,8 +27,8 @@ func (f *CmdN2IP6Flags) Init() { ) } -// CmdN2IP6 converts a number to an IPv6 address -func CmdN2IP6(f CmdN2IP6Flags, args []string, printHelp func()) error { +// CmdToolN2IP6 converts a number to an IPv6 address +func CmdToolN2IP6(f CmdToolN2IP6Flags, args []string, printHelp func()) error { if f.NoColor { color.NoColor = true } diff --git a/lib/init.go b/lib/init.go index ae65bbf9..b9ab71c2 100644 --- a/lib/init.go +++ b/lib/init.go @@ -3,4 +3,6 @@ package lib func init() { bogonIP4List = GetBogonRange4() bogonIP6List = GetBogonRange6() + ipV4RgxPattern = GetIpV4RgxPattern() + ipV6RgxPattern = GetIpV6RgxPattern() } diff --git a/lib/ip_str.go b/lib/ip_str.go index f331a290..398fdd81 100644 --- a/lib/ip_str.go +++ b/lib/ip_str.go @@ -12,11 +12,8 @@ func StrIsIPStr(ipStr string) bool { // StrIsIPv4Str checks if the given string is an IPv4 address func StrIsIPv4Str(expression string) bool { - // Define the regular expression pattern for matching IPv4 addresses - ipV4Pattern := `^(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$` - // Compile the regular expression - ipV4Regex := regexp.MustCompile(ipV4Pattern) + ipV4Regex := regexp.MustCompile(ipV4RgxPattern) // Use the MatchString function to check if the expression matches the IPv4 pattern return ipV4Regex.MatchString(expression) @@ -24,11 +21,8 @@ func StrIsIPv4Str(expression string) bool { // StrIsIPv6Str checks if the given string is an IPv6 address func StrIsIPv6Str(expression string) bool { - // Define the regular expression pattern for matching IPv6 addresses - ipV6Pattern := `^(([0-9a-fA-F]{1,4}:){7,7}[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,7}:|([0-9a-fA-F]{1,4}:){1,6}:[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,5}(:[0-9a-fA-F]{1,4}){1,2}|([0-9a-fA-F]{1,4}:){1,4}(:[0-9a-fA-F]{1,4}){1,3}|([0-9a-fA-F]{1,4}:){1,3}(:[0-9a-fA-F]{1,4}){1,4}|([0-9a-fA-F]{1,4}:){1,2}(:[0-9a-fA-F]{1,4}){1,5}|[0-9a-fA-F]{1,4}:((:[0-9a-fA-F]{1,4}){1,6})|:((:[0-9a-fA-F]{1,4}){1,7}|:)|fe80:(:[0-9a-fA-F]{0,4}){0,4}%[0-9a-zA-Z]{1,}|::(ffff(:0{1,4}){0,1}:){0,1}((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])\.){3,3}(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])|([0-9a-fA-F]{1,4}:){1,4}:((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])\.){3,3}(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9]))$` - // Compile the regular expression - ipV6Regex := regexp.MustCompile(ipV6Pattern) + ipV6Regex := regexp.MustCompile(ipV6RgxPattern) // Use the MatchString function to check if the expression matches the IPv6 pattern return ipV6Regex.MatchString(expression) diff --git a/lib/regex.go b/lib/regex.go new file mode 100644 index 00000000..61beb42f --- /dev/null +++ b/lib/regex.go @@ -0,0 +1,21 @@ +package lib + +// ipV4RgxPattern is a global variable used to store a regular expression pattern for matching IPv4 addresses. +var ipV4RgxPattern string + +// ipV6RgxPattern is a global variable used to store a regular expression pattern for matching IPv4 addresses. +var ipV6RgxPattern string + +// GetIpV4RgxPattern returns a regular expression pattern for matching IPv6 addresses. +// The returned regular expression can be used to validate strings and determine whether they represent +// valid IPv4 addresses. +func GetIpV4RgxPattern() string { + return `((25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)` +} + +// GetIpV6RgxPattern returns a regular expression pattern for matching IPv6 addresses. +// The returned regular expression can be used to validate strings and determine whether they represent +// valid IPv6 addresses. +func GetIpV6RgxPattern() string { + return `(([0-9a-fA-F]{1,4}:){7,7}[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,7}:|([0-9a-fA-F]{1,4}:){1,6}:[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,5}(:[0-9a-fA-F]{1,4}){1,2}|([0-9a-fA-F]{1,4}:){1,4}(:[0-9a-fA-F]{1,4}){1,3}|([0-9a-fA-F]{1,4}:){1,3}(:[0-9a-fA-F]{1,4}){1,4}|([0-9a-fA-F]{1,4}:){1,2}(:[0-9a-fA-F]{1,4}){1,5}|[0-9a-fA-F]{1,4}:((:[0-9a-fA-F]{1,4}){1,6})|:((:[0-9a-fA-F]{1,4}){1,7}|:)|fe80:(:[0-9a-fA-F]{0,4}){0,4}%[0-9a-zA-Z]{1,}|::(ffff(:0{1,4}){0,1}:){0,1}((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])\.){3,3}(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])|([0-9a-fA-F]{1,4}:){1,4}:((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])\.){3,3}(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9]))` +} diff --git a/lib/stack.go b/lib/stack.go index a70060d9..f661aa68 100644 --- a/lib/stack.go +++ b/lib/stack.go @@ -1,36 +1,33 @@ package lib -// Stack type +// Stack represents a stack data structure that holds a collection of strings. +// The elements in the stack are managed using the Last-In-First-Out (LIFO) principle, +// which means that the last element added to the stack will be the first one to be removed. type Stack []string -// IsEmpty check if stack is empty +// IsEmpty checks if the stack is empty. +// It returns true if the stack contains no elements (i.e., it is empty), and false otherwise. func (st *Stack) IsEmpty() bool { return len(*st) == 0 } -// Push a new value onto the stack +// Push adds a new value onto the stack. +// The new value (given as a string) is appended to the top of the stack, effectively making it the new top element. func (st *Stack) Push(str string) { *st = append(*st, str) //Simply append the new value to the end of the stack } -// Pop Remove top element of stack. Return false if stack is empty. -func (st *Stack) Pop() bool { +// Pop removes the top element from the stack and returns it. If the stack is empty, +// it returns an empty string and true indicating that the stack is empty. +// Otherwise, it returns the top element of the stack and false. +// The stack is modified in-place by removing the top element. +func (st *Stack) Pop() (string, bool) { if st.IsEmpty() { - return false - } else { - index := len(*st) - 1 // Get the index of top most element. - *st = (*st)[:index] // Remove it from the stack by slicing it off. - return true - } -} - -// Top Return top element of stack. Return false if stack is empty. -func (st *Stack) Top() string { - if st.IsEmpty() { - return "" + return "", true } else { index := len(*st) - 1 // Get the index of top most element. element := (*st)[index] // Index onto the slice and obtain the element. - return element + *st = (*st)[:index] // Remove it from the stack by slicing it off. + return element, false } } From 55fa316c80193069e958baeeb0748a9d326c3e2d Mon Sep 17 00:00:00 2001 From: harisabdullah Date: Tue, 1 Aug 2023 14:50:27 +0500 Subject: [PATCH 46/50] Removed unnecessary color flags --- ipinfo/cmd_tool_ip2n.go | 2 +- ipinfo/cmd_tool_n2ip.go | 3 ++- ipinfo/cmd_tool_n2ip6.go | 5 +++-- lib/cmd_tool_ip2n.go | 16 ++-------------- lib/cmd_tool_n2ip.go | 15 ++------------- lib/cmd_tool_n2ip6.go | 16 ++-------------- 6 files changed, 12 insertions(+), 45 deletions(-) diff --git a/ipinfo/cmd_tool_ip2n.go b/ipinfo/cmd_tool_ip2n.go index eb2a57e8..c0ddf36d 100644 --- a/ipinfo/cmd_tool_ip2n.go +++ b/ipinfo/cmd_tool_ip2n.go @@ -43,5 +43,5 @@ func cmdToolIP2n() error { f.Init() pflag.Parse() - return lib.CmdToolIP2n(f, pflag.Args()[2:], printHelpToolIp2n) + return lib.CmdToolIP2n(pflag.Args()[2:], printHelpToolIp2n) } diff --git a/ipinfo/cmd_tool_n2ip.go b/ipinfo/cmd_tool_n2ip.go index 67dd81c9..a1e96e01 100644 --- a/ipinfo/cmd_tool_n2ip.go +++ b/ipinfo/cmd_tool_n2ip.go @@ -24,7 +24,8 @@ func printHelpToolN2IP() { `Usage: %s n2ip tool [] Description: - Converts a given numeric representation to its corresponding IPv4 or IPv6 address, and can also evaluate a mathematical expression for conversion. + Converts a given numeric representation to its corresponding IPv4 or IPv6 address, + and can also evaluate a mathematical expression for conversion. Examples: %[1]s n2ip "4294967295 + 87" diff --git a/ipinfo/cmd_tool_n2ip6.go b/ipinfo/cmd_tool_n2ip6.go index 4f861d5b..81d94b07 100644 --- a/ipinfo/cmd_tool_n2ip6.go +++ b/ipinfo/cmd_tool_n2ip6.go @@ -21,7 +21,8 @@ func printHelpToolN2IP6() { `Usage: %s tool n2ip6 [] Description: - Converts a given numeric representation to its corresponding IPv6 address, and can also evaluate a mathematical expression for conversion. + Converts a given numeric representation to its corresponding IPv6 address, + and can also evaluate a mathematical expression for conversion. Examples: %[1]s n2ip "4294967295 + 87" @@ -43,5 +44,5 @@ func cmdToolN2IP6() error { f.Init() pflag.Parse() - return lib.CmdToolN2IP6(f, pflag.Args()[2:], printHelpToolN2IP6) + return lib.CmdToolN2IP6(pflag.Args()[2:], printHelpToolN2IP6) } diff --git a/lib/cmd_tool_ip2n.go b/lib/cmd_tool_ip2n.go index cd393368..9324f630 100644 --- a/lib/cmd_tool_ip2n.go +++ b/lib/cmd_tool_ip2n.go @@ -2,37 +2,25 @@ package lib import ( "fmt" - "github.com/fatih/color" "github.com/spf13/pflag" ) // CmdToolIP2nFlags are flags expected by CmdToolIP2n type CmdToolIP2nFlags struct { - Help bool - NoColor bool + Help bool } // Init initializes the common flags available to CmdToolIP2n with sensible func (f *CmdToolIP2nFlags) Init() { - _h := "see description in --help" pflag.BoolVarP( &f.Help, "help", "h", false, "show help.", ) - pflag.BoolVar( - &f.NoColor, - "nocolor", false, - _h, - ) } // CmdToolIP2n converts an IP address to a number -func CmdToolIP2n(f CmdToolIP2nFlags, args []string, printHelp func()) error { - if f.NoColor { - color.NoColor = true - } - +func CmdToolIP2n(args []string, printHelp func()) error { if len(args) == 0 { printHelp() return nil diff --git a/lib/cmd_tool_n2ip.go b/lib/cmd_tool_n2ip.go index 35f9d5f8..9e88dbab 100644 --- a/lib/cmd_tool_n2ip.go +++ b/lib/cmd_tool_n2ip.go @@ -2,15 +2,13 @@ package lib import ( "fmt" - "github.com/fatih/color" "github.com/spf13/pflag" ) // CmdToolN2IPFlags are flags expected by CmdToolN2IP type CmdToolN2IPFlags struct { - Help bool - NoColor bool - ipv6 bool + Help bool + ipv6 bool } // Init initializes the common flags available to CmdToolN2IP with sensible @@ -21,11 +19,6 @@ func (f *CmdToolN2IPFlags) Init() { "help", "h", false, "show help.", ) - pflag.BoolVar( - &f.NoColor, - "nocolor", false, - _h, - ) pflag.BoolVarP( &f.ipv6, "ipv6", "6", false, @@ -35,10 +28,6 @@ func (f *CmdToolN2IPFlags) Init() { // CmdToolN2IP converts a number to an IP address func CmdToolN2IP(f CmdToolN2IPFlags, args []string, printHelp func()) error { - if f.NoColor { - color.NoColor = true - } - if len(args) == 0 { printHelp() return nil diff --git a/lib/cmd_tool_n2ip6.go b/lib/cmd_tool_n2ip6.go index 2ea9dc6b..1ceeb24a 100644 --- a/lib/cmd_tool_n2ip6.go +++ b/lib/cmd_tool_n2ip6.go @@ -2,37 +2,25 @@ package lib import ( "fmt" - "github.com/fatih/color" "github.com/spf13/pflag" ) // CmdToolN2IP6Flags are flags expected by CmdToolN2IP6 type CmdToolN2IP6Flags struct { - Help bool - NoColor bool + Help bool } // Init initializes the common flags available to CmdToolN2IP6 with sensible func (f *CmdToolN2IP6Flags) Init() { - _h := "see description in --help" pflag.BoolVarP( &f.Help, "help", "h", false, "show help.", ) - pflag.BoolVar( - &f.NoColor, - "nocolor", false, - _h, - ) } // CmdToolN2IP6 converts a number to an IPv6 address -func CmdToolN2IP6(f CmdToolN2IP6Flags, args []string, printHelp func()) error { - if f.NoColor { - color.NoColor = true - } - +func CmdToolN2IP6(args []string, printHelp func()) error { if len(args) == 0 { printHelp() return nil From 5bd84961852d09686d3baf24def4849a2f5332cb Mon Sep 17 00:00:00 2001 From: Haris Date: Thu, 3 Aug 2023 15:33:43 +0500 Subject: [PATCH 47/50] Update lib/cmd_tool_n2ip.go Co-authored-by: Uman Shahzad --- lib/cmd_tool_n2ip.go | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/lib/cmd_tool_n2ip.go b/lib/cmd_tool_n2ip.go index 9e88dbab..ccfc7003 100644 --- a/lib/cmd_tool_n2ip.go +++ b/lib/cmd_tool_n2ip.go @@ -38,11 +38,7 @@ func CmdToolN2IP(f CmdToolN2IPFlags, args []string, printHelp func()) error { return ErrInvalidInput } - // n2ip also accepts an expression which is why the following - // Steps are being done - // Convert to postfix - // If it is a single number and not an expression - // The tokenization and evaluation would have no effect on the number + // NOTE: n2ip also accepts an expression, hence the tokenization and evaluation. // Tokenize the expression tokens, err := TokenizeInfix(expression) From ab8fbb0173ef65c4c9e23d26d0c187013d05c080 Mon Sep 17 00:00:00 2001 From: harisabdullah Date: Thu, 3 Aug 2023 16:08:45 +0500 Subject: [PATCH 48/50] - Regex is now global to avoid redundant compilation - Fixed Help flags --- ipinfo/cmd_tool_ip2n.go | 2 +- ipinfo/cmd_tool_n2ip6.go | 12 ++++++------ lib/cmd_calc.go | 2 +- lib/cmd_grepip.go | 6 +++--- lib/cmd_tool_ip2n.go | 4 ++-- lib/cmd_tool_n2ip.go | 5 +++-- lib/cmd_tool_n2ip6.go | 8 ++++---- lib/init.go | 7 +++++-- lib/ip_conversions.go | 3 --- lib/ip_str.go | 9 --------- lib/regex.go | 23 ++++++----------------- 11 files changed, 31 insertions(+), 50 deletions(-) diff --git a/ipinfo/cmd_tool_ip2n.go b/ipinfo/cmd_tool_ip2n.go index c0ddf36d..eb2a57e8 100644 --- a/ipinfo/cmd_tool_ip2n.go +++ b/ipinfo/cmd_tool_ip2n.go @@ -43,5 +43,5 @@ func cmdToolIP2n() error { f.Init() pflag.Parse() - return lib.CmdToolIP2n(pflag.Args()[2:], printHelpToolIp2n) + return lib.CmdToolIP2n(f, pflag.Args()[2:], printHelpToolIp2n) } diff --git a/ipinfo/cmd_tool_n2ip6.go b/ipinfo/cmd_tool_n2ip6.go index 81d94b07..2cc7dbdd 100644 --- a/ipinfo/cmd_tool_n2ip6.go +++ b/ipinfo/cmd_tool_n2ip6.go @@ -25,11 +25,11 @@ Description: and can also evaluate a mathematical expression for conversion. Examples: - %[1]s n2ip "4294967295 + 87" - %[1]s n2ip "4294967295" - %[1]s n2ip "201523715" - %[1]s n2ip "51922968585348276285304963292200960" - %[1]s n2ip "a:: - 4294967295" + %[1]s n2ip6 "4294967295 + 87" + %[1]s n2ip6 "4294967295" + %[1]s n2ip6 "201523715" + %[1]s n2ip6 "51922968585348276285304963292200960" + %[1]s n2ip6 "a:: - 4294967295" Options: General: @@ -44,5 +44,5 @@ func cmdToolN2IP6() error { f.Init() pflag.Parse() - return lib.CmdToolN2IP6(pflag.Args()[2:], printHelpToolN2IP6) + return lib.CmdToolN2IP6(f, pflag.Args()[2:], printHelpToolN2IP6) } diff --git a/lib/cmd_calc.go b/lib/cmd_calc.go index 4407d0f0..99f0432d 100644 --- a/lib/cmd_calc.go +++ b/lib/cmd_calc.go @@ -313,7 +313,7 @@ func digitsAfterDecimal(float big.Float) int { // CmdCalc Function is the handler for the "calc" command. func CmdCalc(f CmdCalcFlags, args []string, printHelp func()) error { - if len(args) == 0 { + if len(args) == 0 || f.Help { printHelp() return nil } diff --git a/lib/cmd_grepip.go b/lib/cmd_grepip.go index 8a806cee..24643db2 100644 --- a/lib/cmd_grepip.go +++ b/lib/cmd_grepip.go @@ -122,11 +122,11 @@ func CmdGrepIP( // prepare regexp var rexp *regexp.Regexp if ipv == 4 { - rexp = regexp.MustCompilePOSIX(ipV4RgxPattern) + rexp = ipV4Regex } else if ipv == 6 { - rexp = regexp.MustCompilePOSIX(ipV4RgxPattern) + rexp = ipV6Regex } else { - rexp = regexp.MustCompilePOSIX(ipV4RgxPattern + "|" + ipV6RgxPattern) + rexp = ipRegex } fmtSrc := color.New(color.FgMagenta) diff --git a/lib/cmd_tool_ip2n.go b/lib/cmd_tool_ip2n.go index 9324f630..62d37449 100644 --- a/lib/cmd_tool_ip2n.go +++ b/lib/cmd_tool_ip2n.go @@ -20,8 +20,8 @@ func (f *CmdToolIP2nFlags) Init() { } // CmdToolIP2n converts an IP address to a number -func CmdToolIP2n(args []string, printHelp func()) error { - if len(args) == 0 { +func CmdToolIP2n(f CmdToolIP2nFlags, args []string, printHelp func()) error { + if len(args) == 0 || f.Help { printHelp() return nil } diff --git a/lib/cmd_tool_n2ip.go b/lib/cmd_tool_n2ip.go index ccfc7003..e4378151 100644 --- a/lib/cmd_tool_n2ip.go +++ b/lib/cmd_tool_n2ip.go @@ -1,6 +1,7 @@ package lib import ( + "errors" "fmt" "github.com/spf13/pflag" ) @@ -28,7 +29,7 @@ func (f *CmdToolN2IPFlags) Init() { // CmdToolN2IP converts a number to an IP address func CmdToolN2IP(f CmdToolN2IPFlags, args []string, printHelp func()) error { - if len(args) == 0 { + if len(args) == 0 || f.Help { printHelp() return nil } @@ -58,7 +59,7 @@ func CmdToolN2IP(f CmdToolN2IPFlags, args []string, printHelp func()) error { // as ip cannot be derived from a float res, err := DecimalStrToIP(result.Text('f', 0), f.ipv6) if err != nil { - return err + return errors.New("number is too large") } fmt.Println(res.String()) diff --git a/lib/cmd_tool_n2ip6.go b/lib/cmd_tool_n2ip6.go index 1ceeb24a..768cd6ab 100644 --- a/lib/cmd_tool_n2ip6.go +++ b/lib/cmd_tool_n2ip6.go @@ -1,6 +1,7 @@ package lib import ( + "errors" "fmt" "github.com/spf13/pflag" ) @@ -20,8 +21,8 @@ func (f *CmdToolN2IP6Flags) Init() { } // CmdToolN2IP6 converts a number to an IPv6 address -func CmdToolN2IP6(args []string, printHelp func()) error { - if len(args) == 0 { +func CmdToolN2IP6(f CmdToolN2IP6Flags, args []string, printHelp func()) error { + if len(args) == 0 || f.Help { printHelp() return nil } @@ -49,13 +50,12 @@ func CmdToolN2IP6(args []string, printHelp func()) error { if err != nil { return err } - // Convert to IP // Precision should be 0 i.e. number of digits after decimal // as ip cannot be derived from a float res, err := DecimalStrToIP(result.Text('f', 0), true) if err != nil { - return err + return errors.New("number is too large") } fmt.Println(res.String()) diff --git a/lib/init.go b/lib/init.go index b9ab71c2..261c6579 100644 --- a/lib/init.go +++ b/lib/init.go @@ -1,8 +1,11 @@ package lib +import "regexp" + func init() { bogonIP4List = GetBogonRange4() bogonIP6List = GetBogonRange6() - ipV4RgxPattern = GetIpV4RgxPattern() - ipV6RgxPattern = GetIpV6RgxPattern() + ipV4Regex = regexp.MustCompile(IPv4RegexPattern) + ipV6Regex = regexp.MustCompile(IPv6RegexPattern) + ipRegex = regexp.MustCompile(IPv4RegexPattern + "|" + IPv6RegexPattern) } diff --git a/lib/ip_conversions.go b/lib/ip_conversions.go index c587a4db..38c0c87b 100644 --- a/lib/ip_conversions.go +++ b/lib/ip_conversions.go @@ -39,7 +39,6 @@ func DecimalStrToIP(decimal string, forceIPv6 bool) (net.IP, error) { fmt.Print(decimal) return nil, ErrInvalidInput } - // Convert to IPv4 if not forcing IPv6 and 'num' is within the IPv4 range if !forceIPv6 && num.Cmp(big.NewInt(4294967295)) <= 0 { ip := make(net.IP, 4) @@ -47,7 +46,6 @@ func DecimalStrToIP(decimal string, forceIPv6 bool) (net.IP, error) { copy(ip[4-len(b):], b) return ip, nil } - // Convert to IPv6 if 'num' is within the IPv6 range maxIpv6 := new(big.Int) maxIpv6.SetString("340282366920938463463374607431768211455", 10) @@ -57,7 +55,6 @@ func DecimalStrToIP(decimal string, forceIPv6 bool) (net.IP, error) { copy(ip[16-len(b):], b) return ip, nil } - return nil, ErrInvalidInput } diff --git a/lib/ip_str.go b/lib/ip_str.go index 398fdd81..2b67772f 100644 --- a/lib/ip_str.go +++ b/lib/ip_str.go @@ -2,7 +2,6 @@ package lib import ( "net" - "regexp" ) // StrIsIPStr checks whether a string is an IP. @@ -12,18 +11,10 @@ func StrIsIPStr(ipStr string) bool { // StrIsIPv4Str checks if the given string is an IPv4 address func StrIsIPv4Str(expression string) bool { - // Compile the regular expression - ipV4Regex := regexp.MustCompile(ipV4RgxPattern) - - // Use the MatchString function to check if the expression matches the IPv4 pattern return ipV4Regex.MatchString(expression) } // StrIsIPv6Str checks if the given string is an IPv6 address func StrIsIPv6Str(expression string) bool { - // Compile the regular expression - ipV6Regex := regexp.MustCompile(ipV6RgxPattern) - - // Use the MatchString function to check if the expression matches the IPv6 pattern return ipV6Regex.MatchString(expression) } diff --git a/lib/regex.go b/lib/regex.go index 61beb42f..474524c2 100644 --- a/lib/regex.go +++ b/lib/regex.go @@ -1,21 +1,10 @@ package lib -// ipV4RgxPattern is a global variable used to store a regular expression pattern for matching IPv4 addresses. -var ipV4RgxPattern string +import "regexp" -// ipV6RgxPattern is a global variable used to store a regular expression pattern for matching IPv4 addresses. -var ipV6RgxPattern string +var ipV4Regex *regexp.Regexp +var ipV6Regex *regexp.Regexp +var ipRegex *regexp.Regexp -// GetIpV4RgxPattern returns a regular expression pattern for matching IPv6 addresses. -// The returned regular expression can be used to validate strings and determine whether they represent -// valid IPv4 addresses. -func GetIpV4RgxPattern() string { - return `((25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)` -} - -// GetIpV6RgxPattern returns a regular expression pattern for matching IPv6 addresses. -// The returned regular expression can be used to validate strings and determine whether they represent -// valid IPv6 addresses. -func GetIpV6RgxPattern() string { - return `(([0-9a-fA-F]{1,4}:){7,7}[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,7}:|([0-9a-fA-F]{1,4}:){1,6}:[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,5}(:[0-9a-fA-F]{1,4}){1,2}|([0-9a-fA-F]{1,4}:){1,4}(:[0-9a-fA-F]{1,4}){1,3}|([0-9a-fA-F]{1,4}:){1,3}(:[0-9a-fA-F]{1,4}){1,4}|([0-9a-fA-F]{1,4}:){1,2}(:[0-9a-fA-F]{1,4}){1,5}|[0-9a-fA-F]{1,4}:((:[0-9a-fA-F]{1,4}){1,6})|:((:[0-9a-fA-F]{1,4}){1,7}|:)|fe80:(:[0-9a-fA-F]{0,4}){0,4}%[0-9a-zA-Z]{1,}|::(ffff(:0{1,4}){0,1}:){0,1}((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])\.){3,3}(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])|([0-9a-fA-F]{1,4}:){1,4}:((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])\.){3,3}(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9]))` -} +const IPv4RegexPattern = `((25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)` +const IPv6RegexPattern = `(([0-9a-fA-F]{1,4}:){7,7}[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,7}:|([0-9a-fA-F]{1,4}:){1,6}:[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,5}(:[0-9a-fA-F]{1,4}){1,2}|([0-9a-fA-F]{1,4}:){1,4}(:[0-9a-fA-F]{1,4}){1,3}|([0-9a-fA-F]{1,4}:){1,3}(:[0-9a-fA-F]{1,4}){1,4}|([0-9a-fA-F]{1,4}:){1,2}(:[0-9a-fA-F]{1,4}){1,5}|[0-9a-fA-F]{1,4}:((:[0-9a-fA-F]{1,4}){1,6})|:((:[0-9a-fA-F]{1,4}){1,7}|:)|fe80:(:[0-9a-fA-F]{0,4}){0,4}%[0-9a-zA-Z]{1,}|::(ffff(:0{1,4}){0,1}:){0,1}((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])\.){3,3}(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])|([0-9a-fA-F]{1,4}:){1,4}:((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])\.){3,3}(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9]))` From 2644c1fa17791475131d4cd34a7c62074b8a9a23 Mon Sep 17 00:00:00 2001 From: Haris Date: Thu, 3 Aug 2023 16:09:26 +0500 Subject: [PATCH 49/50] Update lib/cmd_tool_n2ip6.go Co-authored-by: Uman Shahzad --- lib/cmd_tool_n2ip6.go | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/lib/cmd_tool_n2ip6.go b/lib/cmd_tool_n2ip6.go index 768cd6ab..d4e5f097 100644 --- a/lib/cmd_tool_n2ip6.go +++ b/lib/cmd_tool_n2ip6.go @@ -32,11 +32,7 @@ func CmdToolN2IP6(f CmdToolN2IP6Flags, args []string, printHelp func()) error { return ErrInvalidInput } - // n2ip also accepts an expression which is why the following - // Steps are being done - // Convert to postfix - // If it is a single number and not an expression - // The tokenization and evaluation would have no effect on the number + // NOTE: n2ip6 also accepts an expression, hence the tokenization and evaluation. // Tokenize the expression tokens, err := TokenizeInfix(expression) From 7fc3cc66c670da31f584db0430bfa99c9c7f2caa Mon Sep 17 00:00:00 2001 From: harisabdullah Date: Fri, 4 Aug 2023 14:51:48 +0500 Subject: [PATCH 50/50] `MustCompilePOSIX`instead of `MustCompile`. Does not affect the validation of IPs in calc. --- lib/init.go | 6 +++--- lib/ip_conversions.go | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/lib/init.go b/lib/init.go index 261c6579..2f96736b 100644 --- a/lib/init.go +++ b/lib/init.go @@ -5,7 +5,7 @@ import "regexp" func init() { bogonIP4List = GetBogonRange4() bogonIP6List = GetBogonRange6() - ipV4Regex = regexp.MustCompile(IPv4RegexPattern) - ipV6Regex = regexp.MustCompile(IPv6RegexPattern) - ipRegex = regexp.MustCompile(IPv4RegexPattern + "|" + IPv6RegexPattern) + ipV4Regex = regexp.MustCompilePOSIX(IPv4RegexPattern) + ipV6Regex = regexp.MustCompilePOSIX(IPv6RegexPattern) + ipRegex = regexp.MustCompilePOSIX(IPv4RegexPattern + "|" + IPv6RegexPattern) } diff --git a/lib/ip_conversions.go b/lib/ip_conversions.go index 38c0c87b..44d4fe17 100644 --- a/lib/ip_conversions.go +++ b/lib/ip_conversions.go @@ -25,7 +25,7 @@ func IPtoDecimalStr(strIP string) (string, error) { } return strconv.FormatInt(IP4toInt(ip), 10), nil } else { - return "", ErrInvalidInput + return "", ErrNotIP } }