ussg

Unnamed repository; edit this file 'description' to name the repository.
Log | Files | Refs | README

commit 5e87ac36f957c5dab0c675a864cca44e613f8587
parent 5b990e210b16cb952f5112fdd44bd416cb63a16f
Author: Ellenor Bjornsdottir <ellenor@umbrellix.net>
Date:   Mon, 12 Sep 2022 09:45:19 +0000

FIRST LIGHT! It generated a working page!

Diffstat:
MLICENCE.md | 1+
Adoc/examples/headers | 5+++++
Mdoc/examples/page.tpl | 29+++++++++++++++++++++++++++++
Mussg-page | 221+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++--
4 files changed, 252 insertions(+), 4 deletions(-)

diff --git a/LICENCE.md b/LICENCE.md @@ -1,3 +1,4 @@ + Copyright 2021 Umbrellix <ellenor@umbrellix.net>. All Rights Reserved. Your licence to this software is governed under the laws of BC, Canada, except as required by diff --git a/doc/examples/headers b/doc/examples/headers @@ -0,0 +1,5 @@ +Template: doc/examples/page.tpl +X-Site-Title: UmbrellixSSG Test Site +X-Synoptic-Title: UmbrellixSSG Test Site +Verbatim: doc/examples/topnav.htfrag +Verbatim: doc/examples/bottomnav.htfrag diff --git a/doc/examples/page.tpl b/doc/examples/page.tpl @@ -0,0 +1,29 @@ +<!DOCTYPE HTML> +<html> +<head> + <meta charset="UTF-8"> + <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"> + + <link rel="stylesheet" href="/style.css" type="text/css" media="screen, handheld" title="default"> + %title + %heads +</head> +<body> +<header> +%0 +</header> +<h1><a href="/"> +%xsitetitle +<span id="headerSubTitle"> +%xsitedescription +</span> +</a></h1> +<article> +%article +</article> +<footer> +%1 +</footer> +</body> +<!-- 0 - top nav, lowertitle - lower top nav, article - markdown, 1 - footer nav --> +</html> diff --git a/ussg-page b/ussg-page @@ -1,5 +1,7 @@ #!/usr/bin/env tclsh8.6 +chan configure stderr -buffering line + # proc until {test body} {uplevel 1 [list while [concat ! $test] $body]} proc opts {{consume {h v H: M: help version headers: markdown:}} argl} { @@ -196,13 +198,34 @@ proc printhelp {} { set inputfd stdin set outputfd stdout +set inputfile "" +set outputfile "" set headersfiles [list] set headersfds [list] set markdown [list env markdown] set scribble 1 +set moving 0 ;# 1 means that we'll have to move data after. ## Begin command routine +set headers [list] + +# 0 if it can only be set once, 1 if it can be set inf times +# assume 1 +# Headers are case insensitive and stored in lowercase, so ... +set headermulti { + title 0 + template 0 + x-site-title 0 + x-site-description 0 + x-synoptic-title 0 + x-synoptic-text 0 + x-synoptic-image 0 + favicon 0 + style 1 + verbatim 1 +} + set parsedargs [opts [list h v H: M: help version headers: markdown:] $argv] # opts [list h v H: M: help version headers: markdown:] [list input --markdown /usr/bin/markdown output -HMEQI .headers /usr/bin/markdown -- -EQI -- -- --] # warning: option -E not recognized by this program. Treating as a SEPARATE non-option argument - if this wasn't intended, put the argument containing this option after a '--'! @@ -262,12 +285,13 @@ if {[llength $arg] == 2} { puts stderr [format "Error: output file \'%s\' already exists. You asked me not to scribble on it, so I am not scribbling on it." $outputfile] exit 73 } - if {[catch {[open [set ::tmpoutputfile [picktmpfilename [set outputfile [file normalize $outputfile]]]] w]} provoutputfd]} { + if {[catch {open [set ::tmpoutputfile [picktmpfilename [set outputfile [file normalize $outputfile]]]] w} provoutputfd]} { puts stderr [format "Error: temporary output file \'%s\' could not be opened for writing. \[open\] reports:" $::tmpoutputfile] puts stderr $provoutputfd exit 73 } { set ::outputfd $provoutputfd + set ::moving 1 } } } @@ -277,11 +301,200 @@ if {[llength $arg] > 0} { if {$inputfile == "-"} { } else { # In the positive, the input file desc is already set correctly. - if {[catch {[open [file normalize $inputfile] r]} provinputfd]} { + if {[catch {open [file normalize $inputfile] r} provinputfd]} { puts stderr [format "Error: input file \'%s\' could not be opened for reading. \[open\] reports:" $::inputfile] - puts stderr $provoutputfd + puts stderr $provinputfd exit 66 + } { + set ::inputfd $provinputfd } } } -foreach headersfile $::headersfiles +foreach headersfile $::headersfiles { + if {[catch {open [file normalize $headersfile] r} provinputfd]} { + puts stderr [format "Error: headers file \'%s\' could not be opened for reading. Balking now; this is fatal. \[open\] reports:" $::headersfile] + puts stderr $provinputfd + exit 66 + } { + lappend ::headersfds $provinputfd + } +} + +proc parseheader {lin} { + set content [string range [join [lassign [split $lin ":"] word] ":"] 1 end] + set word [string tolower $word] + puts stderr [format "info: found header %s: %s" $word $content] + if {[catch {dict get $::headermulti $word} multi] == 0} { + if {$multi} { + dict lappend ::headers $word $content + } { + dict set ::headers $word $content + # We will just allow overriding. + } + } +} + +foreach headersfd $::headersfds { + while {![eof $headersfd]} { + gets $headersfd lin + parseheader $lin + } + # we are done, we can close the header now. + close $headersfd +} + +set firstblankline 0 +if {[catch {open [format "|%s" $markdown] r+} err]} { + puts stderr [format "Error: processor \'%s\' could not be executed for reading and writing. \[open\] reports:" $markdown] + puts stderr $err + exit 70 +} { + set mkdownfd $err +} + +while {![eof $inputfd] && !$firstblankline} { + gets $inputfd lin + if {$lin == ""} { + set firstblankline 1 + puts stderr "info: blank line found. Begin processing document as document." + } { + parseheader $lin + } +} + +if {[eof $inputfd]} { + puts stderr "Error: document file ended without a document. As this would produce an empty document, this is not allowed." + exit 66 +} +# Hold up. Stop accepting lines from inputfd. + +# By this stage, we must have a Template: header. +# In the template, %%article%% must be on a line by itself as it's expected to be quite large, so unsuitable for [string map]. +if {[catch {dict get $::headers template} templatehdr]} { + puts stderr $::headers + puts stderr [format "Error: template file not specified in headers or document file. Without a template, we cannot create a document."] + exit 78 +} { + if {[catch {open [file normalize $templatehdr] r} provinputfd]} { + puts stderr [format "Error: template file \'%s\' could not be opened for reading. Balking now; this is fatal. \[open\] reports:" $::templatehdr] + puts stderr $provinputfd + exit 66 + } { + set templfd $provinputfd + } +} + +namespace eval ::templcmds { + proc title {} { + # output page title + if {![catch {dict get $::headers title} title]} { puts $::outputfd [format "<title>%s</title>" $title] } + } + + proc xsitetitle {} { + # output Site Title + if {![catch {dict get $::headers x-site-title} title]} { puts -nonewline $::outputfd $title } + } + + proc xsitedescription {} { + if {![catch {dict get $::headers x-site-description} title]} { puts -nonewline $::outputfd $title } + } + + proc heads {} { + # here we check for synoptic headers, and output them as appropriate + # e.g. <link rel="stylesheet" href="/pub/style/style.css" type="text/css" media="screen, handheld" title="default"> + if {![catch {dict get $::headers x-synoptic-title} title]} { + puts $::outputfd [format "<meta property=\"og:title\" content=\"%s\"></meta>" $title] + } + if {![catch {dict get $::headers x-synoptic-text} title]} { + puts $::outputfd [format "<meta property=\"og:description\" content=\"%s\"></meta>" $title] + } + if {![catch {dict get $::headers x-synoptic-image} title]} { + puts $::outputfd [format "<meta property=\"og:image\" content=\"%s\"></meta>" $title] + } + if {![catch {dict get $::headers x-synoptic-url} title]} { + puts $::outputfd [format "<meta property=\"og:url\" content=\"%s\"></meta>" $title] + } + if {![catch {dict get $::headers favicon} title]} { + puts $::outputfd [format "<link rel=\"shortcut icon\" href=\"/%s\" type=\"image/vnd.microsoft.icon\">" $title] + } + if {![catch {dict get $::headers style} title]} { + puts $::outputfd [format "<link rel=\"stylesheet\" href=\"/%s\" type=\"text/css\">" $title] + } + } + + proc article {} { + # finally, our raison d'etre ! + # we expect to get eof on input. + chan copy $::inputfd $::mkdownfd + chan flush $::mkdownfd + chan close $::mkdownfd write + chan close $::inputfd + # begone, input document + chan copy $::mkdownfd $::outputfd + chan flush $::outputfd + chan close $::mkdownfd read + # begone, markdown + } + + proc verbatim {num} { + set verbatimhdr [lindex [dict get $::headers verbatim] $num] + if {![file exists $verbatimhdr]} { + puts stderr [format "Warning: verbatim file no. %s \'%s\' does not exist." $num $verbatimhdr] + return + } + if {[catch {open [file normalize $verbatimhdr] r} provinputfd]} { + puts stderr [format "Warning: verbatim file no. %s \'%s\' could not be opened for reading. \[open\] reports:" $num $verbatimhdr] + puts stderr $provinputfd + return + } { + chan copy $provinputfd $::outputfd + chan flush $::outputfd + close $provinputfd + } + } + + namespace export * + namespace ensemble create +} + +proc templcmd {command} { + if {[string is entier [string trimleft $command "%"]]} { + templcmds verbatim [string trimleft $command "%"] + } { + templcmds [string trimleft $command "%"] + } + chan flush $::outputfd +} + +# We expect the template to be small enough that reading it, in full, into memory, will not be a problem even on the smallest system we expect to work on. +set template [list] +while {![eof $templfd]} { + lappend template [gets $templfd] +} +# We're done, we can close the template now +close $templfd +foreach {tplline} $template { + if {[string first "%" [string trimleft $tplline " \t"]] == 0} { + # special + # a single percentage sign, with only tabs or spaces before it, is a template command. + # if it's "article", then print the article. if it's a number, print that number of verbatim (or nothing if it doesn't exist) + set tplcmd [string trimleft $tplline " \t"] + templcmd $tplcmd + } { + # just print to $::outputfd + puts $::outputfd $tplline + } +} +chan flush $::outputfd +chan close $::outputfd + +# Out of the loop: we're getting there... +# Moving? If not, we're probably done. +if {${moving}} { + puts stderr [format "Info: Moving temporary file \'%s\' to its permanent location, \'%s\'" $::tmpoutputfile $::outputfile] + if {[catch {file rename -force $::tmpoutputfile $::outputfile} err]} { + puts stderr [format "Error: While moving temporary file \'%s\' to its permanent location, \'%s\', \[file rename\] reported:" $::tmpoutputfile $::outputfile] + puts stderr $err + exit 74 + } +}