Compare commits
10 commits
683d71ff8b
...
ca0376a181
Author | SHA1 | Date | |
---|---|---|---|
ca0376a181 | |||
9d97a50e6e | |||
c3188ae353 | |||
f8f6dd4917 | |||
427f2773bf | |||
b0a28f9f16 | |||
2f3bf35b02 | |||
a90d17bb97 | |||
96832abac6 | |||
6119e52888 |
5 changed files with 141 additions and 35 deletions
1
.gitignore
vendored
1
.gitignore
vendored
|
@ -5,3 +5,4 @@ deps.zig
|
||||||
/test.in
|
/test.in
|
||||||
/test.out
|
/test.out
|
||||||
/test.expect
|
/test.expect
|
||||||
|
/dist
|
||||||
|
|
15
Makefile
Normal file
15
Makefile
Normal file
|
@ -0,0 +1,15 @@
|
||||||
|
dist: inttest
|
||||||
|
mkdir dist
|
||||||
|
cp zig-out/bin/skip dist/
|
||||||
|
|
||||||
|
inttest: zig-out/bin/skip
|
||||||
|
./test.sh
|
||||||
|
|
||||||
|
zig-out/bin/skip: unittest
|
||||||
|
zig build
|
||||||
|
|
||||||
|
unittest: zigmod src/main.zig
|
||||||
|
zig build test
|
||||||
|
|
||||||
|
zigmod: zig.mod
|
||||||
|
zigmod ci
|
47
README.md
47
README.md
|
@ -42,6 +42,8 @@ line 4
|
||||||
|
|
||||||
### Skip until a number of matching lines
|
### Skip until a number of matching lines
|
||||||
|
|
||||||
|
The whole line must match.
|
||||||
|
|
||||||
This example reads the named file.
|
This example reads the named file.
|
||||||
|
|
||||||
File: `input.txt`
|
File: `input.txt`
|
||||||
|
@ -67,7 +69,9 @@ gamma
|
||||||
alpha
|
alpha
|
||||||
```
|
```
|
||||||
|
|
||||||
### Skip lines until a number of tokens as seen
|
### Skip lines until a number of tokens are seen
|
||||||
|
|
||||||
|
Looks for a string within a line, counting each occurance.
|
||||||
|
|
||||||
This example reads the file from stdin.
|
This example reads the file from stdin.
|
||||||
|
|
||||||
|
@ -99,3 +103,44 @@ commodo consequat.
|
||||||
|
|
||||||
It matches the first `dolor` on line 1,
|
It matches the first `dolor` on line 1,
|
||||||
and the second on line 4 as part of the word `dolore`.
|
and the second on line 4 as part of the word `dolore`.
|
||||||
|
|
||||||
|
### Skip lines until a lines with tokens are seen
|
||||||
|
|
||||||
|
Looks for a string within a line, only counting each matching line once.
|
||||||
|
|
||||||
|
This example reads the file from stdin.
|
||||||
|
|
||||||
|
File: `input.txt`
|
||||||
|
|
||||||
|
```text
|
||||||
|
Lorem ipsum dolor sit amet,
|
||||||
|
consectetur adipiscing elit,
|
||||||
|
sed do eiusmod tempor incididunt
|
||||||
|
ut labore et dolore magna aliqua.
|
||||||
|
Ut enim ad minim veniam,
|
||||||
|
quis nostrud exercitation ullamco
|
||||||
|
laboris nisi ut aliquip ex ea
|
||||||
|
commodo consequat.
|
||||||
|
```
|
||||||
|
|
||||||
|
```bash
|
||||||
|
cat input.txt | skip 4 --token m --ignore-extras
|
||||||
|
```
|
||||||
|
|
||||||
|
Will output:
|
||||||
|
|
||||||
|
```text
|
||||||
|
quis nostrud exercitation ullamco
|
||||||
|
laboris nisi ut aliquip ex ea
|
||||||
|
commodo consequat.
|
||||||
|
```
|
||||||
|
|
||||||
|
Without `--ignore-extras`, it would have found the fourth `m` on line 3, and displayed:
|
||||||
|
|
||||||
|
```text
|
||||||
|
ut labore et dolore magna aliqua.
|
||||||
|
Ut enim ad minim veniam,
|
||||||
|
quis nostrud exercitation ullamco
|
||||||
|
laboris nisi ut aliquip ex ea
|
||||||
|
commodo consequat.
|
||||||
|
```
|
||||||
|
|
51
src/main.zig
51
src/main.zig
|
@ -46,6 +46,7 @@ const Config = struct {
|
||||||
file: ?fs.File,
|
file: ?fs.File,
|
||||||
line: ?[]const u8 = null,
|
line: ?[]const u8 = null,
|
||||||
token: ?[]const u8 = null,
|
token: ?[]const u8 = null,
|
||||||
|
ignoreExtras: bool,
|
||||||
|
|
||||||
pub fn deinit(self: @This()) void {
|
pub fn deinit(self: @This()) void {
|
||||||
if (self.file) |f| {
|
if (self.file) |f| {
|
||||||
|
@ -56,17 +57,18 @@ const Config = struct {
|
||||||
|
|
||||||
fn parseArgs(allocator: mem.Allocator) !Config {
|
fn parseArgs(allocator: mem.Allocator) !Config {
|
||||||
const params = comptime [_]clap.Param(clap.Help) {
|
const params = comptime [_]clap.Param(clap.Help) {
|
||||||
clap.parseParam("<N> The number of lines to skip") catch unreachable,
|
clap.parseParam("<N> The number of lines to skip") catch unreachable,
|
||||||
clap.parseParam("[<FILE>] The file to read or stdin if not given") catch unreachable,
|
clap.parseParam("[<FILE>] The file to read or stdin if not given") catch unreachable,
|
||||||
clap.parseParam("-l, --line <STR> Skip until N lines matching this") catch unreachable,
|
clap.parseParam("-l, --line <STR> Skip until N lines matching this") catch unreachable,
|
||||||
clap.parseParam("-t, --token <STR> Skip lines until N tokens found") catch unreachable,
|
clap.parseParam("-t, --token <STR> Skip lines until N tokens found") catch unreachable,
|
||||||
clap.parseParam("-h, --help Display this help and exit") catch unreachable,
|
clap.parseParam("-i, --ignore-extras Only count the first token on each line") catch unreachable,
|
||||||
clap.parseParam("-v, --version Display the version") catch unreachable,
|
clap.parseParam("-h, --help Display this help and exit") catch unreachable,
|
||||||
|
clap.parseParam("-v, --version Display the version") catch unreachable,
|
||||||
};
|
};
|
||||||
var diag = clap.Diagnostic{};
|
var diag = clap.Diagnostic{};
|
||||||
var args = clap.parse(clap.Help, ¶ms, .{ .diagnostic = &diag }) catch |err| {
|
var args = clap.parse(clap.Help, ¶ms, .{ .diagnostic = &diag }) catch |err| {
|
||||||
diag.report(io.getStdErr().writer(), err) catch {};
|
diag.report(io.getStdErr().writer(), err) catch {};
|
||||||
return error.EarlyExit;
|
return error.BadArgs;
|
||||||
};
|
};
|
||||||
defer args.deinit();
|
defer args.deinit();
|
||||||
|
|
||||||
|
@ -97,6 +99,15 @@ fn parseArgs(allocator: mem.Allocator) !Config {
|
||||||
if (args.option("--token")) |match| {
|
if (args.option("--token")) |match| {
|
||||||
token = try allocator.dupe(u8, match);
|
token = try allocator.dupe(u8, match);
|
||||||
}
|
}
|
||||||
|
var ignoreExtras: bool = false;
|
||||||
|
if (args.flag("--ignore-extras")) {
|
||||||
|
if (token) |_| {
|
||||||
|
ignoreExtras = true;
|
||||||
|
} else {
|
||||||
|
try io.getStdErr().writer().print("Error: --ignore-extras requires --token\n", .{});
|
||||||
|
return error.BadArgs;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
var n: u32 = 0;
|
var n: u32 = 0;
|
||||||
var file: ?fs.File = null;
|
var file: ?fs.File = null;
|
||||||
|
@ -122,6 +133,7 @@ fn parseArgs(allocator: mem.Allocator) !Config {
|
||||||
.file = file,
|
.file = file,
|
||||||
.line = line,
|
.line = line,
|
||||||
.token = token,
|
.token = token,
|
||||||
|
.ignoreExtras = ignoreExtras,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -132,22 +144,23 @@ fn dumpInput(config: Config, in: fs.File, out: fs.File, allocator: mem.Allocator
|
||||||
var c: usize = 0;
|
var c: usize = 0;
|
||||||
while (c < config.lines) {
|
while (c < config.lines) {
|
||||||
const line = it.next();
|
const line = it.next();
|
||||||
if (config.line) |match| {
|
if (line) |memory| {
|
||||||
if (line) |memory| {
|
if (config.line) |match| {
|
||||||
if (mem.eql(u8, match, memory)) {
|
if (mem.eql(u8, match, memory)) {
|
||||||
c += 1;
|
c += 1;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if (config.token) |token| {
|
|
||||||
if (line) |memory| {
|
|
||||||
c += mem.count(u8, memory, token);
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
c += 1;
|
if (config.token) |token| {
|
||||||
|
const occurances = mem.count(u8, memory, token);
|
||||||
|
if (config.ignoreExtras and occurances > 0) {
|
||||||
|
c += 1;
|
||||||
|
} else {
|
||||||
|
c += occurances;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
c += 1;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
if (line) |memory| {
|
|
||||||
allocator.free(memory);
|
allocator.free(memory);
|
||||||
} else return;
|
} else return;
|
||||||
}
|
}
|
||||||
|
@ -166,6 +179,7 @@ test "dumpInput skip 1 line" {
|
||||||
const config = Config{
|
const config = Config{
|
||||||
.lines = 1,
|
.lines = 1,
|
||||||
.file = file,
|
.file = file,
|
||||||
|
.ignoreExtras = false,
|
||||||
};
|
};
|
||||||
|
|
||||||
try dumpInput(config, file, output, testing.allocator);
|
try dumpInput(config, file, output, testing.allocator);
|
||||||
|
@ -196,6 +210,7 @@ test "dumpInput skip 2 line 'alpha'" {
|
||||||
.lines = 2,
|
.lines = 2,
|
||||||
.file = file,
|
.file = file,
|
||||||
.line = "alpha",
|
.line = "alpha",
|
||||||
|
.ignoreExtras = false,
|
||||||
};
|
};
|
||||||
|
|
||||||
try dumpInput(config, file, output, testing.allocator);
|
try dumpInput(config, file, output, testing.allocator);
|
||||||
|
|
62
test.sh
62
test.sh
|
@ -2,6 +2,14 @@
|
||||||
|
|
||||||
set -e
|
set -e
|
||||||
|
|
||||||
|
SKIP="./zig-out/bin/skip"
|
||||||
|
DIFF="diff -u --color"
|
||||||
|
|
||||||
|
if test ! -x $SKIP ; then
|
||||||
|
echo "File missing: $SKIP - try 'zig build'"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
echo "> skip a line when reading from stdin"
|
echo "> skip a line when reading from stdin"
|
||||||
INPUT=$(cat<<EOF
|
INPUT=$(cat<<EOF
|
||||||
line 1
|
line 1
|
||||||
|
@ -9,8 +17,9 @@ line 2
|
||||||
EOF
|
EOF
|
||||||
)
|
)
|
||||||
echo "line 2" > test.expect
|
echo "line 2" > test.expect
|
||||||
echo "$INPUT" | ./skip 1 > test.out
|
echo "$INPUT" | $SKIP 1 > test.out
|
||||||
diff --brief test.expect test.out
|
$DIFF test.expect test.out
|
||||||
|
rm test.expect test.out
|
||||||
|
|
||||||
echo "> skip a line when reading from a file"
|
echo "> skip a line when reading from a file"
|
||||||
cat<<EOF > test.in
|
cat<<EOF > test.in
|
||||||
|
@ -18,8 +27,9 @@ line 1
|
||||||
line 2
|
line 2
|
||||||
EOF
|
EOF
|
||||||
echo "line 2" > test.expect
|
echo "line 2" > test.expect
|
||||||
./skip 1 test.in > test.out
|
$SKIP 1 test.in > test.out
|
||||||
diff --brief test.expect test.out
|
$DIFF test.expect test.out
|
||||||
|
rm test.expect test.out
|
||||||
|
|
||||||
echo "> skip until 2 matching lines seen"
|
echo "> skip until 2 matching lines seen"
|
||||||
cat<<EOF > test.in
|
cat<<EOF > test.in
|
||||||
|
@ -35,8 +45,9 @@ alpha
|
||||||
gamma
|
gamma
|
||||||
alpha
|
alpha
|
||||||
EOF
|
EOF
|
||||||
./skip 2 test.in --line alpha > test.out
|
$SKIP 2 test.in --line alpha > test.out
|
||||||
diff --brief test.expect test.out
|
$DIFF test.expect test.out
|
||||||
|
rm test.in test.expect test.out
|
||||||
|
|
||||||
echo "> skip lines until 2 tokens seen"
|
echo "> skip lines until 2 tokens seen"
|
||||||
cat<<EOF > test.in
|
cat<<EOF > test.in
|
||||||
|
@ -55,17 +66,37 @@ quis nostrud exercitation ullamco
|
||||||
laboris nisi ut aliquip ex ea
|
laboris nisi ut aliquip ex ea
|
||||||
commodo consequat.
|
commodo consequat.
|
||||||
EOF
|
EOF
|
||||||
./skip 2 test.in --token dolor > test.out
|
$SKIP 2 test.in --token dolor > test.out 2
|
||||||
diff --brief test.expect test.out
|
$DIFF test.expect test.out
|
||||||
|
rm test.in test.expect test.out
|
||||||
|
|
||||||
echo "> handle unknown parameter with simple error message"
|
echo "> handle unknown parameter with simple error message"
|
||||||
cat<<EOF > test.expect
|
cat<<EOF > test.expect.err
|
||||||
Invalid argument '--foo'
|
Invalid argument '--foo'
|
||||||
EOF
|
EOF
|
||||||
./skip --foo > test.out 2>&1 || ## error is expected
|
cat<<EOF > test.expect
|
||||||
diff --brief test.expect test.out
|
EOF
|
||||||
|
touch test.out test.err
|
||||||
|
$SKIP --foo > test.out 2> test.err
|
||||||
|
$DIFF test.expect test.out
|
||||||
|
$DIFF test.expect.err test.err
|
||||||
|
rm test.expect test.out
|
||||||
|
rm test.expect.err test.err
|
||||||
|
|
||||||
echo "> skip lines until 3 tokens seen - ignored extra tokens on same line"
|
echo "> handle ignore-extra when token is missing"
|
||||||
|
cat<<EOF > test.expect.err
|
||||||
|
Error: --ignore-extras requires --token
|
||||||
|
EOF
|
||||||
|
cat<<EOF > test.expect
|
||||||
|
EOF
|
||||||
|
touch test.out test.err
|
||||||
|
$SKIP --ignore-extras > test.out 2> test.err
|
||||||
|
$DIFF test.expect test.out
|
||||||
|
$DIFF test.expect.err test.err
|
||||||
|
rm test.expect test.out
|
||||||
|
rm test.expect.err test.err
|
||||||
|
|
||||||
|
echo "> skip lines until 4 tokens seen - ignored extra tokens on same line"
|
||||||
cat<<EOF > test.in
|
cat<<EOF > test.in
|
||||||
Lorem ipsum dolor sit amet,
|
Lorem ipsum dolor sit amet,
|
||||||
consectetur adipiscing elit,
|
consectetur adipiscing elit,
|
||||||
|
@ -81,9 +112,8 @@ quis nostrud exercitation ullamco
|
||||||
laboris nisi ut aliquip ex ea
|
laboris nisi ut aliquip ex ea
|
||||||
commodo consequat.
|
commodo consequat.
|
||||||
EOF
|
EOF
|
||||||
./skip 3 test.in --token m --ignore-extras > test.out
|
$SKIP 4 test.in --token m --ignore-extras > test.out
|
||||||
diff --brief test.expect test.out
|
$DIFF test.expect test.out
|
||||||
|
rm test.in test.expect test.out
|
||||||
rm test.in test.out test.expect
|
|
||||||
|
|
||||||
echo done
|
echo done
|
||||||
|
|
Loading…
Reference in a new issue