From: Siraaj Khandkar Date: Wed, 5 Sep 2018 20:48:23 +0000 (-0400) Subject: Redesign message format X-Git-Url: https://git.xandkar.net/?a=commitdiff_plain;h=8482fea64a51f9a9b97895c974a88a986b42aa15;p=khatus.git Redesign message format - supports distributed khatus (adds node field) - separates messages into types (data, error, alert, log) --- diff --git a/Makefile b/Makefile index 5dbefc2..1f39234 100644 --- a/Makefile +++ b/Makefile @@ -24,9 +24,10 @@ AWK_EXECUTABLES := \ bin/khatus_parse_upower define BUILD_AWK_EXE - echo '#! $(PATH_TO_AWK) -f' > $@ && \ - echo 'BEGIN {Module = "$(notdir $@)"}' >> $@ && \ - cat $^ >> $@ && \ + echo '#! $(PATH_TO_AWK) -f' > $@ && \ + echo 'BEGIN {Node = Node ? Node : "$(shell hostname)"}' >> $@ && \ + echo 'BEGIN {Module = "$(notdir $@)"}' >> $@ && \ + cat $^ >> $@ && \ chmod +x $@ endef @@ -46,42 +47,49 @@ clean: bin/khatus_bar: \ src/awk/exe/bar.awk \ src/awk/lib/cache.awk \ + src/awk/lib/str.awk \ src/awk/lib/msg_in.awk \ src/awk/lib/msg_out.awk \ - src/awk/lib/util.awk + src/awk/lib/num.awk $(BUILD_AWK_EXE) bin/khatus_actuate_alert_to_notify_send: \ src/awk/exe/actuate_alert_to_notify_send.awk \ + src/awk/lib/str.awk \ src/awk/lib/msg_in.awk $(BUILD_AWK_EXE) bin/khatus_actuate_device_add_to_automount: \ src/awk/exe/actuate_device_add_to_automount.awk \ + src/awk/lib/str.awk \ src/awk/lib/msg_in.awk \ src/awk/lib/msg_out.awk $(BUILD_AWK_EXE) bin/khatus_actuate_status_bar_to_xsetroot_name: \ src/awk/exe/actuate_status_bar_to_xsetroot_name.awk \ + src/awk/lib/str.awk \ src/awk/lib/msg_in.awk $(BUILD_AWK_EXE) bin/khatus_monitor_devices: \ src/awk/exe/monitor_devices.awk \ + src/awk/lib/str.awk \ src/awk/lib/msg_in.awk \ src/awk/lib/msg_out.awk $(BUILD_AWK_EXE) bin/khatus_monitor_energy: \ src/awk/exe/monitor_energy.awk \ + src/awk/lib/str.awk \ src/awk/lib/msg_in.awk \ src/awk/lib/msg_out.awk \ - src/awk/lib/util.awk + src/awk/lib/num.awk $(BUILD_AWK_EXE) bin/khatus_monitor_errors: \ src/awk/exe/monitor_errors.awk \ + src/awk/lib/str.awk \ src/awk/lib/msg_in.awk \ src/awk/lib/msg_out.awk $(BUILD_AWK_EXE) @@ -124,7 +132,7 @@ bin/khatus_parse_loadavg_file: \ bin/khatus_parse_metar_d_output: \ src/awk/exe/parse_metar_d_output.awk \ src/awk/lib/msg_out.awk \ - src/awk/lib/util.awk + src/awk/lib/str.awk $(BUILD_AWK_EXE) bin/khatus_parse_mpd_status_currentsong: \ diff --git a/README.md b/README.md index 834d6ab..c02c1f8 100644 --- a/README.md +++ b/README.md @@ -258,7 +258,7 @@ Idea grab bag - report history and trends on when and how-often each device/category is plugged-in, how-long it stays plaugged-in, etc. - daemonize `khatus`, so we don't have to re-launch `X11` to re-launch `khatus` -- interoperate with other khatus instances +- interoperate with other khatus nodes - prefix machine ID to each data source (What should that ID be? Hostname? Pub key?) - fetch remote data and process locally diff --git a/bin/khatus b/bin/khatus index 747fd32..ec8270f 100755 --- a/bin/khatus +++ b/bin/khatus @@ -36,18 +36,18 @@ run_producer() { 2> >( while read line do - echo "ERROR${MSG_FS}${executable_name}${MSG_FS}$line" > "$pipe" + echo "${NODE}${MSG_FS}${executable_name}${MSG_FS}error${MSG_FS}$line" > "$pipe" done \ ) \ | while read line do - echo "OK${MSG_FS}${executable_name}${MSG_FS}$line" > "$pipe" + echo "${NODE}${MSG_FS}${executable_name}${MSG_FS}data${MSG_FS}$line" > "$pipe" done cmd_exit_code=${PIPESTATUS[0]} if [ "$cmd_exit_code" -ne 0 ] then echo - "ERROR${MSG_FS}${executable_name}${MSG_FS}NON_ZERO_EXIT_CODE${MSG_FS}$cmd_exit_code" \ + "${NODE}${MSG_FS}${executable_name}${MSG_FS}error${MSG_FS}NON_ZERO_EXIT_CODE${MSG_FS}$cmd_exit_code" \ > "$pipe" fi } @@ -87,6 +87,7 @@ fork_poller() { main() { declare -A opts=( + ["--node"]=$(hostname) ["--dir_bin"]="$HOME/bin" ["--dir_perf_logs"]='' ["--file_pipe"]=$(mktemp) @@ -160,6 +161,8 @@ main() { echo '----------------------------------------------' ) >&2 + NODE="${opts['--node']}" + screen_brightness_device_path='/sys/class/backlight' screen_brightness_device_path+="/${opts['--screen_brightness_device_name']}" diff --git a/src/awk/exe/actuate_alert_to_notify_send.awk b/src/awk/exe/actuate_alert_to_notify_send.awk index 2cebbff..a49a723 100755 --- a/src/awk/exe/actuate_alert_to_notify_send.awk +++ b/src/awk/exe/actuate_alert_to_notify_send.awk @@ -6,31 +6,22 @@ BEGIN { Display = Display ? Display : ":0" } -$1 == "OK" && \ -$3 ~ /^alert/ { - src = $2 - key = $3 - split(key, key_parts, Kfs) - priority = key_parts[2] - subject = key_parts[3] - # Not just using $6 for body - because body might contain a character - # identical to FS - len_line = length($0) - len_head = length($1 FS $2 FS $3 FS $4 FS $5 FS) - len_body = len_line - len_head - body = substr($0, len_head + 1, len_body) +$3 == "alert" { + delete msg + msg_in_parse(msg, $0) + body = msg["body"] sep = body ? "\n" : "" - body = body sep "--" src - urgency = priority + body = body sep "--" msg["node"] ":" msg["module"] + urgency = msg["priority"] sub("hi" , "critical", urgency) sub("med", "normal" , urgency) cmd = \ sprintf(\ "DISPLAY=%s notify-send -u %s %s \" %s\"", - Display, urgency, subject, body \ + Display, urgency, msg["subject"], body \ ) system(cmd) next diff --git a/src/awk/exe/actuate_device_add_to_automount.awk b/src/awk/exe/actuate_device_add_to_automount.awk index d1a56aa..9510720 100755 --- a/src/awk/exe/actuate_device_add_to_automount.awk +++ b/src/awk/exe/actuate_device_add_to_automount.awk @@ -3,11 +3,12 @@ BEGIN { Execute_On_Mount = Execute_On_Mount ? Execute_On_Mount : "" } -$1 == "OK" && \ +$1 == Node && \ $2 == "khatus_sensor_devices" && \ -$3 == "add" && \ -$4 ~ /[0-9]$/ { - mount_device($4) +$3 == "data" && \ +$4 == "add" && \ +$5 ~ /[0-9]$/ { + mount_device($5) } function mount_device(path, cmd, line, lines, line_count, status, i, @@ -28,15 +29,15 @@ function mount_device(path, cmd, line, lines, line_count, status, i, path_mnt=line sub("^Mounted " path_dev " at ", "", path_mnt) sub("\.$", "", path_mnt) - msg_out_ok_alert("low", "successfully-mounted", path_dev " to " path_mnt) + msg_out_alert_low("successfully-mounted", path_dev " to " path_mnt) if (Execute_On_Mount) { system(Execute_On_Mount " '" path_mnt "'") } } else { - msg_out_ok_alert("hi", "unexpected-success-line", line) + msg_out_alert_hi("unexpected-success-line", line) } } } else { - msg_out_ok_alert("hi", "failed-to-mount-device", path) + msg_out_alert_hi("failed-to-mount-device", path) } } diff --git a/src/awk/exe/actuate_status_bar_to_xsetroot_name.awk b/src/awk/exe/actuate_status_bar_to_xsetroot_name.awk index b97538b..6f87344 100755 --- a/src/awk/exe/actuate_status_bar_to_xsetroot_name.awk +++ b/src/awk/exe/actuate_status_bar_to_xsetroot_name.awk @@ -1,12 +1,8 @@ -$1 == "OK" && \ +$1 == Node && \ $2 == "khatus_bar" && \ $3 == "status_bar" { - # Not just using $4 for val - because val might contain a character - # identical to FS - len_line = length($0) - len_head = length($1 FS $2 FS $3 FS) - len_val = len_line - len_head - val = substr($0, len_head + 1, len_val) - system("xsetroot -name \"" val "\"") + delete msg + msg_in_parse(msg, $0) + system("xsetroot -name \"" msg["status_bar"] "\"") next } diff --git a/src/awk/exe/bar.awk b/src/awk/exe/bar.awk index 9be1adb..43126ff 100755 --- a/src/awk/exe/bar.awk +++ b/src/awk/exe/bar.awk @@ -10,18 +10,20 @@ # ----------------------------------------------------------------------------- # Input # ----------------------------------------------------------------------------- -$1 == "OK" { - cache_update() +$3 == "data" { + delete msg + msg_in_parse(msg, $0) + cache_update(msg["node"], msg["module"], msg["key"], msg["val"]) } -$1 == "OK" && \ -$2 == "khatus_sensor_datetime" { +$1 == Node && \ +$2 == "khatus_sensor_datetime" && \ +$3 == "data" { # Code for bar_make_status is expected to be passed as an # additional source file, using -f flag. - msg_out_ok("status_bar", bar_make_status()) + msg_out_status_bar(bar_make_status()) } - # ----------------------------------------------------------------------------- # Energy # ----------------------------------------------------------------------------- @@ -54,7 +56,7 @@ function bar_make_status_mem_percent( total, used, percent, percent_str) { !used["is_expired"] && \ total["value"] \ ) { - percent = util_round((used["value"] / total["value"]) * 100) + percent = num_round((used["value"] / total["value"]) * 100) percent_str = sprintf("%d", percent) } else { percent_str = "__" @@ -198,7 +200,7 @@ function bar_make_status_volume_pulseaudio_sink(sink, mu, vl, vr, show) { if (mu["value"] == "yes") {show = "X"} else if (mu["value"] == "no") {show = vl["value"] " " vr["value"]} else { - msg_out_error(\ + msg_out_log_error(\ "bar_make_status_volume_pulseaudio_sink: " sink ". ", \ "Unexpected value for 'mute' field: " mu["value"] \ ) @@ -221,7 +223,7 @@ function bar_make_status_mpd( state, status) { } else if (state["value"] == "stop") { status = bar_make_status_mpd_state_known("⬛") } else { - msg_out_error(\ + msg_out_log_error(\ "bar_make_status_mpd", \ "Unexpected value for 'state' field: " state["value"] \ ) diff --git a/src/awk/exe/monitor_devices.awk b/src/awk/exe/monitor_devices.awk index d43f67c..efb7b79 100755 --- a/src/awk/exe/monitor_devices.awk +++ b/src/awk/exe/monitor_devices.awk @@ -1,5 +1,10 @@ -$1 == "OK" && \ -$2 == "khatus_sensor_devices" \ +$2 == "khatus_sensor_devices" && \ +$3 == "data" \ { - msg_out_ok_alert("low", "BlockDeviceEvent", $3 " " $4) + delete msg + msg_in_parse(msg, $0) + msg_out_alert_low( \ + "BlockDeviceEvent", + msg["key"] " " msg["val"] " on " msg["node"]\ + ) } diff --git a/src/awk/exe/monitor_energy.awk b/src/awk/exe/monitor_energy.awk index 0d3f339..518d687 100755 --- a/src/awk/exe/monitor_energy.awk +++ b/src/awk/exe/monitor_energy.awk @@ -8,37 +8,40 @@ BEGIN { bat_alert_spec[5] = "hi|Energy_CRITICALLY_Low|CHARGE NOW!!! GO GO GO!!!" } -$1 == "OK" && \ +$1 == Node && \ $2 == "khatus_sensor_energy" && \ -$3 == "line_power" { +$3 == "data" && \ +$4 == "line_power" { line_power_prev = line_power_curr - line_power_curr = $4 + line_power_curr = $5 if (line_power_curr == "no" && line_power_prev != "no") { - msg_out_ok_alert("low", "PowerUnplugged", "") + msg_out_alert_low("PowerUnplugged", "") } } -$1 == "OK" && \ +$1 == Node && \ $2 == "khatus_sensor_energy" && \ -$3 == "battery_state" { +$3 == "data" && \ +$4 == "battery_state" { battery_state_prev = battery_state_curr - battery_state_curr = $4 + battery_state_curr = $5 } -$1 == "OK" && \ +$1 == Node && \ $2 == "khatus_sensor_energy" && \ -$3 == "battery_percentage" { +$3 == "data" && \ +$4 == "battery_percentage" { # TODO: Re-think the spec - can't rely on order of keys - battery_percentage = util_ensure_numeric($4) + battery_percentage = num_ensure_numeric($5) if (battery_state_curr == "discharging") { for (threshold in bat_alert_spec) { - threshold = util_ensure_numeric(threshold) + threshold = num_ensure_numeric(threshold) if (battery_percentage <= threshold && !_alerted[threshold]) { - split(bat_alert_spec[threshold], msg, "|") - priority = msg[1] - subject = msg[2] - body = sprintf("%d%% %s", battery_percentage, msg[3]) - msg_out_ok_alert(priority, subject, body) + split(bat_alert_spec[threshold], alert, "|") + priority = alert[1] + subject = alert[2] + body = sprintf("%d%% %s", battery_percentage, alert[3]) + msg_out_alert(priority, subject, body) _alerted[threshold]++ } } diff --git a/src/awk/exe/monitor_errors.awk b/src/awk/exe/monitor_errors.awk index 13130cc..36fd534 100755 --- a/src/awk/exe/monitor_errors.awk +++ b/src/awk/exe/monitor_errors.awk @@ -1,10 +1,6 @@ -/^ERROR/ { - src = $2 - # Not just using $3 for body - because body might contain a character - # identical to FS - len_line = length($0) - len_head = length($1 FS $2 FS) - len_body = len_line - len_head - body = substr($0, len_head + 1, len_body) - msg_out_ok_alert("hi", "ERROR_IN_" src, body) +$3 == "error" { + delete msg + msg_in_parse(msg, $0) + subject = "ERROR_IN_" msg["node"] ":" msg["module"] + msg_out_alert_hi(subject, msg["line"]) } diff --git a/src/awk/exe/parse_metar_d_output.awk b/src/awk/exe/parse_metar_d_output.awk index fa354da..82d2e29 100755 --- a/src/awk/exe/parse_metar_d_output.awk +++ b/src/awk/exe/parse_metar_d_output.awk @@ -8,15 +8,15 @@ /[A-z][a-z]+ *: / { split($0, line, ":") - key = util_strip(line[1]) - val = util_strip(line[2]) + key = str_strip(line[1]) + val = str_strip(line[2]) values[NR] = val first[key] = NR last[key] = NR } /^ +/ { - values[NR] = util_strip($0) + values[NR] = str_strip($0) last[key] = NR } diff --git a/src/awk/lib/cache.awk b/src/awk/lib/cache.awk index fbdd122..a2dd879 100755 --- a/src/awk/lib/cache.awk +++ b/src/awk/lib/cache.awk @@ -31,86 +31,79 @@ BEGIN { _scale["khatus_sensor_net_addr_io", "bytes_read" ] = 1 / _bytes_per_mb } -function cache_update( src, key, val, len_line, len_head, len_val, time) { - src = $2 - key = $3 - # Not just using $4 for val - because an unstructured value (like name of a - # song) might contain a character identical to FS - len_line = length($0) - len_head = length($1 FS $2 FS $3 FS) - len_val = len_line - len_head - val = substr($0, len_head + 1, len_val) - val = cache_maybe_total_to_diff(src, key, val) - val = cache_maybe_scale(src, key, val) - _cache[src, key] = val +function cache_update(node, module, key, val, time) { + # TODO: Use node value + val = cache_maybe_total_to_diff(module, key, val) + val = cache_maybe_scale(module, key, val) + _cache[module, key] = val time = cache_get_time() - _cache_mtime[src, key] = time + _cache_mtime[module, key] = time if (time % GC_Interval == 0) { cache_gc() } } -function cache_get(result, src, key, ttl, time, age, is_expired) { +function cache_get(result, module, key, ttl, time, age, is_expired) { time = cache_get_time() - _cache_atime[src, key] = time - age = time - _cache_mtime[src, key] + _cache_atime[module, key] = time + age = time - _cache_mtime[module, key] result["is_expired"] = ttl && age > ttl # ttl = 0 => forever - result["value"] = _cache[src, key] + result["value"] = _cache[module, key] } function cache_res_fmt_or_def(result, format, default) { return result["is_expired"] ? default : sprintf(format, result["value"]) } -function cache_get_fmt_def(src, key, ttl, format, default, result) { +function cache_get_fmt_def(module, key, ttl, format, default, result) { default = default ? default : "--" - cache_get(result, src, key, ttl) + cache_get(result, module, key, ttl) return cache_res_fmt_or_def(result, format, default) } -function cache_get_time( src, key, time) { - src = "khatus_sensor_datetime" +function cache_get_time( module, key, time) { + module = "khatus_sensor_datetime" key = "epoch" - time = _cache[src, key] - _cache_atime[src, key] = time + time = _cache[module, key] + _cache_atime[module, key] = time return time } -function cache_gc( src_and_key, parts, src, key, unused_for) { - for (src_and_key in _cache) { - split(src_and_key, parts, SUBSEP) - src = parts[1] +function cache_gc( module_and_key, parts, module, key, unused_for) { + for (module_and_key in _cache) { + split(module_and_key, parts, SUBSEP) + module = parts[1] key = parts[2] - val = _cache[src, key] - unused_for = cache_get_time() - _cache_atime[src, key] + val = _cache[module, key] + unused_for = cache_get_time() - _cache_atime[module, key] if (unused_for > GC_Interval) { - msg_out_info(\ + msg_out_log_info(\ "cache_gc", sprintf(\ - "Deleting unused data SRC=%s KEY=%s VAL=%s", - src, key, val\ + "Deleting unused data MODULE=%s KEY=%s VAL=%s", + module, key, val\ ) \ ) - delete _cache[src, key] + delete _cache[module, key] } } } -function cache_maybe_total_to_diff(src, key, val, key_parts) { +function cache_maybe_total_to_diff(module, key, val, key_parts) { split(key, key_parts, Kfs) - if (_total_to_diff[src, key_parts[1]]) { - _prev[src, key] = _curr[src, key] - _curr[src, key] = val - return (_curr[src, key] - _prev[src, key]) + if (_total_to_diff[module, key_parts[1]]) { + _prev[module, key] = _curr[module, key] + _curr[module, key] = val + return (_curr[module, key] - _prev[module, key]) } else { return val } } -function cache_maybe_scale(src, key, val, key_parts) { +function cache_maybe_scale(module, key, val, key_parts) { split(key, key_parts, Kfs) - if ((src SUBSEP key_parts[1]) in _scale) { - return val * _scale[src, key_parts[1]] + if ((module SUBSEP key_parts[1]) in _scale) { + return val * _scale[module, key_parts[1]] } else { return val } diff --git a/src/awk/lib/msg_in.awk b/src/awk/lib/msg_in.awk index d1f62d6..cda89e8 100755 --- a/src/awk/lib/msg_in.awk +++ b/src/awk/lib/msg_in.awk @@ -1,4 +1,38 @@ BEGIN { - FS = Msg_fs ? Msg_fs : "|" - Kfs = Key_fs ? Key_fs : ":" + FS1 = "|" # Fiels separator, level 1 (record to fields) + FS2 = ":" # Fiels separator, level 2 (field to subfields) + + FS = FS1 + Kfs = FS2 +} + +function msg_in_parse(msg, line, fields, type) { + split(line, fields, FS1) + msg["node"] = fields[1] + msg["module"] = fields[2] + type = fields[3] + msg["type"] = type + + if (type == "data") { + msg["key"] = fields[4] + msg["val"] = str_tail(str_join(fields, 1, 4, FS1) FS1, line) + } else if (type == "error") { + msg["line"] = str_tail(str_join(fields, 1, 3, FS1) FS1, line) + } else if (type == "alert") { + msg["priority"] = fields[4] + msg["subject"] = fields[5] + msg["body"] = str_tail(str_join(fields, 1, 5, FS1) FS1, line) + } else if (type == "log") { + msg["location"] = fields[4] + msg["level"] = fields[5] + msg["msg"] = str_tail(str_join(fields, 1, 5, FS1) FS1, line) + } else if (type == "status_bar") { + msg["status_bar"] = str_tail(str_join(fields, 1, 3, FS1) FS1, line) + } else { + msg_out_log_error(\ + "msg_in_parse", + "Unexpected msg type: " type " in given input line: " line \ + ) + exit 1 + } } diff --git a/src/awk/lib/msg_out.awk b/src/awk/lib/msg_out.awk index 6f5f945..24393c6 100755 --- a/src/awk/lib/msg_out.awk +++ b/src/awk/lib/msg_out.awk @@ -1,29 +1,58 @@ BEGIN { - OFS = Msg_fs ? Msg_fs : "|" - Kfs = Key_fs ? Key_fs : ":" + FS1 = "|" # Fiels separator, level 1 (record to fields) + FS2 = ":" # Fiels separator, level 2 (field to subfields + + OFS = FS1 + Kfs = FS2 +} + +# ----------------------------------------------------------------------------- +# alert +# ----------------------------------------------------------------------------- +function msg_out_alert_low(subject, body) { + msg_out_alert("low", subject, body) +} + +function msg_out_alert_med(subject, body) { + msg_out_alert("med", subject, body) } -function msg_out_ok_alert(priority, subject, body, key, val) { +function msg_out_alert_hi(subject, body) { + msg_out_alert("hi", subject, body) +} + +function msg_out_alert(priority, subject, body) { # priority : "low" | "med" | "hi" # subject : string without spaces # body : anything - key = "alert" Kfs priority Kfs subject - val = body - msg_out_ok(key, val) + print(Node, Module, "alert", priority, subject, body) +} + +# ----------------------------------------------------------------------------- +# log +# ----------------------------------------------------------------------------- +function msg_out_log_info(location, msg) { + msg_out_log("info", location, msg) } -function msg_out_ok(key, val) { - msg_out("OK", key, val, "/dev/stdout") +function msg_out_log_error(location, msg) { + msg_out_log("error", location, msg) } -function msg_out_info(location, msg) { - msg_out("INFO", location, msg, "/dev/stderr") +function msg_out_log(level, location, msg) { + print(Node, Module, "log", location, level, msg) > "/dev/stderr" } -function msg_out_error(location, msg) { - msg_out("ERROR", location, msg, "/dev/stderr") +# ----------------------------------------------------------------------------- +# status_bar +# ----------------------------------------------------------------------------- +function msg_out_status_bar(bar) { + print(Node, Module, "status_bar", bar) } -function msg_out(status, key, val, channel) { - print(status, Module, key, val) > channel +# ----------------------------------------------------------------------------- +# data +# ----------------------------------------------------------------------------- +function msg_out_data(key, val) { + print(Node, Module, "data", key, val) } diff --git a/src/awk/lib/util.awk b/src/awk/lib/num.awk similarity index 84% rename from src/awk/lib/util.awk rename to src/awk/lib/num.awk index b7a92e6..4c22f9d 100755 --- a/src/awk/lib/util.awk +++ b/src/awk/lib/num.awk @@ -1,20 +1,14 @@ -function util_strip(s) { - sub("^ *", "", s) - sub(" *$", "", s) - return s -} - -function util_round(n) { +function num_round(n) { return int(n + 0.5) } -function util_ensure_numeric(n) { +function num_ensure_numeric(n) { return n + 0 } -#------------------------------------ -# Why do we need util_ensure_numeric? -#------------------------------------ +#----------------------------------- +# Why do we need num_ensure_numeric? +#----------------------------------- # awk appears to be guessing the type of an inputted scalar based on usage, so # if we read-in a number, but did not use it in any numeric operations, but did # use as a string (even in just a format string!) - it will be treated as a diff --git a/src/awk/lib/str.awk b/src/awk/lib/str.awk new file mode 100755 index 0000000..422ff66 --- /dev/null +++ b/src/awk/lib/str.awk @@ -0,0 +1,23 @@ +function str_join(array, from, to, sep_given, str, sep, i) { + str = "" + sep = "" + for (i=from; i<=to; i++) { + str = str sep array[i] + sep = sep_given + } + return str +} + +function str_tail(head, full, tail, len_tail, len_head, len_full) { + len_full = length(full) + len_head = length(head) + len_tail = len_full - len_head + tail = substr(full, len_head + 1, len_tail) + return tail +} + +function str_strip(s) { + sub("^ *", "", s) + sub(" *$", "", s) + return s +}