make_command.sh (30869B)
1 #!/usr/bin/env bash 2 3 umask 022 4 shopt -s nullglob 5 6 function mkd { 7 [[ -d $1 ]] || mkdir -p "$1" 8 } 9 10 function download { 11 local url=$1 dst=$2 12 if [[ ! -s $dst ]]; then 13 [[ $dst == ?*/* ]] && mkd "${dst%/*}" 14 if type wget &>/dev/null; then 15 wget "$url" -O "$dst.part" && mv "$dst.part" "$dst" 16 else 17 echo "make_command: 'wget' not found." >&2 18 exit 2 19 fi 20 fi 21 } 22 23 function ble/array#push { 24 while (($#>=2)); do 25 builtin eval "$1[\${#$1[@]}]=\$2" 26 set -- "$1" "${@:3}" 27 done 28 } 29 30 function sub:help { 31 printf '%s\n' \ 32 'usage: make_command.sh SUBCOMMAND args...' \ 33 '' 'SUBCOMMAND' '' 34 local sub 35 for sub in $(declare -F | sed -n 's|^declare -[fx]* sub:\([^/]*\)$|\1|p'); do 36 if declare -f sub:"$sub"/help &>/dev/null; then 37 sub:"$sub"/help 38 else 39 printf ' %s\n' "$sub" 40 fi 41 done 42 printf '\n' 43 } 44 45 #------------------------------------------------------------------------------ 46 47 function sub:install { 48 # read options 49 local flag_error= flag_release= 50 local opt_strip_comment= 51 while [[ $1 == -* ]]; do 52 local arg=$1; shift 53 case $arg in 54 (--release) flag_release=1 ;; 55 (--strip-comment=*) 56 opt_strip_comment=${arg#*=} ;; 57 (*) echo "install: unknown option $arg" >&2 58 flag_error=1 ;; 59 esac 60 done 61 [[ $flag_error ]] && return 1 62 63 local src=$1 64 local dst=$2 65 mkd "${dst%/*}" 66 if [[ $src == *.sh ]]; then 67 local nl=$'\n' q=\' 68 69 # header comment 70 local script='1i\ 71 # Copyright 2015 Koichi Murase <myoga.murase@gmail.com>. All rights reserved.\ 72 # This script is a part of blesh (https://github.com/akinomyoga/ble.sh)\ 73 # provided under the BSD-3-Clause license. Do not edit this file because this\ 74 # is not the original source code: Various pre-processing has been applied.\ 75 # Also, the code comments and blank lines are stripped off in the installation\ 76 # process. Please find the corresponding source file(s) in the repository\ 77 # "akinomyoga/ble.sh".' 78 if [[ $src == out/ble.sh ]]; then 79 script=$script'\ 80 #\ 81 # Source: /ble.pp' 82 local file 83 for file in $(git ls-files src); do 84 [[ $file == *.sh ]] || continue 85 script=$script"\\ 86 # Source: /$file" 87 done 88 else 89 script=$script'\ 90 #\ 91 # Source: /'"${src#out/}" 92 fi 93 94 # strip comments 95 if [[ $opt_strip_comment != no ]]; then 96 script=$script' 97 /<<[[:space:]]*EOF/,/^[[:space:]]*EOF/{p;d;} 98 /^[[:space:]]*#/d 99 /^[[:space:]]*$/d' 100 else 101 script=$script'\ 102 #------------------------------------------------------------------------------' 103 fi 104 105 [[ $flag_release ]] && 106 script=$script$nl's/^\([[:space:]]*_ble_base_repository=\)'$q'.*'$q'\([[:space:]]*\)$/\1'${q}release:$dist_git_branch$q'/' 107 sed "$script" "$src" >| "$dst.part" && mv "$dst.part" "$dst" 108 else 109 cp "$src" "$dst" 110 fi 111 } 112 function sub:install/help { 113 printf ' install src dst\n' 114 } 115 116 function sub:uninstall { 117 rm -rf "$@" 118 119 local file children 120 for file; do 121 while 122 file=${file%/*} 123 [[ -d $file ]] || break 124 children=("$file"/* "$file"/.*) 125 ((${#children[@]} == 0)) 126 do 127 rmdir "$file" 128 done 129 done 130 } 131 132 function sub:dist { 133 local dist_git_branch=$(git rev-parse --abbrev-ref HEAD) 134 local tmpdir=ble-$FULLVER 135 local src 136 for src in "$@"; do 137 local dst=$tmpdir${src#out} 138 sub:install --release "$src" "$dst" 139 done 140 [[ -d dist ]] || mkdir -p dist 141 tar caf "dist/$tmpdir.$(date +'%Y%m%d').tar.xz" "$tmpdir" && rm -r "$tmpdir" 142 } 143 144 function sub:ignoreeof-messages { 145 ( 146 cd ~/local/build/bash-4.3/po 147 sed -nr '/msgid "Use \\"%s\\" to leave the shell\.\\n"/{n;s/^[[:space:]]*msgstr "(.*)"[^"]*$/\1/p;}' *.po | while builtin read -r line || [[ $line ]]; do 148 [[ $line ]] || continue 149 echo $(printf "$line" exit) # $() は末端の改行を削除するため 150 done 151 ) >| lib/core-edit.ignoreeof-messages.new 152 } 153 154 #------------------------------------------------------------------------------ 155 # sub:check 156 # sub:check-all 157 158 function sub:check { 159 local bash=${1-bash} 160 "$bash" out/ble.sh --test 161 } 162 function sub:check-all { 163 local -x _ble_make_command_check_count=0 164 local bash rex_version='^bash-([0-9]+)\.([0-9]+)$' 165 for bash in $(compgen -c -- bash- | grep -E '^bash-(dev|[0-9]+\.[0-9]+)$' | sort -Vr); do 166 [[ $bash =~ $rex_version && ${BASH_REMATCH[1]} -ge 3 ]] || continue 167 "$bash" out/ble.sh --test || return 1 168 ((_ble_make_command_check_count++)) 169 done 170 } 171 172 #------------------------------------------------------------------------------ 173 # sub:scan 174 175 _make_rex_escseq='(\[[ -?]*[@-~])*' 176 177 function sub:scan/grc-source { 178 local -a options=(--color --exclude=./{test,memo,ext,wiki,contrib,[TD]????.*} --exclude=\*.{md,awk} --exclude=./{GNUmakefile,make_command.sh}) 179 grc "${options[@]}" "$@" 180 } 181 function sub:scan/list-command { 182 local -a options=(--color --exclude=./{test,memo,ext,wiki,contrib,[TD]????.*} --exclude=\*.{md,awk}) 183 184 # read arguments 185 local flag_exclude_this= flag_error= 186 local command= 187 while (($#)); do 188 local arg=$1; shift 189 case $arg in 190 (--exclude-this) 191 flag_exclude_this=1 ;; 192 (--exclude=*) 193 ble/array#push options "$arg" ;; 194 (--) 195 [[ $1 ]] && command=$1 196 break ;; 197 (-*) 198 echo "check: unknown option '$arg'" >&2 199 flag_error=1 ;; 200 (*) 201 command=$arg ;; 202 esac 203 done 204 if [[ ! $command ]]; then 205 echo "check: command name is not specified." >&2 206 flag_error=1 207 fi 208 [[ $flag_error ]] && return 1 209 210 [[ $flag_exclude_this ]] && ble/array#push options --exclude=./make_command.sh 211 grc "${options[@]}" "(^|[^-./\${}=#])\b$command"'\b([[:space:]|&;<>()`"'\'']|$)' 212 } 213 214 function sub:scan/builtin { 215 echo "--- $FUNCNAME $1 ---" 216 local command=$1 esc=$_make_rex_escseq 217 sub:scan/list-command --exclude-this --exclude={generate-release-note.sh,lib/test-*.sh,make,ext} "$command" "${@:2}" | 218 grep -Ev "$rex_grep_head([[:space:]]*|[[:alnum:][:space:]]*[[:space:]])#|(\b|$esc)(builtin|function)$esc([[:space:]]$esc)+$command(\b|$esc)" | 219 grep -Ev "$command(\b|$esc)=" | 220 grep -Ev "ble\.sh $esc\($esc$command$esc\)$esc" | 221 sed -E 'h;s/'"$_make_rex_escseq"'//g 222 \Z^\./lib/test-[^:]+\.sh:[0-9]+:.*ble/test Zd 223 s/^[^:]*:[0-9]+:[[:space:]]*// 224 \Z(\.awk|push|load|==|#(push|pop)) \b'"$command"'\bZd 225 g' 226 } 227 228 function sub:scan/check-todo-mark { 229 echo "--- $FUNCNAME ---" 230 grc --color --exclude=./make_command.sh '@@@' 231 } 232 function sub:scan/a.txt { 233 echo "--- $FUNCNAME ---" 234 grc --color --exclude={test,ext,./lib/test-\*.sh,./make_command.sh,\*.md} --exclude=check-mem.sh '[/[:space:]<>"'\''][a-z]\.txt|/dev/(pts/|pty)[0-9]*|/dev/tty' | 235 sed -E 'h;s/'"$_make_rex_escseq"'//g 236 \Z^\./memo/Zd 237 \Zgithub302-perlre-server\.bashZd 238 \Z^\./contrib/integration/fzf-git.bash:[0-9]+:Zd 239 s/^[^:]*:[0-9]+:[[:space:]]*// 240 \Z^[[:space:]]*#Zd 241 \ZDEBUG_LEAKVARZd 242 \Z\[\[ -t 4 && -t 5 ]]Zd 243 \Z^ble/fd#alloc .*Zd 244 \Zbuiltin read -et 0.000001 dummy </dev/ttyZd 245 g' 246 } 247 248 function sub:scan/bash300bug { 249 echo "--- $FUNCNAME ---" 250 # bash-3.0 では local arr=(1 2 3) とすると 251 # local arr='(1 2 3)' と解釈されてしまう。 252 grc '(local|declare|typeset) [_a-zA-Z]+=\(' --exclude=./{test,ext} --exclude=./make_command.sh --exclude=ChangeLog.md --color | 253 grep -v '#D0184' 254 255 # bash-3.0 では local -a arr=("$hello") とすると 256 # クォートしているにも拘らず $hello の中身が単語分割されてしまう。 257 grc '(local|declare|typeset) -a [[:alnum:]_]+=\([^)]*[\"'\''`]' --exclude=./{test,ext} --exclude=./make_command.sh --color | 258 grep -v '#D0525' 259 260 # bash-3.0 では "${scalar[@]/xxxx}" は全て空になる 261 grc '\$\{[_a-zA-Z0-9]+\[[*@]\]/' --exclude=./{text,ext} --exclude=./make_command.sh --exclude=\*.md --color | 262 grep -v '#D1570' 263 264 # bash-3.0 では "..${var-$'hello'}.." は (var が存在しない時) "..'hello'..." になる。 265 grc '".*\$\{[^{}]*\$'\''([^\\'\'']|\\.)*'\''\}.*"' --exclude={./make_command.sh,memo,\*.md} --color | 266 grep -v '#D1774' 267 268 } 269 270 function sub:scan/bash301bug-array-element-length { 271 echo "--- $FUNCNAME ---" 272 # bash-3.1 で ${#arr[index]} を用いると、 273 # 日本語の文字数が変になる。 274 grc '\$\{#[[:alnum:]]+\[[^@*]' --exclude={test,ChangeLog.md} --color | 275 grep -Ev '^([^#]*[[:space:]])?#' | 276 grep -v '#D0182' 277 } 278 279 function sub:scan/bash400bug { 280 echo "--- $FUNCNAME ---" 281 282 # bash-3.0..4.0 で $'' 内に \' を入れていると '' の入れ子状態が反転して履歴展 283 # 開が '' の内部で起こってしまう。 284 grc '\$'\''([^\'\'']|\\[^'\''])*\\'\''([^\'\'']|\\.|'\''([^\'\'']|\\*)'\'')*![^=[:space:]]' --exclude={test,ChangeLog.md} --color | 285 grep -v '9f0644470' 286 } 287 288 function sub:scan/bash401-histexpand-bgpid { 289 echo "--- $FUNCNAME ---" 290 grc '"\$!"' --exclude={test,ChangeLog.md} --color | 291 grep -Ev '#D2028' 292 } 293 294 function sub:scan/bash404-no-argument-return { 295 echo "--- $FUNCNAME ---" 296 grc --color 'return[[:space:]]*($|[;|&<>])' --exclude={test,wiki,ChangeLog.md,make,docs,make_command.sh} | 297 sed -E 'h;s/'"$_make_rex_escseq"'//g;s/^[^:]*:[0-9]+:[[:space:]]*// 298 299 \Z@returnZd 300 \Z\) return;Zd 301 \Zreturn;[[:space:]]*$Zd 302 \Zif \(REQ == "[A-Z]+"\)Zd 303 \Z\(return\|ret\)Zd 304 \Z_ble_trap_done=return$Zd 305 \Z\bwe return\bZd 306 307 g' 308 } 309 310 function sub:scan/bash501-arith-base { 311 echo "--- $FUNCNAME ---" 312 # bash-5.1 で $((10#)) の取り扱いが変わった。 313 grc '\b10#\$' --exclude={test,ChangeLog.md} 314 } 315 316 function sub:scan/bash502-patsub_replacement { 317 echo "--- $FUNCNAME ---" 318 # bash-5.2 patsub_replacement で ${var/pat/string} の string 中の & が特別な 319 # 意味を持つ様になったので、特に意識する場合を除いては quote が必要になった。 320 grc --color '\$\{[[:alnum:]_]+(\[[^][]*\])?//?([^{}]|\{[^{}]*\})+/[^{}"'\'']*([&$]|\\)' --exclude=./test | 321 sed -E 'h;s/'"$_make_rex_escseq"'//g;s/^[^:]*:[0-9]+:[[:space:]]*// 322 \Z//?\$q/\$Q\}Zd 323 \Z//?\$q/\$qq\}Zd 324 \Z//?\$qq/\$q\}Zd 325 \Z//?\$__ble_q/\$__ble_Q\}Zd 326 \Z//?\$_ble_local_q/\$_ble_local_Q\}Zd 327 \Z/\$\(\([^()]+\)\)\}Zd 328 \Z/\$'\''([^\\]|\\.)+'\''\}Zd 329 330 \Z\$\{[_a-zA-Z0-9]+//(ARR|DICT|PREFIX|NAME)/\$([_a-zA-Z0-9]+|\{[_a-zA-Z0-9#:-]+\})\}Zd 331 \Z\$\{[_a-zA-Z0-9]+//'\''%[dlcxy]'\''/\$[_a-zA-Z0-9]+\}Zd # src/canvas.sh 332 333 \Z#D1738Zd 334 \Z\$\{_ble_edit_str//\$'\''\\n'\''/\$'\''\\n'\''"\$comment_begin"\}Zd # edit.sh 335 g' 336 337 grc --color '"[^"]*\$\{[[:alnum:]_]+(\[[^][]*\])?//?([^{}]|\{[^{}]*\})+/[^{}"'\'']*"[^"]*([&$]|\\)' --exclude=./test | 338 sed -E 'h;s/'"$_make_rex_escseq"'//g;s/^[^:]*:[0-9]+:[[:space:]]*// 339 \Z#D1751Zd 340 g' 341 } 342 343 function sub:scan/gawk402bug-regex-check { 344 echo "--- $FUNCNAME ---" 345 grc --color '\[\^?\][^]]*\[:[^]]*:\].[^]]*\]' --exclude={test,ext,\*.md} | grep -Ev '#D1709 safe' 346 } 347 348 function sub:scan/assign { 349 echo "--- $FUNCNAME ---" 350 local command="$1" 351 grc --color --exclude=./test --exclude=./memo '\$\([^()]' | 352 grep -Ev "$rex_grep_head#|[[:space:]]#" 353 } 354 355 function sub:scan/memo-numbering { 356 echo "--- $FUNCNAME ---" 357 358 grep -ao '\[#D....\]' note.txt memo/done.txt | awk ' 359 function report_error(message) { 360 printf("memo-numbering: \x1b[1;31m%s\x1b[m\n", message) > "/dev/stderr"; 361 } 362 !/\[#D[0-9]{4}\]/ { 363 report_error("invalid number \"" $0 "\"."); 364 next; 365 } 366 { 367 num = $0; 368 gsub(/^\[#D0+|\]$/, "", num); 369 if (prev != "" && num != prev - 1) { 370 if (prev < num) { 371 report_error("reverse ordering " num " has come after " prev "."); 372 } else if (prev == num) { 373 report_error("duplicate number " num "."); 374 } else { 375 for (i = prev - 1; i > num; i--) { 376 report_error("memo-numbering: missing number " i "."); 377 } 378 } 379 } 380 prev = num; 381 } 382 END { 383 if (prev != 1) { 384 for (i = prev - 1; i >= 1; i--) 385 report_error("memo-numbering: missing number " i "."); 386 } 387 } 388 ' 389 cat note.txt memo/done.txt | sed -n '0,/^[[:space:]]\{1,\}Done/d;/ \* .*\[#D....\]$/d;/^ \* /p' 390 } 391 392 # 誤って ((${#arr[@]})) を ((${arr[@]})) などと書いてしまうミス。 393 function sub:scan/array-count-in-arithmetic-expression { 394 echo "--- $FUNCNAME ---" 395 grc --exclude=./make_command.sh '\(\([^[:space:]]*\$\{[[:alnum:]_]+\[[@*]\]\}' 396 } 397 398 # unset 変数名 としていると誤って関数が消えることがある。 399 function sub:scan/unset-variable { 400 echo "--- $FUNCNAME ---" 401 sub:scan/list-command unset --exclude-this | 402 sed -E 'h;s/'"$_make_rex_escseq"'//g;s/^[^:]*:[0-9]+:[[:space:]]*// 403 \Zunset[[:space:]]-[vf]Zd 404 \Z^[[:space:]]*#Zd 405 \Zunset _ble_init_(version|arg|exit|command)\bZd 406 \Zbuiltins1=\(.* unset .*\)Zd 407 \Zfunction unsetZd 408 \Zreadonly -f unsetZd 409 \Z'\''\(unset\)'\''Zd 410 \Z"\$__ble_proc" "\$__ble_name" unsetZd 411 \Zulimit umask unalias unset waitZd 412 \ZThe variable will be unset initiallyZd 413 g' 414 } 415 function sub:scan/eval-literal { 416 echo "--- $FUNCNAME ---" 417 sub:scan/grc-source 'builtin eval "\$' | 418 sed -E 'h;s/'"$_make_rex_escseq"'//g;s/^[^:]*:[0-9]+:[[:space:]]*// 419 \Zeval "(\$[[:alnum:]_]+)+(\[[^]["'\''\$`]+\])?\+?=Zd 420 g' 421 } 422 423 function sub:scan/WA-localvar_inherit { 424 echo "--- $FUNCNAME ---" 425 grc 'local [^;&|()]*"\$\{[_a-zA-Z0-9]+\[@*\]\}"' | 426 sed -E 'h;s/'"$_make_rex_escseq"'//g;s/^[^:]*:[0-9]+:[[:space:]]*// 427 \Ztest_command='\''ble/bin/stty -echo -nl -icrnl -icanon "\$\{_ble_term_stty_flags_enter\[@]}" size'\''Zd 428 g' 429 } 430 431 function sub:scan/mistake-_ble_bash { 432 echo "--- $FUNCNAME ---" 433 grc '\(\(.*\b_ble_base\b.*\)\)' 434 } 435 436 function sub:scan/command-layout { 437 echo "--- $FUNCNAME ---" 438 grc '/(enter-command-layout|\.insert-newline|\.newline)([[:space:]]|$)' --exclude=./{text,ext} --exclude=./make_command.sh --exclude=\*.md --color | 439 sed -E 'h;s/'"$_make_rex_escseq"'//g;s/^[^:]*:[0-9]+:[[:space:]]*// 440 \Z^[[:space:]]*#Zd 441 \Z^[[:space:]]*function [^[:space:]]* \{$Zd 442 \Z[: ]keep-infoZd 443 \Z#D1800Zd 444 g' 445 } 446 447 function sub:scan/word-splitting-number { 448 echo "--- $FUNCNAME ---" 449 # #D1835 一般には IFS に整数が含まれるている場合もあるので ${#...} や 450 # $((...)) や >&$fd であってもちゃんと quote する必要がある。 451 grc '[<>]&\$|([[:space:]]|=\()\$(\(\(|\{#|\?)' --exclude={docs,mwg_pp.awk,memo} | 452 sed -E 'h;s/'"$_make_rex_escseq"'//g;s/^[^:]*:[0-9]+:[[:space:]]*// 453 \Z^[^#]*(^|[[:space:]])#Zd 454 \Z^([^"]|"[^\#]*")*"[^"]*([& (]\$)Zd 455 \Z^[^][]*\[\[[^][]*([& (]\$)Zd 456 \Z\(\([_a-zA-Z0-9]+=\(\$Zd 457 \Z\$\{#[_a-zA-Z0-9]+\}[<>?&]Zd 458 \Z \$\{\#[_a-zA-Z0-9]+\[@\]\} -gt 0 \]\]Zd 459 \Zcase \$\? inZd 460 \Zcase \$\(\(.*\)\) inZd 461 g' 462 } 463 464 function sub:scan/check-readonly-unsafe { 465 echo "--- $FUNCNAME ---" 466 local rex_varname='\b(_[_a-zA-Z0-9]+|[_A-Z][_A-Z0-9]+)\b' 467 grc -Wg,-n -Wg,--color=always -o "$rex_varname"'\+?=\b|(/assign|/assign-array|#split) '"$rex_varname"'| -v '"$rex_varname"' ' --exclude={memo,wiki,test,make,\*.md,make_command.sh,GNUmakefile} | 468 sed -E 'h;s/'"$_make_rex_escseq"'//g 469 470 # Exceptions in each file 471 /^\.\/ble.pp:[0-9]*:BLEOPT=$/d 472 /^\.\/lib\/core-complete.sh:[0-9]+:KEY=$/d 473 /^\.\/lib\/core-syntax.sh:[0-9]+:VAR=$/d 474 /^\.\/lib\/init-(cmap|term).sh:[0-9]+:TERM=$/d 475 /^\.\/src\/edit.sh:[0-9]+:_dirty=$/d 476 /^\.\/src\/history.sh:[0-9]+:_history_index=$/d 477 /^\.\/src\/util.sh:[0-9]+:(ARRI|OPEN|TERM)=$/d 478 /^\.\/lib\/core-cmdspec.sh:[0-9]+:OLD=$/d 479 480 # (extract only variable names) 481 s/^[^:]*:[0-9]+:[[:space:]]*//; 482 s/^-v (.*) $/\1/;s/\+?=$//;s/^.+ //; 483 484 # other frameworks & integrations 485 /^__bp_blesh_invoking_through_blesh$/d 486 /^BP_PROMPT_COMMAND_.*$/d 487 488 # common variables 489 /^__?ble[_a-zA-Z0-9]*$/d 490 /^[A-Z]$/d 491 /^BLE_[_A-Z0-9]*$/d 492 /^ADVICE_[_A-Z0-9]*$/d 493 /^COMP_[_A-Z0-9]*$/d 494 /^COMPREPLY$/d 495 /^READLINE_[_A-Z0-9]*$/d 496 /^LC_[_A-Z0-9]*$/d 497 /^LANG$/d 498 499 # other uppercase variables that ble.sh is allowed to use. 500 /^(FUNCNEST|IFS|IGNOREEOF|POSIXLY_CORRECT|TMOUT)$/d 501 /^(PWD|OLDPWD|CDPATH)$/d 502 /^(BASHPID|GLOBIGNORE|MAPFILE|REPLY)$/d 503 /^INPUTRC$/d 504 /^(LINES|COLUMNS)$/d 505 /^HIST(CONTROL|IGNORE|SIZE|TIMEFORMAT)$/d 506 /^(PROMPT_COMMAND|PS1)$/d 507 /^(BASH_COMMAND|BASH_REMATCH|HISTCMD|LINENO|PIPESTATUS|TIMEFORMAT)$/d 508 /^(BASH_XTRACEFD|PS4)$/d 509 /^(CC|LESS|MANOPT|MANPAGER|PAGER|PATH|MANPATH)$/d 510 /^(BUFF|KEYS|KEYMAP|WIDGET|LASTWIDGET|DRAW_BUFF)$/d 511 /^(D(MIN|MAX|MAX0)|(HIGHLIGHT|PREV)_(BUFF|UMAX|UMIN)|LEVEL|LAYER_(UMAX|UMIN))$/d 512 /^(HISTINDEX_NEXT|FILE|LINE|INDEX|INDEX_FILE)$/d 513 /^(ARG|FLAG|REG)$/d 514 /^(COMP[12SV]|ACTION|CAND|DATA|INSERT|PREFIX_LEN)$/d 515 /^(PRETTY_NAME|NAME|VERSION)$/d 516 517 # variables in awk/comments/etc 518 /^AWKTYPE$/d 519 /^FOO$/d 520 g' 521 } 522 523 function sub:scan { 524 if ! type grc >/dev/null; then 525 echo 'blesh check: grc not found. grc can be found in github.com:akinomyoga/mshex.git/' >&2 526 exit 527 fi 528 529 local esc=$_make_rex_escseq 530 local rex_grep_head="^$esc[[:graph:]]+$esc:$esc[[:digit:]]*$esc:$esc" 531 532 # builtin return break continue : eval echo unset は unset しているので大丈夫のはず 533 534 #sub:scan/builtin 'history' 535 sub:scan/builtin 'echo' --exclude=./lib/keymap.vi_test.sh --exclude=./ble.pp | 536 sed -E 'h;s/'"$_make_rex_escseq"'//g;s/^[^:]*:[0-9]+:[[:space:]]*// 537 \Z\bstty[[:space:]]+echoZd 538 \Zecho \$PPIDZd 539 \Zble/keymap:vi_test/check Zd 540 \Zmandb-help=%'\''help echo'\''Zd 541 \Zalias aaa4='\''echo'\''Zd 542 g' 543 #sub:scan/builtin '(compopt|type|printf)' 544 sub:scan/builtin 'bind' | 545 sed -E 'h;s/'"$_make_rex_escseq"'//g;s/^[^:]*:[0-9]+:[[:space:]]*// 546 \Zinvalid bind typeZd 547 \Zline = "bind"Zd 548 \Z'\'' bindZd 549 \Z\(bind\) ble-bindZd 550 \Z^alias bind cd command compgenZd 551 \Zoutputs of the "bind" builtinZd 552 \Zif ble/string#match "\$_ble_edit_str" '\''bindZd 553 g' 554 sub:scan/builtin 'read' | 555 sed -E 'h;s/'"$_make_rex_escseq"'//g;s/^[^:]*:[0-9]+:[[:space:]]*// 556 \ZDo not read Zd 557 \Zfailed to read Zd 558 \Zpushd read readonly set shoptZd 559 g' 560 sub:scan/builtin 'exit' | 561 sed -E 'h;s/'"$_make_rex_escseq"'//g;s/^[^:]*:[0-9]+:[[:space:]]*// 562 \Zble.pp.*return 1 2>/dev/null || exit 1Zd 563 \Z^[-[:space:][:alnum:]_./:=$#*]+('\''[^'\'']*|"[^"()`]*|([[:space:]]|^)#.*)\bexit\bZd 564 \Z\(exit\) ;;Zd 565 \Zprint NR; exit;Zd;g' 566 sub:scan/builtin 'eval' | 567 sed -E 'h;s/'"$_make_rex_escseq"'//g;s/^[^:]*:[0-9]+:[[:space:]]*// 568 \Z\('\''eval'\''\)Zd 569 \Zbuiltins1=\(.* eval .*\)Zd 570 \Z\^eval --Zd 571 \Zt = "eval -- \$"Zd 572 \Ztext = "eval -- \$'\''Zd 573 \Zcmd '\''eval -- %q'\''Zd 574 \Z\$\(eval \$\(call .*\)\)Zd 575 \Z^[[:space:]]*local rex_[_a-zA-Z0-9]+='\''[^'\'']*'\''[[:space:]]*$Zd 576 \ZLINENO=\$_ble_edit_LINENO evalZd 577 \Z^ble/cmdspec/opts Zd 578 g' 579 sub:scan/builtin 'unset' | 580 sed -E 'h;s/'"$_make_rex_escseq"'//g;s/^[^:]*:[0-9]+:[[:space:]]*// 581 \Zunset _ble_init_(version|arg|exit|command)\bZd 582 \Zreadonly -f unsetZd 583 \Zunset -f builtinZd 584 \Z'\''\(unset\)'\''Zd 585 \Z"\$__ble_proc" "\$__ble_name" unsetZd 586 \Zumask unalias unset wait$Zd 587 \ZThe variable will be unset initiallyZd 588 g' 589 sub:scan/builtin 'unalias' | 590 sed -E 'h;s/'"$_make_rex_escseq"'//g;s/^[^:]*:[0-9]+:[[:space:]]*// 591 \Zbuiltins1=\(.* unalias .*\)Zd 592 \Zumask unalias unset wait$Zd 593 g' 594 595 #sub:scan/assign 596 sub:scan/builtin 'trap' | 597 sed -E 'h;s/'"$_make_rex_escseq"'//g;s/^[^:]*:[0-9]+:[[:space:]]*// 598 \Z_ble_trap_handler="trap -- '\''\$\{_ble_trap_handler//\$q/\$Q}'\'' \$nZd 599 \Zline = "bind"Zd 600 \Ztrap_command=["'\'']trap -- Zd 601 \Z_ble_builtin_trap_handlers_reload\[sig\]="trap -- Zd 602 \Zlocal trap$Zd 603 \Z"trap -- '\''"Zd 604 \Z\('\'' trap '\''\*Zd 605 \Z\(trap \| ble/builtin/trap\) .*;;Zd 606 \Zble/function#trace trap Zd 607 \Z# EXIT trapZd 608 \Zread readonly set shopt trapZd 609 \Zble/util/print "custom trap"Zd 610 g' 611 612 sub:scan/builtin 'readonly' | 613 sed -E 'h;s/'"$_make_rex_escseq"'//g;s/^[^:]*:[0-9]+:[[:space:]]*// 614 \Z^[[:space:]]*#Zd 615 \ZWA readonlyZd 616 \Z\('\''declare'\''(\|'\''[a-z]+'\'')+\)Zd 617 \Z readonly was blocked\.Zd 618 \Z\[\[ \$\{FUNCNAME\[i]} == \*readonly ]]Zd 619 \Zread readonly set shopt trapZd 620 g' 621 622 sub:scan/a.txt 623 sub:scan/check-todo-mark 624 sub:scan/bash300bug 625 sub:scan/bash301bug-array-element-length 626 sub:scan/bash400bug 627 sub:scan/bash401-histexpand-bgpid 628 sub:scan/bash404-no-argument-return 629 sub:scan/bash501-arith-base 630 sub:scan/bash502-patsub_replacement 631 sub:scan/gawk402bug-regex-check 632 sub:scan/array-count-in-arithmetic-expression 633 sub:scan/unset-variable 634 sub:scan/eval-literal 635 sub:scan/WA-localvar_inherit 636 sub:scan/mistake-_ble_bash 637 sub:scan/command-layout 638 sub:scan/word-splitting-number 639 sub:scan/check-readonly-unsafe 640 641 sub:scan/memo-numbering 642 } 643 644 function sub:show-contrib/canonicalize { 645 sed 's/, /\n/g;s/ and /\n/g' | sed 's/[[:space:]]/_/g' | LANG=C sort 646 } 647 function sub:show-contrib/count { 648 LANG=C sort | uniq -c | LANG=C sort -rnk1 | 649 awk 'function xflush() {if(c!=""){printf("%4d %s\n",c,n);}} {if($1!=c){xflush();c=$1;n=$2}else{n=n", "$2;}}END{xflush()}' | 650 ifold -w 131 -s --indent=' +[0-9] +' 651 } 652 function sub:show-contrib { 653 local cache_contrib_github=out/contrib-github.txt 654 if [[ ! ( $cache_contrib_github -nt .git/refs/remotes/origin/master ) ]]; then 655 { 656 wget 'https://api.github.com/repos/akinomyoga/ble.sh/issues?state=all&per_page=100&pulls=true' -O - 657 wget 'https://api.github.com/repos/akinomyoga/ble.sh/issues?state=all&per_page=100&pulls=true&page=2' -O - 658 wget 'https://api.github.com/repos/akinomyoga/blesh-contrib/issues?state=all&per_page=100&pulls=true' -O - 659 } | 660 sed -n 's/^[[:space:]]*"login": "\(.*\)",$/\1/p' | 661 sub:show-contrib/canonicalize > "$cache_contrib_github" 662 fi 663 664 echo "Contributions (from GitHub Issues/PRs)" 665 < "$cache_contrib_github" sub:show-contrib/count 666 667 echo "Contributions (from memo.txt)" 668 sed -En 's/^ \* .*\([^()]+ by ([^()]+)\).*/\1/p' memo/done.txt note.txt | sub:show-contrib/canonicalize | sub:show-contrib/count 669 670 echo "Contributions (from ChangeLog.md)" 671 sed -n 's/.*([^()]* by \([^()]*\)).*/\1/p' docs/ChangeLog.md | sub:show-contrib/canonicalize | sub:show-contrib/count 672 673 echo "Σ: Issues/PRs + max(memo.txt,ChangeLog)" 674 675 LANG=C join -j 2 -e 0 \ 676 <(sed -En 's/^ \* .*\([^()]+ by ([^()]+)\).*/\1/p' memo/done.txt note.txt | sub:show-contrib/canonicalize | uniq -c | LANG=C sort -k2) \ 677 <(sed -n 's/.*([^()]* by \([^()]*\)).*/\1/p' docs/ChangeLog.md | sub:show-contrib/canonicalize | uniq -c | LANG=C sort -k2) | 678 LANG=C join -e 0 -1 1 - -2 2 <(uniq -c "$cache_contrib_github" | LANG=C sort -k2) | 679 awk 'function max(x,y){return x<y?y:x;}{printf("%4d %s\n",max($2,$3)+$4,$1)}' | 680 sort -rnk1 | 681 awk 'function xflush() {if(c!=""){printf("%4d %s\n",c,n);}} {if($1!=c){xflush();c=$1;n=$2}else{n=n", "$2;}}END{xflush()}' | 682 ifold -w 131 -s --indent=' +[0-9] +' 683 echo 684 } 685 686 #------------------------------------------------------------------------------ 687 # sub:release-note 688 # 689 # 使い方 690 # ./make_command.sh release-note v0.3.2..v0.3.3 691 692 function sub:release-note/help { 693 printf ' release-note v0.3.2..v0.3.3 [--changelog CHANGELOG]\n' 694 } 695 696 function sub:release-note/read-arguments { 697 flags= 698 fname_changelog=memo/ChangeLog.md 699 while (($#)); do 700 local arg=$1; shift 1 701 case $arg in 702 (--changelog) 703 if (($#)); then 704 fname_changelog=$1; shift 705 else 706 flags=E$flags 707 echo "release-note: missing option argument for '$arg'." >&2 708 fi ;; 709 esac 710 done 711 } 712 713 function sub:release-note/.find-commit-pairs { 714 { 715 echo __MODE_HEAD__ 716 git log --format=format:'%h%s' --date-order --abbrev-commit "$1"; echo 717 echo __MODE_MASTER__ 718 git log --format=format:'%h%s' --date-order --abbrev-commit master; echo 719 } | awk -F '' ' 720 /^__MODE_HEAD__$/ { 721 mode = "head"; 722 nlist = 0; 723 next; 724 } 725 /^__MODE_MASTER__$/ { mode = "master"; next; } 726 727 function reduce_title(str) { 728 str = $2; 729 #if (match(str, /^.*\[(originally: )?(.+: .+)\]$/, m)) str = m[2]; 730 gsub(/["`]/, "", str); 731 #print str >"/dev/stderr"; 732 return str; 733 } 734 735 mode == "head" { 736 i = nlist++; 737 titles[i] = $2; 738 commit_head[i] = $1; 739 title2index[reduce_title($2)] = i; 740 } 741 mode == "master" && (i = title2index[reduce_title($2)]) != "" && commit_master[i] == "" { 742 commit_master[i] = $1; 743 } 744 745 END { 746 for (i = 0; i < nlist; i++) { 747 print commit_head[i] ":" commit_master[i] ":" titles[i]; 748 } 749 } 750 ' 751 } 752 753 function sub:release-note { 754 local flags fname_changelog 755 sub:release-note/read-arguments "$@" 756 757 ## @arr commits 758 ## この配列は after:before の形式の要素を持つ。 759 ## 但し after は前の version から release までに加えられた変更の commit である。 760 ## そして before は after に対応する master における commit である。 761 local -a commits 762 IFS=$'\n' eval 'commits=($(sub:release-note/.find-commit-pairs "$@"))' 763 764 local commit_pair 765 for commit_pair in "${commits[@]}"; do 766 local hash=${commit_pair%%:*} 767 commit_pair=${commit_pair:${#hash}+1} 768 local hash_base=${commit_pair%%:*} 769 local title=${commit_pair#*:} 770 771 local rex_hash_base=$hash_base 772 if ((${#hash_base} == 7)); then 773 rex_hash_base=$hash_base[0-9a-f]? 774 elif ((${#hash_base} == 8)); then 775 rex_hash_base=$hash_base? 776 fi 777 778 local result= 779 [[ $hash_base ]] && result=$(awk ' 780 sub(/^##+ +/, "") { heading = "[" $0 "] "; next; } 781 sub(/\y'"$rex_hash_base"'\y/, "'"$hash (master: $hash_base)"'") {print heading $0;} 782 ' "$fname_changelog") 783 if [[ $result ]]; then 784 echo "$result" 785 elif [[ $title ]]; then 786 echo "- $title $hash (master: ${hash_base:-N/A}) ■NOT-FOUND■" 787 else 788 echo "■not found $hash" 789 fi 790 done | tac 791 } 792 793 # 以下の様な形式のファイルをセクション毎に分けて出力します。 794 # 795 # [Fixes] - foo bar 796 # [New features] - foo bar 797 # [Fixes] - foo bar 798 # [Fixes] - foo bar 799 # ... 800 function sub:release-note-sort { 801 local file=$1 802 awk ' 803 match($0, /\[[^][]+\]/) { 804 key = substr($0, 1, RLENGTH); 805 gsub(/^\[|]$/, "", key); 806 807 line = substr($0, RLENGTH + 1); 808 gsub(/^[[:space:]]+|[[:space:]]+$/, "", line); 809 if (line == "") next; 810 if (line !~ /^- /) line = "- " line; 811 812 if (sect[key] == "") 813 keys[nkey++] = key; 814 sect[key] = sect[key] line "\n" 815 next; 816 } 817 {print} 818 819 END { 820 for (i=0;i<nkey;i++) { 821 key = keys[i]; 822 print "## " key; 823 print sect[key]; 824 } 825 } 826 ' "$file" 827 } 828 829 #------------------------------------------------------------------------------ 830 831 function sub:list-functions/help { 832 printf ' list-functions [-p] files...\n' 833 } 834 function sub:list-functions { 835 local -a files; files=() 836 local opt_literal= 837 local i=0 N=$# args; args=("$@") 838 while ((i<N)); do 839 local arg=${args[i++]} 840 if [[ ! $opt_literal && $arg == -* ]]; then 841 if [[ $arg == -- ]]; then 842 opt_literal=1 843 elif [[ $arg == --* ]]; then 844 printf 'list-functions: unknown option "%s"\n' "$arg" >&2 845 opt_error=1 846 elif [[ $arg == -* ]]; then 847 local j 848 for ((j=1;j<${#arg};j++)); do 849 local o=${arg:j:1} 850 case $o in 851 (p) opt_public=1 ;; 852 (*) printf 'list-functions: unknown option "-%c"\n' "$o" >&2 853 opt_error=1 ;; 854 esac 855 done 856 fi 857 else 858 files+=("$arg") 859 fi 860 done 861 862 if ((${#files[@]}==0)); then 863 files=($(find out -name \*.sh -o -name \*.bash)) 864 fi 865 866 if [[ $opt_public ]]; then 867 local rex_function_name='[^[:space:]()/]*' 868 else 869 local rex_function_name='[^[:space:]()]*' 870 fi 871 sed -n 's/^[[:space:]]*function \('"$rex_function_name"'\)[[:space:]].*/\1/p' "${files[@]}" | sort -u 872 } 873 874 function sub:first-defined { 875 local name dir 876 for name; do 877 for dir in ../ble-0.{1..3} ../ble.sh; do 878 (cd "$dir"; grc "$name" &>/dev/null) || continue 879 echo "$name $dir" 880 return 0 881 done 882 done 883 echo "$name not found" 884 return 1 885 } 886 function sub:first-defined/help { 887 printf ' first-defined ERE...\n' 888 } 889 890 #------------------------------------------------------------------------------ 891 892 function sub:scan-words { 893 # sed -E "s/'[^']*'//g;s/(^| )[[:space:]]*#.*/ /g" $(findsrc --exclude={wiki,test,\*.md}) | 894 # grep -hoE '\$\{?[_a-zA-Z][_a-zA-Z0-9]*\b|\b[_a-zA-Z][-:._/a-zA-Z0-9]*\b' | 895 # sed -E 's/^\$\{?//g;s.^ble/widget/..;\./.!d;/:/d' | 896 # sort | uniq -c | sort -n 897 sed -E "s/(^| )[[:space:]]*#.*/ /g" $(findsrc --exclude={memo,wiki,test,\*.md}) | 898 grep -hoE '\b[_a-zA-Z][_a-zA-Z0-9]{3,}\b' | 899 sed -E 's/^bleopt_//' | 900 sort | uniq -c | sort -n | less 901 } 902 function sub:scan-varnames { 903 sed -E "s/(^| )[[:space:]]*#.*/ /g" $(findsrc --exclude={wiki,test,\*.md}) | 904 grep -hoE '\$\{?[_a-zA-Z][_a-zA-Z0-9]*\b|\b[_a-zA-Z][_a-zA-Z0-9]*=' | 905 sed -E 's/^\$\{?(.*)/\1$/g;s/[$=]//' | 906 sort | uniq -c | sort -n | less 907 } 908 909 function sub:check-dependency/identify-funcdef { 910 local funcname=$1 911 grep -En "\bfunction $funcname +\{" ble.pp src/*.sh | awk -F : -v funcname="$funcname" ' 912 { 913 if ($1 == "ble.pp") { 914 if (funcname ~ /^ble\/util\/assign$|^ble\/bin\/grep$/) next; 915 if (funcname == "ble/util/print" && $2 < 30) next; 916 } else if ($1 == "src/benchmark.sh") { 917 if (funcname ~ /^ble\/util\/(unlocal|print|print-lines)$/) next; 918 } 919 print $1 ":" $2; 920 exit 921 } 922 ' 923 } 924 925 function sub:check-dependency { 926 local file=$1 927 grep -Eo '\bble(hook|opt|-[[:alnum:]]+)?/[^();&|[:space:]'\''"]+' "$file" | sort -u | 928 grep -Fvx "$(grep -Eo '\bfunction [^();&|[:space:]'\''"]+ +\{' "$file" | sed -E 's/^function | +\{$//g' | sort -u)" | 929 while read -r funcname; do 930 location=$(sub:check-dependency/identify-funcdef "$funcname") 931 echo "${location:-unknown:0}:$funcname" 932 done | sort -t : -Vk 1,2 | less -FSXR 933 } 934 935 #------------------------------------------------------------------------------ 936 # sub:check-readline-bindable 937 938 function sub:check-readline-bindable { 939 join -v1 <( 940 for bash in bash $(compgen -c -- bash-); do 941 [[ $bash == bash-[12]* ]] && continue 942 "$bash" -c 'bind -l' 2>/dev/null 943 done | sort -u 944 ) <(sort lib/core-decode.emacs-rlfunc.txt) 945 } 946 947 #------------------------------------------------------------------------------ 948 949 if (($#==0)); then 950 sub:help 951 elif declare -f sub:"$1" &>/dev/null; then 952 sub:"$@" 953 else 954 echo "unknown subcommand '$1'" >&2 955 builtin exit 1 956 fi