diff --git a/.gitignore b/.gitignore index 76d6532..560b4b0 100644 --- a/.gitignore +++ b/.gitignore @@ -5,3 +5,4 @@ deps.zig /test.in /test.out /test.expect +/dist diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..c05bd82 --- /dev/null +++ b/Makefile @@ -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 diff --git a/README.md b/README.md index aee0153..88eda5e 100644 --- a/README.md +++ b/README.md @@ -42,6 +42,8 @@ line 4 ### Skip until a number of matching lines +The whole line must match. + This example reads the named file. File: `input.txt` @@ -67,7 +69,9 @@ gamma 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. @@ -99,3 +103,44 @@ commodo consequat. It matches the first `dolor` on line 1, 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. +``` diff --git a/src/main.zig b/src/main.zig index 7a9b0d5..04c9a63 100644 --- a/src/main.zig +++ b/src/main.zig @@ -11,12 +11,6 @@ const clap = @import("clap"); const version = "0.1.0"; -// step 1: [x] read in a file from stdin and write out to stdout -// step 2: [x] read in a named file in parameters and write out to stdout -// step 3: [x] skip a number of lines -// step 4: [ ] skip a number of matching lines -// step 5: [ ] skip a number of tokens - const maxLineLength = 4096; pub fn main() anyerror!void { @@ -52,6 +46,7 @@ const Config = struct { file: ?fs.File, line: ?[]const u8 = null, token: ?[]const u8 = null, + ignoreExtras: bool, pub fn deinit(self: @This()) void { if (self.file) |f| { @@ -62,17 +57,18 @@ const Config = struct { fn parseArgs(allocator: mem.Allocator) !Config { const params = comptime [_]clap.Param(clap.Help) { - clap.parseParam(" The number of lines to skip") catch unreachable, - clap.parseParam("[] The file to read or stdin if not given") catch unreachable, - clap.parseParam("-l, --line Skip until N lines matching this") catch unreachable, - clap.parseParam("-t, --token Skip lines until N tokens found") catch unreachable, - clap.parseParam("-h, --help Display this help and exit") catch unreachable, - clap.parseParam("-v, --version Display the version") catch unreachable, + clap.parseParam(" The number of lines to skip") catch unreachable, + clap.parseParam("[] The file to read or stdin if not given") catch unreachable, + clap.parseParam("-l, --line Skip until N lines matching this") catch unreachable, + clap.parseParam("-t, --token Skip lines until N tokens found") catch unreachable, + clap.parseParam("-i, --ignore-extras Only count the first token on each line") 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 args = clap.parse(clap.Help, ¶ms, .{ .diagnostic = &diag }) catch |err| { diag.report(io.getStdErr().writer(), err) catch {}; - return err; + return error.BadArgs; }; defer args.deinit(); @@ -103,6 +99,15 @@ fn parseArgs(allocator: mem.Allocator) !Config { if (args.option("--token")) |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 file: ?fs.File = null; @@ -128,6 +133,7 @@ fn parseArgs(allocator: mem.Allocator) !Config { .file = file, .line = line, .token = token, + .ignoreExtras = ignoreExtras, }; } @@ -138,22 +144,23 @@ fn dumpInput(config: Config, in: fs.File, out: fs.File, allocator: mem.Allocator var c: usize = 0; while (c < config.lines) { const line = it.next(); - if (config.line) |match| { - if (line) |memory| { + if (line) |memory| { + if (config.line) |match| { if (mem.eql(u8, match, memory)) { c += 1; } - } - } else { - if (config.token) |token| { - if (line) |memory| { - c += mem.count(u8, memory, token); - } } 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); } else return; } @@ -172,6 +179,7 @@ test "dumpInput skip 1 line" { const config = Config{ .lines = 1, .file = file, + .ignoreExtras = false, }; try dumpInput(config, file, output, testing.allocator); @@ -202,6 +210,7 @@ test "dumpInput skip 2 line 'alpha'" { .lines = 2, .file = file, .line = "alpha", + .ignoreExtras = false, }; try dumpInput(config, file, output, testing.allocator); diff --git a/test.sh b/test.sh index 769152f..09148e1 100755 --- a/test.sh +++ b/test.sh @@ -2,6 +2,14 @@ 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" INPUT=$(cat< test.expect -echo "$INPUT" | ./skip 1 > test.out -diff --brief test.expect test.out +echo "$INPUT" | $SKIP 1 > test.out +$DIFF test.expect test.out +rm test.expect test.out echo "> skip a line when reading from a file" cat< test.in @@ -18,8 +27,9 @@ line 1 line 2 EOF echo "line 2" > test.expect -./skip 1 test.in > test.out -diff --brief test.expect test.out +$SKIP 1 test.in > test.out +$DIFF test.expect test.out +rm test.expect test.out echo "> skip until 2 matching lines seen" cat< test.in @@ -35,8 +45,9 @@ alpha gamma alpha EOF -./skip 2 test.in --line alpha > test.out -diff --brief test.expect test.out +$SKIP 2 test.in --line alpha > test.out +$DIFF test.expect test.out +rm test.in test.expect test.out echo "> skip lines until 2 tokens seen" cat< test.in @@ -55,9 +66,54 @@ quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. EOF -./skip 2 test.in --token dolor > test.out -diff --brief test.expect test.out +$SKIP 2 test.in --token dolor > test.out 2 +$DIFF test.expect test.out +rm test.in test.expect test.out -rm test.in test.out test.expect +echo "> handle unknown parameter with simple error message" +cat< test.expect.err +Invalid argument '--foo' +EOF +cat< test.expect +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 "> handle ignore-extra when token is missing" +cat< test.expect.err +Error: --ignore-extras requires --token +EOF +cat< 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< test.in +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. +EOF +cat< test.expect +quis nostrud exercitation ullamco +laboris nisi ut aliquip ex ea +commodo consequat. +EOF +$SKIP 4 test.in --token m --ignore-extras > test.out +$DIFF test.expect test.out +rm test.in test.expect test.out echo done