inits http ssg with mkws

This commit is contained in:
2026-05-12 20:56:32 +01:00
parent 512f130117
commit eb48cdb9a3
17 changed files with 1042 additions and 0 deletions
+1
View File
@@ -0,0 +1 @@
public
Executable
BIN
View File
Binary file not shown.
Executable
+29
View File
@@ -0,0 +1,29 @@
#!/bin/sh -e
export PATH="$(dirname $(realpath $0)):$PATH"
usage() {
>&2 printf "usage: %s url [path]\\n" "$(basename "$0")"
exit 1
}
test $# -lt 1 && usage
export SRC="$(realpath src)"
export SHARE="$(realpath share)"
export OUT="$(realpath public)"
if ! test -f "${SRC}"/index.upphtml
then
>&2 printf "no index.upphtml file found\\n"
exit 1
fi
for t in "${SRC}"/*.upphtml
do
echo "Making $(basename "${t%.upphtml}".html)"
pp "${SHARE}"/l.upphtml "${t}" "$1" > \
"${OUT}/$(basename "${t%.upphtml}".html)"
done
echo "Making sitemap.xml"
pp "${SHARE}"/sitemap.uppxml "$1" > "${OUT}"/sitemap.xml
Executable
BIN
View File
Binary file not shown.
+42
View File
@@ -0,0 +1,42 @@
<!-- IndieWeb -->
<link rel="authorization_endpoint" href="https://indieauth.com/auth">
<link rel="token_endpoint" href="https://tokens.indieauth.com/token">
<link rel="micropub" href="https://tasty-windows-lick.loca.lt">
<!-- Stylesheets -->
<link rel="stylesheet" href="/css/reset.css" />
<link rel="stylesheet" href="/css/base.css" />
<link rel="stylesheet" href="/css/hcard.css" />
<link rel="stylesheet" href="/css/feed.css" />
<!-- Global Metadata -->
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width,initial-scale=1" />
<link rel="icon" type="image/svg+xml" href="/favicon.svg" />
<meta name="generator" content="mkws" />
<!-- Canonical URL -->
<link rel="canonical" href={canonicalURL} />
<!-- Primary Meta Tags -->
<title>{title}</title>
<meta name="title" content={title} />
<meta name="description" content={description} />
<!-- Open Graph / Facebook -->
<meta property="og:type" content="website" />
<meta property="og:url" content={Astro.url} />
<meta property="og:title" content={title} />
<meta property="og:description" content={description} />
<meta property="og:image" content={new URL(image, Astro.url)} />
<!-- Twitter -->
<meta property="twitter:card" content="summary_large_image" />
<meta property="twitter:url" content={Astro.url} />
<meta property="twitter:title" content={title} />
<meta property="twitter:description" content={description} />
<meta property="twitter:image" content={new URL(image, Astro.url)} />
<!-- Feeds -->
<link rel="alternate" type="text/xml" title="Blog RSS" href="/blog/rss.xml">
<link rel="alternate" type="text/xml" title="Links RSS" href="/links/rss.xml">
+516
View File
@@ -0,0 +1,516 @@
/** Variables */
:root {
--colour-primary-10: #060300;
--colour-primary-15: #150800;
--colour-primary-20: #1f1400;
--colour-primary-30: #3c2b00;
--colour-primary-40: #5c4300;
--colour-primary-50: #7f5d00;
--colour-primary-60: #a37800;
--colour-primary-70: #c89500;
--colour-primary-80: #efb300;
--colour-primary-90: #ffd98c;
--colour-primary-95: #ffecc8;
--colour-hyperlink-10: #000409;
--colour-hyperlink-20: #001829;
--colour-hyperlink-30: #00314d;
--colour-hyperlink-40: #004d75;
--colour-hyperlink-50: #006a9f;
--colour-hyperlink-60: #1388c9;
--colour-hyperlink-70: #41a8ea;
--colour-hyperlink-80: #78c7ff;
--colour-hyperlink-90: #bfe3ff;
--colour-hyperlink-95: #e0f1ff;
--colour-grey-10: oklch(0.1 0.01 84);
--colour-grey-20: oklch(0.2 0.01 84);
--colour-grey-30: oklch(0.3 0.01 84);
--colour-grey-40: oklch(0.4 0.01 84);
--colour-grey-50: oklch(0.5 0.01 84);
--colour-grey-60: oklch(0.6 0.01 84);
--colour-grey-70: oklch(0.7 0.01 84);
--colour-grey-80: oklch(0.8 0.01 84);
--colour-grey-90: oklch(0.9 0.01 84);
--colour-grey-95: oklch(0.95 0.01 84);
--colour-error-10: oklch(0.1 0.2 26);
--colour-error-20: oklch(0.2 0.2 26);
--colour-error-30: oklch(0.3 0.2 26);
--colour-error-40: oklch(0.4 0.2 26);
--colour-error-50: oklch(0.5 0.2 26);
--colour-error-60: oklch(0.6 0.2 26);
--colour-error-70: oklch(0.7 0.2 26);
--colour-error-80: oklch(0.8 0.2 26);
--colour-error-90: oklch(0.9 0.2 26);
--colour-error-95: oklch(0.95 0.2 26);
--colour-warn-10: oklch(0.1 0.2 46);
--colour-warn-20: oklch(0.2 0.2 46);
--colour-warn-30: oklch(0.3 0.2 46);
--colour-warn-40: oklch(0.4 0.2 46);
--colour-warn-50: oklch(0.5 0.2 46);
--colour-warn-60: oklch(0.6 0.2 46);
--colour-warn-70: oklch(0.7 0.2 46);
--colour-warn-80: oklch(0.8 0.2 46);
--colour-warn-90: oklch(0.9 0.2 46);
--colour-warn-95: oklch(0.95 0.2 46);
--colour-primary-fg: var(--colour-primary-90);
--colour-primary-fg-accent: var(--colour-primary-80);
--colour-primary-bg: var(--colour-primary-10);
--colour-primary-bg-accent: var(--colour-primary-20);
--colour-code-fg: var(--colour-primary-90);
--colour-code-bg: var(--colour-primary-15);
--colour-hyperlink: var(--colour-hyperlink-80);
--colour-grey-fg: var(--colour-grey-70);
--colour-grey-bg: var(--colour-grey-30);
--colour-error-fg: var(--colour-error-90);
--colour-error-bg: var(--colour-error-40);
--colour-warn-fg: var(--colour-warn-20);
--colour-warn-bg: var(--colour-warn-80);
--font-size-sm: 1rem;
--font-size-base: 1.125rem;
--font-size-md: 1.5rem;
--font-size-lg: 2rem;
--font-size-xl: 3rem;
--spacing-block-xs: 0.5rem;
--spacing-block-sm: 1.75rem;
--spacing-block-md: 2.5rem;
--spacing-block-lg: 3.5rem;
--spacing-block-xl: 5rem;
--spacing-inline-xs: 0.25rem;
--spacing-inline-sm: 0.5rem;
--spacing-inline-md: 1.5rem;
--spacing-inline-lg: 3rem;
--spacing-inline-xl: 6rem;
}
/** Light theme */
@media (prefers-color-scheme: light) {
:root {
--colour-primary-fg: var(--colour-primary-20);
--colour-primary-fg-accent: var(--colour-primary-40);
--colour-primary-bg: var(--colour-primary-95);
--colour-hyperlink: var(--colour-hyperlink-40);
--colour-grey-fg: var(--colour-grey-40);
--colour-grey-bg: var(--colour-grey-80);
--colour-error-fg: var(--colour-error-20);
--colour-error-bg: var(--colour-error-80);
}
}
/** Base typography */
body {
font-size: var(--font-size-base);
color: var(--colour-primary-fg);
font-weight: light;
background-color: var(--colour-primary-bg);
line-height: 1.5;
/* Geometric Humanist stack from https://modernfontstacks.com */
font-family:
Avenir, Montserrat, Corbel, "URW Gothic", source-sans-pro, sans-serif;
}
small {
font-size: var(--font-size-sm);
}
:is(p, h1, h2, h3, h4, h5, h6, hr, img, figure, ul, ol, blockquote) {
margin-block-start: var(--spacing-block-sm);
}
hr {
height: 2px;
}
/** Base layout */
body {
--body-margin-inline-start: var(--spacing-inline-lg);
--body-margin-inline-end: var(--body-margin-inline-start);
--body-margin-block-end: var(--spacing-block-xl);
margin-inline: var(--body-margin-inline-start) var(--body-margin-inline-end);
margin-block-end: var(--body-margin-block-end);
}
:is(h1, h2, h3, h4, h5, h6) {
text-align: center;
}
img {
margin-inline: auto;
height: auto;
}
@media (min-width: 60rem) {
body {
display: grid;
grid-template-columns:
[grid-start media-start]
var(--grid-margin-inline)
[media-end content-start]
minmax(var(--grid-max-content-width), auto)
[content-end grid-end];
grid-auto-rows: max-content;
column-gap: var(--spacing-block-sm);
max-width: var(--grid-total-width);
--body-margin-inline-end: 6rem;
--grid-margin-inline: 6rem;
--grid-total-width: 48rem;
--grid-max-content-width: calc(
var(--grid-total-width) - var(--body-margin-inline-start) -
var(--grid-margin-inline) - var(--spacing-block-sm) -
var(--grid-margin-inline)
);
}
:is(main, article, nav) {
display: grid;
grid-column: grid;
grid-template-columns: subgrid;
> :is(section, header, aside, form) {
display: grid;
grid-template-columns: subgrid;
grid-column: grid;
> :not(.not-grid-content) {
grid-column: content;
}
}
}
:is(h1, h2, h3, h4, h5, h6) {
text-align: start;
}
}
@media (min-width: 80rem) {
body {
grid-template-columns:
[grid-start media-start]
var(--grid-margin-inline)
[media-end content-start]
minmax(auto, var(--grid-max-content-width))
[content-end margin-start]
auto
[margin-end grid-end];
--grid-total-width: 80rem;
--grid-max-content-width: 40rem;
}
}
/** Headings */
h1 {
font-size: var(--font-size-xl);
font-weight: 900;
margin-block-start: 0;
}
h2 {
font-size: var(--font-size-lg);
font-weight: 900;
margin-block-start: var(--spacing-block-xl);
}
h3,
h4,
h5,
h6 {
font-size: var(--font-size-md);
font-weight: 600;
margin-block-start: var(--spacing-block-lg);
}
/** Hyperlinks */
a:is(:link, :visited) {
color: var(--colour-hyperlink);
text-decoration: underline;
display: inline;
}
a:hover {
text-decoration: wavy;
}
/** Definition lists */
dl {
margin-block-start: var(--spacing-block-md);
}
dl dt {
font-weight: bold;
}
dl dd + dt {
margin-block-start: var(--spacing-block-md);
}
/** figcaptions */
figcaption {
font-size: var(--font-size-sm);
font-style: italic;
text-align: center;
}
/** Lists */
:is(ol, ul) {
margin-inline-start: var(--spacing-inline-lg);
}
/** Navigation bar */
nav {
margin-block: var(--spacing-block-sm);
}
nav ul {
grid-column: media-start / content-end;
display: flex;
justify-content: center;
gap: var(--spacing-inline-md);
list-style: none;
margin: 0;
}
@media (min-width: 36rem) {
nav {
display: grid;
grid-template-columns: subgrid;
}
nav ul {
justify-content: end;
}
}
/** Emphasis */
strong {
font-weight: bold;
font-style: italic;
}
/** Blog feed */
.h-feed ul {
list-style: none;
margin-inline: 0;
}
/** Block quotes */
blockquote {
padding-inline-start: var(--spacing-inline-lg);
border-inline-start: 2px solid var(--colour-primary-fg);
font-style: italic;
}
blockquote footer {
font-style: initial;
}
blockquote :is(b, strong) {
font-style: italic;
font-weight: bold;
}
blockquote :is(i, em) {
font-style: normal;
}
/** Small caps */
.small-caps {
font-variant: small-caps;
}
/** Pre-formatted blocks */
pre {
border: 2px solid var(--colour-primary-fg);
border-radius: 2px;
background-color: var(--colour-code-bg) !important;
margin-block-start: var(--spacing-block-sm);
padding-inline: var(--spacing-inline-sm);
padding-block: var(--spacing-block-xs);
}
/** Code blocks */
code {
border: 2px solid var(--colour-primary-fg);
border-radius: 2px;
padding-inline: var(--spacing-inline-xs);
color: var(--colour-code-fg);
background-color: var(--colour-code-bg);
font-size: var(--font-size-sm);
}
pre code {
border: none;
border-radius: none;
padding: none;
}
/* verse */
.verse {
span {
display: block;
}
}
.verse--hanging-indents {
span + span:not(.not-hanging) {
margin-inline-start: var(--spacing-inline-md);
}
}
/* block-comment */
block-comment {
display: grid;
grid-template-columns: subgrid;
grid-column: grid;
> * {
grid-column: content;
}
> blockquote:last-of-type {
padding-block: var(--spacing-block-sm);
padding-inline: var(--spacing-inline-md);
overflow-y: scroll;
border: 2px solid var(--colour-primary-fg);
--colour-scroll-shadow: color-mix(
in srgb,
var(--colour-primary-fg),
transparent 20%
);
background:
linear-gradient(var(--colour-primary-bg) 30%, transparent) center top,
linear-gradient(transparent, var(--colour-primary-bg) 70%) center bottom,
radial-gradient(
farthest-side at 50% 0,
var(--colour-scroll-shadow),
transparent
)
center top,
radial-gradient(
farthest-side at 50% 100%,
var(--colour-scroll-shadow),
transparent
)
center bottom;
background-repeat: no-repeat;
background-size:
100% 2rem,
100% 2rem,
100% 1rem,
100% 1rem;
background-attachment: local, local, scroll, scroll;
> :first-child {
margin-block-start: 0;
}
}
}
@media (min-width: 80rem) {
block-comment > blockquote:last-of-type {
grid-column: margin;
max-height: 67vh;
}
}
/* forms */
form {
margin-inline: auto;
max-width: max-content;
:is(button, fieldset, input, label, object, output, select, textarea, img) {
display: block;
}
label {
margin-block-end: var(--spacing-block-xs);
}
* + :is(label, input[type="submit"], button) {
margin-block-start: var(--spacing-block-md);
}
:is(input[type="submit"], input[type="button"], button) {
padding-inline: var(--spacing-inline-sm);
max-width: max-content;
&:not(dialog *) {
margin-inline-start: auto;
}
}
:is(input[type="email"], input[type="text"], select) {
max-width: 100%;
width: 16rem;
}
textarea {
max-width: 100%;
width: 40rem;
}
}
/* dialogs */
dialog {
background: var(--colour-primary-bg-accent);
border: 2px solid var(--colour-primary-fg-accent);
bottom: auto;
color: var(--colour-primary-fg);
left: calc(0.5 * (100vw - min(90vw, 36rem)));
margin: 0;
padding-block: var(--spacing-block-sm);
padding-inline: var(--spacing-inline-sm);
text-align: center;
top: auto;
width: min(90vw, 36rem);
}
@media (min-width: 80rem) {
dialog {
left: calc(
var(--body-margin-inline-start) + var(--grid-margin-inline) + 2 *
var(--spacing-inline-md)
);
}
}
/* utilities */
:is(
.para-spacing-tight:is(p, h1, h2, h3, h4, h5, h6, hr, img, figure, ul, ol),
.para-spacing-tight :is(p, h1, h2, h3, h4, h5, h6, hr, img, figure, ul, ol)
) {
margin-block-start: var(--spacing-block-xs);
}
.error {
background: var(--colour-error-bg);
border: 2px solid var(--colour-error-fg);
color: var(--colour-error-fg);
padding-block: var(--spacing--block-sm);
padding-inline: var(--spacing-inline-sm);
}
.warn {
background: var(--colour-warn-bg);
border: 2px solid var(--colour-warn-fg);
color: var(--colour-warn-fg);
padding-block: var(--spacing--block-sm);
padding-inline: var(--spacing-inline-sm);
--colour-hyperlink: var(--colour-hyperlink-40);
}
+36
View File
@@ -0,0 +1,36 @@
/* Assumes there is at most one level of subheading for sub-dividing entries */
.h-feed :is(h2, h3, h4, h5, h6) {
margin-block-start: var(--spacing-block-md);
}
.h-feed .h-entry {
display: flex;
flex-direction: column;
}
.h-feed .h-entry + .h-entry {
margin-block-start: var(--spacing-block-md);
}
.h-feed .h-entry > * {
order: 1;
margin: 0;
}
.h-feed .h-entry .dt-published {
order: 0;
font-size: var(--font-size-sm);
}
.h-feed .h-entry .p-name {
font-size: var(--font-size-md);
font-weight: 400;
}
.h-feed .full-feed-link {
text-align: end;
}
.h-feed :is(a.full-feed-link, .full-feed-link a)::after {
content: " >";
}
+57
View File
@@ -0,0 +1,57 @@
.h-card div:has(img) {
width: 6rem;
height: 6rem;
margin-inline: auto;
}
.h-card img {
width: 6rem;
height: 6rem;
border-radius: 1rem;
filter: contrast(1.25);
}
.h-card div:has(img)::after {
/* Colour overlay */
background-color: var(--colour-primary-80);
opacity: 0.3;
/* Same size and shape as the img */
border-radius: 1rem;
width: 6rem;
height: 6rem;
/* Positioned on top of the img */
display: block;
position: relative;
top: -6rem;
/* A content value is needed to get the ::after to render */
content: "";
}
@media (min-width: 36rem) {
.h-card {
grid-column: media-start / content-end;
display: grid;
grid-template-columns: subgrid; /** Subgrid of main column layout */
grid-template-rows: min-content 1fr;
grid-template-areas:
"empty heading"
"photo text";
column-gap: var(--spacing-block-sm);
}
.h-card div:has(img) {
grid-area: photo;
margin-block-start: var(--spacing-block-sm);
}
.h-card header {
grid-area: heading;
}
.h-card__text {
grid-area: text;
}
}
+29
View File
@@ -0,0 +1,29 @@
.otp-inputs {
display: flex;
flex-direction: row;
gap: var(--spacing-inline-sm);
justify-content: center;
input {
border: none;
border-block-end: 2px solid var(--colour-grey-fg);
font-size: var(--font-size-lg);
text-align: center;
width: var(--font-size-lg);
}
}
.otp-form {
align-items: center;
display: flex;
flex-direction: column;
gap: var(--spacing-block-sm);
> * {
margin: 0;
}
input[type="submit"] {
max-width: max-content;
}
}
+8
View File
@@ -0,0 +1,8 @@
.p-summary {
font-style: italic;
font-size: var(--font-size-sm);
}
h1 {
margin-block-start: var(--spacing-block-md);
}
+93
View File
@@ -0,0 +1,93 @@
/* Based on Andy Bells More Modern CSS Reset: https://piccalil.li/blog/a-more-modern-css-reset/ */
/* Box sizing rules */
*,
*::before,
*::after {
box-sizing: border-box;
}
/* Prevent font size inflation */
html {
-moz-text-size-adjust: none;
-webkit-text-size-adjust: none;
text-size-adjust: none;
}
/* Remove default margin in favour of better control in authored CSS */
body,
h1,
h2,
h3,
h4,
p,
figure,
blockquote,
dl,
dd {
margin-block: 0;
}
/* Remove list styles on ul, ol elements with a list role, which suggests default styling will be removed */
ul[role="list"],
ol[role="list"] {
list-style: none;
}
/* Set core body defaults */
body {
min-height: 100vh;
}
/* Set shorter line heights on headings and interactive elements */
h1,
h2,
h3,
h4,
button,
input,
label {
line-height: 1.1;
}
/* Balance text wrapping on headings */
h1,
h2,
h3,
h4 {
text-wrap: balance;
}
/* Make images easier to work with */
img,
picture {
max-width: 100%;
display: block;
}
/* Inherit fonts for inputs and buttons */
input,
button,
textarea,
select {
font: inherit;
}
/* Make sure textareas without a rows attribute are not tiny */
textarea:not([rows]) {
min-height: 10em;
}
/* Anything that has been anchored to should have extra scroll margin */
:target {
scroll-margin-block: 5ex;
}
* {
margin: 0;
padding: 0;
}
[hidden] {
display: none;
}
+17
View File
@@ -0,0 +1,17 @@
<!doctype html>
<html lang="en-GB">
<head>
#!
pp "${SHARE}"/components/head.upphtml
#!
</head>
<body>
<main>
#!
pp "$1"
#!
</main>
</body>
</html>
+55
View File
@@ -0,0 +1,55 @@
.Dd Sep 4, 2020
.Dt lmt
.Os
.Sh NAME
.Nm lmt
.Nd last modification time
.Sh SYNOPSIS
.Nm lmt
.Op Fl f Ar format
.Ar file ...
.Sh DESCRIPTION
The
.Nm
utility prints the last modification time for the given
.Ar file
to standard output.
The information displayed is obtained by calling
.Xr lstat 2
with the given
argument and evaluating the returned structure.
.Pp
The options are as follows:
.Bl -tag -width Ds
.It Fl f Ar format
The format in which to display the time.
The format string may contain any of the conversion specifications
described in the
.Xr strftime 3
manual page, as well as any arbitrary text.
A newline
.Pq Ql \en
character is always output after the characters specified by
the format string.
The format string for the default display is:
.Bd -literal -offset indent
%a %b %e %H:%M:%S %Z %Y
.Ed
.El
.Sh EXIT STATUS
.Ex -std
.Sh EXAMPLES
Print last modification time for
.Pa file1
to standard output:
.Pp
.Dl $ lmt file1
.Pp
Print last modification time for
.Pa file1
to standard output in ISO 8601 format:
.Pp
.Dl $ lmt -f "%Y-%m-%dT%H:%M:%S%z" file1
.Sh SEE ALSO
.Xr stat 1
.Xr strftime 3
+91
View File
@@ -0,0 +1,91 @@
.Dd Dec 13, 2019
.Dt mkws 1
.Os
.Sh NAME
.Nm mkws
.Nd make web site
.Sh SYNOPSIS
.Nm
.Ar url
.Op path
.Sh DESCRIPTION
The
.Nm
utility uses
.Xr pp 1
and the theme files in the
.Ev MKWSTHEMEPATH
directory
to preprocess all files in
.Op path
in order to produce a static web site in the current directory. If no
.Op path
is specified, the current directory is used instead.
.Pp
The options are as follows:
.Bl -tag -width Ds
.It Ar url
The url of the web site. It is used for generating
.Pa sitemap.xml
.
.It Op path
The path where the unpreprocessed files are located. If no
.Op path
is specified, the current directory is used instead.
.El
.Sh ENVIRONMENT
.Bl -tag -width MKWSTHEMEDIR
.It Ev MKWSTHEMEDIR
The directory containing the theme files. See the
.Em FILES
section for the minimum required files for a theme. If no
.Ev MKWSTHEMEDIR
variable is set
.Pa [path]/share
wil be used.
.It Ev LANG
The language for the website. It will be parsed to fill in the
.Em lang
attribute of the
.Em html
element and then
.Em charset
.Em meta
element.
.El
.Sh FILES
.Bl -tag -width "/tmp/vi.recover
.It Pa $MKWSTHEMEDIR/l.upphtml
The layout file of the website. It contains the
.Em DOCTYPE ,
.Em html ,
.Em head ,
.Em title ,
.Em meta
,
.Em link
,
.Em body
elements.
.It Pa $MKWSTHEMEDIR/sitemap.xml
The
.Pa sitemap.txt
template.
.El
.Sh EXIT STATUS
.Ex -std
.Sh EXAMPLES
Make https://example.com web site using the unpreprocessed files in the
current directory
.Pp
.Dl $ mkws https://example.com
.Pp
Make https://example.com web site using the unpreprocessed files in the
.Ar ~/src/ws
directory
.Pp
.Dl $ mkws https://example.com ~/src/ws
.Sh SEE ALSO
.Xr sh 1
.Xr pp 1
.Xr locale 1
+46
View File
@@ -0,0 +1,46 @@
.Dd Apr 25, 2019
.Dt pp 1
.Os
.Sh NAME
.Nm pp
.Nd preprocessor
.Sh SYNOPSIS
.Nm pp
.Op Fl d
.Ar file
.Op args ...
.Sh DESCRIPTION
The
.Nm
utility processes
.Ar file
to produce output that is used as input to another
program.
.Pp
It executes code enclosed in
.Dq #!\en
delimiters using
.Xr sh 1 .
.Op args ...
are passed to the embbeded script and are available
via the usual
.Em $1, $2, ...
positional parameters.
.Pp
The options are as follows:
.Bl -tag -width Ds
.It Fl d
.Nm
will dump the generated
.Xr sh 1
code instead of executing it. This is useful for debugging.
.El
.Sh EXIT STATUS
.Ex -std
.Sh EXAMPLES
Preprocess file with arguments:
.Pp
.Dl $ pp file.upphtml a b
.Pp
.Sh SEE ALSO
.Xr sh 1
+21
View File
@@ -0,0 +1,21 @@
<?xml version='1.0' encoding='UTF-8'?>
<urlset xmlns='http://www.sitemaps.org/schemas/sitemap/0.9'>
#!
for f in ${OUT}/*.html
do
#!
<url>
#!
loc=$1/$(basename "${f}")
#!
<loc>${loc}</loc>
#!
lastmod=$(lmt -f '%Y-%m-%dT%H:%M:%SZ' "${f}" | cut -d' ' -f1)
#!
<lastmod>${lastmod}</lastmod>
<priority>1.0</priority>
</url>
#!
done
#!
</urlset>
+1
View File
@@ -0,0 +1 @@
hello, world