disp bug fix
This commit is contained in:
@@ -96,7 +96,7 @@ A bar-style prompt showing current time, execution time of the last command
|
||||
| `clean` | filefct | Erase backup files in given directories, optionally recursive |
|
||||
| `conf_dump` | conf | Display the profile configuration file; `-s/--section NAME` restricts output to one section; `-a/--all` also lists commented-out keys (documented defaults, skipped when the key is set); an optional pattern argument filters keys by substring match; honours `NO_COLOR` |
|
||||
| `conf_save` | conf | Save or update a key=value pair in a configuration section: `conf_save <section> <key> <value>`; replaces an existing key in place or appends it to the section (creating the section if absent); works through a symlinked file and keeps its permissions; the value must be single-line and the running shell is not updated (applies at next load) |
|
||||
| `disp` | disp | Display formatted info / warning / error / debug messages; long messages are word-wrapped and continuation lines are indented to align with the message text |
|
||||
| `disp` | disp | Display formatted info / warning / error / debug messages; long messages are word-wrapped and continuation lines are indented to align with the message text; honours `NO_COLOR`, `QUIET=1` (hides info messages) and `DEBUG` > 1 (shows debug messages); warnings, errors and debug go to stderr |
|
||||
| `dwl` | net | Download a URL using curl, wget, or fetch transparently; supports `-t <seconds>` / `--timeout <seconds>` to cap the transfer time |
|
||||
| `expandlist` | filefct | Expand glob expressions into a quoted, separated list |
|
||||
| `fake_compile` | fun | Simulate a long compilation process with random warnings and errors |
|
||||
@@ -121,7 +121,7 @@ A bar-style prompt showing current time, execution time of the last command
|
||||
| `ku` | processes | Kill all processes owned by the given user name or ID |
|
||||
| `matrix` | rain | Console screensaver with Matrix-style digital rain (binary, kana, ascii charset) |
|
||||
| `mcd` | filefct | Create a directory and immediately move into it |
|
||||
| `mdcat` | disp | Render Markdown files in terminal with colors, inline formatting, and framed code blocks |
|
||||
| `mdcat` | disp | Render Markdown files (or stdin) in terminal with colors, inline formatting, tables and framed code blocks; honours `NO_COLOR` |
|
||||
| `meteo` | info | Display weather forecast for the configured or given city |
|
||||
| `myextip` | net | Get information about your public IP address |
|
||||
| `get_pkgmgr` | packages | Detect the active package manager of the running distribution (`apt`, `dnf`, `yum`, `zypper`, `pacman`, `apk`, `portage`, `xbps`, `nix`) |
|
||||
|
||||
@@ -46,6 +46,13 @@ Versions follow `MAJOR.MINOR.PATCH-REVISION_STAGE_N` (e.g. `3.99.1-4_rc_1`).
|
||||
- All modules: counters updated with `((x++))`-style commands (which return a
|
||||
non-zero status when the value was 0) now use plain assignments, so they no
|
||||
longer trigger the ERR trace or break `set -e`.
|
||||
- `disp`: a message hidden by `QUIET`/`DEBUG` no longer returns a non-zero
|
||||
status; no reset escape sequence under `NO_COLOR`; debug messages go to
|
||||
stderr; works under `set -u`; faster wrapping.
|
||||
- `mdcat`: tables inside fenced code blocks are shown as code; code spans and
|
||||
`\_`-style escapes are protected from emphasis/link parsing; `NO_COLOR` is
|
||||
honoured in tables and headings; fewer subprocesses when rendering tables;
|
||||
newlines in arguments no longer break option parsing.
|
||||
- `compress.sh`: option flags and loop variables no longer leak into the
|
||||
interactive shell.
|
||||
|
||||
|
||||
+129
-72
@@ -124,10 +124,23 @@ export -f set_colors
|
||||
# Display a message
|
||||
# Usage: disp <type> <message>
|
||||
# Types:
|
||||
# I : info (green)
|
||||
# W : warning (yellow)
|
||||
# E : error (red)
|
||||
# D : debug (cyan)
|
||||
# I : info (green), stdout
|
||||
# W : warning (yellow), stderr
|
||||
# E : error (red), stderr
|
||||
# D : debug (cyan), stderr, shown only when DEBUG is an integer > 1
|
||||
# Without a recognised type (case-insensitive), the arguments are the message
|
||||
# itself, printed unprefixed on stdout like an info.
|
||||
#
|
||||
# Environment:
|
||||
# NO_COLOR non-empty: no colour or reset sequence is emitted
|
||||
# QUIET=1 hides info and unprefixed messages (warnings/errors remain)
|
||||
# COLUMNS terminal width used for wrapping (tput cols otherwise, 80 fallback)
|
||||
#
|
||||
# The message is wrapped to the terminal width on spaces, ANSI sequences count
|
||||
# for no width, and continuation lines are aligned after the prefix. Backslash
|
||||
# escapes (\t, \e, ...) are interpreted when printing (printf %b), but wrapping
|
||||
# is computed before that, so a literal "\t" counts as two characters.
|
||||
# A message hidden by QUIET/DEBUG still returns 0.
|
||||
disp()
|
||||
{
|
||||
local _disp_print_wrapped
|
||||
@@ -167,13 +180,18 @@ disp()
|
||||
local wrapped="" line="" line_vis=0 last_space=-1
|
||||
local _i=0 _n=${#message}
|
||||
while (( _i < _n )); do
|
||||
local _sub=${message:_i}
|
||||
if [[ $_sub =~ ^($ansi_re) ]]; then
|
||||
line+=${BASH_REMATCH[0]}
|
||||
_i=$(( _i + ${#BASH_REMATCH[0]} ))
|
||||
continue
|
||||
fi
|
||||
local _c=${message:_i:1}
|
||||
# Only a real ESC or a literal backslash can start an ANSI sequence;
|
||||
# testing this first avoids copying the message tail (and running
|
||||
# the regex) for every ordinary character.
|
||||
if [[ $_c == $'\e' || $_c == '\' ]]; then
|
||||
local _sub=${message:_i}
|
||||
if [[ $_sub =~ ^($ansi_re) ]]; then
|
||||
line+=${BASH_REMATCH[0]}
|
||||
_i=$(( _i + ${#BASH_REMATCH[0]} ))
|
||||
continue
|
||||
fi
|
||||
fi
|
||||
_i=$(( _i + 1 ))
|
||||
line+=$_c
|
||||
(( ++line_vis ))
|
||||
@@ -196,38 +214,37 @@ disp()
|
||||
done
|
||||
wrapped+=$line
|
||||
|
||||
# RESETCOL closes any colour left open by the message. It is skipped
|
||||
# under NO_COLOR so that no escape sequence at all is emitted.
|
||||
local reset="${RESETCOL:-}"
|
||||
[[ -n ${NO_COLOR:-} ]] && reset=""
|
||||
|
||||
# >&"$target_fd" sends the output to stdout (1) or stderr (2). The
|
||||
# first line carries the prefix, continuation lines are indented to
|
||||
# line up with the message body.
|
||||
local first_line=1
|
||||
local line
|
||||
while IFS= read -r line || [[ -n "$line" ]]; do
|
||||
if (( first_line )); then
|
||||
if [[ -n "$prefix" ]]; then
|
||||
if [[ "$target_fd" -eq 2 ]]; then
|
||||
printf "%b\n" "${prefix} ${line}${RESETCOL}" >&2
|
||||
else
|
||||
printf "%b\n" "${prefix} ${line}${RESETCOL}"
|
||||
fi
|
||||
printf "%b\n" "${prefix} ${line}${reset}" >&"$target_fd"
|
||||
else
|
||||
if [[ "$target_fd" -eq 2 ]]; then
|
||||
printf "%b\n" "${line}${RESETCOL}" >&2
|
||||
else
|
||||
printf "%b\n" "${line}${RESETCOL}"
|
||||
fi
|
||||
printf "%b\n" "${line}${reset}" >&"$target_fd"
|
||||
fi
|
||||
first_line=0
|
||||
else
|
||||
if [[ "$target_fd" -eq 2 ]]; then
|
||||
printf "%*s%b\n" "$indent_len" "" "${line}${RESETCOL}" >&2
|
||||
else
|
||||
printf "%*s%b\n" "$indent_len" "" "${line}${RESETCOL}"
|
||||
fi
|
||||
printf "%*s%b\n" "$indent_len" "" "${line}${reset}" >&"$target_fd"
|
||||
fi
|
||||
done <<< "$wrapped"
|
||||
}
|
||||
|
||||
# Handle NO_COLOR: disable colors if set
|
||||
local color_enabled=1
|
||||
[[ -n $NO_COLOR ]] && color_enabled=0
|
||||
[[ -n ${NO_COLOR:-} ]] && color_enabled=0
|
||||
|
||||
# Each branch ends with an "if" (never a bare "[[ ]] && cmd") so that a
|
||||
# message hidden by QUIET/DEBUG still returns 0: a non-zero status would
|
||||
# trip "set -e", ERR traps, and "disp ... && next" chains.
|
||||
case ${1^^} in
|
||||
"I")
|
||||
if [[ $color_enabled -eq 1 ]]; then
|
||||
@@ -236,8 +253,9 @@ disp()
|
||||
local heads="[ info ]"
|
||||
fi
|
||||
shift
|
||||
[[ -z $QUIET || $QUIET -ne 1 ]] && \
|
||||
if [[ ${QUIET:-} != 1 ]]; then
|
||||
_disp_print_wrapped "$heads" 1 "$*"
|
||||
fi
|
||||
;;
|
||||
"W")
|
||||
if [[ $color_enabled -eq 1 ]]; then
|
||||
@@ -264,12 +282,17 @@ disp()
|
||||
local heads="[ debug ]"
|
||||
fi
|
||||
shift
|
||||
[[ -n $DEBUG && $DEBUG -gt 1 ]] && \
|
||||
_disp_print_wrapped "$heads" 1 "$*"
|
||||
# Shown only when DEBUG is an integer greater than 1; sent to stderr
|
||||
# like the other diagnostics, so it never pollutes captured output.
|
||||
if [[ ${DEBUG:-} =~ ^[0-9]+$ && $DEBUG -gt 1 ]]; then
|
||||
_disp_print_wrapped "$heads" 2 "$*"
|
||||
fi
|
||||
;;
|
||||
* )
|
||||
[[ -z $QUIET || $QUIET -ne 1 ]] && \
|
||||
# No (known) type: the whole argument list is the message, as for info
|
||||
if [[ ${QUIET:-} != 1 ]]; then
|
||||
_disp_print_wrapped "" 1 "$*"
|
||||
fi
|
||||
;;
|
||||
esac
|
||||
}
|
||||
@@ -298,8 +321,29 @@ mdcat()
|
||||
style_off="$RESETCOL"
|
||||
fi
|
||||
|
||||
# Apply inline transforms in a safe order: code, links, then emphasis.
|
||||
# This prevents emphasis parsing inside code spans or link URLs.
|
||||
# Inline transforms. Code spans and backslash-escaped punctuation
|
||||
# (\<, \>, \_, \*, ...) are lifted out first and replaced by numbered
|
||||
# placeholders (\x01N\x02 and \x03N\x04), so that nothing inside them is
|
||||
# reinterpreted (links, emphasis); they are put back, code spans styled,
|
||||
# at the very end. Then links, bare URLs and emphasis.
|
||||
local -a spans=() escs=()
|
||||
local ph_open=$'\x01' ph_close=$'\x02' esc_open=$'\x03' esc_close=$'\x04'
|
||||
while [[ "$text" =~ \`([^\`]+)\` ]]; do
|
||||
local match="${BASH_REMATCH[0]}"
|
||||
local before="${text%%"$match"*}"
|
||||
local after="${text#*"$match"}"
|
||||
spans+=("${BASH_REMATCH[1]}")
|
||||
text="${before}${ph_open}$(( ${#spans[@]} - 1 ))${ph_close}${after}"
|
||||
done
|
||||
while [[ "$text" =~ \\([[:punct:]]) ]]; do
|
||||
local match="${BASH_REMATCH[0]}"
|
||||
local before="${text%%"$match"*}"
|
||||
local after="${text#*"$match"}"
|
||||
escs+=("${BASH_REMATCH[1]}")
|
||||
text="${before}${esc_open}$(( ${#escs[@]} - 1 ))${esc_close}${after}"
|
||||
done
|
||||
|
||||
# Links: [label](url)
|
||||
while [[ "$text" =~ \[([^][]+)\]\(([^()]+)\) ]]; do
|
||||
local match="${BASH_REMATCH[0]}"
|
||||
local label="${BASH_REMATCH[1]}"
|
||||
@@ -333,14 +377,6 @@ mdcat()
|
||||
done
|
||||
fi
|
||||
|
||||
while [[ "$text" =~ \`([^\`]+)\` ]]; do
|
||||
local match="${BASH_REMATCH[0]}"
|
||||
local val="${BASH_REMATCH[1]}"
|
||||
local before="${text%%"$match"*}"
|
||||
local after="${text#*"$match"}"
|
||||
text="${before}${code_on}${val}${style_off}${after}"
|
||||
done
|
||||
|
||||
while [[ "$text" =~ \*\*([^*]+)\*\* ]]; do
|
||||
local match="${BASH_REMATCH[0]}"
|
||||
local val="${BASH_REMATCH[1]}"
|
||||
@@ -376,13 +412,14 @@ mdcat()
|
||||
text="${before}${pre}${italic_on}${val}${style_off}${post}${after}"
|
||||
done
|
||||
|
||||
# Unescape Markdown punctuation escapes, such as \<, \>, \_, and \*.
|
||||
while [[ "$text" =~ \\([[:punct:]]) ]]; do
|
||||
local match="${BASH_REMATCH[0]}"
|
||||
local val="${BASH_REMATCH[1]}"
|
||||
local before="${text%%"$match"*}"
|
||||
local after="${text#*"$match"}"
|
||||
text="${before}${val}${after}"
|
||||
# Put the styled code spans and the escaped characters back (see the
|
||||
# placeholders above).
|
||||
local idx
|
||||
for idx in "${!spans[@]}"; do
|
||||
text="${text/"${ph_open}${idx}${ph_close}"/"${code_on}${spans[idx]}${style_off}"}"
|
||||
done
|
||||
for idx in "${!escs[@]}"; do
|
||||
text="${text/"${esc_open}${idx}${esc_close}"/"${escs[idx]}"}"
|
||||
done
|
||||
|
||||
printf "%s\n" "$text"
|
||||
@@ -471,11 +508,16 @@ mdcat()
|
||||
_mdcat_print_table()
|
||||
{
|
||||
local _mdcat_parse_table_row
|
||||
# Split one table row into cells, joined with $sep (set by the caller,
|
||||
# reached through dynamic scoping). Cells are separated by '|' only; an
|
||||
# escaped "\|" is not supported. Trimming uses parameter expansion
|
||||
# (strip leading, then trailing, whitespace) to avoid forking sed.
|
||||
_mdcat_parse_table_row()
|
||||
{
|
||||
local input="$1"
|
||||
local line
|
||||
line=$(printf '%s' "$input" | sed -E 's/^[[:space:]]+//; s/[[:space:]]+$//')
|
||||
local input="$1" j
|
||||
local line="$input"
|
||||
line="${line#"${line%%[![:space:]]*}"}"
|
||||
line="${line%"${line##*[![:space:]]}"}"
|
||||
line="${line#|}"
|
||||
line="${line%|}"
|
||||
|
||||
@@ -490,7 +532,8 @@ mdcat()
|
||||
cell="$rest"
|
||||
rest=""
|
||||
fi
|
||||
cell=$(printf '%s' "$cell" | sed -E 's/^[[:space:]]+//; s/[[:space:]]+$//')
|
||||
cell="${cell#"${cell%%[![:space:]]*}"}"
|
||||
cell="${cell%"${cell##*[![:space:]]}"}"
|
||||
cells+=("$cell")
|
||||
[[ -z "$rest" ]] && break
|
||||
done
|
||||
@@ -530,7 +573,12 @@ mdcat()
|
||||
for ((j=0; j<${#row[@]}; ++j)); do
|
||||
local styled visible key="$orow:$j"
|
||||
styled=$(_mdcat_style_inline "${row[j]}")
|
||||
visible=$(printf '%b' "$styled" | sed -E 's/\x1B\[[0-9;]*[mK]//g')
|
||||
# Visible width: expand the literal \e sequences, then strip
|
||||
# every ANSI CSI sequence (no sed fork per cell).
|
||||
printf -v visible '%b' "$styled"
|
||||
while [[ $visible =~ $'\e'\[[0-9\;]*[A-Za-z] ]]; do
|
||||
visible=${visible/"${BASH_REMATCH[0]}"/}
|
||||
done
|
||||
cell_styled["$key"]="$styled"
|
||||
cell_vislen["$key"]=${#visible}
|
||||
[[ -z "${col_widths[j]}" ]] && col_widths[j]=0
|
||||
@@ -544,47 +592,54 @@ mdcat()
|
||||
[[ -z "${col_widths[j]}" ]] && col_widths[j]=0
|
||||
done
|
||||
|
||||
# Draw top border
|
||||
local border="+"
|
||||
# Colours are dropped under NO_COLOR, like everywhere else in mdcat.
|
||||
local c_frame="$IBlack" c_head="$BBlue" c_off="$RESETCOL" c_cell="$RESETCOL"
|
||||
if [[ -n ${NO_COLOR:-} ]]; then
|
||||
c_frame="" c_head="" c_off="" c_cell=""
|
||||
fi
|
||||
|
||||
# Draw top border: one run of dashes per column (cell width + 2 spaces)
|
||||
local border="+" seg
|
||||
for ((j=0; j<ncols; ++j)); do
|
||||
local w=$((col_widths[j]+2))
|
||||
border+="$(printf '%*s' "$w" "" | tr ' ' '-')+"
|
||||
printf -v seg '%*s' "$((col_widths[j] + 2))" ""
|
||||
border+="${seg// /-}+"
|
||||
done
|
||||
printf "%b\n" "$IBlack$border$RESETCOL"
|
||||
printf "%b\n" "$c_frame$border$c_off"
|
||||
|
||||
# Print header row (cached styled cells)
|
||||
printf "%b|" "$IBlack"
|
||||
printf "%b|" "$c_frame"
|
||||
for ((j=0; j<ncols; ++j)); do
|
||||
local hkey="0:$j"
|
||||
local styled_cell="${cell_styled[$hkey]:-}"
|
||||
local pad=$((col_widths[j] - ${cell_vislen[$hkey]:-0}))
|
||||
(( pad < 0 )) && pad=0
|
||||
printf " %b%b%*s%b |" "$BBlue" "$styled_cell" "$pad" "" "$IBlack"
|
||||
printf " %b%b%*s%b |" "$c_head" "$styled_cell" "$pad" "" "$c_frame"
|
||||
done
|
||||
printf "%b\n" "$RESETCOL"
|
||||
printf "%b\n" "$c_off"
|
||||
|
||||
# Header separator
|
||||
printf "%b|" "$IBlack"
|
||||
printf "%b|" "$c_frame"
|
||||
for ((j=0; j<ncols; ++j)); do
|
||||
printf " %s |" "$(printf '%*s' "${col_widths[j]}" "" | tr ' ' '-')"
|
||||
printf -v seg '%*s' "${col_widths[j]}" ""
|
||||
printf " %s |" "${seg// /-}"
|
||||
done
|
||||
printf "%b\n" "$RESETCOL"
|
||||
printf "%b\n" "$c_off"
|
||||
|
||||
# Print data rows (cached styled cells)
|
||||
for ((i=1; i<${#table_rows[@]}; ++i)); do
|
||||
printf "%b|" "$IBlack"
|
||||
printf "%b|" "$c_frame"
|
||||
for ((j=0; j<ncols; ++j)); do
|
||||
local key="$i:$j"
|
||||
local styled_cell="${cell_styled[$key]:-}"
|
||||
local pad=$((col_widths[j] - ${cell_vislen[$key]:-0}))
|
||||
(( pad < 0 )) && pad=0
|
||||
printf " %b%b%*s%b |" "$RESETCOL" "$styled_cell" "$pad" "" "$IBlack"
|
||||
printf " %b%b%*s%b |" "$c_cell" "$styled_cell" "$pad" "" "$c_frame"
|
||||
done
|
||||
printf "%b\n" "$RESETCOL"
|
||||
printf "%b\n" "$c_off"
|
||||
done
|
||||
|
||||
# Draw bottom border
|
||||
printf "%b\n" "$IBlack$border$RESETCOL"
|
||||
printf "%b\n" "$c_frame$border$c_off"
|
||||
}
|
||||
|
||||
local PARSED
|
||||
@@ -595,9 +650,8 @@ mdcat()
|
||||
return 1
|
||||
fi
|
||||
|
||||
local -a _pargs=()
|
||||
readarray -td '' _pargs < <(printf '%s' "$PARSED" | xargs printf '%s\0')
|
||||
set -- "${_pargs[@]}"
|
||||
# getopt emits a shell-quoted, normalised argument list ("--" ends options)
|
||||
eval set -- "$PARSED"
|
||||
while true; do
|
||||
case "$1" in
|
||||
-h|--help)
|
||||
@@ -664,7 +718,8 @@ mdcat()
|
||||
line="${raw%$'\r'}"
|
||||
|
||||
# Table detection: line with |, next line with | and ---
|
||||
if [[ $in_table -eq 0 && "$line" =~ ^[[:space:]]*\|.*\|[[:space:]]*$ ]]; then
|
||||
# (never inside a fenced code block, where a table is just sample text)
|
||||
if [[ $in_code -eq 0 && $in_table -eq 0 && "$line" =~ ^[[:space:]]*\|.*\|[[:space:]]*$ ]]; then
|
||||
local next="" next_ok=1
|
||||
IFS= read -r next || next_ok=0
|
||||
# Accept: | --- | --- | or |:---|---:| etc.
|
||||
@@ -722,7 +777,9 @@ mdcat()
|
||||
*) h_on="$BIWhite" ;;
|
||||
esac
|
||||
fi
|
||||
printf "%b%s%b\n" "$h_on" "$title" "$RESETCOL"
|
||||
local h_off="$RESETCOL"
|
||||
[[ -n $NO_COLOR ]] && h_off=""
|
||||
printf "%b%s%b\n" "$h_on" "$title" "$h_off"
|
||||
continue
|
||||
fi
|
||||
|
||||
|
||||
Reference in New Issue
Block a user