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:
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
+ }
+}