about summary refs log tree commit diff stats
diff options
context:
space:
mode:
authorCase Duckworth2020-05-30 13:35:32 -0500
committerCase Duckworth2020-05-30 13:35:32 -0500
commit482e2659aef19691fd0196e6744bc7448e908757 (patch)
tree14e88b05951eae859130ff971a65de0e2e0d1b04
parentStart testing transform_uri (diff)
downloadbollux-482e2659aef19691fd0196e6744bc7448e908757.tar.gz
bollux-482e2659aef19691fd0196e6744bc7448e908757.zip
Backup
-rw-r--r--fold.sh26
-rw-r--r--test.gmi57
-rw-r--r--transform_uri.sh157
-rw-r--r--typeset_gemini.awk122
-rw-r--r--typeset_gemini.sh168
5 files changed, 373 insertions, 157 deletions
diff --git a/fold.sh b/fold.sh new file mode 100644 index 0000000..ee88c6e --- /dev/null +++ b/fold.sh
@@ -0,0 +1,26 @@
1#!/usr/bin/env bash
2
3fold() {
4 shopt -s checkwinsize
5 (
6 :
7 :
8 )
9 width="${1:-$COLUMNS}"
10 while read -r line; do
11 : "${line// / }"
12 IFS=$'\n' read -d "" -ra words <<<"${line// /$'\n'}"
13 ll=0
14 for word in "${words[@]}"; do
15 wl="${#word}"
16 if ((ll + wl > width)); then
17 printf '\n'
18 else
19 ((ll += wl))
20 printf '%s ' "$word"
21 fi
22 done
23 done
24}
25
26fold "$@"
diff --git a/test.gmi b/test.gmi new file mode 100644 index 0000000..d7cce94 --- /dev/null +++ b/test.gmi
@@ -0,0 +1,57 @@
1 Welcome to Conman Laboraties Gemini Server!
2 The Number 1 Gemini Server
3 (that is, the first one, not necessarily the best one)
4
5 This is your host, Sean.
6
7=> http://boston.conman.org/ Blog
8=> gopher://gopher.conman.org/1phlog.gopher Phlog
9=> mailto:sean@conman.org Email
10=> http://www.conman.org/people/spc/ Web
11
12 * * * * *
13
14This is the site of the first Gemini servers in existance. Things are pretty rough around here as we work out what exactly is needed and what isn't. If you want to check out the source code that runs this place:
15
16=> news.txt News about this server
17=> /gRFC/ Gemini Request For Comments
18=> gemini://gemini.conman.org/sourcecode/ Source Code
19=> https://github.com/spc476/GLV-1.12556 Source Code on Github
20=> /extensions/ Custom Extensions to the Source Code
21=> /sourcecode/ Alternative Link
22=> /modules/ Modules
23=> /source-code/ Old link
24
25And some other content you might find intersting:
26
27=> /hilo/ A Simple Guessing Game
28=> /test/torture/ Gemini Client Torture Test
29=> /test/testwrap.gemini Text wrapping samples
30=> /cgi/test We support CGI scripts!
31=> /scgi-sample We support SCGI programs!
32=> /king_james_bible.gemini The King James Bible
33=> /qotd Quote O' the Moment
34=> gemini://zaibatsu.circumlunar.space/spec-spec.txt Specification
35=> /test/ Test files
36=> /obsolete Outdated Docs
37=> /no-longer-here/ Old stuff
38
39A Private Area is available for your perusal---all that's needed is a client certificate to be presented. Any client certificate will do. Just create one (there are many guides online for how to do it), and ensure it's available when making a request to this area.
40
41=> /private/ Private Area
42
43There's also the Conman Labs Private Area. This is off limits to non-authorized personel. Please contact sean@conman.org about getting access to this restricted area.
44
45=> /conman-labs-private/ Conman Labs Private Area
46
47Other Gemini Servers---These were the first five to be created:
48
49=> gemini://gemini.conman.org/ The First One
50=> gemini://zaibatsu.circumlunar.space/ Project Gemini
51=> gemini://carcosa.net/ Carcosa.Net
52=> gemini://heavysquare.com/ HeavySquare
53=> gemini://mozz.us/ Mozz
54
55=> gemini://gus.guru/known-hosts And an up-to-date list of servers
56
57Thank you for your support.
diff --git a/transform_uri.sh b/transform_uri.sh deleted file mode 100644 index e9c9fc9..0000000 --- a/transform_uri.sh +++ /dev/null
@@ -1,157 +0,0 @@
1#!/usr/bin/env bash
2# transform-url
3# cf. https://tools.ietf.org/html/rfc3986#section-5 and
4# cf. https://tools.ietf.org/html/rfc3986#section-5.1
5# cf. also https://tools.ietf.org/html/rfc3986#appendix-B -- regex
6
7# TEST WITH https://tools.ietf.org/html/rfc3986#section-5.4
8
9transform_resource() { # 5.2.2
10 declare -A R B T # reference, base url, target
11 eval "$(parse_url R "$2")" # XXX CHANGE
12 eval "$(parse_url B "$1")"
13 # Basically going to follow the pseudocode in the spec.
14 # the '+x' bit after the fields of the arrays tests if they're set
15 if [[ "${R['scheme']+x}" ]]; then
16 T['scheme']="${R['scheme']}"
17 T['authority']="${R['authority']}"
18 T['path']="$(remove_dot_segments "${R['path']}")"
19 T['query']="${R['query']}"
20 else
21 if [[ "${R['authority']+x}" ]]; then
22 T['authority']="${R['authority']}"
23 T['path']="$(remove_dot_segments "${R['path']}")"
24 T['query']="${R['query']}"
25 else
26 if [[ "${R['path']-x}" == "" ]]; then
27 T['path']="${B['path']}"
28 if [[ "${R['query']-x}" ]]; then
29 T['query']="${R['query']}"
30 else
31 T['query']="${B['query']}"
32 fi
33 else
34 if [[ "${R['path']}" == /* ]]; then
35 T['path']="$(remove_dot_segments "${R['path']}")"
36 else
37 T['path']="$(merge "${B['authority']-?}" \
38 "${B['path']}" "${R['path']}")"
39 T['path']="$(remove_dot_segments "${T['path']}")"
40 fi
41 T['query']="${R['query']}"
42 fi
43 T['authority']="${B['authority']}"
44 fi
45 T['scheme']="${B['scheme']}"
46 fi
47 T['fragment']="${R['fragment']}"
48 # 5.3 -- recomposition
49 local r=""
50 [[ "${T['scheme']-x}" ]] &&
51 r="$r${T['scheme']}:"
52 [[ "${T['authority']-x}" ]] &&
53 r="$r//${T['authority']}"
54 r="$r${T['path']}"
55 [[ "${T['query']-x}" ]] &&
56 r="$r?${T['query']}"
57 [[ "${T['fragment']-x}" ]] &&
58 r="$r#${T['fragment']}"
59 printf '%s\n' "$r"
60}
61
62merge() { # 5.2.3
63 #>If the base URI has a defined authority component and an empty
64 #>path, then return a string consisting of "/" concatenated with the
65 #>reference's path; otherwise,
66 #>return a string consisting of the reference's path component
67 #>appended to all but the last segment of the base URI's path (i.e.,
68 #>excluding any characters after the right-most "/" in the base URI
69 #>path, or excluding the entire base URI path if it does not contain
70 #>any "/" characters).
71 B_authority="$1" # if ? is here, it means undefined (see caller)
72 B_path="$2"
73 R_path="$3"
74 if [[ -z "$R_path" ]]; then
75 printf '%q\n' "$B_path" |
76 sed 's,//,/,g' # XXX is this okay....?
77 return
78 fi
79
80 if [[ "${B_authority:-?}" != "?" && "${B_path-x}" == "" ]]; then
81 printf '/%q\n' "$R_path"
82 else
83 if [[ "$B_path" == */* ]]; then
84 B_path="${B_path%/*}/"
85 else
86 B_path=""
87 fi
88 printf '%q/%q\n' "$B_path" "$R_path" # XXX - %q vs %s
89 fi
90}
91
92# I can probably just use normalize_path already in bollux here
93remove_dot_segments() { # 5.2.4
94 local input="$1"
95 local output=
96 while [[ -n "$input" ]]; do
97 if [[ "$input" == ../* || "$input" == ./* ]]; then
98 input="${input#*/}"
99 elif [[ "$input" == /./* ]]; then
100 input="${input#/./}/"
101 elif [[ "$input" == /.* ]]; then
102 input="${input#/.}/b"
103 elif [[ "$input" == /../* ]]; then
104 input="${input#/../}/c"
105 output="${output%/*}"
106 elif [[ "$input" == /..* ]]; then
107 input="${input#/..}/d"
108 output="${output%/*}"
109 elif [[ "$input" == . || "$input" == .. ]]; then
110 input=
111 else
112 # move the first path segment in the input buffer to the end of
113 # the output buffer, including the initial "/" character (if
114 # any) and any subsequent characters up to, but not including,
115 # the next "/" character or the end of the input buffer.
116 [[ $input =~ ^(/?[^/]*)(/?.*)$ ]] || echo NOMATCH >&2
117 output="$output${BASH_REMATCH[1]}"
118 input="${BASH_REMATCH[2]}"
119 fi
120 done
121 printf '%s\n' "$output" |
122 sed 's,//,/,g' # XXX is this okay....?
123}
124
125# *FINDING* URLS ... IN PURE BASH !!!
126parse_url() { # eval "$(split_url NAME STRING)" => NAME[...]
127 local name="$1"
128 local string="$2"
129 local re='^(([^:/?#]+):)?(//([^/?#]*))?([^?#]*)(\?([^#]*))?(#(.*))?'
130 [[ $string =~ $re ]] || return $?
131
132 local scheme="${BASH_REMATCH[2]}"
133 local authority="${BASH_REMATCH[4]}"
134 local path="${BASH_REMATCH[5]}"
135 local query="${BASH_REMATCH[7]}"
136 local fragment="${BASH_REMATCH[9]}"
137
138 for c in scheme authority path query fragment; do
139 [[ "${!c}" ]] &&
140 printf '%s[%s]=%s\n' "$name" "$c" "${!c}" |
141 sed 's/[\|&;()<>]/\\&/g' # quote shell metacharacters
142 done
143}
144
145# ease-of-life functions
146isdefined() { # isdefined NAME => tests if NAME is defined ONLY
147 [[ "${!1+x}" ]]
148}
149isempty() { # isempty NAME => tests if NAME is empty ONLY
150 [[ ! "${!1-x}" ]]
151}
152
153set -x
154transform_resource "$@"
155
156# NEXT ....
157# NORMALIZATION !!!
diff --git a/typeset_gemini.awk b/typeset_gemini.awk new file mode 100644 index 0000000..eb5b7e1 --- /dev/null +++ b/typeset_gemini.awk
@@ -0,0 +1,122 @@
1BEGIN {
2 pre = 0
3 margin = margin ? margin : 4
4 # Core lines
5 txs = "" # text style
6 lns = "\033[1m" # link number style
7 lus = "\033[36m" # link url style
8 lts = "\033[4m" # link text style
9 pfs = "" # preformatted style
10 # Advanced lines
11 h1s = "\033[1;4m" # h1 style
12 h2s = "\033[1m" # h2 style
13 h3s = "\033[3m" # h3 style
14 lis = "" # list item style
15 # Reset
16 res = "\033[0m" # reset style
17}
18/```/ {
19 pre = ! pre
20 next
21}
22pre {
23 mark = "```"
24 fmt = pfs "%s" res
25 text = $0
26}
27/^#/ {
28 match($0, /#+/)
29 mark = substr($0, RSTART, RLENGTH)
30 sub(/#+[[:space:]]*/, "", $0)
31 level = length(mark)
32 if (level == 1) {
33 fmt = h1s "%s" res
34 } else if (level == 2) {
35 fmt = h2s "%s" res
36 } else {
37 fmt = h3s "%s" res
38 }
39}
40/^=>/ {
41 mark = "=>"
42 sub(/=>[[:space:]]*/, "", $0)
43 desc = $1
44 text = ""
45 for (w = 2; w <= NF; w++) {
46 text = text (text ? " " : "") $w
47 }
48 fmt = lns "[" (++ln) "]" res " " lts "%s" res "\t" lus "%s" res
49}
50/^\*[[:space:]]/ {
51 mark = "*"
52 sub(/\*[[:space:]]*/, "", $0)
53 fmt = lis "%s" res
54}
55{
56 mark = mark ? mark : mark
57 fmt = fmt ? fmt : "%s"
58 text = text ? text : fold($0, " ")
59 desc = desc ? desc : ""
60 printf "%-" margin "s" fmt "\n", mark, text, desc
61 mark = fmt = text = desc = ""
62}
63function fold(str, sep, cols, out, cmd, i, j, len, chars, c, last, f, first)
64{
65 if (! cols) {
66 # checks if stdout is a tty
67 if (system("test -t 1")) {
68 cols = 80
69 } else {
70 cmd = "tput cols"
71 cmd | getline cols
72 close(cmd)
73 }
74 }
75 # squeeze tabs and newlines to spaces
76 gsub(/[\t\n]/, " ", str)
77 # if "sep" is empty, just fold on cols with substr
78 if (! length(sep)) {
79 len = length(str)
80 out = substr(str, 1, cols)
81 for (i = cols + 1; i <= len; i += cols) {
82 out = out "\n"
83 for (j = 1; j < margin; j++) {
84 out = out " "
85 }
86 out = out substr(str, i, cols)
87 }
88 return out
89 # otherwise, we have to loop over every character (can't split() on sep, it
90 # would destroy the existing separators)
91 } else {
92 # split string into char array
93 len = split(str, chars, "")
94 # set boolean, used to assign the first line differently
95 first = 1
96 for (i = 1; i <= len; i += last) {
97 f = 0
98 for (c = i + cols - 1; c >= i; c--) {
99 if (index(sep, chars[c])) {
100 last = c - i + 1
101 f = 1
102 break
103 }
104 }
105 if (! f) {
106 last = cols
107 }
108 if (first) {
109 out = substr(str, i, last)
110 first = 0
111 } else {
112 out = out "\n"
113 for (j = 0; j < margin; j++) {
114 out = out " "
115 }
116 out = out substr(str, i, cols)
117 }
118 }
119 }
120 # return the output
121 return out
122}
diff --git a/typeset_gemini.sh b/typeset_gemini.sh new file mode 100644 index 0000000..e8aed41 --- /dev/null +++ b/typeset_gemini.sh
@@ -0,0 +1,168 @@
1typeset_gemini() {
2 local pre=false
3
4 while read -r line; do
5 case "$line" in
6 '```')
7 flip pre
8 continue
9 ;;
10 =\>*) gemini_link "$line" $pre ;;
11 \#*) gemini_header "$line" $pre ;;
12 \**) gemini_list "$line" $pre ;;
13 *) gemini_text "$line" $pre ;;
14 esac
15 done
16}
17
18gemini_link() {
19 local re="^(=>)[[:blank:]]*([^[:blank:]]+)[[:blank:]]*(.*)"
20 local s t a # sigil, text, annotation(url)
21 if ! ${2-false} && [[ "$1" =~ $re ]]; then
22 s="${BASH_REMATCH[1]}"
23 t="${BASH_REMATCH[3]}"
24 a="${BASH_REMATCH[2]}"
25
26 printf "$C_SIGIL%-${MARGIN}s" "$s"
27 fold_line "$WIDTH" "$(printf "$C_LINK_TITLE%s $C_LINK_URL%s$C_RESET\n" \
28 "$t" "$a")"
29 else
30 gemini_pre "$1"
31 fi
32}
33
34gemini_header() {
35 local re="^(#+)[[:blank:]]*(.*)"
36 local s t a # sigil, text, annotation(lvl)
37 if ! ${2-false} && [[ "$1" =~ $re ]]; then
38 s="${BASH_REMATCH[1]}"
39 a="${#BASH_REMATCH[1]}"
40 t="${BASH_REMATCH[2]}"
41
42 local hdrfmt
43 hdrfmt="$(eval echo "\$C_HEADER$a")"
44 printf "$C_SIGIL%-${MARGIN}s$hdrfmt%s$C_RESET\n" \
45 "$s" "$(fold_line "$WIDTH" "$t")"
46 else
47 gemini_pre "$1"
48 fi
49}
50
51gemini_list() {
52 local re="^(\*)[[:blank:]]*(.*)"
53 local s t a # sigil, text, annotation(n/a)
54 if ! ${2-false} && [[ "$1" =~ $re ]]; then
55 s="${BASH_REMATCH[1]}"
56 t="${BASH_REMATCH[2]}"
57
58 printf "$C_SIGIL%-${MARGIN}s$C_LIST%s$C_RESET\n" \
59 "$s" "$(fold_line "$WIDTH" "$t")"
60 else
61 gemini_pre "$1"
62 fi
63}
64
65gemini_text() {
66 printf "%${MARGIN}s" ' '
67 if ! ${2-false}; then
68 fold_line "$WIDTH" "$1"
69 else
70 gemini_pre "$1"
71 fi
72}
73
74gemini_pre() {
75 printf "%${MARGIN}s%s" ' ' "$1"
76}
77
78flip() { # flip NAME
79 [[ "${!1}" == true || "${!1}" == false ]] || return 1
80
81 if "${!1}"; then
82 eval "$1=false"
83 else
84 eval "$1=true"
85 fi
86}
87
88fold_line() { # fold_line WIDTH TEXT
89 local width="$1"
90 local ll=0 wl plain
91 # shellcheck disable=2086
92 # TODO: determine if this is the best way to do it
93 set -- $2
94
95 for word; do
96 plain="${word//$'\x1b'\[*([0-9;])m/}"
97 wl=$((${#plain} + 1))
98 if (((ll + wl) >= width)); then
99 printf "\n%${MARGIN}s" ' '
100 ll=$wl
101 else
102 ll=$((ll + wl))
103 fi
104 printf '%s ' "$word"
105 done
106 printf '\n'
107}
108
109# just here for reference
110strip() { # strip control sequences
111 # https://stackoverflow.com/a/55872518
112 shopt -s extglob
113 while IFS='' read -r x; do
114 # remove colors
115 echo "${x//$'\x1b'\[*([0-9;])m/}"
116 done
117}
118
119test() {
120 MARGIN=4
121 WIDTH=60
122 #shopt -s checkwinsize; (:;:)
123 #WIDTH="$((COLUMNS - (MARGIN*2)))"
124 C_LINK_TITLE=$'\e[34m'
125 C_LINK_URL=$'\e[31m'
126 C_RESET=$'\e[0m'
127 typeset_gemini <<-'EOF'
128 # Project Gemini
129
130 ## Overview
131
132 Gemini is a new internet protocol which:
133
134 * Is heavier than gopher
135 * Is lighter than the web
136 * Will not replace either
137 * Strives for maximum power to weight ratio
138 * Takes user privacy very seriously
139
140 ## Resources
141
142 => docs/ Gemini documentation
143 => software/ Gemini software
144 => servers/ Known Gemini servers
145 => https://lists.orbitalfox.eu/listinfo/gemini Gemini mailing list
146 => gemini://gemini.conman.org/test/torture/ Gemini client torture test
147
148 ## Web proxies
149
150 => https://portal.mozz.us/?url=gemini%3A%2F%2Fgemini.circumlunar.space%2F&fmt=fixed Gemini-to-web proxy service
151 => https://proxy.vulpes.one/gemini/gemini.circumlunar.space Another Gemini-to-web proxy service
152
153 ## Search engines
154
155 => gemini://gus.guru/ Gemini Universal Search engine
156 => gemini://houston.coder.town Houston search engine
157
158 ## Geminispace aggregators (experimental!)
159
160 => capcom/ CAPCOM
161 => gemini://rawtext.club:1965/~sloum/spacewalk.gmi Spacewalk
162
163 ## Free Gemini hosting
164
165 => users/ Users with Gemini content on this server
166 EOF
167}
168test