This commit is contained in:
Case Duckworth 2020-05-30 13:35:32 -05:00
parent 2e6b42e5c1
commit 482e2659ae
5 changed files with 373 additions and 157 deletions

26
fold.sh Normal file
View File

@ -0,0 +1,26 @@
#!/usr/bin/env bash
fold() {
shopt -s checkwinsize
(
:
:
)
width="${1:-$COLUMNS}"
while read -r line; do
: "${line// / }"
IFS=$'\n' read -d "" -ra words <<<"${line// /$'\n'}"
ll=0
for word in "${words[@]}"; do
wl="${#word}"
if ((ll + wl > width)); then
printf '\n'
else
((ll += wl))
printf '%s ' "$word"
fi
done
done
}
fold "$@"

57
test.gmi Normal file
View File

@ -0,0 +1,57 @@
Welcome to Conman Laboraties Gemini Server!
The Number 1 Gemini Server
(that is, the first one, not necessarily the best one)
This is your host, Sean.
=> http://boston.conman.org/ Blog
=> gopher://gopher.conman.org/1phlog.gopher Phlog
=> mailto:sean@conman.org Email
=> http://www.conman.org/people/spc/ Web
* * * * *
This 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:
=> news.txt News about this server
=> /gRFC/ Gemini Request For Comments
=> gemini://gemini.conman.org/sourcecode/ Source Code
=> https://github.com/spc476/GLV-1.12556 Source Code on Github
=> /extensions/ Custom Extensions to the Source Code
=> /sourcecode/ Alternative Link
=> /modules/ Modules
=> /source-code/ Old link
And some other content you might find intersting:
=> /hilo/ A Simple Guessing Game
=> /test/torture/ Gemini Client Torture Test
=> /test/testwrap.gemini Text wrapping samples
=> /cgi/test We support CGI scripts!
=> /scgi-sample We support SCGI programs!
=> /king_james_bible.gemini The King James Bible
=> /qotd Quote O' the Moment
=> gemini://zaibatsu.circumlunar.space/spec-spec.txt Specification
=> /test/ Test files
=> /obsolete Outdated Docs
=> /no-longer-here/ Old stuff
A 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.
=> /private/ Private Area
There'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.
=> /conman-labs-private/ Conman Labs Private Area
Other Gemini Servers---These were the first five to be created:
=> gemini://gemini.conman.org/ The First One
=> gemini://zaibatsu.circumlunar.space/ Project Gemini
=> gemini://carcosa.net/ Carcosa.Net
=> gemini://heavysquare.com/ HeavySquare
=> gemini://mozz.us/ Mozz
=> gemini://gus.guru/known-hosts And an up-to-date list of servers
Thank you for your support.

View File

