From 167bbbfb84e17ddef81bd9ef7abc6b430d044b94 Mon Sep 17 00:00:00 2001 From: Mitya Selivanov Date: Mon, 12 Aug 2024 09:07:48 +0200 Subject: Add code --- Dockerfile | 24 + bootstrap.sh | 285 +++++ compile_flags.txt | 4 + main.c | 3351 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 3664 insertions(+) create mode 100644 Dockerfile create mode 100644 bootstrap.sh create mode 100644 compile_flags.txt create mode 100755 main.c diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..15f1596 --- /dev/null +++ b/Dockerfile @@ -0,0 +1,24 @@ +FROM nginx:alpine + +RUN apk add git-daemon cgit certbot certbot-nginx fcgiwrap apache2-utils gcc musl-dev +RUN adduser -D -h /srv/git git + +WORKDIR /usr/build/ +COPY bootstrap.sh . +COPY main.c . +COPY static ./static + +RUN DEBIAN_FRONTEND=noninteractive sh bootstrap.sh + +RUN printf "\ + git daemon --reuseaddr --base-path=/srv/git/ /srv/git/ &\n\ + fcgiwrap -f &\n\ + nginx &\n\ + wait -n\n\ + exit $?\ +" >> /usr/local/bin/entrypoint + +# ssh, http, https, git +EXPOSE 22 80 443 9418 + +ENTRYPOINT [ "entrypoint" ] diff --git a/bootstrap.sh b/bootstrap.sh new file mode 100644 index 0000000..b0e9eff --- /dev/null +++ b/bootstrap.sh @@ -0,0 +1,285 @@ +echo "[ 1/9] Setting up Git" + +if ! id git >/dev/null 2>&1; then + useradd -m -b /srv git + [ $? -eq 0 ] || exit $? +else + echo "[SKIP] Git user already created." +fi + +GIT_HOME=$( grep git /etc/passwd | cut -d: -f6 ) + +if [ "$GIT_HOME" = "" ]; then + echo "[ERROR] No git home!" + exit 1 +fi + +echo "[INFO] Git home: $GIT_HOME" + +if ! command -v git >/dev/null 2>&1; then + apt-get install -y git + [ $? -eq 0 ] || exit $? +else + echo "[SKIP] Git already installed." +fi + +if ls /usr/lib/git-core/git-http-backend >/dev/null 2>&1; then + GIT_HTTP_BACKEND=/usr/lib/git-core/git-http-backend +elif ls /usr/libexec/git-core/git-http-backend >/dev/null 2>&1; then + GIT_HTTP_BACKEND=/usr/libexec/git-core/git-http-backend +else + echo "[ERROR] git-http-backend not found!" + exit 1 +fi + +echo "[INFO] git-http-backend: $GIT_HTTP_BACKEND" + +echo "[ 2/9] Setting up cgit" + +if [ ! command -v /usr/share/webapps/cgit/cgit.cgi >/dev/null 2>&1 ] && + [ ! command -v /usr/lib/cgit/cgit.cgi >/dev/null 2>&1 ]; then + apt-get install -y cgit + [ $? -eq 0 ] || exit $? +else + echo "[SKIP] cgit already installed." +fi + +if command -v /usr/share/webapps/cgit/cgit.cgi >/dev/null 2>&1; then + CGIT_CGI=/usr/share/webapps/cgit/cgit.cgi +elif command -v /usr/lib/cgit/cgit.cgi >/dev/null 2>&1; then + CGIT_CGI=/usr/lib/cgit/cgit.cgi +else + echo "[ERROR] cgit not found!" + exit 1 +fi + +echo "[INFO] Found cgit: $CGIT_CGI" + +echo "[ 3/9] Setting up nginx" + +if ! command -v nginx >/dev/null 2>&1; then + apt-get install -y nginx + [ $? -eq 0 ] || exit $? +else + echo "[SKIP] nginx already installed." +fi + +echo "[ 4/9] Setting up certbot" + +if ! command -v certbot >/dev/null 2>&1; then + apt-get install -y python3-certbot-nginx + [ $? -eq 0 ] || exit $? +else + echo "[SKIP] certbot already installed." +fi + +echo "[ 5/9] Setting up fcgiwrap" + +if ! command -v fcgiwrap >/dev/null 2>&1; then + apt-get install -y fcgiwrap + [ $? -eq 0 ] || exit $? +else + echo "[SKIP] fcgiwrap already installed." +fi + +echo "[ 6/9] Setting up htpasswd" + +if ! command -v htpasswd >/dev/null 2>&1; then + apt-get install -y apache2-utils + [ $? -eq 0 ] || exit $? +else + echo "[SKIP] htpasswd already installed." +fi + +echo "[ 7/9] Setting up GCC" + +if ! command -v gcc >/dev/null 2>&1; then + apt-get install -y gcc + [ $? -eq 0 ] || exit $? +else + echo "[SKIP] GCC already installed." +fi + +echo "[ 8/9] Compiling and setting up CGI program" + +COMPILE="\ + -Wno-old-style-declaration \ + -Wno-missing-field-initializers -Wno-missing-braces \ + -Wall -Wextra -Werror -pedantic -mshstk \ + -O3 -o main main.c" + +SAN=-fsanitize=address,undefined,leak + +if gcc $SAN $COMPILE >/dev/null 2>&1; then + gcc $SAN $COMPILE + [ $? -eq 0 ] || exit $? +else + echo "[INFO] Sanitizers are disabled." + gcc $COMPILE + [ $? -eq 0 ] || exit $? +fi + +chown root:root main +[ $? -eq 0 ] || exit $? + +mv -f main /srv/ +[ $? -eq 0 ] || exit $? + +mv -f static /srv/static +[ $? -eq 0 ] || exit $? + +echo "[ 9/9] Configuring" + +if [ ! -d /etc/nginx/sites-available ]; then + mkdir /etc/nginx/sites-available + [ $? -eq 0 ] || exit $? +fi + +if [ ! -d /etc/nginx/sites-enabled ]; then + mkdir /etc/nginx/sites-enabled + [ $? -eq 0 ] || exit $? +fi + +if [ ! -f /etc/nginx/sites-enabled/default ]; then + ln -s /etc/nginx/sites-available/default /etc/nginx/sites-enabled/default + [ $? -eq 0 ] || exit $? +fi + +cat </etc/nginx/sites-available/default +server { + listen 80; + listen [::]:80; + + server_name _; + + location ~ ^/git_write/ { + rewrite ^/git_write/(.*) /\$1 break; + + auth_basic "Git"; + auth_basic_user_file $GIT_HOME.htpasswd; + + include fastcgi_params; + fastcgi_param SCRIPT_FILENAME $GIT_HTTP_BACKEND; + fastcgi_param GIT_PROJECT_ROOT $GIT_HOME; + fastcgi_param PATH_INFO \$uri; + fastcgi_pass unix:/var/run/fcgiwrap.socket; + } + + location ~ ^/git_read/ { + rewrite ^/git_read/(.*) /\$1 break; + + include fastcgi_params; + fastcgi_param SCRIPT_FILENAME $GIT_HTTP_BACKEND; + fastcgi_param GIT_PROJECT_ROOT $GIT_HOME; + fastcgi_param PATH_INFO \$uri; + fastcgi_pass unix:/var/run/fcgiwrap.socket; + } + + location ~ \\.git { + if (\$arg_service = git-receive-pack) { + rewrite /(.*) /git_write/\$1 last; + } + if (\$uri ~ ^/.*/git-receive-pack\$) { + rewrite /(.*) /git_write/\$1 last; + } + if (\$arg_service = git-upload-pack) { + rewrite /(.*) /git_read/\$1 last; + } + if (\$uri ~ ^/.*/git-upload-pack\$) { + rewrite /(.*) /git_read/\$1 last; + } + } + + location ^~ /git/ { + rewrite ^/git/(.*) /\$1 break; + include fastcgi_params; + fastcgi_param SCRIPT_FILENAME $CGIT_CGI; + fastcgi_param PATH_INFO \$uri; + fastcgi_param QUERY_STRING \$args; + fastcgi_pass unix:/var/run/fcgiwrap.socket; + } + + location ~* \\.(txt|asc|htm|css|svg|jpg|png|gif|ico|woff|woff2|js|wasm|mp3)\$ { + rewrite ^/(.*) /static/plain/\$1 break; + include fastcgi_params; + fastcgi_param SCRIPT_FILENAME $CGIT_CGI; + fastcgi_param PATH_INFO \$uri; + fastcgi_param QUERY_STRING \$args; + fastcgi_pass unix:/var/run/fcgiwrap.socket; + } + + location / { + include /etc/nginx/fastcgi_params; + fastcgi_param SCRIPT_FILENAME /srv/main.cgi; + fastcgi_pass unix:/var/run/fcgiwrap.socket; + } +} +EOF +[ $? -eq 0 ] || exit $? + +echo "[INFO] Written /etc/nginx/sites-available/default:" + +cat /etc/nginx/sites-available/default + +cat </etc/nginx/mime.types +types { + text/plain txt; + text/plain asc; + text/html htm; + text/css css; + image/svg+xml svg; + image/jpeg jpg; + image/png png; + image/gif gif; + image/x-icon ico; + application/font-woff woff; + application/font-woff2 woff2; + application/javascript js; + application/wasm wasm; + audio/mpeg mp3; +} +EOF +[ $? -eq 0 ] || exit $? + +echo "[INFO] Written /etc/nginx/mime.types:" + +cat /etc/nginx/mime.types + +cat </etc/nginx/nginx.conf +user git; +worker_processes 1; +pid /run/nginx.pid; +include /etc/nginx/modules-enabled/*.conf; + +events { + worker_connections 768; +} + +http { + sendfile on; + tcp_nopush on; + types_hash_max_size 2048; + server_names_hash_bucket_size 256; + + include /etc/nginx/mime.types; + default_type application/octet-stream; + + ssl_protocols TLSv1 TLSv1.1 TLSv1.2 TLSv1.3; + ssl_prefer_server_ciphers on; + + access_log /var/log/nginx/access.log; + error_log /var/log/nginx/error.log; + + gzip on; + + include /etc/nginx/conf.d/*.conf; + include /etc/nginx/sites-enabled/*; +} +EOF +[ $? -eq 0 ] || exit $? + +echo "[INFO] Written /etc/nginx/nginx.conf:" + +cat /etc/nginx/nginx.conf + +echo "[INFO] All done!" diff --git a/compile_flags.txt b/compile_flags.txt new file mode 100644 index 0000000..1a27fb0 --- /dev/null +++ b/compile_flags.txt @@ -0,0 +1,4 @@ +-Wall +-Wextra +-Werror +-pedantic diff --git a/main.c b/main.c new file mode 100755 index 0000000..975d9fd --- /dev/null +++ b/main.c @@ -0,0 +1,3351 @@ +#if 0 /* +#/ ================================================================ +#/ +#/ main.c +#/ +#/ CGI program to render HTTP-only web site. +#/ +#/ ---------------------------------------------------------------- +#/ +#/ Qualities +#/ +#/ - Single source file +#/ - No JavaScript +#/ - No external dependencies +#/ - No dynamic memory management +#/ - Simple bootstrap script +#/ - Not a single LLM-generated line of code +#/ +#/ NOTE +#/ +#/ - At the start of this file you can see a shell script +#/ that will compile and install the program into `/srv/` +#/ +#/ TODO +#/ +#/ - CGI program +#/ - Hierarchical tags. +#/ - Blacklist and throttling. +#/ - Wrap other CGI programs. +#/ - CI terminal +#/ - Convay's Game of Life with `` +#/ - Bootstrap script +#/ - ssh +#/ - git config +#/ - highlight +#/ - tor, onion service +#/ - daemons +#/ - ufw +#/ +#/ ================================================================ + +USR=git +if [ -n "$1" ]; then + USR=$1 +fi +SRC=${0##*./} +BIN=${SRC%.*}.cgi +export REQUEST_METHOD=GET +export DOCUMENT_URI=/theme +gcc \ + -Wno-old-style-declaration \ + -Wno-missing-field-initializers -Wno-missing-braces \ + -Wimplicit-fallthrough=0 \ + -Wall -Wextra -Werror -pedantic \ + -fsanitize=address,undefined,leak -mshstk -O3 \ + -o $BIN $SRC \ + && ./$BIN \ + && sudo chown root:root $BIN \ + && sudo mv -f $BIN /srv/ +exit $? # */ +#endif + +// ================================================================ +// +// Headers +// +// ================================================================ + +#include +#include +#include +#include + +// ================================================================ +// +// Configuration +// +// ================================================================ + +#define COOKIE_THEME "theme" + +#define FORM_NAME "submit" +#define FORM_ITEM_DATA "data" + +enum { + DEFAULT_SCALE = 100, + DEFAULT_WIDTH = 42, + + SCALE_MIN = 1, + SCALE_MAX = 1000, + WIDTH_MIN = 20, + WIDTH_MAX = 100, + + MAX_REQUEST_METHOD_SIZE = 8, + MAX_REQUEST_SIZE = 512, + MAX_COOKIE_SIZE = 512, + MAX_CONTENT_TYPE_SIZE = 64, +}; + +// ================================================================ +// +// Basic definitions +// +// ================================================================ + +typedef unsigned char u8; +typedef int i32; +typedef unsigned int u32; +typedef long long i64; +typedef unsigned long long u64; +typedef signed char b8; +typedef char c8; + +typedef struct { + c8 const *family; + c8 const *file; + c8 const *weight; + c8 const *style; +} Font; + +typedef struct { + c8 const *text; + c8 const *href; + c8 const *target; +} Link; + +typedef struct { + i32 type; + i32 color; + i32 background; + i32 width; + i32 layout; + i32 icon; + i32 action; + i32 padding_top; + i32 scale; + i32 line_height; + b8 nowrap; + b8 unselectable; + c8 const *text; + c8 const *href; + c8 const *image; +} Tag; + +typedef struct { + c8 const *id; + c8 const *id_parent; + c8 const *title; + Tag * content; +} Doc; + +typedef struct { + c8 const *method; + c8 const *uri; + i32 index; +} Location; + +typedef struct { + c8 const *alias; + c8 const *url; +} Redirect; + +// ================================================================ +// +// Content data +// +// ================================================================ + +#define INSERT_THEME ((c8 const *) 1) + +enum { + SECONDS_IN_MONTH = 2629746, + + DOC_404 = 0, + DOC_MAIN, + DOC_ABOUT, + DOC_MUSIC, + DOC_PROJECTS, + DOC_BLOG, + DOC_PGP, + DOC_LINKS, + DOC_THEME, + DOC_LINKS_MONERO, + DOC_LINKS_BITCOIN, + DOC_SAW, + + NUM_DOCS, + + LAYOUT_NONE = 0, + LAYOUT_LEFT, + LAYOUT_CENTER, + LAYOUT_RIGHT, + LAYOUT_FLOAT_LEFT, + LAYOUT_FLOAT_CENTER, + LAYOUT_FLOAT_RIGHT, + LAYOUT_MARGIN_LEFT, + LAYOUT_MARGIN_RIGHT, + + ICON_NONE = 0, + ICON_REGULAR, + ICON_SOLID, + ICON_BRAND, + + TAG_PLAIN = 0, + TAG_RAW, + TAG_END, + TAG_BEGIN, + TAG_CAPTION, + TAG_SUBTITLE, + TAG_BLOCK, + TAG_CODE, + TAG_CLIPBOARD, + TAG_BLACK_FLAG, + TAG_SUBMIT_TEXT, + + ACTION_BLANK = 0, + ACTION_SELF, + ACTION_SUBMIT, + + // Color IDs + + COLOR_NONE = 0, + COLOR_FOREGROUND_TEXT, + COLOR_FOREGROUND_HOVER, + COLOR_FOREGROUND_LINK, + COLOR_FOREGROUND_NAV, + COLOR_FOREGROUND_TITLE, + COLOR_FOREGROUND_DIM, + COLOR_FOREGROUND_FADE, + COLOR_FOREGROUND_HEAD, + COLOR_FOREGROUND_FOOT, + COLOR_FOREGROUND_SPOT, + COLOR_BACKGROUND_NAV, + COLOR_BACKGROUND_FAR, + COLOR_BACKGROUND_FADE, + COLOR_BACKGROUND_NEAR, + + COLOR_RAINBOW_LIGHT, + COLOR_RAINBOW_DARK, + + COLORS_MAX, + + THEME_GUATTARI = 0, + THEME_MONERO, + THEME_BLACK, + THEME_WHITE, + THEME_PINK, + THEME_LIGHT, +}; + +static c8 const *RAINBOW_LIGHT[] = { + "ff3050", + "ff8080", + "e7d000", + "00ff00", + "00ffff", + "00ff80", + "a0b0ff", + "ff00ff", +}; + +static c8 const *RAINBOW_DARK[] = { + "7f1020", + "7f4040", + "736800", + "007f00", + "007f7f", + "007f40", + "40407f", + "7f007f", +}; + +#define NUM_COLORS ((i32) (sizeof COLORS / sizeof *COLORS)) + +static c8 COLORS[][7] = { + [COLOR_NONE] = "d4d4d4", + [COLOR_FOREGROUND_TEXT] = "d4d4d4", + [COLOR_FOREGROUND_HOVER] = "e8e4d0", + [COLOR_FOREGROUND_LINK] = "a090a0", + [COLOR_FOREGROUND_NAV] = "a090a0", + [COLOR_FOREGROUND_TITLE] = "d4d4d4", + [COLOR_FOREGROUND_DIM] = "707070", + [COLOR_FOREGROUND_FADE] = "707070", + [COLOR_FOREGROUND_HEAD] = "6b6770", + [COLOR_FOREGROUND_FOOT] = "b0b0b0", + [COLOR_FOREGROUND_SPOT] = "a090a0", + [COLOR_BACKGROUND_NAV] = "222222", + [COLOR_BACKGROUND_FAR] = "19171a", + [COLOR_BACKGROUND_FADE] = "17151a", + [COLOR_BACKGROUND_NEAR] = "0a0a0a", +}; + +static c8 THEMES[][NUM_COLORS][7] = { + [THEME_GUATTARI] = { + "d4d4d4", // none + "d4d4d4", // text + "e8e4d0", // hover + "a090a0", // link + "a090a0", // nav + "d4d4d4", // title + "707070", // dim + "707070", // fade + "6b6770", // head + "b0b0b0", // foot + "a090a0", // spot + "222222", // back nav + "19171a", // back far + "17151a", // back fade + "0a0a0a", // back near + }, + [THEME_MONERO] = { + "e0e0e0", // none + "e0e0e0", // text + "ffe0a0", // hover + "909090", // link + "e07010", // nav + "c0b0a0", // title + "878067", // dim + "877757", // fade + "877757", // head + "878067", // foot + "878067", // spot + "222222", // back nav + "201710", // back far + "2c2007", // back fade + "000000", // back near + }, + [THEME_BLACK] = { + "ffffff", // none + "ffffff", // text + "ffffff", // hover + "b0b0b0", // link + "b0b0b0", // nav + "ffffff", // title + "808080", // dim + "ffffff", // fade + "ffffff", // head + "ffffff", // foot + "ffffff", // spot + "000000", // back nav + "000000", // back far + "000000", // back fade + "000000", // back near + }, + [THEME_WHITE] = { + "000000", // none + "000000", // text + "000000", // hover + "404040", // link + "404040", // nav + "000000", // title + "808080", // dim + "000000", // fade + "000000", // head + "000000", // foot + "000000", // spot + "ffffff", // back nav + "ffffff", // back far + "ffffff", // back fade + "ffffff", // back near + }, + [THEME_PINK] = { + "d4d4d4", // none + "d4d4d4", // text + "f0a0f0", // hover + "909090", // link + "d040d0", // nav + "b0b0b0", // title + "707070", // dim + "707070", // fade + "707070", // head + "6b6770", // foot + "d040d0", // spot + "222222", // back nav + "0a0a0a", // back far + "301030", // back fade + "19171a", // back near + }, + [THEME_LIGHT] = { + "040404", // none + "040404", // text + "0a0a0a", // hover + "474747", // link + "305040", // nav + "101010", // title + "808080", // dim + "707070", // fade + "707070", // head + "707070", // foot + "305040", // spot + "d0e0e0", // back nav + "c0c0c0", // back far + "d0e0e0", // back fade + "f7f7f7", // back near + }, +}; + +#define COLOR_MAP_SIZE ((i32) (sizeof COLOR_MAP / sizeof *COLOR_MAP)) + +static i32 COLOR_MAP[] = { + [COLOR_NONE] = COLOR_FOREGROUND_TEXT, + [COLOR_FOREGROUND_TEXT] = COLOR_FOREGROUND_TEXT, + [COLOR_FOREGROUND_HOVER] = COLOR_FOREGROUND_HOVER, + [COLOR_FOREGROUND_LINK] = COLOR_FOREGROUND_LINK, + [COLOR_FOREGROUND_NAV] = COLOR_FOREGROUND_NAV, + [COLOR_FOREGROUND_TITLE] = COLOR_FOREGROUND_TITLE, + [COLOR_FOREGROUND_DIM] = COLOR_FOREGROUND_DIM, + [COLOR_FOREGROUND_FADE] = COLOR_FOREGROUND_FADE, + [COLOR_FOREGROUND_HEAD] = COLOR_FOREGROUND_HEAD, + [COLOR_FOREGROUND_FOOT] = COLOR_FOREGROUND_FOOT, + [COLOR_FOREGROUND_SPOT] = COLOR_FOREGROUND_SPOT, + [COLOR_BACKGROUND_NAV] = COLOR_BACKGROUND_NAV, + [COLOR_BACKGROUND_FAR] = COLOR_BACKGROUND_FAR, + [COLOR_BACKGROUND_FADE] = COLOR_BACKGROUND_FADE, + [COLOR_BACKGROUND_NEAR] = COLOR_BACKGROUND_NEAR, + [COLOR_RAINBOW_LIGHT] = COLOR_RAINBOW_LIGHT, + [COLOR_RAINBOW_DARK] = COLOR_RAINBOW_DARK, +}; + +static i32 SCALE = DEFAULT_SCALE; +static i32 WIDTH = DEFAULT_WIDTH; + +static Redirect redirects[] = { + { + .alias = "/contact", + .url = "https://t.me/mitryz", + }, { + .alias = "/github", + .url = "https://github.com/automainint", + }, { + .alias = "/gitlab", + .url = "https://gitlab.com/automainint", + }, { + .alias = "/manifold", + .url = "https://manifold.markets/deleuze", + }, { + .alias = "/youtube", + .url = "https://youtube.com/@MityaSelivanov", + }, { + .alias = "/odysee", + .url = "https://odysee.com/@angsthotep:d", + }, { + .alias = "/twitch", + .url = "https://twitch.tv/angsthotep", + }, { + .alias = "/soundcloud", + .url = "https://soundcloud.com/angsthotep", + }, { + .alias = "/bandcamp", + .url = "https://angsthotep.bandcamp.com/", + }, { + .alias = "/spotify", + .url = "https://open.spotify.com/artist/0rr6un5mTZBZSDymK7QDWb", + }, { + .alias = "/telegram", + .url = "https://t.me/eterna", + }, { + .alias = "/discord", + .url = "https://discord.gg/jstDS8x", + }, { + .alias = "/twitter", + .url = "https://twitter.com/angsthotep", + }, { + .alias = "/linkedin", + .url = "https://linkedin.com/in/guattari", + }, { + .alias = "/instagram", + .url = "https://instagram.com/anotherdeleuze", + }, { + .alias = "/facebook", + .url = "https://facebook.com/eternaforgeso", + }, { + .alias = "/reddit", + .url = "https://reddit.com/u/angsthotep", + }, { + .alias = "/simplexchat", + .url = + "https://simplex.chat/contact/#/" + "?v=1-4&smp=smp%3A%2F%" + "2Fhejn2gVIqNU6xjtGM3OwQeuk8ZEbDXVJXAlnSBJBWUA%3D%" + "40smp16.simplex.im%2F8r8DH3EY7nAy1iS3LONVU9Wjc3TiVHxT%" + "23%2F%3Fv%3D1-2%26dh%3DMCowBQYDK2VuAyEAc_" + "Do6A3LfIKd49my6sv324pikcN3y9pFRQLzFG3rA1U%253D%26srv%" + "3Dp3ktngodzi6qrf7w64mmde3syuzrv57y55hxabqcq3l5p6oi7yzze6" + "qd.onion", + }, +}; + +static Location locs[] = { + { + .method = "GET", + .uri = "/", + .index = DOC_MAIN, + }, { + .method = "GET", + .uri = "/about", + .index = DOC_ABOUT, + }, { + .method = "GET", + .uri = "/music", + .index = DOC_MUSIC, + }, { + .method = "GET", + .uri = "/projects", + .index = DOC_PROJECTS, + }, { + .method = "GET", + .uri = "/blog", + .index = DOC_BLOG, + }, { + .method = "GET", + .uri = "/pgp", + .index = DOC_PGP, + }, { + .method = "GET", + .uri = "/links", + .index = DOC_LINKS, + }, { + .method = "GET", + .uri = "/theme", + .index = DOC_THEME, + }, { + .method = "GET", + .uri = "/links/monero", + .index = DOC_LINKS_MONERO, + }, { + .method = "GET", + .uri = "/links/bitcoin", + .index = DOC_LINKS_BITCOIN, + }, { + .method = "GET", + .uri = "/saw", + .index = DOC_SAW, + } +}; + +static Font fonts[] = { + { + .family = "Lithos Pro", + .file = "lithos_pro_black.woff2", + .weight = "900", + .style = "normal", + }, { + .family = "Verdana", + .file = "verdana.woff2", + .weight = "normal", + .style = "normal", + }, { + .family = "Verdana", + .file = "verdana_bold.woff2", + .weight = "bold", + .style = "normal", + }, { + .family = "Verdana", + .file = "verdana_italic.woff2", + .weight = "normal", + .style = "italic", + }, { + .family = "Segoe UI", + .file = "segoe_ui.woff2", + .weight = "normal", + .style = "normal", + }, { + .family = "Segoe UI", + .file = "segoe_ui_bold.woff2", + .weight = "bold", + .style = "normal", + }, { + .family = "Segoe UI", + .file = "segoe_ui_italic.woff2", + .weight = "normal", + .style = "italic", + }, { + .family = "Icon", + .file = "fa_regular_400.woff2", + .weight = "normal", + .style = "normal", + }, { + .family = "Icon Solid", + .file = "fa_solid_900.woff2", + .weight = "normal", + .style = "normal", + }, { + .family = "Brand", + .file = "fa_brands_400.woff2", + .weight = "normal", + .style = "normal", + }, +}; + +static Link navigation_left[] = { + { + .text = "Main", + .href = "/", + .target = "_self", + }, { + .text = "About Me", + .href = "/about", + .target = "_self", + }, { + .text = "Music", + .href = "/music", + .target = "_self", + }, { + .text = "Projects", + .href = "/projects", + .target = "_self", + }, { + .text = "Blog", + .href = "/blog", + .target = "_self", + }, { + .text = "Git", + .href = "/git", + .target = "_blank", + }, +}; + +static Link navigation_right[] = { + { + .text = " PGP", + .href = "/pgp", + .target = "_self", + }, { + .text = "Links", + .href = "/links", + .target = "_self", + }, +}; + +static Doc docs[NUM_DOCS] = { + [DOC_404] = { + .id = "404", + .title = "Page Not Found", + .content = (Tag[]) { + { + .type = TAG_CAPTION, + .text = "404", + }, { + .type = TAG_END, + .text = "Page Not Found", + }, + }, + }, + [DOC_MAIN] = { + .id = NULL, + .title = "Guattari Tech", + .content = (Tag[]) { + { + .type = TAG_BLOCK, + .layout = LAYOUT_CENTER, + .padding_top = 40, + .scale = 100, + .href = "/2024_shadows.jpg", + .image = "/2024_shadows_small.jpg", + }, { + .type = TAG_SUBTITLE, + .layout = LAYOUT_CENTER, + .scale = 150, + .text = "New album - coming soon...", + }, { + .type = TAG_BLOCK, + .padding_top = 80, + .text = "Monero accepted here!", + }, { + .type = TAG_BEGIN, + .layout = LAYOUT_CENTER, + }, { + .scale = 34, + .href = "https://monerica.com", + .image = "/MCEP.svg", + }, { + .type = TAG_END, + }, { + .type = TAG_END, + } + }, + }, + [DOC_ABOUT] = { + .id = "about", + .title = "About Me - Guattari Tech", + .content = (Tag[]) { + { + .type = TAG_CAPTION, + .layout = LAYOUT_CENTER, + .text = "Dungeonstep", + }, { + .text = " Hello! My name is Mitya Selivanov, aka Dmitry Babushkin.
" + " I'm a ", + }, { + .type = TAG_BLACK_FLAG, + .text = " freelance computer programmer and musician. " + " Inspired to build decentralization and cooperation technologies.
", + }, { + .layout = LAYOUT_RIGHT, + .image = "/pepe.gif", + }, { + .type = TAG_CAPTION, + .layout = LAYOUT_CENTER, + .text = "Interested in", + }, { + .color = COLOR_RAINBOW_LIGHT, + .width = 140, + .icon = ICON_SOLID, + .text = " ", + }, { + .text = "Semantic compression (see ", + }, { + .text = "Casey's post", + .href = "https://caseymuratori.com/blog_0015", + }, { + .text = ")
", + }, { + .color = COLOR_RAINBOW_LIGHT, + .width = 140, + .icon = ICON_SOLID, + .text = " ", + }, { + .text = "Low-level programming
", + }, { + .color = COLOR_RAINBOW_LIGHT, + .width = 140, + .icon = ICON_SOLID, + .text = " ", + }, { + .text = "Performance engineering
", + }, { + .color = COLOR_RAINBOW_LIGHT, + .width = 140, + .icon = ICON_SOLID, + .text = " ", + }, { + .text = "Prediction markets (see ", + }, { + .text = "Scott Alexander's FAQ", + .href = "https://www.astralcodexten.com/p/prediction-market-faq", + }, { + .text = ")
", + }, { + .color = COLOR_RAINBOW_LIGHT, + .width = 140, + .icon = ICON_SOLID, + .text = " ", + }, { + .text = "Blockchain & ", + }, { + .nowrap = 1, + .text = "Proof-of-Work", + }, { + .text = "

", + }, { + .color = COLOR_RAINBOW_LIGHT, + .width = 140, + .icon = ICON_SOLID, + .text = " ", + }, { + .text = "Puzzle games
", + }, { + .color = COLOR_RAINBOW_LIGHT, + .width = 140, + .icon = ICON_SOLID, + .text = " ", + }, { + .text = "Music synthesis
", + }, { + .color = COLOR_RAINBOW_LIGHT, + .width = 140, + .icon = ICON_SOLID, + .text = " ", + }, { + .text = "Meeting new people
", + }, { + .type = TAG_CAPTION, + .layout = LAYOUT_CENTER, + .text = "I use", + }, { + .width = 120, + .icon = ICON_SOLID, + .text = " ", + }, { + .type = TAG_CODE, + .text = "C" + }, { + .text = ", ", + }, { + .width = 120, + .icon = ICON_SOLID, + .text = " ", + }, { + .type = TAG_CODE, + .text = "snake_case", + }, { + .text = ", ", + }, { + .width = 120, + .icon = ICON_SOLID, + .text = " ", + }, { + .type = TAG_CODE, + .text = "helix", + .href = "https://helix-editor.com/", + }, { + .text = ", ", + }, { + .width = 120, + .icon = ICON_BRAND, + .text = " ", + }, { + .type = TAG_CODE, + .text = "Linux", + }, { + .text = "
", + }, { + .type = TAG_CODE, + .text = "Alacritty", + .href = "https://github.com/alacritty/alacritty", + }, { + .text = ", WebAssembly, OpenGL, Vulkan
", + }, { + .type = TAG_CODE, + .text = "nginx", + .href = "https://hg.nginx.org/nginx", + }, { + .text = ", ", + }, { + .type = TAG_CODE, + .text = "wolfSSL", + .href = "https://github.com/wolfSSL/wolfssl", + }, { + .text = ", ", + }, { + .type = TAG_CODE, + .text = "stb", + .href = "https://github.com/nothings/stb", + }, { + .text = ", ", + }, { + .type = TAG_CODE, + .text = "sokol", + .href = "https://github.com/floooh/sokol", + }, { + .text = ", ", + }, { + .type = TAG_CODE, + .text = "nanovg", + .href = "https://github.com/memononen/nanovg", + }, { + .text = ", ", + }, { + .type = TAG_CODE, + .text = "miniaudio", + .href = "https://github.com/mackron/miniaudio", + }, { + .text = ", ", + }, { + .type = TAG_CODE, + .text = "gf", + .href = "https://github.com/nakst/gf", + }, { + .text = "
", + }, { + .type = TAG_CAPTION, + .layout = LAYOUT_CENTER, + .text = "My music alias", + }, { + .nowrap = 1, + .text = "Guattari – electronic", + }, { + .type = TAG_END, + .text = " music, mostly dungeon synth, witch house; and black metal. " + " Feel free to use my music in your projects. " + " All music is licensed under a Creative Commons License (Attribution). " + " Contact me if you need additional information. ", + }, + }, + }, + [DOC_MUSIC] = { + .id = "music", + .title = "Music - Guattari Tech", + .content = (Tag[]) { + { + .type = TAG_BLOCK, + .layout = LAYOUT_CENTER, + .padding_top = 40, + .scale = 100, + .href = "/2021_accelerationism.jpg", + .image = "/2021_accelerationism_small.jpg", + }, { + .type = TAG_CAPTION, + .color = COLOR_FOREGROUND_FADE, + .layout = LAYOUT_MARGIN_RIGHT, + .scale = 80, + .padding_top = -110, + .text = "2021", + }, { + .type = TAG_SUBTITLE, + .layout = LAYOUT_CENTER, + .text = "Guattari – Accelerationism", + }, { + .type = TAG_BEGIN, + .layout = LAYOUT_CENTER, + }, { + .scale = 80, + .text = "Apple / iTunes", + .href = "https://music.apple.com/us/album/accelerationism-ep/1548020164", + }, { + .type = TAG_CODE, + .text = " | ", + }, { + .scale = 80, + .text = "Spotify", + .href = "https://open.spotify.com/album/5N6tZYohr2mrmr0XelAwlK?si=RzcBdOAKRWKozDYJ91iREA", + }, { + .type = TAG_CODE, + .text = " | ", + }, { + .scale = 80, + .text = "Deezer", + .href = "https://deezer.page.link/PXS8q1jEqECa79rEA", + }, { + .type = TAG_END, + }, { + .line_height = 40, + .text = "
", + }, { + .type = TAG_BEGIN, + .line_height = 160, + }, { + .width = 180, + .unselectable = 1, + .text = " 1.", + }, { + .text = "Willing Core", + }, { + .width = 100, + .layout = LAYOUT_FLOAT_RIGHT, + .icon = ICON_BRAND, + .line_height = 80, + .padding_top = 55, + .text = "", + .href = "https://youtu.be/FwFcJqiRFh8", + }, { + .width = 240, + .layout = LAYOUT_FLOAT_RIGHT, + .text = "mp3", + .href = "/git/music/plain/2021_accelerationism/01_willing_core.mp3", + }, { + .type = TAG_END, + .text = "
", + }, { + .type = TAG_BEGIN, + .background = COLOR_BACKGROUND_FADE, + .line_height = 160, + }, { + .width = 180, + .unselectable = 1, + .text = " 2.", + }, { + .text = "Desert Cult", + }, { + .width = 100, + .layout = LAYOUT_FLOAT_RIGHT, + .icon = ICON_BRAND, + .line_height = 80, + .padding_top = 55, + .text = "", + .href = "https://youtu.be/MxkNiq6JnfE", + }, { + .width = 240, + .layout = LAYOUT_FLOAT_RIGHT, + .text = "mp3", + .href = "/git/music/plain/2021_accelerationism/02_desert_cult.mp3", + }, { + .type = TAG_END, + .text = "
", + }, { + .type = TAG_BEGIN, + .line_height = 160, + }, { + .width = 180, + .unselectable = 1, + .text = " 3.", + }, { + .text = "Artificial Womb", + }, { + .width = 100, + .layout = LAYOUT_FLOAT_RIGHT, + .icon = ICON_BRAND, + .line_height = 80, + .padding_top = 55, + .text = "", + .href = "https://youtu.be/hTeYtI4ECJ4", + }, { + .width = 240, + .layout = LAYOUT_FLOAT_RIGHT, + .text = "mp3", + .href = "/git/music/plain/2021_accelerationism/03_artificial_womb.mp3", + }, { + .type = TAG_END, + .text = "
", + }, { + .type = TAG_BEGIN, + .background = COLOR_BACKGROUND_FADE, + .line_height = 160, + }, { + .width = 180, + .unselectable = 1, + .text = " 4.", + }, { + .text = "Phenotypic Revolution", + }, { + .width = 100, + .layout = LAYOUT_FLOAT_RIGHT, + .icon = ICON_BRAND, + .line_height = 80, + .padding_top = 55, + .text = "", + .href = "https://youtu.be/FxDsTCq5LvE", + }, { + .width = 240, + .layout = LAYOUT_FLOAT_RIGHT, + .text = "mp3", + .href = "/git/music/plain/2021_accelerationism/04_phenotypic_revolution.mp3", + }, { + .type = TAG_END, + .text = "
", + }, { + .type = TAG_BEGIN, + .line_height = 160, + }, { + .width = 180, + .unselectable = 1, + .text = " 5.", + }, { + .text = "Above The Sky", + }, { + .width = 100, + .layout = LAYOUT_FLOAT_RIGHT, + .icon = ICON_BRAND, + .line_height = 80, + .padding_top = 55, + .text = "", + .href = "https://youtu.be/K4BIiTCNvy4", + }, { + .width = 240, + .layout = LAYOUT_FLOAT_RIGHT, + .text = "mp3", + .href = "/git/music/plain/2021_accelerationism/05_above_the_sky.mp3", + }, { + .type = TAG_END, + .text = "
", + }, { + .type = TAG_BEGIN, + .background = COLOR_BACKGROUND_FADE, + .line_height = 160, + }, { + .width = 180, + .unselectable = 1, + .text = " 6.", + }, { + .text = "Elimination", + }, { + .width = 100, + .layout = LAYOUT_FLOAT_RIGHT, + .icon = ICON_BRAND, + .line_height = 80, + .padding_top = 55, + .text = "", + .href = "https://youtu.be/OiTB5KRBuI0", + }, { + .width = 240, + .layout = LAYOUT_FLOAT_RIGHT, + .text = "mp3", + .href = "/git/music/plain/2021_accelerationism/06_elimination.mp3", + }, { + .type = TAG_END, + .text = "
", + }, { + .text = "
", + }, { + .type = TAG_BLOCK, + .layout = LAYOUT_CENTER, + .padding_top = 40, + .scale = 100, + .href = "/2020_unalbum.jpg", + .image = "/2020_unalbum_small.jpg", + }, { + .type = TAG_CAPTION, + .color = COLOR_FOREGROUND_FADE, + .layout = LAYOUT_MARGIN_RIGHT, + .scale = 80, + .padding_top = -110, + .text = "2020", + }, { + .type = TAG_SUBTITLE, + .layout = LAYOUT_CENTER, + .text = "Guattari – Anti-Album", + }, { + .type = TAG_BEGIN, + .layout = LAYOUT_CENTER, + }, { + .scale = 80, + .text = "YouTube", + .href = "https://www.youtube.com/playlist?list=PLcgDcJaNt0a-wSyZdGARyF3OMAqOQY36J", + }, { + .type = TAG_END, + }, { + .text = "
", + }, { + .type = TAG_BEGIN, + .line_height = 160, + }, { + .width = 180, + .unselectable = 1, + .text = " 1.", + }, { + .text = "Casual Track 018", + }, { + .width = 100, + .layout = LAYOUT_FLOAT_RIGHT, + .icon = ICON_BRAND, + .line_height = 80, + .padding_top = 55, + .text = "", + .href = "https://youtu.be/Uhveh_67zyQ", + }, { + .width = 240, + .layout = LAYOUT_FLOAT_RIGHT, + .text = "mp3", + .href = "/git/music/plain/2020_unalbum/01_casual_track_018.mp3", + }, { + .type = TAG_END, + .text = "
", + }, { + .type = TAG_BEGIN, + .background = COLOR_BACKGROUND_FADE, + .line_height = 160, + }, { + .width = 180, + .unselectable = 1, + .text = " 2.", + }, { + .text = "Casual Track 019", + }, { + .width = 100, + .layout = LAYOUT_FLOAT_RIGHT, + .icon = ICON_BRAND, + .line_height = 80, + .padding_top = 55, + .text = "", + .href = "https://youtu.be/JEQf2cvjOko", + }, { + .width = 240, + .layout = LAYOUT_FLOAT_RIGHT, + .text = "mp3", + .href = "/git/music/plain/2020_unalbum/02_casual_track_019.mp3", + }, { + .type = TAG_END, + .text = "
", + }, { + .type = TAG_BEGIN, + .line_height = 160, + }, { + .width = 180, + .unselectable = 1, + .text = " 3.", + }, { + .text = "Casual Track 020", + }, { + .width = 100, + .layout = LAYOUT_FLOAT_RIGHT, + .icon = ICON_BRAND, + .line_height = 80, + .padding_top = 55, + .text = "", + .href = "https://youtu.be/FYu2_kSUnMc", + }, { + .width = 240, + .layout = LAYOUT_FLOAT_RIGHT, + .text = "mp3", + .href = "/git/music/plain/2020_unalbum/03_casual_track_020.mp3", + }, { + .type = TAG_END, + .text = "
", + }, { + .type = TAG_BEGIN, + .background = COLOR_BACKGROUND_FADE, + .line_height = 160, + }, { + .width = 180, + .unselectable = 1, + .text = " 4.", + }, { + .text = "Casual Track 021", + }, { + .width = 100, + .layout = LAYOUT_FLOAT_RIGHT, + .icon = ICON_BRAND, + .line_height = 80, + .padding_top = 55, + .text = "", + .href = "https://youtu.be/2oP4CfHWFqE", + }, { + .width = 240, + .layout = LAYOUT_FLOAT_RIGHT, + .text = "mp3", + .href = "/git/music/plain/2020_unalbum/04_casual_track_021.mp3", + }, { + .type = TAG_END, + .text = "
", + }, { + .type = TAG_BEGIN, + .line_height = 160, + }, { + .width = 180, + .unselectable = 1, + .text = " 5.", + }, { + .text = "Casual Track 022", + }, { + .width = 100, + .layout = LAYOUT_FLOAT_RIGHT, + .icon = ICON_BRAND, + .line_height = 80, + .padding_top = 55, + .text = "", + .href = "https://youtu.be/-WDp3EWTjrc", + }, { + .width = 240, + .layout = LAYOUT_FLOAT_RIGHT, + .text = "mp3", + .href = "/git/music/plain/2020_unalbum/05_casual_track_022.mp3", + }, { + .type = TAG_END, + .text = "
", + }, { + .type = TAG_BEGIN, + .background = COLOR_BACKGROUND_FADE, + .line_height = 160, + }, { + .width = 180, + .unselectable = 1, + .text = " 6.", + }, { + .text = "Casual Track 023", + }, { + .width = 100, + .layout = LAYOUT_FLOAT_RIGHT, + .icon = ICON_BRAND, + .line_height = 80, + .padding_top = 55, + .text = "", + .href = "https://youtu.be/pKuf9PkWhko", + }, { + .width = 240, + .layout = LAYOUT_FLOAT_RIGHT, + .text = "mp3", + .href = "/git/music/plain/2020_unalbum/06_casual_track_023.mp3", + }, { + .type = TAG_END, + .text = "
", + }, { + .type = TAG_BEGIN, + .line_height = 160, + }, { + .width = 180, + .unselectable = 1, + .text = " 7.", + }, { + .text = "Casual Track 024", + }, { + .width = 100, + .layout = LAYOUT_FLOAT_RIGHT, + .icon = ICON_BRAND, + .line_height = 80, + .padding_top = 55, + .text = "", + .href = "https://youtu.be/XwE493hj4z4", + }, { + .width = 240, + .layout = LAYOUT_FLOAT_RIGHT, + .text = "mp3", + .href = "/git/music/plain/2020_unalbum/07_casual_track_024.mp3", + }, { + .type = TAG_END, + .text = "
", + }, { + .type = TAG_BEGIN, + .background = COLOR_BACKGROUND_FADE, + .line_height = 160, + }, { + .width = 180, + .unselectable = 1, + .text = " 8.", + }, { + .text = "Casual Track 025", + }, { + .width = 100, + .layout = LAYOUT_FLOAT_RIGHT, + .icon = ICON_BRAND, + .line_height = 80, + .padding_top = 55, + .text = "", + .href = "https://youtu.be/XKCEUS7yzFU", + }, { + .width = 240, + .layout = LAYOUT_FLOAT_RIGHT, + .text = "mp3", + .href = "/git/music/plain/2020_unalbum/08_casual_track_025.mp3", + }, { + .type = TAG_END, + .text = "
", + }, { + .type = TAG_BEGIN, + .line_height = 160, + }, { + .width = 180, + .unselectable = 1, + .text = " 9.", + }, { + .text = "Casual Track 026", + }, { + .width = 100, + .layout = LAYOUT_FLOAT_RIGHT, + .icon = ICON_BRAND, + .line_height = 80, + .padding_top = 55, + .text = "", + .href = "https://youtu.be/-MbUQp3hbqM", + }, { + .width = 240, + .layout = LAYOUT_FLOAT_RIGHT, + .text = "mp3", + .href = "/git/music/plain/2020_unalbum/09_casual_track_026.mp3", + }, { + .type = TAG_END, + .text = "
", + }, { + .text = "
", + }, { + .type = TAG_BLOCK, + .layout = LAYOUT_CENTER, + .padding_top = 40, + .scale = 100, + .href = "/2016_determine.jpg", + .image = "/2016_determine_small.jpg", + }, { + .type = TAG_CAPTION, + .color = COLOR_FOREGROUND_FADE, + .layout = LAYOUT_MARGIN_RIGHT, + .scale = 80, + .padding_top = -110, + .text = "2016", + }, { + .type = TAG_SUBTITLE, + .layout = LAYOUT_CENTER, + .text = "Guattari – Determine", + }, { + .type = TAG_BEGIN, + .layout = LAYOUT_CENTER, + }, { + .scale = 80, + .text = "YouTube", + .href = "https://youtu.be/GfUYhgaBzik", + }, { + .type = TAG_END, + }, { + .type = TAG_BEGIN, + .line_height = 160, + }, { + .width = 180, + .unselectable = 1, + .text = " 1.", + }, { + .text = "Introduction", + }, { + .width = 100, + .layout = LAYOUT_FLOAT_RIGHT, + .icon = ICON_BRAND, + .line_height = 80, + .padding_top = 55, + .text = "", + .href = "https://youtu.be/Rwyd4idcR2A", + }, { + .width = 240, + .layout = LAYOUT_FLOAT_RIGHT, + .text = "mp3", + .href = "/git/music/plain/2016_determine/01_introduction.mp3", + }, { + .type = TAG_END, + .text = "
", + }, { + .type = TAG_BEGIN, + .background = COLOR_BACKGROUND_FADE, + .line_height = 160, + }, { + .width = 180, + .unselectable = 1, + .text = " 2.", + }, { + .text = "Fugue in Eb minor", + }, { + .width = 100, + .layout = LAYOUT_FLOAT_RIGHT, + .icon = ICON_BRAND, + .line_height = 80, + .padding_top = 55, + .text = "", + .href = "https://youtu.be/VgZ8jjuzLow", + }, { + .width = 240, + .layout = LAYOUT_FLOAT_RIGHT, + .text = "mp3", + .href = "/git/music/plain/2016_determine/02_fugue_in_eb_minor.mp3", + }, { + .type = TAG_END, + .text = "
", + }, { + .type = TAG_BEGIN, + .line_height = 160, + }, { + .width = 180, + .unselectable = 1, + .text = " 3.", + }, { + .text = "Lonely Harp", + }, { + .width = 100, + .layout = LAYOUT_FLOAT_RIGHT, + .icon = ICON_BRAND, + .line_height = 80, + .padding_top = 55, + .text = "", + .href = "https://youtu.be/IufFOFv9vwA", + }, { + .width = 240, + .layout = LAYOUT_FLOAT_RIGHT, + .text = "mp3", + .href = "/git/music/plain/2016_determine/03_lonely_harp.mp3", + }, { + .type = TAG_END, + .text = "
", + }, { + .type = TAG_BEGIN, + .background = COLOR_BACKGROUND_FADE, + .line_height = 160, + }, { + .width = 180, + .unselectable = 1, + .text = " 4.", + }, { + .type = TAG_CODE, + .text = ".SILENT", + }, { + .width = 100, + .layout = LAYOUT_FLOAT_RIGHT, + .icon = ICON_BRAND, + .line_height = 80, + .padding_top = 55, + .text = "", + .href = "https://youtu.be/docPTZi6EOI", + }, { + .width = 240, + .layout = LAYOUT_FLOAT_RIGHT, + .text = "mp3", + .href = "/git/music/plain/2016_determine/04_dot_silent.mp3", + }, { + .type = TAG_END, + .text = "
", + }, { + .type = TAG_BEGIN, + .line_height = 160, + }, { + .width = 180, + .unselectable = 1, + .text = " 5.", + }, { + .text = "Interlude", + }, { + .width = 100, + .layout = LAYOUT_FLOAT_RIGHT, + .icon = ICON_BRAND, + .line_height = 80, + .padding_top = 55, + .text = "", + .href = "https://youtu.be/QnzE7Knzx8Q", + }, { + .width = 240, + .layout = LAYOUT_FLOAT_RIGHT, + .text = "mp3", + .href = "/git/music/plain/2016_determine/05_interlude.mp3", + }, { + .type = TAG_END, + .text = "
", + }, { + .type = TAG_BEGIN, + .background = COLOR_BACKGROUND_FADE, + .line_height = 160, + }, { + .width = 180, + .unselectable = 1, + .text = " 6.", + }, { + .text = "96 cycles of C minor", + }, { + .width = 100, + .layout = LAYOUT_FLOAT_RIGHT, + .icon = ICON_BRAND, + .line_height = 80, + .padding_top = 55, + .text = "", + .href = "https://youtu.be/QgU65MvM50M", + }, { + .width = 240, + .layout = LAYOUT_FLOAT_RIGHT, + .text = "mp3", + .href = "/git/music/plain/2016_determine/06_96_cycles_of_c_minor.mp3", + }, { + .type = TAG_END, + .text = "
", + }, { + .type = TAG_BEGIN, + .line_height = 160, + }, { + .width = 180, + .unselectable = 1, + .text = " 7.", + }, { + .text = "Vast", + }, { + .width = 100, + .layout = LAYOUT_FLOAT_RIGHT, + .icon = ICON_BRAND, + .line_height = 80, + .padding_top = 55, + .text = "", + .href = "https://youtu.be/NLNJZCMMbVY", + }, { + .width = 240, + .layout = LAYOUT_FLOAT_RIGHT, + .text = "mp3", + .href = "/git/music/plain/2016_determine/07_vast.mp3", + }, { + .type = TAG_END, + .text = "
", + }, { + .type = TAG_BEGIN, + .background = COLOR_BACKGROUND_FADE, + .line_height = 160, + }, { + .width = 180, + .unselectable = 1, + .text = " 8.", + }, { + .text = "Machinery", + }, { + .width = 100, + .layout = LAYOUT_FLOAT_RIGHT, + .icon = ICON_BRAND, + .line_height = 80, + .padding_top = 55, + .text = "", + .href = "https://youtu.be/DWzFyCOwGaI", + }, { + .width = 240, + .layout = LAYOUT_FLOAT_RIGHT, + .text = "mp3", + .href = "/git/music/plain/2016_determine/08_machinery.mp3", + }, { + .type = TAG_END, + .text = "
", + }, { + .type = TAG_BEGIN, + .line_height = 160, + }, { + .width = 180, + .unselectable = 1, + .text = " 9.", + }, { + .text = "Epilogue", + }, { + .width = 100, + .layout = LAYOUT_FLOAT_RIGHT, + .icon = ICON_BRAND, + .line_height = 80, + .padding_top = 55, + .text = "", + .href = "https://youtu.be/38DaJiwZvtM", + }, { + .width = 240, + .layout = LAYOUT_FLOAT_RIGHT, + .text = "mp3", + .href = "/git/music/plain/2016_determine/09_epilogue.mp3", + }, { + .type = TAG_END, + }, { + .type = TAG_END, + .text = "
", + }, + }, + }, + [DOC_PROJECTS] = { + .id = "projects", + .title = "Projects - Guattari Tech", + .content = (Tag[]) { + { + .type = TAG_CAPTION, + .layout = LAYOUT_CENTER, + .text = "Saw", + }, { + .text = "A new experimental music sequencer and audio editor.

" + "There is a lot of things I don't like in currently available music software. " + "Some of them are: bad performance, instability (bugs and crashes), complicated system environment requirements, inconvenient UI, lack of features, closedness, and, most important, bad standards. " + "I decided to make a new music sequencer from scratch, so I can experiment with it and find better ways to make music.

" + "Code is cross-platform and written in C.

", + }, { + .text = "Used libraries: ", + }, { + .type = TAG_CODE, + .text = "kit", + .href = "/git/kit", + }, { + .text = ", ", + }, { + .type = TAG_CODE, + .text = "stb", + .href = "https://github.com/nothings/stb", + }, { + .text = ", ", + }, { + .type = TAG_CODE, + .text = "miniaudio", + .href = "https://github.com/mackron/miniaudio", + }, { + .text = ", ", + }, { + .type = TAG_CODE, + .text = "nanovg", + .href = "https://github.com/memononen/nanovg", + }, { + .text = ", ", + }, { + .type = TAG_CODE, + .text = "sokol", + .href = "https://github.com/floooh/sokol", + }, { + .text = ".

", + }, { + .width = 120, + .icon = ICON_SOLID, + .text = " ", + }, { + .text = "Source code", + .href = "/git/saw", + }, { + .text = " - Git repository
", + }, { + .width = 120, + .icon = ICON_SOLID, + .text = " ", + }, { + .text = "Try out", + .href = "/saw", + }, { + .text = " - run the prototype in the browser
", + }, { + .width = 120, + .icon = ICON_SOLID, + .text = " ", + }, { + .text = "Project page", + .href = "https://handmade.network/p/432/saw", + }, { + .type = TAG_END, + .text = " on Handmade Network", + }, + }, + }, + [DOC_BLOG] = { + .id = "blog", + .title = "Blog - Guattari Tech", + .content = (Tag[]) { + { + .type = TAG_CAPTION, + .color = COLOR_RAINBOW_LIGHT, + .text = "Work in progress...", + }, { + .type = TAG_END, + }, + }, + }, + [DOC_PGP] = { + .id = "pgp", + .title = "PGP - Guattari Tech", + .content = (Tag[]) { + { + .type = TAG_CAPTION, + .text = "Authenticity", + }, { + .text = "I use the corresponding private key to sign all my commits:

", + }, { + .type = TAG_BEGIN, + .action = ACTION_SELF, + .href = "/guattari_tech.asc", + }, { + .icon = ICON_SOLID, + .width = 140, + .text = " ", + }, { + .text = "Public key", + }, { + .type = TAG_END, + }, { + .text = " ", + }, { + .type = TAG_CODE, + .text = "52D474B70D8FD302", + }, { + .text = "

", + }, { + .type = TAG_CODE, + .color = COLOR_FOREGROUND_DIM, + .line_height = 95, + .text = "-----BEGIN PGP PUBLIC KEY BLOCK-----
" + "
" + "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
" + "c+F4YwNAaA==
" + "=YwR7
" + "-----END PGP PUBLIC KEY BLOCK-----
", + }, { + .type = TAG_END, + }, + }, + }, + [DOC_LINKS] = { + .id = "links", + .title = "Links - Guattari Tech", + .content = (Tag[]) { + { + .type = TAG_CAPTION, + .color = COLOR_FOREGROUND_DIM, + .layout = LAYOUT_MARGIN_LEFT, + .scale = 70, + .unselectable = 1, + .text = "For tip", + }, { + .text = "
", + }, { + .layout = LAYOUT_RIGHT, + .text = "
", + }, { + .type = TAG_BEGIN, + .layout = LAYOUT_LEFT, + }, { + .action = ACTION_SELF, + .href = "/links/monero", + .text = "Monero", + }, { + .type = TAG_END, + }, { + .type = TAG_BEGIN, + .layout = LAYOUT_LEFT, + }, { + .action = ACTION_SELF, + .href = "/links/bitcoin", + .text = "Bitcoin", + }, { + .type = TAG_END, + }, { + .type = TAG_CAPTION, + .color = COLOR_FOREGROUND_DIM, + .layout = LAYOUT_MARGIN_RIGHT, + .scale = 70, + .unselectable = 1, + .text = "Mirrors", + }, { + .text = "

", + }, { + .type = TAG_BEGIN, + .layout = LAYOUT_CENTER, + }, { + .type = TAG_BEGIN, + .action = ACTION_SELF, + .nowrap = 1, + .href = "https://guattari.tech/links", + }, { + .icon = ICON_SOLID, + .width = 140, + .text = "", + }, { + .text = "https://guattari.tech", + }, { + .type = TAG_END, + }, { + .type = TAG_END, + }, { + .type = TAG_BEGIN, + .layout = LAYOUT_CENTER, + }, { + .type = TAG_BEGIN, + .action = ACTION_SELF, + .nowrap = 1, + .href = "http://guattari22ojifbuvudvd4ceurbcqfvfkxjlul5ax6pyquhg4c3chbyd.onion/links", + }, { + .icon = ICON_SOLID, + .width = 140, + .text = "", + }, { + .text = "http://guattari22...onion", + }, { + .type = TAG_END, + }, { + .type = TAG_END, + }, { + .type = TAG_CAPTION, + .color = COLOR_FOREGROUND_DIM, + .layout = LAYOUT_MARGIN_LEFT, + .scale = 70, + .unselectable = 1, + .text = "Code", + }, { + .text = "

", + }, { + .type = TAG_BEGIN, + .layout = LAYOUT_CENTER, + }, { + .type = TAG_BEGIN, + .action = ACTION_BLANK, + .href = "/github", + }, { + .text = "GitHub", + }, { + .type = TAG_END, + }, { + .type = TAG_END, + }, { + .type = TAG_BEGIN, + .layout = LAYOUT_CENTER, + }, { + .type = TAG_BEGIN, + .action = ACTION_BLANK, + .href = "/gitlab", + }, { + .text = "GitLab", + }, { + .type = TAG_END, + }, { + .type = TAG_END, + }, { + .type = TAG_CAPTION, + .color = COLOR_FOREGROUND_DIM, + .layout = LAYOUT_MARGIN_RIGHT, + .scale = 60, + .unselectable = 1, + .text = "Prediction
markets", + }, { + .text = "

", + }, { + .type = TAG_BEGIN, + .layout = LAYOUT_CENTER, + }, { + .type = TAG_BEGIN, + .action = ACTION_BLANK, + .href = "/manifold", + }, { + .width = 600, + .text = "Manifold", + }, { + .type = TAG_END, + }, { + .type = TAG_END, + }, { + .text = "
", + }, { + .type = TAG_CAPTION, + .color = COLOR_FOREGROUND_DIM, + .layout = LAYOUT_MARGIN_LEFT, + .scale = 70, + .unselectable = 1, + .text = "Video", + }, { + .text = "
", + }, { + .type = TAG_BEGIN, + .layout = LAYOUT_CENTER, + }, { + .type = TAG_BEGIN, + .action = ACTION_BLANK, + .href = "/youtube", + }, { + .text = "YouTube", + }, { + .type = TAG_END, + }, { + .type = TAG_END, + }, { + .type = TAG_BEGIN, + .layout = LAYOUT_CENTER, + }, { + .type = TAG_BEGIN, + .action = ACTION_BLANK, + .href = "/odysee", + }, { + .text = "Odysee", + }, { + .type = TAG_END, + }, { + .type = TAG_END, + }, { + .type = TAG_BEGIN, + .layout = LAYOUT_CENTER, + }, { + .type = TAG_BEGIN, + .action = ACTION_BLANK, + .href = "/twitch", + }, { + .text = "Twitch", + }, { + .type = TAG_END, + }, { + .type = TAG_END, + }, { + .text = "
", + }, { + .type = TAG_CAPTION, + .color = COLOR_FOREGROUND_DIM, + .layout = LAYOUT_MARGIN_RIGHT, + .scale = 70, + .unselectable = 1, + .text = "Audio", + }, { + .text = "
", + }, { + .type = TAG_BEGIN, + .layout = LAYOUT_CENTER, + }, { + .type = TAG_BEGIN, + .action = ACTION_BLANK, + .href = "/soundcloud", + }, { + .text = "SoundCloud", + }, { + .type = TAG_END, + }, { + .type = TAG_END, + }, { + .type = TAG_BEGIN, + .layout = LAYOUT_CENTER, + }, { + .type = TAG_BEGIN, + .action = ACTION_BLANK, + .href = "/bandcamp", + }, { + .text = "Bandcamp", + }, { + .type = TAG_END, + }, { + .type = TAG_END, + }, { + .type = TAG_BEGIN, + .layout = LAYOUT_CENTER, + }, { + .type = TAG_BEGIN, + .action = ACTION_BLANK, + .href = "/spotify", + }, { + .text = "Spotify", + }, { + .type = TAG_END, + }, { + .type = TAG_END, + }, { + .text = "
", + }, { + .type = TAG_CAPTION, + .color = COLOR_FOREGROUND_DIM, + .layout = LAYOUT_MARGIN_LEFT, + .scale = 70, + .unselectable = 1, + .text = "Chat", + }, { + .text = "
", + }, { + .type = TAG_BEGIN, + .layout = LAYOUT_CENTER, + }, { + .type = TAG_BEGIN, + .action = ACTION_BLANK, + .href = "/simplexchat", + }, { + .text = "SimpleX Chat", + }, { + .type = TAG_END, + }, { + .type = TAG_END, + }, { + .type = TAG_BEGIN, + .layout = LAYOUT_CENTER, + }, { + .type = TAG_BEGIN, + .action = ACTION_BLANK, + .href = "/telegram", + }, { + .text = "Telegram", + }, { + .type = TAG_END, + }, { + .type = TAG_END, + }, { + .type = TAG_BEGIN, + .layout = LAYOUT_CENTER, + }, { + .type = TAG_BEGIN, + .action = ACTION_BLANK, + .href = "/discord", + }, { + .text = "Discord", + }, { + .type = TAG_END, + }, { + .type = TAG_END, + }, { + .text = "
", + }, { + .type = TAG_CAPTION, + .color = COLOR_FOREGROUND_DIM, + .layout = LAYOUT_MARGIN_RIGHT, + .scale = 70, + .unselectable = 1, + .text = "Social", + }, { + .text = "
", + }, { + .type = TAG_BEGIN, + .layout = LAYOUT_CENTER, + }, { + .type = TAG_BEGIN, + .action = ACTION_BLANK, + .href = "/twitter", + }, { + .text = "Twitter", + }, { + .type = TAG_END, + }, { + .type = TAG_END, + }, { + .type = TAG_BEGIN, + .layout = LAYOUT_CENTER, + }, { + .type = TAG_BEGIN, + .action = ACTION_BLANK, + .href = "/linkedin", + }, { + .text = "Linkedin", + }, { + .type = TAG_END, + }, { + .type = TAG_END, + }, { + .type = TAG_BEGIN, + .layout = LAYOUT_CENTER, + }, { + .type = TAG_BEGIN, + .action = ACTION_BLANK, + .href = "/reddit", + }, { + .text = "Reddit", + }, { + .type = TAG_END, + }, { + .type = TAG_END, + }, { + .text = "
", + }, { + .type = TAG_END, + }, + }, + }, + [DOC_THEME] = { + .id = "theme", + .title = "Theme - Guattari Tech", + .content = (Tag[]) { + { + .type = TAG_CAPTION, + .color = COLOR_RAINBOW_LIGHT, + .layout = LAYOUT_CENTER, + .text = "Theme Setup", + }, { + .type = TAG_BEGIN, + .action = ACTION_SELF, + .href = "/theme?guattari" + }, { + .icon = ICON_SOLID, + .width = 140, + .text = " ", + }, { + .text = "Guattari", + }, { + .type = TAG_END, + .text = "
", + }, { + .type = TAG_BEGIN, + .layout = LAYOUT_FLOAT_RIGHT, + .action = ACTION_SELF, + .href = "/theme?wide" + }, { + .text = "Wide", + }, { + .icon = ICON_SOLID, + .width = 140, + .text = " ", + }, { + .type = TAG_END, + .text = "
" + }, { + .type = TAG_BEGIN, + .action = ACTION_SELF, + .href = "/theme?monero" + }, { + .icon = ICON_SOLID, + .width = 140, + .text = " ", + }, { + .text = "Monero", + }, { + .type = TAG_END, + .text = "
", + }, { + .type = TAG_BEGIN, + .layout = LAYOUT_FLOAT_RIGHT, + .action = ACTION_SELF, + .href = "/theme?narrow" + }, { + .text = "Narrow", + }, { + .icon = ICON_SOLID, + .width = 140, + .text = " ", + }, { + .type = TAG_END, + .text = "
" + }, { + .type = TAG_BEGIN, + .action = ACTION_SELF, + .href = "/theme?black" + }, { + .icon = ICON_SOLID, + .width = 140, + .text = " ", + }, { + .text = "Black", + }, { + .type = TAG_END, + .text = "
" + }, { + .type = TAG_BEGIN, + .action = ACTION_SELF, + .href = "/theme?white" + }, { + .icon = ICON_SOLID, + .width = 140, + .text = " ", + }, { + .text = "White", + }, { + .type = TAG_END, + .text = "
" + }, { + .type = TAG_BEGIN, + .action = ACTION_SELF, + .href = "/theme?pink" + }, { + .icon = ICON_SOLID, + .width = 140, + .text = " ", + }, { + .text = "Pink", + }, { + .type = TAG_END, + .text = "
" + }, { + .type = TAG_BEGIN, + .action = ACTION_SELF, + .href = "/theme?light" + }, { + .icon = ICON_SOLID, + .width = 140, + .text = " ", + }, { + .text = "Light", + }, { + .type = TAG_END, + .text = "
" + }, { + .type = TAG_BEGIN, + .action = ACTION_SELF, + .href = "/theme?random" + }, { + .color = COLOR_RAINBOW_LIGHT, + .icon = ICON_SOLID, + .width = 140, + .text = " ", + }, { + .text = "Random", + }, { + .type = TAG_END, + .text = "
" + }, { + .type = TAG_BEGIN, + .action = ACTION_SELF, + .href = "/theme?madness" + }, { + .color = COLOR_RAINBOW_LIGHT, + .icon = ICON_SOLID, + .width = 140, + .text = " ", + }, { + .text = "Madness", + }, { + .type = TAG_END, + .text = "

" + }, { + .type = TAG_SUBMIT_TEXT, + .text = INSERT_THEME, + }, { + .text = "
" + }, { + .icon = ICON_SOLID, + .width = 140, + .padding_top = 30, + .text = "" + }, { + .action = ACTION_SUBMIT, + .text = "Apply", + .href = "/theme", + }, { + .type = TAG_CAPTION, + .layout = LAYOUT_CENTER, + .text = "Preview", + }, { + .color = COLOR_NONE, + .text = "DEFAULT
" + }, { + .color = COLOR_FOREGROUND_TEXT, + .text = "FOREGROUND TEXT
" + }, { + .color = COLOR_FOREGROUND_HOVER, + .text = "FOREGROUND HOVER
" + }, { + .color = COLOR_FOREGROUND_LINK, + .text = "FOREGROUND LINK
" + }, { + .color = COLOR_FOREGROUND_TITLE, + .text = "FOREGROUND TITLE
" + }, { + .color = COLOR_FOREGROUND_DIM, + .text = "FOREGROUND DIM
" + }, { + .color = COLOR_FOREGROUND_FADE, + .text = "FOREGROUND FADE
" + }, { + .color = COLOR_FOREGROUND_HEAD, + .text = "FOREGROUND HEAD
" + }, { + .color = COLOR_FOREGROUND_FOOT, + .text = "FOREGROUND FOOT
" + }, { + .color = COLOR_FOREGROUND_SPOT, + .text = "FOREGROUND SPOT
" + }, { + .background = COLOR_BACKGROUND_NAV, + .text = "BACKGROUND NAV
" + }, { + .background = COLOR_BACKGROUND_FAR, + .text = "BACKGROUND FAR
" + }, { + .background = COLOR_BACKGROUND_FADE, + .text = "BACKGROUND FADE
" + }, { + .background = COLOR_BACKGROUND_NEAR, + .text = "BACKGROUND NEAR
" + }, { + .color = COLOR_RAINBOW_LIGHT, + .text = "RAINBOW LIGHT
" + }, { + .type = TAG_END, + .color = COLOR_RAINBOW_DARK, + .text = "RAINBOW DARK" + }, + }, + }, + [DOC_LINKS_MONERO] = { + .id = "monero", + .id_parent = "links", + .title = "Monero - Guattari Tech", + .content = (Tag[]) { + { + .text = "
Monero

" + }, { + .type = TAG_CODE, + .text = "8BuZuaXHH3d3hCRoAvyF9Z9tFS55A7B1mPDCCsdeMosV11DBvWb3XB54yE6mWwh35RcZe4RaaXnwiK5se3qn1d9H2X6Sfqd", + }, { + .type = TAG_END, + }, + }, + }, + [DOC_LINKS_BITCOIN] = { + .id = "bitcoin", + .id_parent = "links", + .title = "Bitcoin - Guattari Tech", + .content = (Tag[]) { + { + .text = "
Bitcoin

" + }, { + .type = TAG_CODE, + .scale = 120, + .text = "bc1qhk0uezw98wxwweaeelwh8z0znk6jsl4qd5l8v7", + }, { + .type = TAG_END, + }, + }, + }, + [DOC_SAW] = { + .id = "saw", + .title = "Saw - Guattari Tech", + .content = (Tag[]) { + { + .type = TAG_RAW, + .text = "" + "" + "" + "" + "" + "" + "Saw - Guattari Tech" + "" + "" + "" + "" + "" + "" + "", + } + } + } +}; + +c8 const *headlines[] = { + "There is no need to fear or hope, but only to look for new weapons.", + "Handmade software in the age of LLMs.", + "Decentralization, engineering, and music.", + "Simplicity, reliability, and performance.", + "Accessibility, utility, and self custody.", + "Joy of programming.", + "Dark atmospheric music for Nomads.", + "A renaissance of the Pythagorean mindset.", +}; + +// ================================================================ +// +// Procedures +// +// ================================================================ + +c8 const *get_color(i32 index) { + static i64 rainbow_index = -1; + if (rainbow_index == -1) + rainbow_index = rand(); + + i32 n = index < 0 || index >= COLOR_MAP_SIZE ? COLOR_MAP[COLOR_NONE] : COLOR_MAP[index]; + if (n == COLOR_NONE) + return COLORS[COLOR_FOREGROUND_TEXT]; + if (n == COLOR_RAINBOW_LIGHT) + return RAINBOW_LIGHT[(rainbow_index++) % (sizeof RAINBOW_LIGHT / sizeof *RAINBOW_LIGHT)]; + if (n == COLOR_RAINBOW_DARK) + return RAINBOW_DARK[(rainbow_index++) % (sizeof RAINBOW_DARK / sizeof *RAINBOW_DARK)]; + if (n < 0 || n >= NUM_COLORS) + return COLORS[COLOR_NONE]; + return COLORS[index]; +} + +i32 unpack_hex(c8 h) { + if (h >= '0' && h <= '9') + return h - '0'; + if (h >= 'a' && h <= 'f') + return 10 + (h - 'a'); + if (h >= 'A' && h <= 'F') + return 10 + (h - 'A'); + return 0; +} + +i32 color_brightness(c8 const *color) { + i32 rgb[3] = { + unpack_hex(color[0]) * 16 + unpack_hex(color[1]), + unpack_hex(color[2]) * 16 + unpack_hex(color[3]), + unpack_hex(color[4]) * 16 + unpack_hex(color[5]), + }; + return rgb[0] + rgb[1] + rgb[2]; +} + +b8 is_dark_color(i32 index) { + i32 n = + index < 0 || index >= COLOR_MAP_SIZE + ? COLOR_MAP[COLOR_NONE] + : COLOR_MAP[index]; + + if (n == COLOR_RAINBOW_LIGHT) + return 0; + if (n == COLOR_RAINBOW_DARK) + return 1; + + i32 bg = color_brightness(COLORS[COLOR_BACKGROUND_NEAR]); + if (n < 0 || n >= (i32)(sizeof COLORS / sizeof *COLORS)) + return 127 * 3 < bg; + + i32 fg = color_brightness(COLORS[n]); + return fg < bg; +} + +void print_theme() { + printf("%d // scale ", SCALE); + printf("%d // width ", WIDTH); + + for (i32 i = 0; i < COLOR_MAP_SIZE; ++i) { + if (COLOR_MAP[i] == COLOR_RAINBOW_LIGHT) + printf("rainbow_light"); + else if (COLOR_MAP[i] == COLOR_RAINBOW_DARK) + printf("rainbow_dark"); + else if (COLOR_MAP[i] == i) { + for (i32 j = 0; j < 6; ++j) + printf("%c", COLORS[i][j]); + } else { + printf("%d", COLOR_MAP[i]); + } + if (i == COLOR_NONE) + printf(" // default color"); + if (i == COLOR_FOREGROUND_TEXT) + printf(" // foreground colors"); + if (i == COLOR_BACKGROUND_NAV) + printf(" // background colors"); + if (i == COLOR_RAINBOW_LIGHT) + printf(" // special colors"); + printf(" "); + } +} + +void print_text(c8 const *text, i32 color) { + if (text == INSERT_THEME) + print_theme(); + else if (text != NULL) { + i32 mapped_color = COLOR_MAP[color]; + if (mapped_color == COLOR_RAINBOW_LIGHT || mapped_color == COLOR_RAINBOW_DARK) { + for (i32 j = 0; text[j] != '\0'; ++j) { + if (text[j] == ' ') + printf(" "); + else if (text[j] == '<') { + for (; text[j] != '\0' && text[j] != '>'; ++j) + printf("%c", text[j]); + if (text[j] == '\0') + break; + printf("%c", text[j]); + } else if (text[j] == '&') { + for (; text[j] != '\0' && text[j] != ';'; ++j) + printf("%c", text[j]); + if (text[j] == '\0') + break; + printf("%c", text[j]); + } else + printf( + "%c", + get_color(mapped_color), + text[j] + ); + } + } else + printf("%s", text); + } +} + +void print_element(Tag *t, b8 is_on_top, i32 default_color, i32 margin, b8 *is_link, b8 *need_styling) { + *is_link = (t->href != NULL && t->action != ACTION_SUBMIT); + b8 is_form = (t->href != NULL && t->action == ACTION_SUBMIT); + + *need_styling = (t->type != TAG_PLAIN && + t->type != TAG_BEGIN && + t->type != TAG_END) || + t->color != COLOR_NONE || + t->background != COLOR_NONE || + t->width > 0 || + t->layout != LAYOUT_NONE || + t->padding_top != 0 || + t->line_height != 0 || + t->nowrap || + t->unselectable || + margin != LAYOUT_NONE || + *is_link; + + if (*need_styling) { + if (*is_link) + printf("unselectable) + printf("-webkit-user-select:none;-ms-user-select:none;user-select:none;"); + + if (margin == LAYOUT_MARGIN_LEFT) + printf("float:left;"); + if (margin == LAYOUT_MARGIN_RIGHT) + printf("float:right;"); + if (margin != LAYOUT_NONE && margin != t->layout) + printf("visibility:hidden;"); + + i32 padding_top = t->padding_top; + + if (t->type == TAG_CAPTION) { + if (is_on_top) + padding_top += 68; + else + padding_top += 160; + } + if (t->type == TAG_SUBTITLE) + padding_top += 40; + + if (padding_top != 0) + printf("padding-top:%.2fem;", .01f * padding_top); + + if (t->type == TAG_BLOCK || + t->line_height != 0 || + t->layout == LAYOUT_LEFT || + t->layout == LAYOUT_RIGHT || + t->layout == LAYOUT_CENTER) + printf("display:block;"); + + if (t->line_height != 0) + printf("line-height:%.2f;", .01f * t->line_height); + + if (t->icon != ICON_NONE) + printf("-webkit-user-select:none;-ms-user-select:none;user-select:none;" + "font-family:\"%s\";", + ( t->icon == ICON_SOLID ? "Icon Solid" + : t->icon == ICON_BRAND ? "Brand" + : "Icon")); + + switch (t->type) { + case TAG_CAPTION: + printf("font-family:\"Lithos Pro\";" + "font-size:1.2em;font-weight:900;" + "letter-spacing:0.2em;" + "display:block;" + "padding-bottom:0.2em;"); + break; + + case TAG_SUBTITLE: + printf("font-family:\"Verdana\";" + "font-size:1.2em;" + "display:block;"); + break; + + case TAG_CODE: + if (*is_link) + printf("font-weight:bold;"); + else + printf("font-family:monospace;font-size:0.9em;"); + break; + + default:; + } + + if (t->nowrap) + printf("white-space:nowrap;"); + + if (t->color != COLOR_NONE) + printf("color:#%s;", get_color(t->color)); + + if (t->background != COLOR_NONE) + printf("background-color:#%s;", get_color(t->background)); + + if (t->width > 0) { + if (t->type != TAG_CAPTION) + printf("display:inline-block;"); + printf("width:%.2fem;", .01f * t->width); + } + + switch (t->layout) { + case LAYOUT_LEFT: + printf("text-align:left;"); + break; + + case LAYOUT_CENTER: + printf("text-align:center;"); + break; + + case LAYOUT_RIGHT: + printf("text-align:right;"); + break; + + case LAYOUT_FLOAT_LEFT: + printf("float:left;"); + break; + + case LAYOUT_FLOAT_CENTER: + printf("float:center;"); + break; + + case LAYOUT_FLOAT_RIGHT: + printf("float:right;"); + break; + + default:; + } + + printf("'>"); + } + + if (t->image != NULL) { + printf("scale != 0) + printf(" width='%d%%'", t->scale); + switch (t->layout) { + case LAYOUT_LEFT: + printf(" align=\"left\""); + break; + + case LAYOUT_CENTER: + printf(" align=\"center\""); + break; + + case LAYOUT_RIGHT: + printf(" align=\"right\""); + break; + + default:; + } + printf(" src=\"%s\"", t->image); + printf(">"); + } + + if (t->type == TAG_BLACK_FLAG) { + if (is_dark_color(t->color)) + printf("" + ""); + else + printf("" + ""); + } + + if (is_form) + printf("
", + t->href); + + if (t->type == TAG_SUBMIT_TEXT) + printf(""); +} + +void close_element(b8 is_link, b8 need_styling) { + if (need_styling) { + if (is_link) + printf(""); + else + printf(""); + } +} + +i64 print_tags(Tag *tags, i64 i) { + static i32 current_color = COLOR_NONE; + + for (;; ++i) { + b8 is_link, need_styling; + + if (tags[i].layout == LAYOUT_MARGIN_LEFT || + tags[i].layout == LAYOUT_MARGIN_RIGHT) { + print_element( + tags + i, + i == 0, + current_color, + LAYOUT_MARGIN_LEFT, + &is_link, + &need_styling); + close_element(is_link, need_styling); + + print_element( + tags + i, + i == 0, + current_color, + LAYOUT_MARGIN_RIGHT, + &is_link, + &need_styling); + close_element(is_link, need_styling); + + if (tags[i].type == TAG_END) + break; + } else { + print_element( + tags + i, + i == 0, + current_color, + LAYOUT_NONE, + &is_link, + &need_styling); + + i64 skip = 0; + + if (tags[i].type == TAG_BEGIN) { + i32 prev_color = current_color; + if (tags[i].color != COLOR_NONE) + current_color = tags[i].color; + skip = print_tags(tags, i + 1); + current_color = prev_color; + } + + close_element(is_link, need_styling); + + if (tags[i].type == TAG_BEGIN) + i = skip - 1; + else if (tags[i].type == TAG_END) + break; + } + } + + return i + 1; +} + +i64 year() { + time_t t; + time(&t); + struct tm *exp = gmtime(&t); + return 1900 + exp->tm_year; +} + +void print_time_gmt(i64 add_seconds) { + static c8 const *const WEEK[] = { "Sun", "Mon", "Tue", "Wed", + "Thu", "Fri", "Sat" }; + + static c8 const *const MONTHS[] = { "Jan", "Feb", "Mar", "Apr", + "May", "Jun", "Jul", "Aug", + "Sep", "Oct", "Now", "Dec" }; + + time_t t; + time(&t); + t += add_seconds; + struct tm *exp = gmtime(&t); + + printf("%s, %02d %s %d %02d:%02d:%02d GMT", WEEK[exp->tm_wday], + exp->tm_mday, MONTHS[exp->tm_mon], 1900 + exp->tm_year, + exp->tm_hour, exp->tm_min, exp->tm_sec); +} + +c8 const *getenv_or_empty(c8 const *name) { + c8 const *s = getenv(name); + return s == NULL ? "" : s; +} + +b8 str_eq(c8 const *a, c8 const *a_end, c8 const *b, c8 const *b_end) { + i64 a_len = a_end == NULL ? (i64) strlen(a) : (i64) (a_end - a); + i64 b_len = b_end == NULL ? (i64) strlen(b) : (i64) (b_end - b); + if (a_len == b_len) + return memcmp(a, b, a_len) == 0; + return 0; +} + +b8 str_eq_with_slash(c8 const *a, c8 const *b) { + i64 a_len = strlen(a); + i64 b_len = strlen(b); + if (a_len == b_len) + return memcmp(a, b, a_len) == 0; + if (a_len == b_len + 1 && a_len > 1 && a[a_len - 1] == '/') + return memcmp(a, b, b_len) == 0; + return 0; +} + +i32 check_color(i32 c) { + if (c >= 0 && c < COLORS_MAX) + return c; + return COLOR_NONE; +} + +b8 is_hex(c8 const *s, c8 const *end) { + for (i32 i = 0; s + i < end; ++i) { + if (s[i] >= '0' && s[i] <= '9') + continue; + if (s[i] >= 'a' && s[i] <= 'f') + continue; + return 0; + } + return 1; +} + +c8 check_hex(c8 h) { + if ((h >= '0' && h <= '9') || (h >= 'a' && h <= 'f')) + return h; + return '0'; +} + +b8 is_escaped_eol(c8 const *s) { + return s[0] == '%' && + s[1] == '0' && + (s[2] == 'D' || s[2] == 'A'); +} + +b8 is_escaped_comment(c8 const *s) { + return + s[0] == '%' && s[1] == '2' && s[2] == 'F' && + s[3] == '%' && s[4] == '2' && s[5] == 'F'; +} + +void skip_spaces(c8 const *s, i32 *n) { + for (;;) { + if (s[*n] == '+' || s[*n] == ' ' || s[*n] == '\n' || s[*n] == '\r') + ++*n; + else if (is_escaped_eol(s + *n)) + *n += 3; + else if (is_escaped_comment(s + *n)) { + *n += 6; + while (!is_escaped_eol(s + *n) && s[*n] != '\n' && s[*n] != '\r') + ++*n; + } else + return; + } +} + +void setup_theme_cookie(c8 const *cookie, c8 const *query) { + // Parse cookie values + // + { + c8 const *val = cookie; + c8 const *val_end = NULL; + + while (*val != '\0') { + while (*val == ' ' || *val == ';') ++val; + val_end = val; + while (*val_end != ';' && *val_end != '\0') ++val_end; + + if (val_end - val >= 7 + COLOR_MAP_SIZE + NUM_COLORS * 6 && + str_eq(val, val + sizeof COOKIE_THEME, COOKIE_THEME "=", NULL)) { + // Decode colors data from the cookie + // + i32 n = sizeof COOKIE_THEME; + SCALE = unpack_hex(val[n]) * 0x100 + + unpack_hex(val[n + 1]) * 0x10 + + unpack_hex(val[n + 2]); + WIDTH = unpack_hex(val[n + 3]) * 0x100 + + unpack_hex(val[n + 4]) * 0x10 + + unpack_hex(val[n + 5]); + n += 6; + for (i32 i = 0; i < COLOR_MAP_SIZE; ++i) + COLOR_MAP[i] = check_color( + (unpack_hex(val[n + i * 2]) << 4) | + unpack_hex(val[n + i * 2 + 1]) + ); + for (i32 i = 0; i < NUM_COLORS; ++i) { + i32 k = n + COLOR_MAP_SIZE * 2 + i * 6; + COLORS[i][0] = check_hex(val[k]); + COLORS[i][1] = check_hex(val[k + 1]); + COLORS[i][2] = check_hex(val[k + 2]); + COLORS[i][3] = check_hex(val[k + 3]); + COLORS[i][4] = check_hex(val[k + 4]); + COLORS[i][5] = check_hex(val[k + 5]); + } + + // We don't care about other cookies. + break; + } + + val = val_end; + } + } + + // Handle the query + // + { + #define DEFAULT_MAP \ + do { \ + for (i32 i = 0;i < COLOR_MAP_SIZE; ++i) \ + COLOR_MAP[i] = i; \ + } while (0) + + if (str_eq(query, NULL, "wide", NULL)) { + WIDTH = 100; + } else if (str_eq(query, NULL, "narrow", NULL)) { + WIDTH = 42; + + } else if (str_eq(query, NULL, "guattari", NULL)) { + DEFAULT_MAP; + for (i32 i = 0; i < NUM_COLORS; ++i) + memcpy(COLORS[i], THEMES[THEME_GUATTARI][i], 6); + + } else if (str_eq(query, NULL, "monero", NULL)) { + DEFAULT_MAP; + for (i32 i = 0; i < NUM_COLORS; ++i) + memcpy(COLORS[i], THEMES[THEME_MONERO][i], 6); + + } else if (str_eq(query, NULL, "black", NULL)) { + DEFAULT_MAP; + for (i32 i = 0; i < NUM_COLORS; ++i) + memcpy(COLORS[i], THEMES[THEME_BLACK][i], 6); + + } else if (str_eq(query, NULL, "white", NULL)) { + DEFAULT_MAP; + for (i32 i = 0; i < NUM_COLORS; ++i) + memcpy(COLORS[i], THEMES[THEME_WHITE][i], 6); + + } else if (str_eq(query, NULL, "pink", NULL)) { + DEFAULT_MAP; + for (i32 i = 0; i < NUM_COLORS; ++i) + memcpy(COLORS[i], THEMES[THEME_PINK][i], 6); + + } else if (str_eq(query, NULL, "light", NULL)) { + DEFAULT_MAP; + for (i32 i = 0; i < NUM_COLORS; ++i) + memcpy(COLORS[i], THEMES[THEME_LIGHT][i], 6); + + } else if (str_eq(query, NULL, "random", NULL)) { + DEFAULT_MAP; + for (i32 i = 0; i < NUM_COLORS; ++i) + for (i32 j = 0; j < 6; ++j) + COLORS[i][j] = (rand() % 16)["0123456789abcdef"]; + + } else if (str_eq(query, NULL, "madness", NULL)) { + for (i32 i = 0; i < COLOR_BACKGROUND_NAV; ++i) + COLOR_MAP[i] = COLOR_RAINBOW_LIGHT; + for (i32 i = COLOR_BACKGROUND_NAV; i <= COLOR_BACKGROUND_NEAR; ++i) + COLOR_MAP[i] = COLOR_RAINBOW_DARK; + COLOR_MAP[COLOR_RAINBOW_LIGHT] = COLOR_RAINBOW_LIGHT; + COLOR_MAP[COLOR_RAINBOW_DARK] = COLOR_RAINBOW_DARK; + + } else if (strlen(query) > 5 && + str_eq(query, query + 5, "data=", NULL)) { + i32 n = 5; + i32 k = 0; + i32 val; + + skip_spaces(query, &n); + if (sscanf(query + n, "%d%n", &val, &k) == 1) { + SCALE = val; + n += k; + } + + skip_spaces(query, &n); + if (sscanf(query + n, "%d%n", &val, &k) == 1) { + WIDTH = val; + n += k; + } + + c8 color[6]; + for (i32 i = 0; i < COLOR_MAP_SIZE; ++i) { + skip_spaces(query, &n); + c8 c; + if (sscanf(query + n, "rainbow_ligh%c%n", &c, &k) == 1 && + c == 't') { + COLOR_MAP[i] = COLOR_RAINBOW_LIGHT; + n += k; + } else if (sscanf(query + n, "rainbow_dar%c%n", &c, &k) == 1 && + c == 'k') { + COLOR_MAP[i] = COLOR_RAINBOW_DARK; + n += k; + } else if (sscanf(query + n, "%c%c%c%c%c%c%n", + color, color + 1, color + 2, + color + 3, color + 4, color + 5, + &k) == 6 && is_hex(color, color + 6)) { + COLOR_MAP[i] = i; + for (i32 j = 0; j < 6; ++j) + COLORS[i][j] = check_hex(color[j]); + n += k; + } else if (sscanf(query + n, "%d%n", &val, &k) == 1) { + COLOR_MAP[i] = check_color(val); + n += k; + } else + break; + } + } + + #undef DEFAULT_MAP + } + + // Clamp values + // + { + if (SCALE < SCALE_MIN) + SCALE = SCALE_MIN; + if (SCALE > SCALE_MAX) + SCALE = SCALE_MAX; + if (WIDTH < WIDTH_MIN) + WIDTH = WIDTH_MIN; + if (WIDTH > WIDTH_MAX) + WIDTH = WIDTH_MAX; + } + + // Setup new cookie values + // + { + // Encode colors data into the cookie + // + printf("Set-Cookie: " COOKIE_THEME "="); + printf("%03x%03x", SCALE, WIDTH); + for (i32 i = 0; i < COLOR_MAP_SIZE; ++i) + printf("%02x", COLOR_MAP[i]); + for (i32 i = 0; i < NUM_COLORS; ++i) + printf("%c%c%c%c%c%c", + COLORS[i][0], + COLORS[i][1], + COLORS[i][2], + COLORS[i][3], + COLORS[i][4], + COLORS[i][5]); + printf("; Path=/; SameSite=Lax; Expires="); + print_time_gmt(6 * SECONDS_IN_MONTH); + printf("; HttpOnly\n"); + } +} + +i32 main(i32 argc, c8 **argv) { + (void) argc; + (void) argv; + + struct timespec time_begin; + + timespec_get(&time_begin, TIME_UTC); + srand(time_begin.tv_sec ^ time_begin.tv_nsec); + + c8 const *request_method = getenv_or_empty("REQUEST_METHOD"); + c8 const *request_uri = getenv_or_empty("REQUEST_URI"); + c8 const *query_string = getenv_or_empty("QUERY_STRING"); + c8 const *document_uri = getenv_or_empty("DOCUMENT_URI"); + c8 const *http_cookie = getenv_or_empty("HTTP_COOKIE"); + c8 const *content_type = getenv_or_empty("CONTENT_TYPE"); + + if ( strlen(request_method) > MAX_REQUEST_METHOD_SIZE || + strlen(request_uri) > MAX_REQUEST_SIZE || + strlen(query_string) > MAX_REQUEST_SIZE || + strlen(document_uri) > MAX_REQUEST_SIZE || + strlen(http_cookie) > MAX_COOKIE_SIZE || + strlen(content_type) > MAX_CONTENT_TYPE_SIZE || + (!str_eq(request_method, NULL, "GET", NULL) && + !str_eq(request_method, NULL, "POST", NULL))) { + printf("Status: 500 Internal Server Error\n"); + return 0; + } + + // TODO Check blacklist and throttling + + if (0) { + printf("Status: 400 Forbidden\n"); + return 0; + } + + if (0) { + printf("Status: 429 Too Many Requests\n"); + return 0; + } + + i32 doc = DOC_404; + + for (i64 i = 0; i < (i64)(sizeof redirects / sizeof *redirects); ++i) + if (str_eq_with_slash(document_uri, redirects[i].alias)) { + printf("Status: 301 Redirect\n"); + printf("Location: %s\n", redirects[i].url); + printf("Content-Type: text/html\n"); + printf("\n"); + printf("Redirect: %s\n", redirects[i].url, redirects[i].url); + return 0; + } + + for (i64 i = 0; i < (i64)(sizeof locs / sizeof *locs); ++i) + if (strcmp(request_method, locs[i].method) == 0 && + str_eq_with_slash(document_uri, locs[i].uri)) { + doc = locs[i].index; + break; + } + + if (doc == DOC_404) + printf("Status: 404 Page Not Found\n"); + else + printf("Status: 200 OK\n"); + + setup_theme_cookie(http_cookie, query_string); + + printf("Content-Type: text/html\n"); + printf("\n"); + + if (docs[doc].content[0].type == TAG_RAW) { + printf("%s", docs[doc].content[0].text); + return 0; + } + + printf(""); + { + printf(""); + printf(""); + printf( + ""); + printf(" %s ", docs[doc].title); + printf("", + scaled_nav, + get_color(COLOR_FOREGROUND_LINK), + get_color(COLOR_FOREGROUND_HOVER), + get_color(COLOR_BACKGROUND_NEAR), + get_color(COLOR_FOREGROUND_LINK), + get_color(COLOR_FOREGROUND_HOVER), + get_color(COLOR_FOREGROUND_NAV), + get_color(COLOR_FOREGROUND_NAV), + get_color(COLOR_FOREGROUND_HOVER), + get_color(COLOR_FOREGROUND_HOVER) + ); + } + printf("" + "
" + "
", + get_color(COLOR_BACKGROUND_FAR), + get_color(COLOR_FOREGROUND_TEXT), + SCALE / 100, + SCALE % 100); + { + printf("
"); + { + printf("
", + get_color(COLOR_FOREGROUND_TITLE)); + print_text("Guattari", COLOR_FOREGROUND_TITLE); + printf(" ", + get_color(COLOR_FOREGROUND_SPOT)); + print_text("Tech", COLOR_FOREGROUND_SPOT); + printf("
"); + printf( + "
", + get_color(COLOR_FOREGROUND_HEAD)); + print_text(headlines[rand() % (sizeof headlines / sizeof *headlines)], COLOR_FOREGROUND_HEAD); + printf("
"); + + printf("
    ", + get_color(COLOR_BACKGROUND_NAV)); + for (i64 i = 0; i < (i64)(sizeof navigation_left / sizeof *navigation_left); ++i) + printf("", + navigation_left[i].href, + navigation_left[i].target, + navigation_left[i].text); + for (i64 i = sizeof navigation_right / sizeof *navigation_right - 1; i >= 0; --i) + printf("", + navigation_right[i].href, + navigation_right[i].target, + navigation_right[i].text); + printf("
"); + printf("
"); + print_tags(docs[doc].content, 0); + printf("
"); + } + printf("
", + get_color(COLOR_FOREGROUND_FOOT)); + { + if (docs[doc].id != NULL) { + printf(" root"); + if (docs[doc].id_parent != NULL) + printf(" / %s", + docs[doc].id_parent, + docs[doc].id_parent); + printf(" / %s ", + docs[doc].id); + } else + printf(" root "); + printf( + "© %lld " + "Mitya Selivanov | CC-BY-4.0 ", + year()); + printf("🎨 " + "Theme", + get_color(COLOR_RAINBOW_LIGHT)); + } + printf("
"); + } + printf("
"); + + struct timespec time_end; + timespec_get(&time_end, TIME_UTC); + u64 delta_ns = 1000000000 * (time_end.tv_sec - time_begin.tv_sec) + (time_end.tv_nsec - time_begin.tv_nsec); + u64 delta_micros = (delta_ns + 500) / 1000; + printf("", + delta_micros / 1000, + (delta_micros / 10) % 100); + printf("\n"); + + return 0; +} -- cgit v1.2.3