ansi.sh 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710
  1. #!/usr/bin/env bash
  2. #
  3. # ANSI code generator
  4. #
  5. # © Copyright 2015 Tyler Akins
  6. # Licensed under the MIT license with an additional non-advertising clause
  7. # See http://github.com/fidian/ansi
  8. ansi::addCode() {
  9. local N
  10. if [[ "$1" == *=* ]]; then
  11. N="${1#*=}"
  12. N="${N//,/;}"
  13. else
  14. N=""
  15. fi
  16. OUTPUT="$OUTPUT$CSI$N$2"
  17. }
  18. ansi::addColor() {
  19. OUTPUT="$OUTPUT$CSI${1}m"
  20. if [ ! -z "$2" ]; then
  21. SUFFIX="$CSI${2}m$SUFFIX"
  22. fi
  23. }
  24. ansi::colorTable() {
  25. local FNB_LOWER FNB_UPPER PADDED
  26. FNB_LOWER="$(ansi::colorize 2 22 f)n$(ansi::colorize 1 22 b)"
  27. FNB_UPPER="$(ansi::colorize 2 22 F)N$(ansi::colorize 1 22 B)"
  28. printf 'bold %s ' "$(ansi::colorize 1 22 Sample)"
  29. printf 'faint %s ' "$(ansi::colorize 2 22 Sample)"
  30. printf 'italic %s\n' "$(ansi::colorize 3 23 Sample)"
  31. printf 'underline %s ' "$(ansi::colorize 4 24 Sample)"
  32. printf 'blink %s ' "$(ansi::colorize 5 25 Sample)"
  33. printf 'inverse %s\n' "$(ansi::colorize 7 27 Sample)"
  34. printf 'invisible %s\n' "$(ansi::colorize 8 28 Sample)"
  35. printf 'strike %s ' "$(ansi::colorize 9 29 Sample)"
  36. printf 'fraktur %s ' "$(ansi::colorize 20 23 Sample)"
  37. printf 'double-underline%s\n' "$(ansi::colorize 21 24 Sample)"
  38. printf 'frame %s ' "$(ansi::colorize 51 54 Sample)"
  39. printf 'encircle %s ' "$(ansi::colorize 52 54 Sample)"
  40. printf 'overline%s\n' "$(ansi::colorize 53 55 Sample)"
  41. printf '\n'
  42. printf ' black red green yellow blue magenta cyan white\n'
  43. for BG in 40:black 41:red 42:green 43:yellow 44:blue 45:magenta 46:cyan 47:white xx:no-bg; do
  44. PADDED="bg-${BG:3} "
  45. PADDED="${PADDED:0:13}"
  46. printf '%s' "$PADDED"
  47. BG=${BG:0:2}
  48. if [[ "$BG" == "xx" ]]; then
  49. BG=""
  50. fi
  51. for FG in 30 31 32 33 34 35 36 37; do
  52. printf '%s%s;%sm' "$CSI" "$BG" "${FG}"
  53. printf '%s' "$FNB_LOWER"
  54. printf '%s%sm' "$CSI" "$(( FG + 60 ))"
  55. printf '%s' "$FNB_UPPER"
  56. printf '%s0m ' "${CSI}"
  57. done
  58. printf '\n'
  59. if [[ -n "$BG" ]]; then
  60. printf ' +intense '
  61. for FG in 30 31 32 33 34 35 36 37; do
  62. printf '%s%s;%sm' "$CSI" "$(( BG + 60 ))" "${FG}"
  63. printf '%s' "$FNB_LOWER"
  64. printf '%s%sm' "$CSI" "$(( FG + 60 ))"
  65. printf '%s' "$FNB_UPPER"
  66. printf '%s0m ' "${CSI}"
  67. done
  68. printf '\n'
  69. fi
  70. done
  71. printf '\n'
  72. printf 'Legend:\n'
  73. printf ' Normal color: f = faint, n = normal, b = bold.\n'
  74. printf ' Intense color: F = faint, N = normal, B = bold.\n'
  75. }
  76. ansi::colorize() {
  77. printf '%s%sm%s%s%sm' "$CSI" "$1" "$3" "$CSI" "$2"
  78. }
  79. ansi::isAnsiSupported() {
  80. local cont c str
  81. if ! test -t 1; then
  82. # stdout is not a terminal
  83. return 1
  84. fi
  85. if hash tput &> /dev/null; then
  86. if [[ "$(tput colors)" -lt 8 ]]; then
  87. return 1
  88. fi
  89. return 0
  90. fi
  91. # Query the console and see if we get ANSI codes back.
  92. # CSI 0 c == CSI c == Primary Device Attributes.
  93. # Idea: CSI c
  94. # Response = CSI ? 6 [234] ; 2 2 c
  95. # The "22" means ANSI color, but terminals don't need to send that back.
  96. printf "%s0c" "$CSI"
  97. str=
  98. cont=true
  99. while $cont && read -n 1 -s -t 0.1 c; do
  100. if [[ -n "$c" ]]; then
  101. str+=$c
  102. else
  103. cont=false
  104. fi
  105. done
  106. set | grep ^str=
  107. # If we get anything back, the terminal is consuming the color codes and
  108. # will probably do its best. Let's assume there's color.
  109. [[ "$str" == "$CSI?6"[234]";"* ]]
  110. }
  111. ansi::report() {
  112. local BUFF C
  113. REPORT=""
  114. printf "%s%s" "$CSI" "$1"
  115. read -r -N ${#2} -s -t 1 BUFF
  116. if [ "$BUFF" != "$2" ]; then
  117. return 1
  118. fi
  119. read -r -N ${#3} -s -t 1 BUFF
  120. while [ "$BUFF" != "$3" ]; do
  121. REPORT="$REPORT${BUFF:0:1}"
  122. read -r -N 1 -s -t 1 C || exit 1
  123. BUFF="${BUFF:1}$C"
  124. done
  125. }
  126. ansi::showHelp() {
  127. cat <<EOF
  128. Generate ANSI escape codes
  129. Please keep in mind that your terminal must support the code in order for you
  130. to see the effect properly.
  131. Usage
  132. ansi [OPTIONS] [TEXT TO OUTPUT]
  133. Option processing stops at the first unknown option or at "--". Options
  134. are applied in order as specified on the command line. Unless --no-restore
  135. is used, the options are unapplied in reverse order, restoring your
  136. terminal to normal.
  137. Optional parameters are surrounded in brackets and use reasonable defaults.
  138. For instance, using --down will move the cursor down one line and --down=10
  139. moves the cursor down 10 lines.
  140. Display Manipulation
  141. --insert-chars[=N], --ich[=N]
  142. Insert blanks at cursor, shifting the line right.
  143. --erase-display[=N], --ed[=N]
  144. Erase in display. 0=below, 1=above, 2=all,
  145. 3=saved.
  146. --erase-line[=N], --el[=N]
  147. Erase in line. 0=right, 1=left, 2=all.
  148. --insert-lines[=N], --il[=N]
  149. --delete-lines[=N], --dl[=N]
  150. --delete-chars[=N], --dch[=N]
  151. --scroll-up[=N], --su[=N]
  152. --scroll-down[=N], --sd[=N]
  153. --erase-chars[=N], --ech[=N]
  154. --repeat[=N], --rep[=N] Repeat preceding character N times.
  155. Cursor:
  156. --up[=N], --cuu[=N]
  157. --down[=N], --cud[=N]
  158. --forward[=N], --cuf[=N]
  159. --backward[=N], --cub[=N]
  160. --next-line[=N], --cnl[=N]
  161. --prev-line[=N], --cpl[=N]
  162. --column[=N], --cha[=N]
  163. --position[=[ROW],[COL]], --cup[=[ROW],[=COL]]
  164. --tab-forward[=N] Move forward N tab stops.
  165. --tab-backward[=N] Move backward N tab stops.
  166. --column-relative[=N], --hpr[=N]
  167. --line[=N], --vpa[=N]
  168. --line-relative[=N], --vpr[=N]
  169. --save-cursor Saves the cursor position. Restores the cursor
  170. after writing text to the terminal unless
  171. --no-restore is also used.
  172. --restore-cursor Just restores the cursor.
  173. --hide-cursor Will automatically show cursor unless --no-restore
  174. is also used.
  175. --show-cursor
  176. Colors:
  177. Attributes:
  178. --bold, --faint, --italic, --underline, --blink, --inverse,
  179. --invisible, --strike, --fraktur, --double-underline, --frame,
  180. --encircle, --overline
  181. Foreground:
  182. --black, --red, --green, --yellow, --blue, --magenta, --cyan, --white,
  183. --black-intense, --red-intense, --green-intense, --yellow-intense,
  184. --blue-intense, --magenta-intense, --cyan-intense, --white-intense
  185. Background:
  186. --bg-black, --bg-red, --bg-green, --bg-yellow, --bg-blue,
  187. --bg-magenta, --bg-cyan, --bg-white, --bg-black-intense,
  188. --bg-red-intense, --bg-green-intense, --bg-yellow-intense,
  189. --bg-blue-intense, --bg-magenta-intense, --bg-cyan-intense,
  190. --bg-white-intense
  191. Reset:
  192. --reset-attrib Removes bold, italics, etc.
  193. --reset-foreground Sets foreground to default color.
  194. --reset-background Sets background to default color.
  195. --reset-color Resets attributes, foreground, background.
  196. Report:
  197. ** NOTE: These require reading from stdin. Results are sent to stdout.
  198. ** If no response from terminal in 1 second, these commands fail.
  199. --report-position ROW,COL
  200. --report-window-state "open" or "iconified"
  201. --report-window-position X,Y
  202. --report-window-pixels HEIGHT,WIDTH
  203. --report-window-chars ROWS,COLS
  204. --report-screen-chars ROWS,COLS of the entire screen
  205. --report-icon
  206. --report-title
  207. Miscellaneous:
  208. --color-table Display a color table.
  209. --icon=NAME Set the terminal's icon name.
  210. --title=TITLE Set the terminal's window title.
  211. --no-restore Do not issue reset codes when changing colors.
  212. For example, if you change the color to green,
  213. normally the color is restored to default
  214. afterwards. With this flag, the color will
  215. stay green even when the command finishes.
  216. -n, --newline Add a newline at the end.
  217. --bell Add the terminal's bell sequence to the output.
  218. --reset Reset colors, clear screen, show cursor, move
  219. cursor to 1,1.
  220. EOF
  221. }
  222. ansi() {
  223. # Handle long options until we hit an unrecognized thing
  224. local CONTINUE=true
  225. local RESTORE=true
  226. local NEWLINE=false
  227. local ESC=$'\033'
  228. local CSI="${ESC}["
  229. local OSC="${ESC}]"
  230. local ST="${ESC}\\"
  231. local OUTPUT=""
  232. local SUFFIX=""
  233. local BELL=$'\007'
  234. while $CONTINUE; do
  235. case "$1" in
  236. --help | -h | -\?)
  237. ansi::showHelp
  238. ;;
  239. # Display Manipulation
  240. --insert-chars | --insert-chars=* | --ich | --ich=*)
  241. ansi::addCode "$1" @
  242. ;;
  243. --erase-display | --erase-display=* | --ed | --ed=*)
  244. ansi::addCode "$1" J
  245. ;;
  246. --erase-line | --erase-line=* | --el | --el=*)
  247. ansi::addCode "$1" K
  248. ;;
  249. --insert-lines | --insert-lines=* | --il | --il=*)
  250. ansi::addCode "$1" L
  251. ;;
  252. --delete-lines | --delete-lines=* | --dl | --dl=*)
  253. ansi::addCode "$1" M
  254. ;;
  255. --delete-chars | --delete-chars=* | --dch | --dch=*)
  256. ansi::addCode "$1" P
  257. ;;
  258. --scroll-up | --scroll-up=* | --su | --su=*)
  259. ansi::addCode "$1" S
  260. ;;
  261. --scroll-down | --scroll-down=* | --sd | --sd=*)
  262. ansi::addCode "$1" T
  263. ;;
  264. --erase-chars | --erase-chars=* | --ech | --ech=*)
  265. ansi::addCode "$1" X
  266. ;;
  267. --repeat | --repeat=* | --rep | --rep=N)
  268. ansi::addCode "$1" b
  269. ;;
  270. # Cursor Positioning
  271. --up | --up=* | --cuu | --cuu=*)
  272. ansi::addCode "$1" A
  273. ;;
  274. --down | --down=* | --cud | --cud=*)
  275. ansi::addCode "$1" B
  276. ;;
  277. --forward | --forward=* | --cuf | --cuf=*)
  278. ansi::addCode "$1" C
  279. ;;
  280. --backward | --backward=*| --cub | --cub=*)
  281. ansi::addCode "$1" D
  282. ;;
  283. --next-line | --next-line=* | --cnl | --cnl=*)
  284. ansi::addCode "$1" E
  285. ;;
  286. --prev-line | --prev-line=* | --cpl | --cpl=*)
  287. ansi::addCode "$1" F
  288. ;;
  289. --column | --column=* | --cha | --cha=*)
  290. ansi::addCode "$1" G
  291. ;;
  292. --position | --position=* | --cup | --cup=*)
  293. ansi::addCode "$1" H
  294. ;;
  295. --tab-forward | --tab-forward=* | --cht | --cht=*)
  296. ansi::addCode "$1" I
  297. ;;
  298. --tab-backward | --tab-backward=* | --cbt | --cbt=*)
  299. ansi::addCode "$1" Z
  300. ;;
  301. --column-relative | --column-relative=* | --hpr | --hpr=*)
  302. ansi::addCode "$1" 'a'
  303. ;;
  304. --line | --line=* | --vpa | --vpa=*)
  305. ansi::addCode "$1" 'd'
  306. ;;
  307. --line-relative | --line-relative=* | --vpr | --vpr=*)
  308. ansi::addCode "$1" 'e'
  309. ;;
  310. --save-cursor)
  311. OUTPUT="$OUTPUT${CSI}s"
  312. SUFFIX="${CSI}u$SUFFIX"
  313. ;;
  314. --restore-cursor)
  315. OUTPUT="$OUTPUT${CSI}u"
  316. ;;
  317. --hide-cursor)
  318. OUTPUT="$OUTPUT${CSI}?25l"
  319. SUFFIX="${CSI}?25h"
  320. ;;
  321. --show-cursor)
  322. OUTPUT="$OUTPUT${CSI}?25h"
  323. ;;
  324. # Colors - Attributes
  325. --bold)
  326. ansi::addColor 1 22
  327. ;;
  328. --faint)
  329. ansi::addColor 2 22
  330. ;;
  331. --italic)
  332. ansi::addColor 3 23
  333. ;;
  334. --underline)
  335. ansi::addColor 4 24
  336. ;;
  337. --blink)
  338. ansi::addColor 5 25
  339. ;;
  340. --inverse)
  341. ansi::addColor 7 27
  342. ;;
  343. --invisible)
  344. ansi::addColor 8 28
  345. ;;
  346. --strike)
  347. ansi::addColor 9 20
  348. ;;
  349. --fraktur)
  350. ansi::addColor 20 23
  351. ;;
  352. --double-underline)
  353. ansi::addColor 21 24
  354. ;;
  355. --frame)
  356. ansi::addColor 51 54
  357. ;;
  358. --encircle)
  359. ansi::addColor 52 54
  360. ;;
  361. --overline)
  362. ansi::addColor 53 55
  363. ;;
  364. # Colors - Foreground
  365. --black)
  366. ansi::addColor 30 39
  367. ;;
  368. --red)
  369. ansi::addColor 31 39
  370. ;;
  371. --green)
  372. ansi::addColor 32 39
  373. ;;
  374. --yellow)
  375. ansi::addColor 33 39
  376. ;;
  377. --blue)
  378. ansi::addColor 34 39
  379. ;;
  380. --magenta)
  381. ansi::addColor 35 39
  382. ;;
  383. --cyan)
  384. ansi::addColor 36 39
  385. ;;
  386. --white)
  387. ansi::addColor 37 39
  388. ;;
  389. --black-intense)
  390. ansi::addColor 90 39
  391. ;;
  392. --red-intense)
  393. ansi::addColor 91 39
  394. ;;
  395. --green-intense)
  396. ansi::addColor 92 39
  397. ;;
  398. --yellow-intense)
  399. ansi::addColor 93 39
  400. ;;
  401. --blue-intense)
  402. ansi::addColor 94 39
  403. ;;
  404. --magenta-intense)
  405. ansi::addColor 95 39
  406. ;;
  407. --cyan-intense)
  408. ansi::addColor 96 39
  409. ;;
  410. --white-intense)
  411. ansi::addColor 97 39
  412. ;;
  413. # Colors - Background
  414. --bg-black)
  415. ansi::addColor 40 49
  416. ;;
  417. --bg-red)
  418. ansi::addColor 41 49
  419. ;;
  420. --bg-green)
  421. ansi::addColor 42 49
  422. ;;
  423. --bg-yellow)
  424. ansi::addColor 43 49
  425. ;;
  426. --bg-blue)
  427. ansi::addColor 44 49
  428. ;;
  429. --bg-magenta)
  430. ansi::addColor 45 49
  431. ;;
  432. --bg-cyan)
  433. ansi::addColor 46 49
  434. ;;
  435. --bg-white)
  436. ansi::addColor 47 49
  437. ;;
  438. --bg-black-intense)
  439. ansi::addColor 100 49
  440. ;;
  441. --bg-red-intense)
  442. ansi::addColor 101 49
  443. ;;
  444. --bg-green-intense)
  445. ansi::addColor 102 49
  446. ;;
  447. --bg-yellow-intense)
  448. ansi::addColor 103 49
  449. ;;
  450. --bg-blue-intense)
  451. ansi::addColor 104 49
  452. ;;
  453. --bg-magenta-intense)
  454. ansi::addColor 105 49
  455. ;;
  456. --bg-cyan-intense)
  457. ansi::addColor 106 49
  458. ;;
  459. --bg-white-intense)
  460. ansi::addColor 107 49
  461. ;;
  462. # Colors - Reset
  463. --reset-attrib)
  464. OUTPUT="$OUTPUT${CSI}22;23;24;25;27;28;29;54;55m"
  465. ;;
  466. --reset-foreground)
  467. OUTPUT="$OUTPUT${CSI}39m"
  468. ;;
  469. --reset-background)
  470. OUTPUT="$OUTPUT${CSI}39m"
  471. ;;
  472. --reset-color)
  473. OUTPUT="$OUTPUT${CSI}0m"
  474. ;;
  475. # Reporting
  476. --report-position)
  477. ansi::report 6n "$CSI" R || exit 1
  478. printf '%s\n' "${REPORT//;/,}"
  479. ;;
  480. --report-window-state)
  481. ansi::report 11t "$CSI" t || exit 1
  482. case "$REPORT" in
  483. 1)
  484. printf 'open\n'
  485. ;;
  486. 2)
  487. printf 'iconified\n'
  488. ;;
  489. *)
  490. printf 'unknown (%s)\n' "$REPORT"
  491. ;;
  492. esac
  493. ;;
  494. --report-window-position)
  495. ansi::report 13t "${CSI}3;" t || exit 1
  496. printf '%s\n' "${REPORT//;/,}"
  497. ;;
  498. --report-window-pixels)
  499. ansi::report 14t "${CSI}4;" t || exit 1
  500. printf '%s\n' "${REPORT//;/,}"
  501. ;;
  502. --report-window-chars)
  503. ansi::report 18t "${CSI}8;" t || exit 1
  504. printf '%s\n' "${REPORT//;/,}"
  505. ;;
  506. --report-screen-chars)
  507. ansi::report 19t "${CSI}9;" t || exit 1
  508. printf '%s\n' "${REPORT//;/,}"
  509. ;;
  510. --report-icon)
  511. ansi::report 20t "${OSC}L" "$ST" || exit 1
  512. printf '%s\n' "$REPORT"
  513. ;;
  514. --report-title)
  515. ansi::report 21t "${OSC}l" "$ST" || exit 1
  516. printf '%s\n' "$REPORT"
  517. ;;
  518. # Miscellaneous
  519. --color-table)
  520. ansi::colorTable
  521. ;;
  522. --icon=*)
  523. OUTPUT="$OUTPUT${OSC}1;${1#*=}$ST"
  524. ;;
  525. --title=*)
  526. OUTPUT="$OUTPUT${OSC}2;${1#*=}$ST"
  527. ;;
  528. --no-restore)
  529. RESTORE=false
  530. ;;
  531. -n | --newline)
  532. NEWLINE=true
  533. ;;
  534. --bell)
  535. OUTPUT="$OUTPUT$BELL"
  536. ;;
  537. --reset)
  538. # 0m - reset all colors and attributes
  539. # 2J - clear terminal
  540. # 1;1H - move to 1,1
  541. # ?25h - show cursor
  542. OUTPUT="$OUTPUT${CSI}0m${CSI}2J${CSI}1;1H${CSI}?25h"
  543. ;;
  544. --)
  545. CONTINUE=false
  546. shift
  547. ;;
  548. *)
  549. CONTINUE=false
  550. ;;
  551. esac
  552. if $CONTINUE; then
  553. shift
  554. fi
  555. done
  556. if ansi::isAnsiSupported; then
  557. printf '%s' "$OUTPUT" "${1-}"
  558. shift || :
  559. if [[ "$#" -gt 0 ]]; then
  560. printf "${IFS:0:1}%s" "${@}"
  561. fi
  562. if $RESTORE; then
  563. printf '%s' "$SUFFIX"
  564. fi
  565. else
  566. printf '%s' "${1+"$@"}"
  567. fi
  568. if $NEWLINE; then
  569. printf '\n'
  570. fi
  571. }
  572. # Run if not sourced
  573. if [[ "$0" == "${BASH_SOURCE[0]}" ]]; then
  574. ansi "$@"
  575. fi