hautils

Unnamed repository; edit this file 'description' to name the repository.
Log | Files | Refs | README | LICENSE

commit 1eed681548cab75223cd0500b5db32f01ca59a0e
parent 9a9e44aaf1947567ba1461c5e9430c864d979a43
Author: Byron Torres <b@torresjrjr.com>
Date:   Thu, 28 Apr 2022 23:00:12 +0100

nl: implement regex line matching

For the -h, -b, and -f options; the p<pattern> behaviour is implemented.
Note, ERE from the stdlib's regex module is used, not BRE as specified
in POSIX nl(1).

Diffstat:
Mnl.ha | 144++++++++++++++++++++++++++++++++++++++++++++++++++++++++-----------------------
1 file changed, 103 insertions(+), 41 deletions(-)

diff --git a/nl.ha b/nl.ha @@ -7,22 +7,28 @@ use getopt; use io; use main; use os; +use regex; use strconv; use strings; use types; +type style = (all | nonempty | none | regex::regex); + +// Number all lines. +type all = void; + +// Number lines which are non-empty. +type nonempty = void; + +// Number no lines. +type none = void; + type section = enum { HEAD, BODY, FOOT, }; -type style = enum { - ALL, // number all lines - TXT, // number lines containing non-whitespace text - NON, // number no lines -}; - type context = struct { linenum: int, conblanks: uint, // counter of consecutive blank lines @@ -54,9 +60,9 @@ export fn utilmain() (void | main::error) = { static const delim_buf: [2]u8 = [0, 0]; let delim = "\\:"; - let head_style = style::NON; - let body_style = style::TXT; - let foot_style = style::NON; + let head_style: style = none; + let body_style: style = nonempty; + let foot_style: style = none; let paged_numbering = true; let startnum = 1; @@ -80,15 +86,25 @@ export fn utilmain() (void | main::error) = { case 'p' => paged_numbering = false; case 'b' => - body_style = switch (opt.1) { - case "a" => - yield style::ALL; - case "t" => - yield style::TXT; - case "n" => - yield style::NON; - case => - usage(help, 'b'); + body_style = if (strings::hasprefix(opt.1, "p")) { + const pat = strings::trimprefix(opt.1, "p"); + yield match(regex::compile(pat)) { + case let re: regex::regex => + yield re; + case let err: regex::error => + fmt::fatal("Error: -bp<string>: {}", err); + }; + } else { + yield switch (opt.1) { + case "a" => + yield all; + case "t" => + yield nonempty; + case "n" => + yield none; + case => + usage(help, 'b'); + }; }; case 'd' => delim = switch (len(opt.1)) { @@ -100,26 +116,46 @@ export fn utilmain() (void | main::error) = { usage(help, 'd'); }; case 'f' => - foot_style = switch (opt.1) { - case "a" => - yield style::ALL; - case "t" => - yield style::TXT; - case "n" => - yield style::NON; - case => - usage(help, 'f'); + foot_style = if (strings::hasprefix(opt.1, "p")) { + const pat = strings::trimprefix(opt.1, "p"); + yield match(regex::compile(pat)) { + case let re: regex::regex => + yield re; + case let err: regex::error => + fmt::fatal("Error: -fp<string>: {}", err); + }; + } else { + yield switch (opt.1) { + case "a" => + yield all; + case "t" => + yield nonempty; + case "n" => + yield none; + case => + usage(help, 'f'); + }; }; case 'h' => - head_style = switch (opt.1) { - case "a" => - yield style::ALL; - case "t" => - yield style::TXT; - case "n" => - yield style::NON; - case => - usage(help, 'h'); + head_style = if (strings::hasprefix(opt.1, "p")) { + const pat = strings::trimprefix(opt.1, "p"); + yield match(regex::compile(pat)) { + case let re: regex::regex => + yield re; + case let err: regex::error => + fmt::fatal("Error: -hp<string>: {}", err); + }; + } else { + yield switch (opt.1) { + case "a" => + yield all; + case "t" => + yield nonempty; + case "n" => + yield none; + case => + usage(help, 'h'); + }; }; case 'i' => ctx.incr = match (strconv::stoi(opt.1)) { @@ -169,6 +205,18 @@ export fn utilmain() (void | main::error) = { usage(help, void); }; + defer { + if (head_style is regex::regex) { + regex::regex_finish(&(head_style as regex::regex)); + }; + if (body_style is regex::regex) { + regex::regex_finish(&(body_style as regex::regex)); + }; + if (foot_style is regex::regex) { + regex::regex_finish(&(foot_style as regex::regex)); + }; + }; + const use_file = len(cmd.args) == 1 && cmd.args[0] != "-"; const input: io::handle = if (use_file) match (os::open(cmd.args[0])) { @@ -241,9 +289,9 @@ export fn utilmain() (void | main::error) = { }; }; -fn println(line: str, style: style, ctx: *context) (void | io::error) = { - switch (style) { - case style::ALL => +fn println(line: str, s: style, ctx: *context) (void | io::error) = { + match (s) { + case all => if (!isblank(line)) { fmt::printf("{%}", ctx.linenum, ctx.mod)?; fmt::print(ctx.sep)?; @@ -258,13 +306,27 @@ fn println(line: str, style: style, ctx: *context) (void | io::error) = { ctx.conblanks = 0; }; }; - case style::TXT => + case nonempty => if (!isblank(line)) { fmt::printf("{%}", ctx.linenum, ctx.mod)?; fmt::print(ctx.sep)?; ctx.linenum += ctx.incr; }; - case style::NON => void; + case none => + fmt::printf("{%}", "", ctx.mod)?; + fmt::print(ctx.sep)?; + case let re: regex::regex => + match (regex::find(re, line)) { + case []regex::matchgroup => + fmt::printf("{%}", ctx.linenum, ctx.mod)?; + fmt::print(ctx.sep)?; + ctx.linenum += ctx.incr; + case void => + fmt::printf("{%}", "", ctx.mod)?; + fmt::print(ctx.sep)?; + case let err: regex::error => + fmt::fatal("Error: regex: {}", err); + }; }; fmt::println(line)?; };