File: mapa.sh 1 #!/bin/sh 2 3 # The MIT License (MIT) 4 # 5 # Copyright (c) 2026 pacman64 6 # 7 # Permission is hereby granted, free of charge, to any person obtaining a copy 8 # of this software and associated documentation files (the "Software"), to deal 9 # in the Software without restriction, including without limitation the rights 10 # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 # copies of the Software, and to permit persons to whom the Software is 12 # furnished to do so, subject to the following conditions: 13 # 14 # The above copyright notice and this permission notice shall be included in 15 # all copies or substantial portions of the Software. 16 # 17 # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 23 # SOFTWARE. 24 25 26 # mapa [options...] [regular expressions...] 27 # 28 # 29 # MAtch PAragraphs only keeps multi-line paragraphs which match any of the 30 # extended-mode regular expressions given. When not given any regex, match 31 # all paragraphs by default. Input lines are always read from the standard 32 # input. 33 # 34 # Each paragraph is defined as 1 or more consecutive non-empty lines. Each 35 # paragraph is matched either as read, with line-feeds along the way, or 36 # with each inner line-feed replaced by a single space. 37 # 38 # The handy case-insensitive shortcut options may cause this tool to fail, 39 # if the main `awk` tool installed doesn't support the special `IGNORECASE` 40 # variable. 41 # 42 # The options are, available both in single and double-dash versions 43 # 44 # -h, -help show this help message 45 # -i, -ins match regexes case-insensitively 46 # -l, -links add a regex to match HTTP/HTTPS links case-insensitively 47 # -q, -quiet never emit output: exit code still succeeds/fails 48 49 50 links=0 51 avoid=0 52 buf=0 53 ins=0 54 quiet=0 55 56 while [ $# -gt 0 ]; do 57 case "$1" in 58 -a|--a|-avoid|--avoid|-v|--v) avoid=1; shift; continue ;; 59 60 -b|--b|-buffered|--buffered) buf=1; shift; continue ;; 61 62 -h|--h|-help|--help) 63 awk '/^# +mapa /, /^$/ { gsub(/^# ?/, ""); print }' "$0" 64 exit 0 65 ;; 66 67 -i|--i|-ins|--ins|-insensitive|--insensitive) ins=1; shift; continue ;; 68 69 -ia|-ai|-iv|-vi) ins=1; avoid=1; shift; continue ;; 70 71 -il|-li) ins=1; links=1; shift; continue ;; 72 73 -ilq|-iql|-liq|-lqi|-qil|-qli|-ils|-isl|-lis|-lsi|-sil|-sli) 74 ins=1; links=1; quiet=1; shift; continue 75 ;; 76 77 -ila|-ial|-lia|-lai|-ail|-ali|-ilv|-ivl|-liv|-lvi|-vil|-vli) 78 ins=1; links=1; avoid=1; shift; continue 79 ;; 80 81 -iq|-qi|-is|-si) ins=1; quiet=1; shift; continue ;; 82 83 -l|--l|-links|--links) links=1; shift; continue ;; 84 85 -lq|-ql|-ls|-sl) links=1; quiet=1; shift; continue ;; 86 87 -q|--q|-quiet|--quiet|-s|--s|-silent|--silent) 88 quiet=1; shift; continue 89 ;; 90 91 -) break ;; 92 93 --) shift; break ;; 94 95 -*) 96 printf "%s: unsupported option %s\n" "$0" "$1" >&2 97 exit 1 98 ;; 99 esac 100 101 break 102 done 103 104 if [ "${quiet}" -eq 1 ]; then 105 buf=1 106 fi 107 108 flush=0 109 if [ "${buf}" -eq 0 ] && { [ -p /dev/stdout ] || [ -t 1 ]; }; then 110 flush=1 111 fi 112 113 re='[Hh][Tt][Tt][Pp][Ss]?://[A-Za-z0-9+_.:%-]+(/[A-Za-z0-9+_.%/,#?&=-]*)*' 114 115 awk -v flush="${flush}" -v ci="${ins}" -v add_links="${links}" \ 116 -v links="${re}" -v quiet="${quiet}" -v avoid="${avoid}" ' 117 BEGIN { 118 if (ci && IGNORECASE == "") { 119 msg = "'"$0"': default AWK lacks case-insensitive regex-matching" 120 print(msg) > "/dev/stderr" 121 exit 125 122 } 123 if (ci) IGNORECASE = 1 124 125 for (i = 1; i < ARGC; i++) { 126 re[i] = ARGV[i] 127 delete ARGV[i] 128 } 129 130 n = ARGC - 1 131 if (add_links) re[++n] = links 132 if (n == 0) re[++n] = "[^\\r]" 133 if (quiet) flush = 0 134 135 FS = RS = "" 136 } 137 138 { 139 plain = $0 140 # ignore cursor-movers and style-changers 141 gsub(/\x1b\[[0-9;]*[A-Za-z]/, "", plain) 142 # ignore OSC sequences 143 gsub(/\x1b\][^\x1b]+\x1b\\/, "", plain) 144 145 flat = plain 146 gsub(/\r?\n/, " ", flat) 147 148 for (i = 1; i <= n; i++) { 149 expr = re[i] 150 if ((plain ~ expr) || (flat ~ expr)) { 151 if (avoid) next 152 got++ 153 if (quiet) next 154 if (got > 1) print "" 155 print 156 if (flush) fflush() 157 next 158 } 159 } 160 161 if (avoid) { 162 got++ 163 if (quiet) next 164 if (got > 1) print "" 165 print 166 if (flush) fflush() 167 } 168 } 169 170 END { exit(got == 0) } 171 ' "$@"