Skip to content

Commit 0372280

Browse files
committed
Add increment and decrement string parsers
1 parent 574fbdb commit 0372280

9 files changed

Lines changed: 184 additions & 104 deletions

File tree

readme.md

Lines changed: 18 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -3,13 +3,13 @@
33
A fast and simple string parser that returns an IP address or a list of them. Accepts CIDR notation or IP fragments. If base IP is not set, unlike in the examples below, the IP of the machine which runs the script is used.
44

55
```go mdox-exec="sh/demo.sh"
6-
# ipex -b 255.255.166.33 22
7-
255.255.166.22
6+
# ipex -b 127.0.0.1 22
7+
127.0.0.22
88

9-
# ipex -b 255.255.166.33 22 33 44
10-
255.255.166.22
11-
255.255.166.33
12-
255.255.166.44
9+
# ipex -b 192.168.166.33 22 33 44
10+
192.168.166.22
11+
192.168.166.33
12+
192.168.166.44
1313

1414
# ipex -b 255.255.166.33 155.22/30 99 200/30
1515
255.255.155.21
@@ -18,4 +18,16 @@ A fast and simple string parser that returns an IP address or a list of them. Ac
1818
255.255.166.201
1919
255.255.166.202
2020

21+
# ipex -b 255.255.166.33 254+4 1-4 128/31
22+
255.255.166.254
23+
255.255.166.255
24+
255.255.167.0
25+
255.255.167.1
26+
255.255.166.1
27+
255.255.166.0
28+
255.255.165.255
29+
255.255.165.254
30+
255.255.166.128
31+
255.255.166.129
32+
2133
```

sh/demo.sh

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ _rcmd() {
1212
echo ""
1313
}
1414

15-
_rcmd r -b 255.255.166.33 22
16-
_rcmd r -b 255.255.166.33 22 33 44
15+
_rcmd r -b 127.0.0.1 22
16+
_rcmd r -b 192.168.166.33 22 33 44
1717
_rcmd r -b 255.255.166.33 155.22/30 99 200/30
18+
_rcmd r -b 255.255.166.33 254+4 1-4 128/31

src/go.trace

2.64 KB
Binary file not shown.

src/ip.go

Lines changed: 21 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -40,13 +40,13 @@ func displayIPs(cidr string) {
4040
return
4141
}
4242

43-
ipAddr, ipNet, err := net.ParseCIDR(cidr)
43+
ipAddr, ipNet, err := parseCIDRString(cidr)
4444
if err != nil {
4545
log.Print(err)
4646
return
4747
}
4848

49-
for ip := ipAddr.Mask(ipNet.Mask); ipNet.Contains(ip); increment(ip) {
49+
for ip := ipAddr.Mask(ipNet.Mask); ipNet.Contains(ip); incrementIP(ip) {
5050
ips = append(ips, ip.String())
5151
}
5252

@@ -62,11 +62,29 @@ func displayIPs(cidr string) {
6262
}
6363
}
6464

