diff options
-rwxr-xr-x | bollux | 217 | ||||
-rw-r--r-- | typeset_gemini.sh | 172 |
2 files changed, 152 insertions, 237 deletions
diff --git a/bollux b/bollux index 87c35fb..6f121b0 100755 --- a/bollux +++ b/bollux | |||
@@ -104,6 +104,7 @@ bollux_config() { | |||
104 | log debug "Can't load config file '$BOLLUX_CONFIG'." | 104 | log debug "Can't load config file '$BOLLUX_CONFIG'." |
105 | fi | 105 | fi |
106 | 106 | ||
107 | ## behavior | ||
107 | : "${BOLLUX_DOWNDIR:=.}" # where to save downloads | 108 | : "${BOLLUX_DOWNDIR:=.}" # where to save downloads |
108 | : "${BOLLUX_LOGLEVEL:=3}" # log level | 109 | : "${BOLLUX_LOGLEVEL:=3}" # log level |
109 | : "${BOLLUX_MAXREDIR:=5}" # max redirects | 110 | : "${BOLLUX_MAXREDIR:=5}" # max redirects |
@@ -112,6 +113,20 @@ bollux_config() { | |||
112 | : "${BOLLUX_LESSKEY:=/tmp/bollux-lesskey}" # where to store binds | 113 | : "${BOLLUX_LESSKEY:=/tmp/bollux-lesskey}" # where to store binds |
113 | : "${BOLLUX_PAGESRC:=/tmp/bollux-src}" # where to save the page source | 114 | : "${BOLLUX_PAGESRC:=/tmp/bollux-src}" # where to save the page source |
114 | : "${BOLLUX_URL:=}" # start url | 115 | : "${BOLLUX_URL:=}" # start url |
116 | ## typesetting | ||
117 | : "${T_MARGIN:=4}" # left and right margin | ||
118 | : "${T_WIDTH:=0}" # width of the viewport -- 0 = get term width | ||
119 | # colors -- these will be wrapped in \e[ __ m | ||
120 | C_RESET='\e[0m' # reset | ||
121 | : "${C_SIGIL:=35}" # sigil (=>, #, ##, ###, *, ```) | ||
122 | : "${C_LINK_NUMBER:=1}" # link number | ||
123 | : "${C_LINK_TITLE:=4}" # link title | ||
124 | : "${C_LINK_URL:=36}" # link URL | ||
125 | : "${C_HEADER1:=1;4}" # header 1 formatting | ||
126 | : "${C_HEADER2:=1}" # header 2 formatting | ||
127 | : "${C_HEADER3:=3}" # header 3 formatting | ||
128 | : "${C_LIST:=0}" # list formatting | ||
129 | : "${C_PRE:=0}" # preformatted text formatting | ||
115 | } | 130 | } |
116 | 131 | ||
117 | prompt() { | 132 | prompt() { |
@@ -399,74 +414,146 @@ mklesskey() { | |||
399 | } | 414 | } |
400 | 415 | ||
401 | normalize_crlf() { | 416 | normalize_crlf() { |
402 | while read -r line; do | 417 | while IFS= read -r; do |
403 | printf '%s\n' "${line//$'\r'?($'\n')/}" | 418 | printf '%s\n' "${REPLY//$'\r'?($'\n')/}" |
404 | done | 419 | done |
405 | } | 420 | } |
406 | 421 | ||
407 | typeset_gemini() { | 422 | typeset_gemini() { |
408 | gawk ' | 423 | local pre=false |
409 | BEGIN { | 424 | local ln=0 # link number |
410 | pre = 0 | 425 | |
411 | margin = margin ? margin : 4 | 426 | if ((T_WIDTH == 0)); then |
412 | txs = "" | 427 | shopt -s checkwinsize |
413 | lns = "\033[1m" | 428 | ( |
414 | lus = "\033[36m" | 429 | : |
415 | lts = "\033[4m" | 430 | : |
416 | pfs = "" | 431 | ) # XXX this doesn't work!? |
417 | h1s = "\033[1;4m" | 432 | log d "LINES=$LINES; COLUMNS=$COLUMNS" |
418 | h2s = "\033[1m" | 433 | T_WIDTH=$COLUMNS |
419 | h3s = "\033[3m" | 434 | fi |
420 | lis = "" | 435 | |
421 | res = "\033[0m" | 436 | WIDTH=$((T_WIDTH - (T_MARGIN * 2))) |
422 | ms = "\033[35m" | 437 | ((WIDTH < 0)) && WIDTH=80 # default if dumb |
423 | } | 438 | |
424 | /```/ { | 439 | log d "T_WIDTH=$T_WIDTH" |
425 | pre = ! pre | 440 | log d "WIDTH=$WIDTH" |
426 | next | 441 | |
427 | } | 442 | while IFS= read -r; do |
428 | pre { | 443 | case "$REPLY" in |
429 | mark = "```" | 444 | '```') |
430 | fmt = pfs "%s" res | 445 | if $pre; then |
431 | text = $0 | 446 | pre=false |
432 | } | 447 | else |
433 | /^#/ { | 448 | pre=true |
434 | match($0, /#+/) | 449 | fi |
435 | mark = substr($0, RSTART, RLENGTH) | 450 | continue |
436 | sub(/#+[[:space:]]*/, "", $0) | 451 | ;; |
437 | level = length(mark) | 452 | =\>*) |
438 | if (level == 1) { | 453 | : $((ln += 1)) |
439 | fmt = h1s "%s" res | 454 | gemini_link "$REPLY" $pre "$ln" |
440 | } else if (level == 2) { | 455 | ;; |
441 | fmt = h2s "%s" res | 456 | \#*) gemini_header "$REPLY" $pre ;; |
442 | } else { | 457 | \**) gemini_list "$REPLY" $pre ;; |
443 | fmt = h3s "%s" res | 458 | *) gemini_text "$REPLY" $pre ;; |
444 | } | 459 | esac |
445 | } | 460 | done |
446 | /^=>/ { | 461 | } |
447 | mark = "=>" | 462 | |
448 | sub(/=>[[:space:]]*/, "", $0) | 463 | gemini_link() { |
449 | desc = $1 | 464 | local re="^(=>)[[:blank:]]*([^[:blank:]]+)[[:blank:]]*(.*)" |
450 | text = "" | 465 | local s t a l # sigil, text, annotation(url), line |
451 | for (w = 2; w <= NF; w++) { | 466 | if ! ${2-false} && [[ "$1" =~ $re ]]; then |
452 | text = text (text ? " " : "") $w | 467 | s="${BASH_REMATCH[1]}" |
453 | } | 468 | a="${BASH_REMATCH[2]}" |
454 | fmt = lns "[" (++ln) "]" res " " lts "%s" res "\t" lus "%s" res | 469 | t="${BASH_REMATCH[3]}" |
455 | } | 470 | if [[ -z "$t" ]]; then |
456 | /^\*[[:space:]]/ { | 471 | t="$a" |
457 | mark = "*" | 472 | a= |
458 | sub(/\*[[:space:]]*/, "", $0) | 473 | fi |
459 | fmt = lis "%s" res | 474 | |
460 | } | 475 | printf "\e[${C_SIGIL}m%-${T_MARGIN}s${C_RESET}" "$s" |
461 | { | 476 | printf -v l "\e[${C_LINK_NUMBER}m[%d]${C_RESET} \ |
462 | mark = mark ? mark : mark | 477 | \e[${C_LINK_TITLE}m%s${C_RESET} \ |
463 | fmt = fmt ? fmt : "%s" | 478 | \e[${C_LINK_URL}m%s${C_RESET}\n" \ |
464 | text = text ? text : $0 | 479 | "$3" "$t" "$a" |
465 | desc = desc ? desc : "" | 480 | fold_line "$WIDTH" "$l" |
466 | printf ms "%" (margin-1) "s " res fmt "\n", mark, text, desc | 481 | else |
467 | mark = fmt = text = desc = "" | 482 | gemini_pre "$1" |
468 | } | 483 | fi |
469 | ' | 484 | } |
485 | |||
486 | gemini_header() { | ||
487 | local re="^(#+)[[:blank:]]*(.*)" | ||
488 | local s t a l # sigil, text, annotation(lvl), line | ||
489 | if ! ${2-false} && [[ "$1" =~ $re ]]; then | ||
490 | s="${BASH_REMATCH[1]}" | ||
491 | a="${#BASH_REMATCH[1]}" | ||
492 | t="${BASH_REMATCH[2]}" | ||
493 | local hdrfmt | ||
494 | hdrfmt="$(eval echo "\$C_HEADER$a")" | ||
495 | |||
496 | printf "\e[${C_SIGIL}m%$((T_MARGIN - 1))s ${C_RESET}" "$s" | ||
497 | printf -v l "\e[${hdrfmt}m%s${C_RESET}\n" "$t" | ||
498 | fold_line "$WIDTH" "$l" | ||
499 | else | ||
500 | gemini_pre "$1" | ||
501 | fi | ||
502 | } | ||
503 | |||
504 | gemini_list() { | ||
505 | local re="^(\*)[[:blank:]]*(.*)" | ||
506 | local s t a l # sigil, text, annotation(n/a), line | ||
507 | if ! ${2-false} && [[ "$1" =~ $re ]]; then | ||
508 | s="${BASH_REMATCH[1]}" | ||
509 | t="${BASH_REMATCH[2]}" | ||
510 | |||
511 | printf "\e[${C_SIGIL}m%-${T_MARGIN}s" "$s" | ||
512 | printf -v l "\e[${C_LIST}m%s${C_RESET}\n" "$t" | ||
513 | fold_line "$WIDTH" "$l" | ||
514 | else | ||
515 | gemini_pre "$1" | ||
516 | fi | ||
517 | } | ||
518 | |||
519 | gemini_text() { | ||
520 | if ! ${2-false}; then | ||
521 | printf "%${T_MARGIN}s" ' ' | ||
522 | fold_line "$WIDTH" "$1" | ||
523 | else | ||
524 | gemini_pre "$1" | ||
525 | fi | ||
526 | } | ||
527 | |||
528 | gemini_pre() { | ||
529 | printf "\e[${C_SIGIL}m%-${T_MARGIN}s" '```' | ||
530 | printf "\e[${C_PRE}m%s${C_RESET}\n" "$1" | ||
531 | } | ||
532 | |||
533 | fold_line() { # fold_line WIDTH TEXT | ||
534 | local width="$1" | ||
535 | local margin="${2%%[![:space:]]*}" | ||
536 | if [[ "$margin" ]]; then | ||
537 | margin="${#margin}" | ||
538 | else | ||
539 | margin="$T_MARGIN" | ||
540 | fi | ||
541 | local ll=0 wl plain | ||
542 | # shellcheck disable=2086 | ||
543 | set -- $2 # TODO: is this the best way? | ||
544 | |||
545 | for word; do | ||
546 | plain="${word//$'\x1b'\[*([0-9;])m/}" | ||
547 | wl=$((${#plain} + 1)) | ||
548 | if (((ll + wl) >= width)); then | ||
549 | printf "\n%${margin}s" ' ' | ||
550 | ll=$wl | ||
551 | else | ||
552 | ll=$((ll + wl)) | ||
553 | fi | ||
554 | printf '%s ' "$word" | ||
555 | done | ||
556 | printf '\n' | ||
470 | } | 557 | } |
471 | 558 | ||
472 | handle_keypress() { | 559 | handle_keypress() { |
@@ -500,7 +587,7 @@ select_url() { | |||
500 | } | 587 | } |
501 | 588 | ||
502 | extract_links() { | 589 | extract_links() { |
503 | gawk -F$'\t' ' | 590 | gawk ' |
504 | /^=>/ { | 591 | /^=>/ { |
505 | sub(/=>[[:space:]]*/,"") | 592 | sub(/=>[[:space:]]*/,"") |
506 | if ($2) | 593 | if ($2) |
diff --git a/typeset_gemini.sh b/typeset_gemini.sh deleted file mode 100644 index 2f9abbc..0000000 --- a/typeset_gemini.sh +++ /dev/null | |||
@@ -1,172 +0,0 @@ | |||
1 | typeset_gemini() { | ||
2 | local pre=false | ||
3 | local ln=0 | ||
4 | |||
5 | while read -r line; do | ||
6 | case "$line" in | ||
7 | '```') | ||
8 | flip pre | ||
9 | continue | ||
10 | ;; | ||
11 | =\>*) | ||
12 | : $((ln += 1)) | ||
13 | gemini_link "$line" $pre | ||
14 | ;; | ||
15 | \#*) gemini_header "$line" $pre ;; | ||
16 | \**) gemini_list "$line" $pre ;; | ||
17 | *) gemini_text "$line" $pre ;; | ||
18 | esac | ||
19 | done | ||
20 | } | ||
21 | |||
22 | gemini_link() { | ||
23 | local re="^(=>)[[:blank:]]*([^[:blank:]]+)[[:blank:]]*(.*)" | ||
24 | local s t a # sigil, text, annotation(url) | ||
25 | if ! ${2-false} && [[ "$1" =~ $re ]]; then | ||
26 | s="${BASH_REMATCH[1]}" | ||
27 | t="${BASH_REMATCH[3]}" | ||
28 | a="${BASH_REMATCH[2]}" | ||
29 | |||
30 | printf "$C_SIGIL%-${MARGIN}s" "$s" | ||
31 | fold_line "$WIDTH" "$(printf "$C_LINK_TITLE%s $C_LINK_URL%s$C_RESET\n" \ | ||
32 | "$t" "$a")" | ||
33 | else | ||
34 | gemini_pre "$1" | ||
35 | fi | ||
36 | } | ||
37 | |||
38 | gemini_header() { | ||
39 | local re="^(#+)[[:blank:]]*(.*)" | ||
40 | local s t a # sigil, text, annotation(lvl) | ||
41 | if ! ${2-false} && [[ "$1" =~ $re ]]; then | ||
42 | s="${BASH_REMATCH[1]}" | ||
43 | a="${#BASH_REMATCH[1]}" | ||
44 | t="${BASH_REMATCH[2]}" | ||
45 | |||
46 | local hdrfmt | ||
47 | hdrfmt="$(eval echo "\$C_HEADER$a")" | ||
48 | printf "$C_SIGIL%-${MARGIN}s$hdrfmt%s$C_RESET\n" \ | ||
49 | "$s" "$(fold_line "$WIDTH" "$t")" | ||
50 | else | ||
51 | gemini_pre "$1" | ||
52 | fi | ||
53 | } | ||
54 | |||
55 | gemini_list() { | ||
56 | local re="^(\*)[[:blank:]]*(.*)" | ||
57 | local s t a # sigil, text, annotation(n/a) | ||
58 | if ! ${2-false} && [[ "$1" =~ $re ]]; then | ||
59 | s="${BASH_REMATCH[1]}" | ||
60 | t="${BASH_REMATCH[2]}" | ||
61 | |||
62 | printf "$C_SIGIL%-${MARGIN}s$C_LIST%s$C_RESET\n" \ | ||
63 | "$s" "$(fold_line "$WIDTH" "$t")" | ||
64 | else | ||
65 | gemini_pre "$1" | ||
66 | fi | ||
67 | } | ||
68 | |||
69 | gemini_text() { | ||
70 | printf "%${MARGIN}s" ' ' | ||
71 | if ! ${2-false}; then | ||
72 | fold_line "$WIDTH" "$1" | ||
73 | else | ||
74 | gemini_pre "$1" | ||
75 | fi | ||
76 | } | ||
77 | |||
78 | gemini_pre() { | ||
79 | printf "%${MARGIN}s%s" ' ' "$1" | ||
80 | } | ||
81 | |||
82 | flip() { # flip NAME | ||
83 | [[ "${!1}" == true || "${!1}" == false ]] || return 1 | ||
84 | |||
85 | if "${!1}"; then | ||
86 | eval "$1=false" | ||
87 | else | ||
88 | eval "$1=true" | ||
89 | fi | ||
90 | } | ||
91 | |||
92 | fold_line() { # fold_line WIDTH TEXT | ||
93 | local width="$1" | ||
94 | local ll=0 wl plain | ||
95 | # shellcheck disable=2086 | ||
96 | # TODO: determine if this is the best way to do it | ||
97 | set -- $2 | ||
98 | |||
99 | for word; do | ||
100 | plain="${word//$'\x1b'\[*([0-9;])m/}" | ||
101 | wl=$((${#plain} + 1)) | ||
102 | if (((ll + wl) >= width)); then | ||
103 | printf "\n%${MARGIN}s" ' ' | ||
104 | ll=$wl | ||
105 | else | ||
106 | ll=$((ll + wl)) | ||
107 | fi | ||
108 | printf '%s ' "$word" | ||
109 | done | ||
110 | printf '\n' | ||
111 | } | ||
112 | |||
113 | # just here for reference | ||
114 | strip() { # strip control sequences | ||
115 | # https://stackoverflow.com/a/55872518 | ||
116 | shopt -s extglob | ||
117 | while IFS='' read -r x; do | ||
118 | # remove colors | ||
119 | echo "${x//$'\x1b'\[*([0-9;])m/}" | ||
120 | done | ||
121 | } | ||
122 | |||
123 | test() { | ||
124 | MARGIN=4 | ||
125 | WIDTH=60 | ||
126 | #shopt -s checkwinsize; (:;:) | ||
127 | #WIDTH="$((COLUMNS - (MARGIN*2)))" | ||
128 | C_LINK_TITLE=$'\e[34m' | ||
129 | C_LINK_URL=$'\e[31m' | ||
130 | C_RESET=$'\e[0m' | ||
131 | typeset_gemini <<-'EOF' | ||
132 | # Project Gemini | ||
133 | |||
134 | ## Overview | ||
135 | |||
136 | Gemini is a new internet protocol which: | ||
137 | |||
138 | * Is heavier than gopher | ||
139 | * Is lighter than the web | ||
140 | * Will not replace either | ||
141 | * Strives for maximum power to weight ratio | ||
142 | * Takes user privacy very seriously | ||
143 | |||
144 | ## Resources | ||
145 | |||
146 | => docs/ Gemini documentation | ||
147 | => software/ Gemini software | ||
148 | => servers/ Known Gemini servers | ||
149 | => https://lists.orbitalfox.eu/listinfo/gemini Gemini mailing list | ||
150 | => gemini://gemini.conman.org/test/torture/ Gemini client torture test | ||
151 | |||
152 | ## Web proxies | ||
153 | |||
154 | => https://portal.mozz.us/?url=gemini%3A%2F%2Fgemini.circumlunar.space%2F&fmt=fixed Gemini-to-web proxy service | ||
155 | => https://proxy.vulpes.one/gemini/gemini.circumlunar.space Another Gemini-to-web proxy service | ||
156 | |||
157 | ## Search engines | ||
158 | |||
159 | => gemini://gus.guru/ Gemini Universal Search engine | ||
160 | => gemini://houston.coder.town Houston search engine | ||
161 | |||
162 | ## Geminispace aggregators (experimental!) | ||
163 | |||
164 | => capcom/ CAPCOM | ||
165 | => gemini://rawtext.club:1965/~sloum/spacewalk.gmi Spacewalk | ||
166 | |||
167 | ## Free Gemini hosting | ||
168 | |||
169 | => users/ Users with Gemini content on this server | ||
170 | EOF | ||
171 | } | ||
172 | test | ||