@ -1,157 +0,0 @@
#!/usr/bin/env bash
# transform-url
# cf. https://tools.ietf.org/html/rfc3986#section-5 and
# cf. https://tools.ietf.org/html/rfc3986#section-5.1
# cf. also https://tools.ietf.org/html/rfc3986#appendix-B -- regex
# TEST WITH https://tools.ietf.org/html/rfc3986#section-5.4
transform_resource() { # 5.2.2
declare -A R B T # reference, base url, target
eval "$(parse_url R "$2")" # XXX CHANGE
eval "$(parse_url B "$1")"
# Basically going to follow the pseudocode in the spec.
# the '+x' bit after the fields of the arrays tests if they're set
if [[ "${R['scheme']+x}" ]]; then
T['scheme']="${R['scheme']}"
T['authority']="${R['authority']}"
T['path']="$(remove_dot_segments "${R['path']}")"
T['query']="${R['query']}"
else
if [[ "${R['authority']+x}" ]]; then
T['authority']="${R['authority']}"
T['path']="$(remove_dot_segments "${R['path']}")"
T['query']="${R['query']}"
else
if [[ "${R['path']-x}" == "" ]]; then
T['path']="${B['path']}"
if [[ "${R['query']-x}" ]]; then
T['query']="${R['query']}"
else
T['query']="${B['query']}"
fi
else
if [[ "${R['path']}" == /* ]]; then
T['path']="$(remove_dot_segments "${R['path']}")"
else
T['path']="$(merge "${B['authority']-?}" \
"${B['path']}" "${R['path']}")"
T['path']="$(remove_dot_segments "${T['path']}")"
fi
T['query']="${R['query']}"
fi
T['authority']="${B['authority']}"
fi
T['scheme']="${B['scheme']}"
fi
T['fragment']="${R['fragment']}"
# 5.3 -- recomposition
local r=""
[[ "${T['scheme']-x}" ]] &&
r="$r${T['scheme']}:"
[[ "${T['authority']-x}" ]] &&
r="$r//${T['authority']}"
r="$r${T['path']}"
[[ "${T['query']-x}" ]] &&
r="$r?${T['query']}"
[[ "${T['fragment']-x}" ]] &&
r="$r#${T['fragment']}"
printf '%s\n' "$r"
}
merge() { # 5.2.3
#>If the base URI has a defined authority component and an empty
#>path, then return a string consisting of "/" concatenated with the
#>reference's path; otherwise,
#>return a string consisting of the reference's path component
#>appended to all but the last segment of the base URI's path (i.e.,
#>excluding any characters after the right-most "/" in the base URI
#>path, or excluding the entire base URI path if it does not contain
#>any "/" characters).
B_authority="$1" # if ? is here, it means undefined (see caller)
B_path="$2"
R_path="$3"
if [[ -z "$R_path" ]]; then
printf '%q\n' "$B_path" |
sed 's,//,/,g' # XXX is this okay....?
return
fi
if [[ "${B_authority:-?}" != "?" && "${B_path-x}" == "" ]]; then
printf '/%q\n' "$R_path"
else
if [[ "$B_path" == */* ]]; then
B_path="${B_path%/*}/"
else
B_path=""
fi
printf '%q/%q\n' "$B_path" "$R_path" # XXX - %q vs %s
fi
}
# I can probably just use normalize_path already in bollux here
remove_dot_segments() { # 5.2.4
local input="$1"
local output=
while [[ -n "$input" ]]; do
if [[ "$input" == ../* || "$input" == ./* ]]; then
input="${input#*/}"
elif [[ "$input" == /./* ]]; then
input="${input#/./}/"
elif [[ "$input" == /.* ]]; then
input="${input#/.}/b"
elif [[ "$input" == /../* ]]; then
input="${input#/../}/c"
output="${output%/*}"
elif [[ "$input" == /..* ]]; then
input="${input#/..}/d"
output="${output%/*}"
elif [[ "$input" == . || "$input" == .. ]]; then
input=
else
# move the first path segment in the input buffer to the end of
# the output buffer, including the initial "/" character (if
# any) and any subsequent characters up to, but not including,
# the next "/" character or the end of the input buffer.
[[ $input =~ ^(/?[^/]*)(/?.*)$ ]] || echo NOMATCH >&2
output="$output${BASH_REMATCH[1]}"
input="${BASH_REMATCH[2]}"
fi
done
printf '%s\n' "$output" |
sed 's,//,/,g' # XXX is this okay....?
}
# *FINDING* URLS ... IN PURE BASH !!!
parse_url() { # eval "$(split_url NAME STRING)" => NAME[...]
local name="$1"
local string="$2"
local re='^(([^:/?#]+):)?(//([^/?#]*))?([^?#]*)(\?([^#]*))?(#(.*))?'
[[ $string =~ $re ]] || return $?
local scheme="${BASH_REMATCH[2]}"
local authority="${BASH_REMATCH[4]}"
local path="${BASH_REMATCH[5]}"
local query="${BASH_REMATCH[7]}"
local fragment="${BASH_REMATCH[9]}"
for c in scheme authority path query fragment; do
[[ "${!c}" ]] &&
printf '%s[%s]=%s\n' "$name" "$c" "${!c}" |
sed 's/[\|&;()<>]/\\&/g' # quote shell metacharacters
done
}
# ease-of-life functions
isdefined() { # isdefined NAME => tests if NAME is defined ONLY
[[ "${!1+x}" ]]
}
isempty() { # isempty NAME => tests if NAME is empty ONLY
[[ ! "${!1-x}" ]]
}
set -x
transform_resource "$@"
# NEXT ....
# NORMALIZATION !!!

122
typeset_gemini.awk Normal file
View File

@ -0,0 +1,122 @@
BEGIN {
pre = 0
margin = margin ? margin : 4
# Core lines
txs = "" # text style
lns = "\033[1m" # link number style
lus = "\033[36m" # link url style
lts = "\033[4m" # link text style
pfs = "" # preformatted style
# Advanced lines
h1s = "\033[1;4m" # h1 style
h2s = "\033[1m" # h2 style
h3s = "\033[3m" # h3 style
lis = "" # list item style
# Reset
res = "\033[0m" # reset style
}
/```/ {
pre = ! pre
next
}
pre {
mark = "```"
fmt = pfs "%s" res
text = $0
}
/^#/ {
match($0, /#+/)
mark = substr($0, RSTART, RLENGTH)
sub(/#+[[:space:]]*/, "", $0)
level = length(mark)
if (level == 1) {
fmt = h1s "%s" res
} else if (level == 2) {
fmt = h2s "%s" res
} else {
fmt = h3s "%s" res
}
}
/^=>/ {
mark = "=>"
sub(/=>[[:space:]]*/, "", $0)
desc = $1
text = ""
for (w = 2; w <= NF; w++) {
text = text (text ? " " : "") $w
}
fmt = lns "[" (++ln) "]" res " " lts "%s" res "\t" lus "%s" res
}
/^\*[[:space:]]/ {
mark = "*"
sub(/\*[[:space:]]*/, "", $0)
fmt = lis "%s" res
}
{
mark = mark ? mark : mark
fmt = fmt ? fmt : "%s"
text = text ? text : fold($0, " ")
desc = desc ? desc : ""
printf "%-" margin "s" fmt "\n", mark, text, desc
mark = fmt = text = desc = ""
}
function fold(str, sep, cols, out, cmd, i, j, len, chars, c, last, f, first)
{
if (! cols) {
# checks if stdout is a tty
if (system("test -t 1")) {
cols = 80
} else {
cmd = "tput cols"
cmd | getline cols
close(cmd)
}
}
# squeeze tabs and newlines to spaces
gsub(/[\t\n]/, " ", str)
# if "sep" is empty, just fold on cols with substr
if (! length(sep)) {
len = length(str)
out = substr(str, 1, cols)
for (i = cols + 1; i <= len; i += cols) {
out = out "\n"
for (j = 1; j < margin; j++) {
out = out " "
}
out = out substr(str, i, cols)
}
return out
# otherwise, we have to loop over every character (can't split() on sep, it
# would destroy the existing separators)
} else {
# split string into char array
len = split(str, chars, "")
# set boolean, used to assign the first line differently
first = 1
for (i = 1; i <= len; i += last) {
f = 0
for (c = i + cols - 1; c >= i; c--) {
if (index(sep, chars[c])) {
last = c - i + 1
f = 1
break
}
}
if (! f) {
last = cols
}
if (first) {
out = substr(str, i, last)
first = 0
} else {
out = out "\n"
for (j = 0; j < margin; j++) {
out = out " "
}
out = out substr(str, i, cols)
}
}
}
# return the output
return out
}

168
typeset_gemini.sh Normal file
View File

@ -0,0 +1,168 @@
typeset_gemini() {
local pre=false
while read -r line; do
case "$line" in
'```')
flip pre
continue
;;
=\>*) gemini_link "$line" $pre ;;
\#*) gemini_header "$line" $pre ;;
\**) gemini_list "$line" $pre ;;
*) gemini_text "$line" $pre ;;
esac
done
}
gemini_link() {
local re="^(=>)[[:blank:]]*([^[:blank:]]+)[[:blank:]]*(.*)"
local s t a # sigil, text, annotation(url)
if ! ${2-false} && [[ "$1" =~ $re ]]; then
s="${BASH_REMATCH[1]}"
t="${BASH_REMATCH[3]}"
a="${BASH_REMATCH[2]}"
printf "$C_SIGIL%-${MARGIN}s" "$s"
fold_line "$WIDTH" "$(printf "$C_LINK_TITLE%s $C_LINK_URL%s$C_RESET\n" \
"$t" "$a")"
else
gemini_pre "$1"
fi
}
gemini_header() {
local re="^(#+)[[:blank:]]*(.*)"
local s t a # sigil, text, annotation(lvl)
if ! ${2-false} && [[ "$1" =~ $re ]]; then
s="${BASH_REMATCH[1]}"
a="${#BASH_REMATCH[1]}"
t="${BASH_REMATCH[2]}"
local hdrfmt
hdrfmt="$(eval echo "\$C_HEADER$a")"
printf "$C_SIGIL%-${MARGIN}s$hdrfmt%s$C_RESET\n" \
"$s" "$(fold_line "$WIDTH" "$t")"
else
gemini_pre "$1"
fi
}
gemini_list() {
local re="^(\*)[[:blank:]]*(.*)"
local s t a # sigil, text, annotation(n/a)
if ! ${2-false} && [[ "$1" =~ $re ]]; then
s="${BASH_REMATCH[1]}"
t="${BASH_REMATCH[2]}"
printf "$C_SIGIL%-${MARGIN}s$C_LIST%s$C_RESET\n" \
"$s" "$(fold_line "$WIDTH" "$t")"
else
gemini_pre "$1"
fi
}
gemini_text() {
printf "%${MARGIN}s" ' '
if ! ${2-false}; then
fold_line "$WIDTH" "$1"
else
gemini_pre "$1"
fi
}
gemini_pre() {
printf "%${MARGIN}s%s" ' ' "$1"
}
flip() { # flip NAME
[[ "${!1}" == true || "${!1}" == false ]] || return 1
if "${!1}"; then
eval "$1=false"
else
eval "$1=true"
fi
}
fold_line() { # fold_line WIDTH TEXT
local width="$1"
local ll=0 wl plain
# shellcheck disable=2086
# TODO: determine if this is the best way to do it
set -- $2
for word; do
plain="${word//$'\x1b'\[*([0-9;])m/}"
wl=$((${#plain} + 1))
if (((ll + wl) >= width)); then
printf "\n%${MARGIN}s" ' '
ll=$wl
else
ll=$((ll + wl))
fi
printf '%s ' "$word"
done
printf '\n'
}
# just here for reference
strip() { # strip control sequences
# https://stackoverflow.com/a/55872518
shopt -s extglob
while IFS='' read -r x; do
# remove colors
echo "${x//$'\x1b'\[*([0-9;])m/}"
done
}
test() {
MARGIN=4
WIDTH=60
#shopt -s checkwinsize; (:;:)
#WIDTH="$((COLUMNS - (MARGIN*2)))"
C_LINK_TITLE=$'\e[34m'
C_LINK_URL=$'\e[31m'
C_RESET=$'\e[0m'
typeset_gemini <<-'EOF'
# Project Gemini
## Overview
Gemini is a new internet protocol which:
* Is heavier than gopher
* Is lighter than the web
* Will not replace either
* Strives for maximum power to weight ratio
* Takes user privacy very seriously
## Resources
=> docs/ Gemini documentation
=> software/ Gemini software
=> servers/ Known Gemini servers
=> https://lists.orbitalfox.eu/listinfo/gemini Gemini mailing list
=> gemini://gemini.conman.org/test/torture/ Gemini client torture test
## Web proxies
=> https://portal.mozz.us/?url=gemini%3A%2F%2Fgemini.circumlunar.space%2F&fmt=fixed Gemini-to-web proxy service
=> https://proxy.vulpes.one/gemini/gemini.circumlunar.space Another Gemini-to-web proxy service
## Search engines
=> gemini://gus.guru/ Gemini Universal Search engine
=> gemini://houston.coder.town Houston search engine
## Geminispace aggregators (experimental!)
=> capcom/ CAPCOM
=> gemini://rawtext.club:1965/~sloum/spacewalk.gmi Spacewalk
## Free Gemini hosting
=> users/ Users with Gemini content on this server
EOF
}
test