65-
func increment(ip net.IP) {
65+
func incrementIP(ip net.IP) {
6666
for i := len(ip) - 1; i >= 0; i-- {
6767
ip[i]++
6868
if ip[i] != 0 {
6969
break
7070
}
7171
}
7272
}
73+
74+
func parseCIDRString(cidr string) (ipAddr net.IP, ipNet *net.IPNet, err error) {
75+
ipAddr, ipNet, err = net.ParseCIDR(cidr)
76+
if err != nil {
77+
log.Print(err)
78+
return
79+
}
80+
return
81+
}
82+
83+
func parseToIPv4(ipstr string) (ip net.IP) {
84+
ip = net.ParseIP(ipstr)
85+
ip = ip.To4()
86+
if ip == nil {
87+
log.Fatal("non ipv4 address")
88+
}
89+
return
90+
}

src/notation.go

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
package main
2+
3+
import (
4+
"strconv"
5+
"strings"
6+
)
7+
8+
type tNotation struct {
9+
Exp string
10+
Pre string
11+
Op string
12+
Suf string
13+
SufInt int
14+
}
15+
16+
func notateSplit(str string) (nt tNotation) {
17+
nt.Exp = str
18+
arr := rxSplit(`[\+|\-|\/]`, str)
19+
if len(arr) > 0 {
20+
nt.Pre = arr[0]
21+
}
22+
if len(arr) > 1 {
23+
nt.Suf = arr[1]
24+
}
25+
nt.SufInt, _ = strconv.Atoi(strings.Trim(nt.Suf, "."))
26+
nt.Op = rxFind(`[\+|\-|\/]`, str)
27+
return
28+
}
29+
30+
func (nt *tNotation) expandIP() {
31+
if isIPv4Full(nt.Pre) {
32+
nt.Exp = nt.Pre
33+
} else if isIPv4Fragment(nt.Pre) || isInt(nt.Pre) {
34+
if !isIPv4Full(localIP) {
35+
localIP = getLocalIP().String()
36+
}
37+
nt.Exp = replaceTail(localIP, nt.Pre)
38+
}
39+
}

src/notation_test.go

Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,83 @@
1+
package main
2+
3+
import "testing"
4+
5+
func TestIsIPFull(t *testing.T) {
6+
7+
assertNotateSplit("192.168.33.1", newNt("192.168.33.1"), t)
8+
assertNotateSplit("192.168.33.1/30", newNt("192.168.33.1", "/", "30"), t)
9+
assertNotateSplit("255.255.0.0", newNt("255.255.0.0"), t)
10+
assertNotateSplit("255.255.0.0/16", newNt("255.255.0.0", "/", "16"), t)
11+
assertNotateSplit("255.0", newNt("255.0"), t)
12+
assertNotateSplit("255.0/8", newNt("255.0", "/", "8"), t)
13+
assertNotateSplit("1", newNt("1"), t)
14+
assertNotateSplit("1/30", newNt("1", "/", "30"), t)
15+
16+
assertNotateSplit("192.168.33.1", newNt("192.168.33.1"), t)
17+
assertNotateSplit("192.168.33.1+30", newNt("192.168.33.1", "+", "30"), t)
18+
assertNotateSplit("255.255.0.0", newNt("255.255.0.0"), t)
19+
assertNotateSplit("255.255.0.0+16", newNt("255.255.0.0", "+", "16"), t)
20+
assertNotateSplit("255.0", newNt("255.0"), t)
21+
assertNotateSplit("255.0+8", newNt("255.0", "+", "8"), t)
22+
assertNotateSplit("1", newNt("1"), t)
23+
assertNotateSplit("1+30", newNt("1", "+", "30"), t)
24+
25+
assertNotateSplit("192.168.33.1", newNt("192.168.33.1"), t)
26+
assertNotateSplit("192.168.33.1-30", newNt("192.168.33.1", "-", "30"), t)
27+
assertNotateSplit("255.255.0.0", newNt("255.255.0.0"), t)
28+
assertNotateSplit("255.255.0.0-16", newNt("255.255.0.0", "-", "16"), t)
29+
assertNotateSplit("255.0", newNt("255.0"), t)
30+
assertNotateSplit("255.0-8", newNt("255.0", "-", "8"), t)
31+
assertNotateSplit("1", newNt("1"), t)
32+
assertNotateSplit("1-30", newNt("1", "-", "30"), t)
33+
34+
assertIsCIDRNotation("192.168.33.1", false, t)
35+
assertIsCIDRNotation("192.168.33.1/30", true, t)
36+
assertIsCIDRNotation("255.255.0.0", false, t)
37+
assertIsCIDRNotation("255.255.0.0/16", true, t)
38+
assertIsCIDRNotation("255.0", false, t)
39+
assertIsCIDRNotation("255.0/8", true, t)
40+
assertIsCIDRNotation("1", false, t)
41+
assertIsCIDRNotation("1/30", true, t)
42+
43+
assertIsIPv4Full("192.168.33.1", true, t)
44+
assertIsIPv4Full("255.255.0.0", true, t)
45+
assertIsIPv4Full("255.255.0", false, t)
46+
assertIsIPv4Full("255.0", false, t)
47+
assertIsIPv4Full("1", false, t)
48+
49+
assertIsIPv4Fragment("192.168.33.1", true, t)
50+
assertIsIPv4Fragment("255.255.0.0", true, t)
51+
assertIsIPv4Fragment("255.255.0", true, t)
52+
assertIsIPv4Fragment("255.0", true, t)
53+
assertIsIPv4Fragment("1", false, t)
54+
55+
assertIsInt("192.168.33.1", false, t)
56+
assertIsInt("255.255.0.0", false, t)
57+
assertIsInt("255.255.0", false, t)
58+
assertIsInt("255.0", false, t)
59+
assertIsInt("1", true, t)
60+
}
61+
62+
func assertNotateSplit(inp string, exp tNotation, t *testing.T) {
63+
res := notateSplit(inp)
64+
if res.Pre != exp.Pre || res.Op != exp.Op || res.Suf != exp.Suf {
65+
t.Errorf(
66+
"notateSplit failed: %s -> %v != %v",
67+
inp, exp, res,
68+
)
69+
}
70+
}
71+
72+
func newNt(params ...any) (nt tNotation) {
73+
if len(params) > 0 {
74+
nt.Pre = params[0].(string)
75+
}
76+
if len(params) > 1 {
77+
nt.Op = params[1].(string)
78+
}
79+
if len(params) > 2 {
80+
nt.Suf = params[2].(string)
81+
}
82+
return nt
83+
}

src/rx.go

Lines changed: 0 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -13,29 +13,6 @@ const (
1313
rxIPv4Fragment = `^(?:\d{1,3}\.)+\d{1,3}$`
1414
)
1515

16-
type tNotation struct {
17-
Pre string
18-
Op string
19-
Suf string
20-
}
21-
22-
func notateSplit(str string) (nt tNotation) {
23-
arr := rxSplit(`[\+|\-|\/]`, str)
24-
if len(arr) > 0 {
25-
nt.Pre = arr[0]
26-
}
27-
if len(arr) > 1 {
28-
nt.Suf = arr[1]
29-
}
30-
nt.Op = rxFind(`[\+|\-|\/]`, str)
31-
// if isCIDRNotation(str) {
32-
// arr = strings.Split(str, "/")
33-
// } else {
34-
// arr = []string{str, "32"}
35-
// }
36-
return
37-
}
38-
3916
func isCIDRNotation(str string) bool {
4017
return strings.Contains(strings.Trim(str, "."), "/")
4118
}

src/rx_test.go

Lines changed: 1 addition & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -2,34 +2,7 @@ package main
22

33
import "testing"
44

5-
func TestIsIPFull(t *testing.T) {
6-
assertNotateSplit("192.168.33.1", newNt("192.168.33.1"), t)
7-
assertNotateSplit("192.168.33.1/30", newNt("192.168.33.1", "/", "30"), t)
8-
assertNotateSplit("255.255.0.0", newNt("255.255.0.0"), t)
9-
assertNotateSplit("255.255.0.0/16", newNt("255.255.0.0", "/", "16"), t)
10-
assertNotateSplit("255.0", newNt("255.0"), t)
11-
assertNotateSplit("255.0/8", newNt("255.0", "/", "8"), t)
12-
assertNotateSplit("1", newNt("1"), t)
13-
assertNotateSplit("1/30", newNt("1", "/", "30"), t)
14-
15-
assertNotateSplit("192.168.33.1", newNt("192.168.33.1"), t)
16-
assertNotateSplit("192.168.33.1+30", newNt("192.168.33.1", "+", "30"), t)
17-
assertNotateSplit("255.255.0.0", newNt("255.255.0.0"), t)
18-
assertNotateSplit("255.255.0.0+16", newNt("255.255.0.0", "+", "16"), t)
19-
assertNotateSplit("255.0", newNt("255.0"), t)
20-
assertNotateSplit("255.0+8", newNt("255.0", "+", "8"), t)
21-
assertNotateSplit("1", newNt("1"), t)
22-
assertNotateSplit("1+30", newNt("1", "+", "30"), t)
23-
24-
assertNotateSplit("192.168.33.1", newNt("192.168.33.1"), t)
25-
assertNotateSplit("192.168.33.1-30", newNt("192.168.33.1", "-", "30"), t)
26-
assertNotateSplit("255.255.0.0", newNt("255.255.0.0"), t)
27-
assertNotateSplit("255.255.0.0-16", newNt("255.255.0.0", "-", "16"), t)
28-
assertNotateSplit("255.0", newNt("255.0"), t)
29-
assertNotateSplit("255.0-8", newNt("255.0", "-", "8"), t)
30-
assertNotateSplit("1", newNt("1"), t)
31-
assertNotateSplit("1-30", newNt("1", "-", "30"), t)
32-
5+
func TestNotations(t *testing.T) {
336
assertIsCIDRNotation("192.168.33.1", false, t)
347
assertIsCIDRNotation("192.168.33.1/30", true, t)
358
assertIsCIDRNotation("255.255.0.0", false, t)
@@ -58,16 +31,6 @@ func TestIsIPFull(t *testing.T) {
5831
assertIsInt("1", true, t)
5932
}
6033

61-
func assertNotateSplit(inp string, exp tNotation, t *testing.T) {
62-
res := notateSplit(inp)
63-
if res.Pre != exp.Pre || res.Op != exp.Op || res.Suf != exp.Suf {
64-
t.Errorf(
65-
"notateSplit failed: %s -> %s != %s",
66-
inp, exp, res,
67-
)
68-
}
69-
}
70-
7134
func assertIsCIDRNotation(inp string, exp bool, t *testing.T) {
7235
res := isCIDRNotation(inp)
7336
if res != exp {
@@ -95,16 +58,3 @@ func assertIsInt(inp string, exp bool, t *testing.T) {
9558
t.Errorf("isInt failed: %s -> %v != %v", inp, exp, res)
9659
}
9760
}
98-
99-
func newNt(params ...any) (nt tNotation) {
100-
if len(params) > 0 {
101-
nt.Pre = params[0].(string)
102-
}
103-
if len(params) > 1 {
104-
nt.Op = params[1].(string)
105-
}
106-
if len(params) > 2 {
107-
nt.Suf = params[2].(string)
108-
}
109-
return nt
110-
}

src/stringparse.go

Lines changed: 19 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1,35 +1,35 @@
11
package main
22

33
import (
4-
"fmt"
4+
"net/netip"
55
)
66

77
func parseInput(args []string) (cidrs []string) {
88
for _, arg := range args {
99
nt := notateSplit(arg)
10+
nt.expandIP()
1011

11-
app := nt.Pre
12+
var app []string
1213
switch nt.Op {
13-
case "/", "":
14-
if isIPv4Full(nt.Pre) {
15-
app = nt.Pre
16-
} else if isIPv4Fragment(nt.Pre) || isInt(nt.Pre) {
17-
if !isIPv4Full(localIP) {
18-
localIP = getLocalIP().String()
19-
}
20-
app = replaceTail(localIP, nt.Pre)
21-
}
22-
if nt.Suf != "" {
23-
app += nt.Op + nt.Suf
24-
}
14+
case "":
15+
app = []string{nt.Exp}
16+
case "/":
17+
app = []string{nt.Exp + nt.Op + nt.Suf}
2518
case "+":
26-
fmt.Println("Tomorrow.")
19+
ip := netip.MustParseAddr(nt.Exp)
20+
for i := 0; i < nt.SufInt; i++ {
21+
app = append(app, ip.String())
22+
ip = ip.Next()
23+
}
2724
case "-":
28-
fmt.Println("In two days.")
25+
ip := netip.MustParseAddr(nt.Exp)
26+
for i := 0; i < nt.SufInt; i++ {
27+
app = append(app, ip.String())
28+
ip = ip.Prev()
29+
}
2930
}
30-
31-
if app != "" {
32-
cidrs = append(cidrs, app)
31+
if len(app) > 0 {
32+
cidrs = append(cidrs, app...)
3333
}
3434
}
3535
return cidrs

0 commit comments

Comments
 (0)