saait

Simple static page generator
git clone https://git.sinitax.com/codemadness/saait
Log | Files | Refs | README | LICENSE | Upstream | sfeed.txt

commit 8c39944139de302656b2e5c075dbd934a1a51a1f
Author: Hiltjo Posthuma <hiltjo@codemadness.org>
Date:   Fri, 25 Nov 2016 16:06:15 +0100

initial insertion

Diffstat:
A.gitignore | 7+++++++
ALICENSE | 15+++++++++++++++
AMakefile | 9+++++++++
AREADME | 64++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
ATODO | 54++++++++++++++++++++++++++++++++++++++++++++++++++++++
Aarg.h | 65+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Aconfig | 25+++++++++++++++++++++++++
Apages/001-gothic-1-guide.cfg | 8++++++++
Apages/001-gothic-1-guide.html | 117+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Apages/002-getting-the-usb-powerline-bridge-to-work-on-linux.cfg | 8++++++++
Apages/002-getting-the-usb-powerline-bridge-to-work-on-linux.html | 97+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Apages/003-driconf-enabling-s3-texture-compression-on-linux.cfg | 7+++++++
Apages/003-driconf-enabling-s3-texture-compression-on-linux.html | 41+++++++++++++++++++++++++++++++++++++++++
Apages/004-query-unused-css-rules-on-current-document-state.cfg | 9+++++++++
Apages/004-query-unused-css-rules-on-current-document-state.html | 28++++++++++++++++++++++++++++
Apages/005-dwm-hiltjo-my-windowmanager-configuration.cfg | 8++++++++
Apages/005-dwm-hiltjo-my-windowmanager-configuration.html | 50++++++++++++++++++++++++++++++++++++++++++++++++++
Apages/006-gtk2-theme-gtk-murrine-rape.cfg | 9+++++++++
Apages/006-gtk2-theme-gtk-murrine-rape.html | 17+++++++++++++++++
Apages/007-seturgent-set-urgency-hints-for-x-applications.cfg | 9+++++++++
Apages/007-seturgent-set-urgency-hints-for-x-applications.html | 11+++++++++++
Apages/009-vim-theme-relaxed.cfg | 8++++++++
Apages/009-vim-theme-relaxed.html | 13+++++++++++++
Apages/010-sfeed-simple-feed-parser.cfg | 8++++++++
Apages/010-sfeed-simple-feed-parser.html | 19+++++++++++++++++++
Apages/011-userscript-block-stupid-fonts.cfg | 8++++++++
Apages/011-userscript-block-stupid-fonts.html | 10++++++++++
Apages/012-userscript-youtube-circumvent-age-verification.cfg | 8++++++++
Apages/012-userscript-youtube-circumvent-age-verification.html | 6++++++
Apages/013-userscript-focus-input-field.cfg | 8++++++++
Apages/013-userscript-focus-input-field.html | 7+++++++
Apages/015-mg-openbsd-port.cfg | 7+++++++
Apages/015-mg-openbsd-port.html | 32++++++++++++++++++++++++++++++++
Apages/016-twitch-interface.cfg | 7+++++++
Apages/016-twitch-interface.html | 18++++++++++++++++++
Apages/017-openbsd-httpd-and-cgit.cfg | 7+++++++
Apages/017-openbsd-httpd-and-cgit.html | 58++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asaait.1 | 17+++++++++++++++++
Asaait.c | 748+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Atemplates/atom.xml/footer.xml | 1+
Atemplates/atom.xml/header.xml | 8++++++++
Atemplates/atom.xml/item.xml | 12++++++++++++
Atemplates/index.html/footer.html | 5+++++
Atemplates/index.html/header.html | 37+++++++++++++++++++++++++++++++++++++
Atemplates/index.html/item.html | 4++++
Atemplates/page/footer.html | 4++++
Atemplates/page/header.html | 35+++++++++++++++++++++++++++++++++++
Atemplates/page/item.html | 3+++
Atemplates/rss.xml/footer.xml | 2++
Atemplates/rss.xml/header.xml | 7+++++++
Atemplates/rss.xml/item.xml | 8++++++++
Atemplates/sitemap.xml/footer.xml | 1+
Atemplates/sitemap.xml/header.xml | 2++
Atemplates/sitemap.xml/item.xml | 1+
Atemplates/urllist.txt/footer.txt | 0
Atemplates/urllist.txt/header.txt | 0
Atemplates/urllist.txt/item.txt | 1+
57 files changed, 1778 insertions(+), 0 deletions(-)

diff --git a/.gitignore b/.gitignore @@ -0,0 +1,7 @@ +bin +output/*.css +output/*.xml +output/*.html +output/urllist.txt +saait +*.core diff --git a/LICENSE b/LICENSE @@ -0,0 +1,15 @@ +ISC License + +Copyright (c) 2016 Hiltjo Posthuma <hiltjo@codemadness.org> + +Permission to use, copy, modify, and/or distribute this software for any +purpose with or without fee is hereby granted, provided that the above +copyright notice and this permission notice appear in all copies. + +THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES +WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR +ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF +OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. diff --git a/Makefile b/Makefile @@ -0,0 +1,9 @@ +test: build + mkdir -p output + ./saait `ls -1r pages/*.cfg` + +build: clean + cc -ggdb saait.c -o saait -Wall -std=c99 -Wextra -pedantic + +clean: + rm -f saait diff --git a/README b/README @@ -0,0 +1,64 @@ +saait - the most boring static page generator + +Meaning of saai (dutch): boring +Pronounciation: site + +Some parts are intentionally hardcoded, C knowledge is required. + + +Dependencies: + requires non-standard field tm_gmtoff in struct tm. + + +Directory structure for pages: + +- pages/file1.html +- pages/file1.cfg + + +Directory structure for templates: + +- templates/<templatename>_item.ext +- templates/<templatename>_header.ext +- templates/<templatename>_footer.ext + +The files are saved as output/<templatename>.ext + +the templatename "page" is special and will be used per page. + + +Cfg file: + +variables set by program: + + +TODO: +basefile +url +urlfull / urlabsolute / absoluteurl +datecreated +dateupdated +RFC* formatted dates (for RSS, Atom, etc). + + + +mandatory variables: + +title +description +id + +variables: +created: format: YYYY-mm-dd HH:MM, when not set will use minimum creation date of file .cfg or .html +updated: format: YYYY-mm-dd HH:MM, when not set will use maximum modified date of file .cfg or .html + +These will be set when 'created' and 'updated' is parsed: + +created_date and updated_date YYYY-mm-dd +created_atom and updated_atom RFC822 date +created_rss and updated_rss RSS date + + +Variables: +${} escaped HTML/XML string. +#{} literal string. diff --git a/TODO b/TODO @@ -0,0 +1,54 @@ +x RSS time is in GMT... + + +parse time (localtime vs gmtime?). + TODO: read times specified without timezone as localtime (not GMT). + TODO: when timezone is specified use it. + TODO: use strftime to show using timezone. + TODO: change siteupdated atom/rss format (don't hardcode GMT). + + +write alot better documentation. + +make time variable special? something like (syntax should change): + + +? read file and output data. +%{"filename"} + +? execute and insert output:: +!{"command"} + +x interpret a few formats: +%Y-%m-%d %H:%M:%S +%Y-%m-%d %H:%M +%Y-%m-%dT%H:%M:%SZ +%Y-%m-%dT%H:%M:%S + + +make template structure: + + templates/name/header.ext + templates/name/item.ext + templates/name/footer.ext + + +add able to check unused variables (-w option?). + add used counter to variable struct. + + +must work on: Linux, OpenBSD, MingW (Windows). + glibc + musl + OpenBSD libc + + +cfg: date timezone: created, updated + + +set builddate as variable to use. +set RFC and standard date as variable to use. + +show line number in error +max 500 LOC +pledge diff --git a/arg.h b/arg.h @@ -0,0 +1,65 @@ +/* + * Copy me if you can. + * by 20h + */ + +#ifndef ARG_H__ +#define ARG_H__ + +extern char *argv0; + +/* use main(int argc, char *argv[]) */ +#define ARGBEGIN for (argv0 = *argv, argv++, argc--;\ + argv[0] && argv[0][0] == '-'\ + && argv[0][1];\ + argc--, argv++) {\ + char argc_;\ + char **argv_;\ + int brk_;\ + if (argv[0][1] == '-' && argv[0][2] == '\0') {\ + argv++;\ + argc--;\ + break;\ + }\ + for (brk_ = 0, argv[0]++, argv_ = argv;\ + argv[0][0] && !brk_;\ + argv[0]++) {\ + if (argv_ != argv)\ + break;\ + argc_ = argv[0][0];\ + switch (argc_) + +/* Handles obsolete -NUM syntax */ +#define ARGNUM case '0':\ + case '1':\ + case '2':\ + case '3':\ + case '4':\ + case '5':\ + case '6':\ + case '7':\ + case '8':\ + case '9' + +#define ARGEND }\ + } + +#define ARGC() argc_ + +#define ARGNUMF() (brk_ = 1, estrtonum(argv[0], 0, INT_MAX)) + +#define EARGF(x) ((argv[0][1] == '\0' && argv[1] == NULL)?\ + ((x), abort(), (char *)0) :\ + (brk_ = 1, (argv[0][1] != '\0')?\ + (&argv[0][1]) :\ + (argc--, argv++, argv[0]))) + +#define ARGF() ((argv[0][1] == '\0' && argv[1] == NULL)?\ + (char *)0 :\ + (brk_ = 1, (argv[0][1] != '\0')?\ + (&argv[0][1]) :\ + (argc--, argv++, argv[0]))) + +#define LNGARG() &argv[0][0] + +#endif diff --git a/config b/config @@ -0,0 +1,25 @@ +# defaults: but can be overwritten by any page. + +# last updated the site. +siteupdated=2016-11-18 15:48:12 + +# site title (part of ${pagetitle} probably). +sitetitle=Codemadness +# prefix site url. +siteurl=http://www.codemadness.nl +# site mail used for contact "mail link". +sitemail=hiltjo@AT@codemadness.DOT.org + +# page + +# page language. +lang=en +# site author (global). +# site keywords (global default), don't use too many. +keywords=blog, suckless, dwm-hiltjo +# site description (global default). +description=blog with various projects and articles about computer-related things +author=hiltjo + +# default title +title=Posts diff --git a/pages/001-gothic-1-guide.cfg b/pages/001-gothic-1-guide.cfg @@ -0,0 +1,8 @@ +title=Gothic 1 game guide +description=Gothic 1 game guide with some useful tips +id=gothic-1-guide +tags=game, gothic, guide +keywords=gothic, game, guide, tips +categories=Articles +created=2009-04-12 00:00 +updated=2009-04-12 00:00 diff --git a/pages/001-gothic-1-guide.html b/pages/001-gothic-1-guide.html @@ -0,0 +1,117 @@ +<p>Disclaimer: <em>Some (including myself) may find some of these hints/exploits cheating. This guide is just for educational and fun purposes. +Some of these hints/tips apply to Gothic 2 as well. I got the meat exploit from a guide somewhere on the internet I can't recall where, anyway kudos to that person. +The other exploits I discovered myself. If you use some hints or exploits from these guide, please add me to your "credits", I will appreciate it :)</em></p> + +<h2 id="config"><a href="#config">Configuration</a></h2> + +<h3>Widescreen resolution</h3> +<p> +Gothic supports widescreen resolutions with a small tweak, add the following text string as a command-line argument: +<code>-zRes:1920,1200,32</code> +Ofcourse 1920 is the width, 1200 the height and 32 the bits, change this to your resolution. +</p> + +<h3>Fix crash with Steam version</h3> +<p> +Disable steam overlay. If that doesn't work rename GameOverlayRenderer.dll in your steam folder to _GameOverlayRenderer.dll. +</p> + + +<h2 id="easy"><a href="#easy">Easy money/weapons/armor/other items</a></h2> + +<h3>Steal from Huno</h3> +<p>At night attack Huno the smith in the Old Camp and steal all his steel. Then make some weapons and sell them with a merchant. +When you ask Huno about blacksmith equipment it will respawn with 5 of each kind of steel. This is also a fairly good starting weapon (requires 20 strength). +Also his chest located near the sharpening stone and fire contains some steel as well, lock-pick it. The combination is: RRLRLL. The chest contains _at least_ 20 raw steel, forge it +to get 20 crude swords which you can sell for 50 ore each to a merchant. This will generate some nice starting money (1000+ ore) :)</p> + +<h3>Steal weapons from the castle in the Old camp</h3> +<p>This tip is useful for getting pretty good starting weapons.</p> + +<p>Before entering the castle itself drop your ore (Left control + down for me) in front of it. This will ensure when you get caught (and you probably will ;)) no ore will get stolen by the guards. Now use the "slip past guard" technique +explained below and you should be able to get into Gomez his castle. Run to the left where some weapons are stored. Now make sure you at least steal the best weapon (battle sword) and steal as much as you can until you get whacked. +I usually stand in the corner since that's where the best weapons are (battle sword, judgment sword, etc). You'll now have some nice starting weapon(s) and the good thing is they require very little attributes (about 13 strength).</p> + +<h3>Free scraper armour new camp</h3> +<p>In the new camp go to the mine and talk to Swiney at the bottom of "The Hollow". Ask who he is and then ask to join the scrapers. +He will give you a "Diggers dress" worth 250 ore. It has the following stats: + 10 against weapons. + 5 against fire. This will also give you free entrance to the bar in the new camp.</p> + +<h3>Armor amulet and increase HP potion</h3> +<p> +In the old camp in the main castle there are at least 3 chests with valuable items that don't require a key: +</p> +<ul> + <li>Left side, 1 chest: + <ul> + <li>lock combination: RLLLLLRR</li> + <li>loot: + <ul> + <li>+8 mana amulet (worth: 600 ore)</li> + <li>2 potions (+70 hp)</li> + <li>dreamcall (weed)</li> + <li>120 coins (worth: nothing)</li> + </ul> + </li> + </ul> + </li> + <li>Right side, 2 chests with: + <ul> + <li>lock combination: RLLLRLLR</li> + <li>loot: + <ul> + <li>armor amulets, +15 against weapons (worth: 600 ore)</li> + <li>maximum life potion, +10 maximum life (worth: 1000 ore)</li> + <li>speed potion (1 minute duration)</li> + <li>4 potions (+70 hp)</li> + </ul> + </li> + </ul> + </li> +</ul> + +<h3>Swamp camp harvest twice</h3> +<p>In the swamp-weed harvest quest you must get swamp-weed for a guru. After this quest you can get the harvest again, but you can keep the harvest without consequences.</p> + +<h2 id="exploits"><a href="#exploits">Exploits</a></h2> +<h3>Slip past guards</h3> +<p>This exploit is really simple, just draw your weapon before you're "targeted" by the guard and run past them this bypasses the dialog sequence. When you're just out of their range holster your weapon again, so the people around won't get pissed off.</p> + +<p>Works really well on the guards in front of the Old camp's castle, Y'Berrion templars and New camp mercenaries near the Water magicians, just to name a few.</p> + +<h3>Meat duplication</h3> +<p>Go to a pan and focus / target it so it says "frying pan" or similar. Now open your inventory and select the meat. Now cook the meat (for me Left Control + Arrow up). You'll now have twice as much meat as you had before. Do this a few times and +you'll have alot of meat, easy for trading with ore/other items as well.</p> + +<h3>Fall from great heights</h3> +<p>Draw your weapon and jump from something high. Now while in the air attack with your weapon. Make sure you attack either continuously or right above the ground before you land. This works because it resets the falling animation.</p> + +<h2 id="exp"><a href="#exp">Experience / level up tips</a></h2> + +<h3>Test of faith (extra exp)</h3> +<p>You get an additional 500 exp (from Lares) when you forge the letter in the new camp and then give it to Diego. You can still join both camps after this.</p> + +<h3>Fighting skeleton mages and their skeletons</h3> +<p>An easy way to get more experience is to let the skeleton mages summon as much skeletons as they can. After you have defeated all of them: kill the skeleton mage.</p> + +<h3>Permanent str/dex/mana/hp potions/items and teachers</h3> +<p>When you want to get the maximum power at the end of the game you should save up the items that give you a permanent boost. Teachers of strength, dexterity and mana won't train over 100 of each skill. +However using potions and quest rewards you can increase this over 100.</p> + +<p>You should also look out for the following:</p> +<ul> + <li>Learn to get extra force into your punch from Horatio (strength +5, this can't be done after level 100 strength).</li> + <li>Smoke the strongest non-quest joint (+2 mana).</li> +</ul> +<p>Again, do this after you are 100 of each skill.</p> + +<h3>Permanent potions in Sleeper temple</h3> +<p>This one is really obvious, but I would like to point out the mummy's on each side where Xardas is located have lots and I mean lots of permanent potions. This will give you a nice boost before the end battle.</p> + +<h3>Permanent potions as reward in quests</h3> +<p>Always pick the permanent potion as a reward for quests when you can, for example the quest for delivering the message to the High Fire magicians (mana potion) or the one for fetching the almanac for the sect camp. +Don't forget to pick up the potions from Riordian the water magician when you're doing the focus stones quest, it contains a strength and dexterity potion (+3).</p> + +<h3>Word after</h3> +<p>When you use the tips described above Gothic should be a really easy game and you should be able to get at a high(er) level with lots of mana/strength/hp.</p> + +<p>Have fun!</p> diff --git a/pages/002-getting-the-usb-powerline-bridge-to-work-on-linux.cfg b/pages/002-getting-the-usb-powerline-bridge-to-work-on-linux.cfg @@ -0,0 +1,8 @@ +title=Getting the USB-powerline bridge to work on Linux +description=A guide to get a USB-powerline bridge with the Intellon 51x1 chipset working on Linux +id=getting-the-usb-powerline-bridge-to-work-on-linux +tags=drivers, hardware, linux +keywords=int51x1, driver, linux +categories=Articles +created=2009-04-13 00:00 +updated=2009-04-13 00:00 diff --git a/pages/002-getting-the-usb-powerline-bridge-to-work-on-linux.html b/pages/002-getting-the-usb-powerline-bridge-to-work-on-linux.html @@ -0,0 +1,97 @@ +<p><strong>NOTE: this guide is obsolete, a working driver is now included in the Linux kernel tree (<a href="http://lkml.org/lkml/2009/4/18/121">since Linux 2.6.31</a>)</strong> + +<h2 id="introduction"><a href="#introduction">1. Introduction</a></h2> +<p> +A USB to powerline bridge is a network device that instead of using an ordinary ethernet cable (CAT5 for example) or wireless LAN it uses the powerlines as a network to communicate with similar devices. +A more comprehensive explanation of what it is and how it works you can find <a href="http://en.wikipedia.org/wiki/IEEE_1901">here</a>. +</p> + +<p>Known products that use the Intellon 51x1 chipset:</p> +<ul> + <li>MicroLink dLAN USB</li> + <li>"Digitus network"</li> + <li>Intellon USB Ethernet powerline adapter</li> + <li>Lots of other USB-powerline adapters...</li> +</ul> + +<p>To check if your device is supported:</p> +<pre><code>$ lsusb | grep -i 09e1 +Bus 001 Device 003: ID 09e1:5121 Intellon Corp.</code></pre> +<p>If the vendor (09e1) and product (5121) ID match then it's probably supported.</p> + +<h2 id="installation"><a href="#installation">2. Installation</a></h2> +<p>Get drivers from the official site<sup><a href="http://www.devolo.co.uk/consumer/downloads-44-microlink-dlan-usb.html?l=en">1</a></sup> or here<sup><a href="/downloads/int51x1/dLAN-linux-package-v4.tar.gz">2</a></sup>. The drivers from the official site are more up-to-date.</p> + +<p>Extract them:</p> +<pre><code>$ tar -xzvf dLAN-linux-package-v4.tar.gz</code></pre> + +<p>Go to the extracted directory and compile them:</p> +<pre><code>$ ./configure +$ make</code></pre> + +<p>Depending on the errors you got you might need to download<sup><a href="/downloads/int51x1/int51x1.patch">3</a></sup> and apply my patch:</p> +<pre><code>$ cd dLAN-linux-package-v4/ (or other path to the source code) +$ patch &lt; int51x1.patch</code></pre> + +<p>Try again:</p> +<pre><code>$ ./configure +$ make</code></pre> + +<p>If that failed try:</p> +<pre><code>$ ./configure +$ KBUILD_NOPEDANTIC=1 make</code></pre> + +<p>If that went ok install the drivers (as root):</p> +<pre><code># make install</code></pre> + +<p>Check if the "devolo_usb" module is loaded:</p> +<pre><code>$ lsmod | grep -i devolo_usb</code></pre> +<p>If it shows up then it's loaded. Now check if the interface is added:</p> +<pre><code>$ ifconfig -a | grep -i dlanusb +dlanusb0 Link encap:Ethernet HWaddr 00:12:34:56:78:9A</code></pre> + +<h2 id="configuration"><a href="#configuration">3. Configuration</a></h2> +<p>It is assumed you use a static ip, otherwise you can just use your DHCP client to get an unused IP address from your DHCP server. Setting up the interface is done like this (change the IP address and netmask accordingly if it's different):</p> +<pre><code># ifconfig dlanusb0 192.168.2.12 netmask 255.255.255.0</code></pre> + +<h2 id="check-network"><a href="#check-network">4. Checking if the network works</a></h2> +<p>Try to ping an IP address on your network to test for a working connection:</p> +<pre><code>$ ping 192.168.2.1 +PING 192.168.2.1 (192.168.2.1) 56(84) bytes of data. +64 bytes from 192.168.2.1: icmp_seq=1 ttl=30 time=2.49 ms +64 bytes from 192.168.2.1: icmp_seq=2 ttl=30 time=3.37 ms +64 bytes from 192.168.2.1: icmp_seq=3 ttl=30 time=2.80 ms +--- 192.168.2.1 ping statistics --- +3 packets transmitted, 3 received, 0% packet loss, time 2005ms +rtt min/avg/max/mdev = 2.497/2.891/3.374/0.368 ms</code></pre> + +<p> +You can now set up a network connection like you normally do with any ethernet device. +The route can be added like this for example:</p> +<pre><code># route add -net 0.0.0.0 netmask 0.0.0.0 gw 192.168.2.1 dlanusb0</code></pre> + +<p>Change the IP address of your local gateway accordingly. Also make sure your nameserver is set in /etc/resolv.conf, something like:</p> +<pre><code>nameserver 192.168.2.1</code></pre> + +<p>Test your internet connection by doing for example:</p> +<pre><code>$ ping codemadness.org +PING codemadness.org (64.13.232.151) 56(84) bytes of data. +64 bytes from acmkoieeei.gs02.gridserver.com (64.13.232.151): icmp_seq=1 ttl=52 time=156 ms +64 bytes from acmkoieeei.gs02.gridserver.com (64.13.232.151): icmp_seq=2 ttl=52 time=156 ms +64 bytes from acmkoieeei.gs02.gridserver.com (64.13.232.151): icmp_seq=3 ttl=52 time=155 ms +--- codemadness.org ping statistics --- +3 packets transmitted, 3 received, 0% packet loss, time 1999ms +rtt min/avg/max/mdev = 155.986/156.312/156.731/0.552 ms</code></pre> + +<p> +If this command failed you probably have not setup your DNS/gateway properly. +If it worked then good for you :) +</p> + +<h2 id="references"><a href="#references">5. References</a></h2> +<ol> + <li><a href="http://www.devolo.co.uk/consumer/downloads-44-microlink-dlan-usb.html?l=en">Download page of drivers (USB version): http://www.devolo.co.uk/consumer/downloads-44-microlink-dlan-usb.html?l=en</a></li> + <li><a href="/downloads/int51x1/dLAN-linux-package-v4.tar.gz">dLAN-linux-package-v4.tar.gz</a></li> + <li><a href="/downloads/int51x1/int51x1.patch">Patch for recent 2.6.x kernels</a></li> + <li><a href="/downloads/int51x1/INT51X1_datasheet.pdf">INT51X1 datasheet</a></li> +</ol> diff --git a/pages/003-driconf-enabling-s3-texture-compression-on-linux.cfg b/pages/003-driconf-enabling-s3-texture-compression-on-linux.cfg @@ -0,0 +1,7 @@ +title=Driconf: enabling S3 texture compression on Linux +description=driconf: enabling S3 texture compression +tags=HOWTO, linux, s3tc, driconf +keywords=driconf, s3tc +categories=Articles +created=2009-07-05 00:00 +updated=2013-12-30 00:00 diff --git a/pages/003-driconf-enabling-s3-texture-compression-on-linux.html b/pages/003-driconf-enabling-s3-texture-compression-on-linux.html @@ -0,0 +1,41 @@ +<p> + S3TC (also known as DXTn or DXTC) is a patented lossy texture compression algorithm. See: <a href="http://en.wikipedia.org/wiki/S3TC">http://en.wikipedia.org/wiki/S3TC</a> for more detailed information. + Many games use S3TC and if you use Wine to play games you definitely want to enable it if your graphics card supports it. + + Because this algorithm is <a href="http://dri.freedesktop.org/wiki/S3TC/">patented it is disabled by default on many Linux distributions</a>. +</p> + +<p> + To enable it you can install the library "libtxc" if your favorite OS has not installed it already. + + For easy configuration you can install the optional utility DRIconf, which you can find at: <a href="http://dri.freedesktop.org/wiki/DriConf">http://dri.freedesktop.org/wiki/DriConf</a>. + DriConf can safely be removed after configuration. +</p> + +<h2 id="steps"><a href="#steps">Steps to enable it</a></h2> +<ol> + <li> + <p>Install libtxc_dxtn</p> + <p>ArchLinux:</p> + <pre><code> # pacman -S libtxc_dxtn</code></pre> + <p>Debian:</p> + <pre><code> # aptitude install libtxc-dxtn-s2tc0</code></pre> + </li> + <li> + <p>Install driconf (optional).</p> + <p>ArchLinux:</p> + <pre><code> # pacman -S driconf</code></pre> + <p>Debian:</p> + <pre><code> # aptitude install driconf</code></pre> + </li> + <li> + <p>Run driconf and enable S3TC</p> + <a href="/downloads/screenshots/driconf.png"><img src="/downloads/screenshots/driconf-thumb.png" title="Screenshot of DRIconf" alt="Screenshot of DRIconf" width="300" height="266" /></a> + </li> +</ol> + +<h2 id="links"><a href="#links">Additional links</a></h2> +<ol> + <li>S3TC: <a href="http://dri.freedesktop.org/wiki/S3TC/">http://dri.freedesktop.org/wiki/S3TC/</a></li> + <li>DriConf: <a href="http://dri.freedesktop.org/wiki/DriConf">http://dri.freedesktop.org/wiki/DriConf</a></li> +</ol> diff --git a/pages/004-query-unused-css-rules-on-current-document-state.cfg b/pages/004-query-unused-css-rules-on-current-document-state.cfg @@ -0,0 +1,9 @@ +title=Query unused CSS rules on current document state +description=How to see all the rules in a stylesheet (CSS) that are not used for the current document +id=query-unused-css-rules-on-current-document-state +tags=programming, web +keywords=css, programming +categories=Articles +created=2010-04-21 00:00 +updated=2010-04-21 00:00 + diff --git a/pages/004-query-unused-css-rules-on-current-document-state.html b/pages/004-query-unused-css-rules-on-current-document-state.html @@ -0,0 +1,28 @@ +<p>Today I was doing some web development and wanted to see all the rules in a stylesheet (CSS) that were not used for the current document. I wrote the following Javascript code which you can paste in the Firebug console and run:</p> + +<pre><code> 1 (function() { + 2 for (var i=0;i&lt;document.styleSheets.length;i++) { + 3 var rules = document.styleSheets[i].cssRules || []; + 4 var sheethref = document.styleSheets[i].href || 'inline'; + 5 for (var r=0;r&lt;rules.length;r++) + 6 if (!document.querySelectorAll(rules[r].selectorText).length) + 7 console.log(sheethref + ': "' + rules[r].selectorText + '" not found.'); + 8 } + 9 })(); +</code></pre> + +<p>This will output all the (currently) unused CSS rules per selector, the output can be for example:</p> + +<pre><code>http://www.codemadness.nl/blog/wp-content/themes/codemadness/style.css: "fieldset, a img" not found. +http://www.codemadness.nl/blog/wp-content/themes/codemadness/style.css: "#headerimg" not found. +http://www.codemadness.nl/blog/wp-content/themes/codemadness/style.css: "a:hover" not found. +http://www.codemadness.nl/blog/wp-content/themes/codemadness/style.css: "h2 a:hover, h3 a:hover" not found. +http://www.codemadness.nl/blog/wp-content/themes/codemadness/style.css: ".postmetadata-center" not found. +http://www.codemadness.nl/blog/wp-content/themes/codemadness/style.css: ".thread-alt" not found. +</code></pre> + +<p>Just a trick I wanted to share, I hope someone finds this useful :)</p> + +<p>For webkit-based browsers you can use "Developer Tools" and use "Audits" under "Web Page Performance" it says "Remove unused CSS rules". For Firefox there is also Google Page Speed: <a href="http://code.google.com/speed/page-speed/">http://code.google.com/speed/page-speed/</a> this adds an extra section under Firebug.</p> + +<p><strong>Update:</strong> Tested on Chrome and Firefox.</p> diff --git a/pages/005-dwm-hiltjo-my-windowmanager-configuration.cfg b/pages/005-dwm-hiltjo-my-windowmanager-configuration.cfg @@ -0,0 +1,8 @@ +title=DWM-hiltjo: my windowmanager configuration +description=My DWM configuration; a few added features to suit my needs +tags=dwm, suckless +keywords=dwm, suckless +categories=Projects / applications +created=2010-08-12 00:00 +updated=2010-08-12 00:00 + diff --git a/pages/005-dwm-hiltjo-my-windowmanager-configuration.html b/pages/005-dwm-hiltjo-my-windowmanager-configuration.html @@ -0,0 +1,50 @@ +<p>As you might know DWM is a very minimal windowmanager: <a href="http://dwm.suckless.org/">http://dwm.suckless.org/</a>. It's known to only have the most essential features one needs, everything else is "do-it-yourself" or extending it with the many available <a href="http://dwm.suckless.org/patches/">patches</a>. The vanilla version is less than 2000 SLOC, because of this it's quite easy in my opinion to see how it works and modify it.</p> + +<p>I really like my configuration at the moment and want to share my changes. Some of the features listed below are patches from suckless.org I applied, but there are also some changes I made.</p> + +<p><em>Disclaimer: this configuration is entirely made for my personal preferences so you might not like it at all :)</em></p> + +<h2 id="download"><a href="#download">Download</a></h2> +<p>Get it with <a href="http://git-scm.com/">git</a> by doing:</p> +<pre><code>git clone -b hiltjo <a href="http://git.codemadness.nl/dwm">git://git.codemadness.nl/dwm</a></code></pre> + +<h2 id="screenshot"><a href="#screenshot">Screenshot</a></h2> +<p><a href="downloads/screenshots/dwm-screenshot.png"><img src="downloads/screenshots/dwm-screenshot-thumb.png" title="Screenshot of dwm-hiltjo" alt="Screenshot of dwm-hiltjo" /></a></p> + +<h2 id="features"><a href="#features">Features</a></h2> +<ul> + <li>Titlebar: + <ul> + <li>Shows all clients of the selected / active tags.</li> + <li>Divide application bars evenly among available space (awesomewm-like taskbar).</li> + <li>Colour urgent clients in the taskbar on active tags.</li> + <li>Left-click focuses clicked client.</li> + <li>Right-click toggles monocle layout.</li> + <li>Middle-click kills the clicked client.</li> + </ul> + </li> + <li>Tagbar: + <ul> + <li>Only show active tags.</li> + <li>Colour inactive tags with urgent clients.</li> + </ul> + </li> + <li>Layouts: + <ul> + <li>Cycle layouts with Modkey + Space (next) and Modkey + Control + Space (previous).</li> + <li>Fullscreen layout (hides topbar and removes borders).</li> + </ul> + </li> + <li>Other: + <ul> + <li>Move tiled clients around with the mouse (drag-move), awesomewm-like.</li> + <li>Add some keybinds for multimedia keyboards (audio play / pause, mute, www, volume buttons, etc).</li> + </ul> + </li> + <li>... and more ;) ...</li> +</ul> +<p> +NOTES: I removed application icons (_NET_WM_ICON support), but if you want it for some reason you can find it in the history of this repo. +Pertag-like behaviour is also removed since it's wrong. +Cycling through urgent clients is also removed. +</p> diff --git a/pages/006-gtk2-theme-gtk-murrine-rape.cfg b/pages/006-gtk2-theme-gtk-murrine-rape.cfg @@ -0,0 +1,9 @@ +title=GTK2 theme: gtk-murrine-rape +description=A plain consistent GTK2 theme with no fluff (rounded borders, animations, etc) +id=gtk2-theme-gtk-murrine-rape +tags=theme, gtk +keywords=gtk, murrine, theme +categories=Projects / applications +created=2010-10-31 00:00 +updated=2010-10-31 00:00 + diff --git a/pages/006-gtk2-theme-gtk-murrine-rape.html b/pages/006-gtk2-theme-gtk-murrine-rape.html @@ -0,0 +1,17 @@ +<h2 id="download"><a href="#download">Download</a></h2> +<p><a href="/downloads/themes/gtk/murrine-rape/gtk-2.0/gtkrc">gtkrc</a></p> + +<h2 id="screenshot"><a href="#screenshot">Screenshot</a></h2> +<p><a href="/downloads/themes/gtk/murrine-rape/gtk-murrine-rape.png"><img src="/downloads/themes/gtk/murrine-rape/gtk-murrine-rape-thumb.png" alt="Screenshot of gtk engine murrine: murrine-rape theme" title="Screenshot of gtk engine murrine: murrine-rape theme" /></a></p> + +<h2 id="features"><a href="#features">Features</a></h2> +<ul> + <li>Good contrast between UI elements and consistency (imho).</li> + <li>Plain colours; no bright colours and (almost) no gradients.</li> + <li>No rounded borders.</li> + <li>No animations (progressbar etc).</li> + <li>Uses the fast and easy customizable gtk-engine-murrine.</li> +</ul> + +<h2 id="dependency"><a href="#dependency">Dependency</a></h2> +<p>Depends on the git version of gtk-engine-murrine last I checked: <a href="http://www.cimitan.com/murrine/">http://www.cimitan.com/murrine/</a></p> diff --git a/pages/007-seturgent-set-urgency-hints-for-x-applications.cfg b/pages/007-seturgent-set-urgency-hints-for-x-applications.cfg @@ -0,0 +1,9 @@ +title=Seturgent: set urgency hints for X applications +description=Seturgent is a small utility to set an application it's urgency hint +id=seturgent-set-urgency-hints-for-x-applications +tags=seturgent +keywords=seturgent +categories=Projects / applications +created=2010-10-31 00:00 +updated=2010-10-31 00:00 + diff --git a/pages/007-seturgent-set-urgency-hints-for-x-applications.html b/pages/007-seturgent-set-urgency-hints-for-x-applications.html @@ -0,0 +1,11 @@ +<p> +Seturgent is a small utility to set an application it's urgency hint. For most windowmanager's and panel applications this will highlight the application and will allow special actions. +</p> + +<h2>Download</h2> +<p> + Clone the git repository:<br/> + <pre><code>git clone git://<a href="http://github.com/hiltjo/seturgent/">github.com/hiltjo/seturgent</a>.git</code></pre> + <br/> + or download the gzipped tarball of the latest release: <a href="/downloads/projects/seturgent/releases/seturgent-1.4.tar.gz">seturgent-1.4.tar.gz</a> +</p> diff --git a/pages/009-vim-theme-relaxed.cfg b/pages/009-vim-theme-relaxed.cfg @@ -0,0 +1,8 @@ +title=Vim theme: relaxed +description=a dark VIM theme I made and use on a daily basis +tags=theme, vim +keywords=vim, relaxed, theme +categories=Projects / applications +created=2011-01-07 00:00 +updated=2011-01-07 00:00 + diff --git a/pages/009-vim-theme-relaxed.html b/pages/009-vim-theme-relaxed.html @@ -0,0 +1,13 @@ +<p>Hereby I publish a dark theme I made for <a href="http://www.vim.org/">vim</a>. This is a theme I personally use for quite a while now and over time tweaked to my liking. It is made for gvim, but also works for 16-colour terminals (with small visual differences). The relaxed.vim file also has my .Xdefaults file colours listed at the top for 16+-colour terminals on X.</p> + +<p>It is inspired by the "desert" theme available at <a href="http://www.vim.org/scripts/script.php?script_id=105">http://www.vim.org/scripts/script.php?script_id=105</a>, although I removed the cursive and bold styles and changed some colours I didn't like.</p> + +<h2 id="download"><a href="#download">Download</a></h2> + +<p><a href="/downloads/themes/vim/relaxed.vim">relaxed.vim</a></p> + +<h2 id="screenshot"><a href="#screenshot">Screenshot</a></h2> + +<p> +<a href="/downloads/themes/vim/vim_relaxed_theme.png"><img src="/downloads/themes/vim/vim_relaxed_theme_thumb.png" alt="Screenshot of VIM theme relaxed" title="Screenshot of VIM theme relaxed" /> +<br/>On the left gvim, on the right vim in urxvt</a></p> diff --git a/pages/010-sfeed-simple-feed-parser.cfg b/pages/010-sfeed-simple-feed-parser.cfg @@ -0,0 +1,8 @@ +title=Sfeed: simple RSS and Atom parser +description=a simple RSS and Atom parser (and scripts to add reader functionality) +id=sfeed-simple-feed-parser +tags=sfeed +keywords=sfeed, rss, atom, parser, reader +categories=Projects / applications +created=2011-04-01 00:00 +updated=2016-03-20 00:00 diff --git a/pages/010-sfeed-simple-feed-parser.html b/pages/010-sfeed-simple-feed-parser.html @@ -0,0 +1,18 @@ +<p>a simple RSS and Atom parser (and scripts to add reader functionality).</p> + +<h2 id="download"><a href="#download">Download</a></h2> + +<p><pre><code>git clone <a href="http://git.codemadness.nl/sfeed/">git://git.codemadness.nl/sfeed</a></code></pre></p> +<!--<p>or download the gzipped tarball of the latest release: <a href="/downloads/projects/sfeed/releases/sfeed-0.9.tar.gz">sfeed-0.9.tar.gz</a></p>--> + +<h2 id="screenshot"><a href="#screenshot">Screenshot</a></h2> +<p><a href="/downloads/screenshots/sfeed-screenshot.png"><img src="/downloads/screenshots/sfeed-thumb.png" alt="Screenshot of sfeed using dmenu" title="Screenshot of sfeed using dmenu" /></a></p> + +<p>Example HTML output: <a href="/downloads/projects/sfeed/EXAMPLE.html">EXAMPLE.html</a>.</p> +<p>Example plain-text output: <a href="/downloads/projects/sfeed/EXAMPLE.txt">EXAMPLE.txt</a>.</p> + +<h2 id="readme"><a href="#readme">Readme</a></h2> +<p>For more (up-to-date) information see the <a href="http://git.codemadness.nl/sfeed/file/README.html">README</a> file.</p> + +<h2 id="license"><a href="#license">License</a></h2> +<p>ISC, see <a href="http://git.codemadness.nl/sfeed/file/LICENSE.html">LICENSE</a> file.</p> +\ No newline at end of file diff --git a/pages/011-userscript-block-stupid-fonts.cfg b/pages/011-userscript-block-stupid-fonts.cfg @@ -0,0 +1,8 @@ +title=Userscript: block stupid fonts +description=Userscript to whitelist your favorite fonts and block the rest +tags=userscript, greasemonkey, block fonts +keywords=userscript, greasemonkey, block fonts +categories=Userscript +created=2012-10-21 00:00 +updated=2012-10-21 00:00 + diff --git a/pages/011-userscript-block-stupid-fonts.html b/pages/011-userscript-block-stupid-fonts.html @@ -0,0 +1,10 @@ +<h2 id="reason"><a href="#reason">Reason I wrote this</a></h2> +<p> + This is an userscript I wrote a while ago which whitelists fonts I like and blocks the rest. + The reason I made this is because I don't like the inconsistency of custom fonts used on alot of websites. +</p> +<h2 id="download"><a href="#download">Download</a></h2> + +<p><strong><a href="/downloads/Block_stupid_fonts_v1.2.user">Download userscript Block_stupid_fonts_v1.2.user</a></strong></p> + +<p>Old version: <a href="/downloads/Block_stupid_fonts.user.js">Download userscript Block_stupid_fonts.user.js</a></p> diff --git a/pages/012-userscript-youtube-circumvent-age-verification.cfg b/pages/012-userscript-youtube-circumvent-age-verification.cfg @@ -0,0 +1,8 @@ +title=Userscript: Youtube circumvent age verification +description=Userscript to circumvent Youtube age verification and redirect to the video +tags=userscript, greasemonkey, youtube, skip, circumvent, age verification +keywords=userscript, greasemonkey, youtube, skip, circumvent, age verification +categories=Userscript +created=2013-02-21 00:00 +updated=2013-02-21 00:00 + diff --git a/pages/012-userscript-youtube-circumvent-age-verification.html b/pages/012-userscript-youtube-circumvent-age-verification.html @@ -0,0 +1,6 @@ +<h2 id="reason"><a href="#reason">Reason I wrote this</a></h2> +<p> + This is an userscript I wrote a while ago which circumvents requiring to login with an account on Youtube if a video requires age verification. +</p> +<h2 id="download"><a href="#download">Download</a></h2> +<p><a href="/downloads/youtube_circumvent_sign_in.user.js">Download userscript Youtube_circumvent_sign_in.user.js</a></p> diff --git a/pages/013-userscript-focus-input-field.cfg b/pages/013-userscript-focus-input-field.cfg @@ -0,0 +1,8 @@ +title=Userscript: focus input field +description=Userscript to focus the first input field on a page with a hotkey +tags=userscript, focus, input, greasemonkey +keywords=userscript, focus, input, greasemonkey +categories=Userscript +created=2014-03-02 00:00 +updated=2014-03-02 00:00 + diff --git a/pages/013-userscript-focus-input-field.html b/pages/013-userscript-focus-input-field.html @@ -0,0 +1,7 @@ +<h2 id="reason"><a href="#reason">Reason I wrote this</a></h2> +<p> + This is an userscript I wrote a while ago which allows to focus the first input field on a page with ctrl+space. + This is useful if a site doesn't specify the autofocus attribute for an input field and you don't want to switch to it using the mouse. +</p> +<h2 id="download"><a href="#download">Download</a></h2> +<p><a href="/downloads/input_focus.user.js">Download userscript input_focus.user.js</a></p> diff --git a/pages/015-mg-openbsd-port.cfg b/pages/015-mg-openbsd-port.cfg @@ -0,0 +1,7 @@ +title=mg: port of OpenBSD mg version to Linux +description=mg: port of OpenBSD mg version to Linux +tags=mg, openbsd, port +keywords=mg, openbsd, port +categories=Projects +created=2014-11-23 00:00 +updated=2014-11-23 00:00 diff --git a/pages/015-mg-openbsd-port.html b/pages/015-mg-openbsd-port.html @@ -0,0 +1,32 @@ +<h2 id="description"><a href="#description">Description</a></h2> +<p> + This is the port of the mg editor (OpenBSD version) to Linux. It should + be possible to compile it with atleast musl libc and glibc. +</p> + +<h2 id="changes"><a href="#changes">Changes</a></h2> +<p> + I tried to keep the changes minimal to make it easy to synchronize with + upstream changes and to not introduce new bugs. However there were some + changes that needed to be made because of differences between OpenBSD and + Linux and these are: +</p> +<ul> + <li>Add OpenBSD libc / libutil functions: strtonum, reallocarray, strlcpy, strlcat, + fparseln, arc4random(hack)</li> + <li>Timespec changes: for struct stat</li> + <li>Ignore REG_STARTEND for compatibility with Musl. At the time of writing it + doesn't support the REG_STARTEND flag for regexec() (non-POSIX). + NOTE: there are differences in glibc's REG_STARTEND implementation compared to + OpenBSD aswell. So expect it to behave a bit differently.</li> + <li>Convenience: suckless.org-style config.mk and Makefile.</li> +</ul> + +<h2 id="download"><a href="#download">Download</a></h2> +<pre><code>git clone <a href="http://git.codemadness.nl/mg">http://git.codemadness.nl/mg</a></code></pre> + +<h2 id="install"><a href="#install">Install</a></h2> +<code><pre> +$ make +# make install</pre></code> + diff --git a/pages/016-twitch-interface.cfg b/pages/016-twitch-interface.cfg @@ -0,0 +1,7 @@ +title=twitch-go: web application to watch Twitch streams +description=twitch-go: web application to watch Twitch streams +tags=twitch, api, Golang +keywords=twitch, api, Golang +categories=Projects +created=2014-11-23 00:00 +updated=2014-11-23 00:00 diff --git a/pages/016-twitch-interface.html b/pages/016-twitch-interface.html @@ -0,0 +1,18 @@ +<h2 id="description"><a href="#description">Description</a></h2> +<p> + This script allows to view streams in your own video player like + <a href="http://mpv.io/">mpv</a>, so the horrible Flash player is not needed. + It uses the Twitch API and is written in Go. +</p> + +<h2 id="view"><a href="#view">View</a></h2> +<p> + <a href="http://twitch.codemadness.nl/">You can view it here</a>. + There is a download link at the top of the page if you want to host the + script yourself and also a <a href="http://www.codemadness.nl/downloads/twitch.sh.txt">shell version</a>. +</p> + +<h2 id="download"><a href="#download">Download</a></h2> +<p>Get it with <a href="http://git-scm.com/">git</a> by doing:</p> +<pre><code>git clone <a href="http://git.codemadness.nl/twitch-go">git://git.codemadness.nl/twitch-go</a></code></pre> + diff --git a/pages/017-openbsd-httpd-and-cgit.cfg b/pages/017-openbsd-httpd-and-cgit.cfg @@ -0,0 +1,7 @@ +title=OpenBSD httpd, slowcgi and cgit +description=OpenBSD httpd, slowcgi and cgit +tags=OpenBSD httpd, slowcgi, cgit, FastCGI +keywords=OpenBSD httpd, slowcgi, cgit, FastCGI +categories=Projects +created=2015-07-05 00:00 +updated=2015-07-05 00:00 diff --git a/pages/017-openbsd-httpd-and-cgit.html b/pages/017-openbsd-httpd-and-cgit.html @@ -0,0 +1,57 @@ +<h2 id="description"><a href="#description">Description</a></h2> +<p> + This is a guide to get <a href="http://git.zx2c4.com/cgit/">cgit</a> working with the + relatively new <a href="http://www.openbsd.org/cgi-bin/man.cgi/OpenBSD-current/man8/httpd.8">OpenBSD httpd(8)</a> and + <a href="http://www.openbsd.org/cgi-bin/man.cgi/OpenBSD-current/man8/slowcgi.8">slowcgi(8)</a> in base. + OpenBSD httpd is very simple to setup, but nevertheless this guide might help someone out there. +</p> + +<h2 id="installation"><a href="#installation">Installation</a></h2> + +<p>Install cgit package:</p> + +<pre><code># pkg_add cgit</code></pre> + +<p>or build it from ports:</p> + +<pre><code># cd /usr/ports/www/cgit && make && make install</code></pre> + +<p>Enable the httpd and slowcgi services, add to /etc/rc.conf.local:</p> + +<pre><code>slowcgi_flags= +httpd_flags=</code></pre> + +<h2 id="configuration"><a href="#configuration">Configuration</a></h2> + +<h3 id="httpd"><a href="#httpd">httpd</a></h3> +<p>An example of <a href="http://www.openbsd.org/cgi-bin/man.cgi/OpenBSD-current/man5/httpd.conf.5">httpd.conf(5)</a>: +<a href="downloads/openbsd-httpd/httpd.conf">httpd.conf</a>.</p> + +<h3 id="slowcgi"><a href="#slowcgi">slowcgi</a></h3> +<p>By default the slowcgi UNIX domain socket is located at: /var/www/run/slowcgi.sock. +For this example we use the defaults.<p> + +<h3 id="cgit"><a href="#cgit">cgit</a></h3> +<p>The cgit binary should be located at: /var/www/cgi-bin/cgit.cgi (default).</p> + +<p>cgit uses the $CGIT_CONFIG environment variable to locate it's config. +By default on OpenBSD this is set to /conf/cgitrc (chroot), which is /var/www/conf/cgitrc. +An example of cgitrc is here: <a href="downloads/openbsd-httpd/cgitrc">cgitrc</a>.</p> + +<p>In this example the cgit cache directory is set to /cgit/cache (chroot), which is /var/www/cgit/cache. +Make sure to give this path read and write permissions for cgit (www:daemon).</p> + +<p>In the example the repository path (scan-path) is set to /htdocs/src (chroot), which is /var/www/htdocs/src.</p> + +<p>The footer file is set to /conf/cgit.footer. Make sure this file exists or you will get warnings:</p> +<pre><code># printf '' > /var/www/conf/cgit.footer</code></pre> + +<p>Make sure cgit.css (stylesheet) and cgit.png (logo) are accesible, by default: /var/www/cgit/cgit.{css,png} +(location can be changed in httpd.conf).</p> + +<h2 id="run"><a href="#run">Running the services</a></h2> + +<p>Start the services:</p> + +<pre><code># /etc/rc.d/httpd start +# /etc/rc.d/slowcgi start</code></pre> +\ No newline at end of file diff --git a/saait.1 b/saait.1 @@ -0,0 +1,17 @@ +.Dd Nov 14, 2016 +.Dt SAAIT 1 +.Os +.Sh NAME +.Nm saait +.Nd static web page generator +.Sh SYNOPSIS +.Nm +.Op Fl c Ar configfile +.Op Fl o Ar outputdir +.Op Fl t Ar templatedir +.Ar pages... +.Sh DESCRIPTION +.Nm +writes static HTML pages to the output directory. +.Sh AUTHORS +.An Hiltjo Posthuma Aq Mt hiltjo@codemadness.org diff --git a/saait.c b/saait.c @@ -0,0 +1,748 @@ +#include <sys/stat.h> +#include <sys/types.h> + +#include <ctype.h> +#include <dirent.h> +#include <errno.h> +#include <limits.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <time.h> +#include <unistd.h> + +/* string and byte-length */ +#define STRP(s) s,sizeof(s)-1 + +#include "arg.h" +char *argv0; + +#define LEN(s) (sizeof(s) / sizeof(*s)) + +struct config { + struct variable *vars; +}; + +struct variable { + char *key; + char *value; + /* linked-list */ + struct variable *next; +}; + +struct template { + char *name; + /* output file */ + FILE *fp; + /* blocks: header, item, footer */ + char *header, *item, *footer; + /* linked-list */ + struct template *next; +}; + +static struct config *global; + +/* Escape characters below as HTML 2.0 / XML 1.0. */ +void +xmlencode(const char *s, FILE *fp) +{ + for (; *s; s++) { + switch(*s) { + case '<': fputs("&lt;", fp); break; + case '>': fputs("&gt;", fp); break; + case '\'': fputs("&apos;", fp); break; + case '&': fputs("&amp;", fp); break; + case '"': fputs("&quot;", fp); break; + default: fputc(*s, fp); + } + } +} + +void * +estrdup(const char *s) +{ + void *p; + + if (!(p = strdup(s))) { + fprintf(stderr, "strdup: %s\n", strerror(errno)); + exit(1); + } + return p; +} + +void * +estrndup(const char *s, size_t maxlen) +{ + void *p; + + if (!(p = strndup(s, maxlen))) { + fprintf(stderr, "strndup: %s\n", strerror(errno)); + exit(1); + } + return p; +} + +void * +ecalloc(size_t nmemb, size_t size) +{ + void *p; + + if (!(p = calloc(nmemb, size))) { + fprintf(stderr, "calloc: %s\n", strerror(errno)); + exit(1); + } + return p; +} + +FILE * +efopen(const char *path, const char *mode) +{ + FILE *fp; + + if (!(fp = fopen(path, mode))) { + fprintf(stderr, "fopen: %s, mode: %s: %s\n", + path, mode, strerror(errno)); + exit(1); + } + return fp; +} + +long long +datetounix(long long year, int mon, int day, int hour, int min, int sec) +{ + static const int secs_through_month[] = { + 0, 31 * 86400, 59 * 86400, 90 * 86400, + 120 * 86400, 151 * 86400, 181 * 86400, 212 * 86400, + 243 * 86400, 273 * 86400, 304 * 86400, 334 * 86400 }; + int is_leap = 0, cycles, centuries = 0, leaps = 0, rem; + long long t; + + if (year - 2ULL <= 136) { + leaps = (year - 68) >> 2; + if (!((year - 68) & 3)) { + leaps--; + is_leap = 1; + } else { + is_leap = 0; + } + t = 31536000 * (year - 70) + 86400 * leaps; + } else { + cycles = (year - 100) / 400; + rem = (year - 100) % 400; + if (rem < 0) { + cycles--; + rem += 400; + } + if (!rem) { + is_leap = 1; + } else { + if (rem >= 300) + centuries = 3, rem -= 300; + else if (rem >= 200) + centuries = 2, rem -= 200; + else if (rem >= 100) + centuries = 1, rem -= 100; + if (rem) { + leaps = rem / 4U; + rem %= 4U; + is_leap = !rem; + } + } + leaps += 97 * cycles + 24 * centuries - is_leap; + t = (year - 100) * 31536000LL + leaps * 86400LL + 946684800 + 86400; + } + t += secs_through_month[mon]; + if (is_leap && mon >= 2) + t += 86400; + t += 86400LL * (day - 1); + t += 3600LL * hour; + t += 60LL * min; + t += sec; + + return t; +} + +/* Get timezone from string, return time offset in seconds from UTC. + * NOTE: only parses timezones in RFC-822, other timezones are ambiguous + * anyway. If needed you can add some yourself, like "cest", "cet" etc. */ +int +gettzoffset(const char *s, long *off) +{ + static struct { + char *name; + size_t len; + int offhour; + } tzones[] = { + { STRP("A"), -1 * 3600 }, + { STRP("CDT"), -5 * 3600 }, + { STRP("CST"), -6 * 3600 }, + { STRP("EDT"), -4 * 3600 }, + { STRP("EST"), -5 * 3600 }, + { STRP("GMT"), 0 }, + { STRP("M"), -2 * 3600 }, + { STRP("MDT"), -6 * 3600 }, + { STRP("MST"), -7 * 3600 }, + { STRP("N"), 1 * 3600 }, + { STRP("PDT"), -7 * 3600 }, + { STRP("PST"), -8 * 3600 }, + { STRP("UT"), 0 }, + { STRP("UTC"), 0 }, + { STRP("Y"), 12 * 3600 }, + { STRP("Z"), 0 }, + }; + const char *p; + int tzhour = 0, tzmin = 0; + size_t i, namelen; + + for (; *s && isspace((int)*s); s++) + ; + switch (*s) { + case '-': /* offset */ + case '+': + for (i = 0, p = s + 1; i < 2 && *p && isdigit(*p); i++, p++) + tzhour = (tzhour * 10) + (*p - '0'); + if (*p && !isdigit(*p)) + p++; + for (i = 0; i < 2 && *p && isdigit(*p); i++, p++) + tzmin = (tzmin * 10) + (*p - '0'); + *off = ((tzhour * 3600) + (tzmin * 60)) * (s[0] == '-' ? -1 : 1); + return 1; + default: /* timezone name */ + for (i = 0; *s && isalpha((int)s[i]); i++) + ; + namelen = i; /* end of name */ + /* optimization: these are always non-matching */ + if (namelen < 1 || namelen > 3) + return 0; + /* compare tz and adjust offset relative to UTC */ + for (i = 0; i < sizeof(tzones) / sizeof(*tzones); i++) { + if (tzones[i].len == namelen && + !strncmp(s, tzones[i].name, namelen)) { + *off = tzones[i].offhour; + return 1; + } + } + } + return 0; +} + +int +parsetime(const char *s, time_t *tp, struct tm *tm, long *tzoff) +{ + const char *end = NULL; + int va[6] = { 0 }, i, v, vi; + long long l; + long off = 0; + struct tm tmp; + time_t t; + + for (; *s && isspace((int)*s); s++) + ; + if (!isdigit((int)*s)) + return -1; + + /* format "%Y-%m-%d %H:%M:%S" or "%Y-%m-%dT%H:%M:%S" */ + vi = 0; + for (; *s && vi < 6; vi++) { + for (i = 0, v = 0; *s && i < 4 && isdigit((int)*s); s++, i++) + v = (v * 10) + (*s - '0'); + va[vi] = v; + if ((vi < 2 && *s == '-') || + (vi == 2 && (*s == 'T' || isspace((int)*s))) || + (vi > 2 && *s == ':')) + s++; + } + /* TODO: only if seconds are parsed (vi == 5)? */ + /* skip milliseconds for: %Y-%m-%dT%H:%M:%S.000Z */ + if (*s == '.') { + for (s++; *s && isdigit((int)*s); s++) + ; + } + end = s; + + /* invalid range */ + if (va[0] < 0 || va[0] > 9999 || + va[1] < 1 || va[1] > 12 || + va[2] < 1 || va[2] > 31 || + va[3] < 0 || va[3] > 23 || + va[4] < 0 || va[4] > 59 || + va[5] < 0 || va[5] > 59) + return -1; + + l = datetounix(va[0] - 1900, va[1] - 1, va[2], va[3], va[4], va[5]); + t = (time_t)l; /* TODO: bug if sizeof(time_t) != sizeof(long long) ? */ + + /* if no timezone found use localtime */ + if (!gettzoffset(end, &off)) { + if (!localtime_r(&t, &tmp)) { + fprintf(stderr, "localtime_r: %s\n", strerror(errno)); + exit(1); + } + off = tmp.tm_gmtoff; + } + if (!gmtime_r(&t, &tmp)) { + fprintf(stderr, "gmtime_r: %s\n", strerror(errno)); + exit(1); + } + tmp.tm_gmtoff = off; + + if (tp) + *tp = l; + if (tm) + memcpy(tm, &tmp, sizeof(*tm)); + if (tzoff) + *tzoff = off; + return 0; +} + +char * +readfile(const char *file) +{ + FILE *fp; + char buf[BUFSIZ], *s = NULL; + size_t n, len = 0, size = 0; + + fp = efopen(file, "rb"); + s = ecalloc(1, size + 1); + while (!feof(fp)) { + if (!(n = fread(buf, 1, sizeof(buf), fp))) + break; + if (len + n > size) { + size = size ? size * 2 : sizeof(buf); /* greedy allocation */ + if (!(s = realloc(s, size + 1))) { + fprintf(stderr, "realloc: %s\n", strerror(errno)); + exit(1); + } + } + memcpy(&s[len], buf, n); + len += n; + s[len] = '\0'; + } + if (ferror(fp)) { + fprintf(stderr, "fread: file: %s %s\n", file, strerror(errno)); + exit(1); + } + fclose(fp); + + return s; +} + +struct variable * +newvar(const char *key, const char *value) +{ + struct variable *v; + + v = ecalloc(1, sizeof(*v)); + v->key = estrdup(key); + v->value = estrdup(value); + + return v; +} + +/* uses var->key as key */ +void +setvar(struct variable **vars, struct variable *var) +{ + struct variable *v; + + /* new */ + if (!*vars) { + *vars = var; + return; + } + + /* search: set or append */ + for (v = *vars; v; v = v->next) { + if (!strcmp(var->key, v->key)) { + free(v->key); + free(v->value); + v->key = var->key; + v->value = var->value; + /* NOTE: keep v->next */ + return; + } + /* append */ + if (!v->next) { + var->next = NULL; + v->next = var; + return; + } + } +} + +struct variable * +getvar(struct variable *vars, char *key) +{ + struct variable *v; + + for (v = vars; v; v = v->next) + if (!strcmp(key, v->key)) + return v; + return NULL; +} + +char * +getvardefault(struct variable *vars, char *key, char *def) +{ + struct variable *v; + char *ret; + + if (!(v = getvar(vars, key))) + ret = def; + return v->value; +} + +void +freevars(struct variable *vars) +{ + struct variable *v, *tmp; + + for (v = vars; v; ) { + tmp = v->next; + free(v->key); + free(v->value); + free(v); + v = tmp; + } +} + +struct variable * +parsevars(const char *s) +{ + struct variable *vars = NULL, *v; + const char *keystart, *keyend, *valuestart, *valueend; + + for (; *s; ) { + /* comment */ + if (*s == '#') { + for (; *s; s++) { + if (*s == '\n') { + s++; + break; + } + } + continue; + } + + keystart = keyend = s; + valuestart = valueend = NULL; + + /* variable: key=value */ + for (; *s && *s != '\n'; s++) { + if (*s == '=') { + keyend = s; + valuestart = ++s; + break; + } + } + for (; *s; s++) { + if (*s == '\n') { + valueend = s++; + break; + } + } + /* no variable: skip */ + if (!*keystart || !valuestart || !valueend) + continue; + + v = ecalloc(1, sizeof(*v)); + v->key = estrndup(keystart, keyend - keystart); + v->value = estrndup(valuestart, valueend - valuestart); + + setvar(&vars, v); + } + return vars; +} + +struct config * +readconfig(const char *file) +{ + struct config *c; + + c = ecalloc(1, sizeof(*c)); + c->vars = parsevars(readfile(file)); + + return c; +} + +void +writepage(FILE *fp, const char *filename, struct config *c, char *s) +{ + struct variable *var, *v; + /* TODO: revert static key size */ + char key[64], escape, *e, *value; + size_t keylen, line = 0; + + char *tmp; /* TODO */ + /* TODO */ + time_t t = 0; + struct tm tm; + char outtime[256]; + + for (; *s; s++) { + /* TODO: error if unterminated */ + + switch (*s) { + case '#': + case '$': + escape = (*s == '$'); + if (*(s + 1) == '{') { + s += 2; + break; + } + fputc(*s, fp); + continue; + case '\n': + line++; /* FALLTHROUGH */ + default: + fputc(*s, fp); + continue; + } + + /* variable case */ + for (keylen = 0; *s; s++) { + if (*s == '}' || *s == '@') + break; + if (!keylen && isspace(*s)) + continue; + if (keylen + 1 >= sizeof(key)) { + fprintf(stderr, "key too long: %s\n", key); + exit(1); + } + key[keylen++] = *s; + key[keylen] = '\0'; + } + /* trim right whitespace */ + for (; keylen && isspace(key[keylen - 1]); ) + key[keylen--] = '\0'; + + /* page config variables */ + v = NULL; + for (var = c ? c->vars: NULL; var && !v; var = var->next) { + if (!strcmp(var->key, key)) { + v = var; + break; + } + } + /* global config variables */ + for (var = global ? global->vars : NULL; var && !v; var = var->next) { + if (!strcmp(var->key, key)) { + v = var; + break; + } + } + if (!v) { + fprintf(stderr, "%s: unknown variable: '%.*s' on line %zu\n", + filename, (int)keylen, key, line + 1); + exit(1); + } + value = v->value; + + if (*s == '@') { + for (e = ++s; *e && *e != '}'; e++) + ; + + /* TODO: check with different input timezones */ + parsetime(v->value, &t, &tm, NULL); + + /* TODO: check */ + tmp = estrndup(s, e - s); + if (!strftime(outtime, sizeof(outtime), tmp, &tm)) { + fprintf(stderr, "strftime: %s\n", strerror(errno)); + exit(1); + } + free(tmp); + + value = outtime; + s = e; + } + + if (escape) + xmlencode(value, fp); + else + fputs(value, fp); + } +} + +void +usage(void) +{ + fprintf(stderr, "saait [-c config] [-o outputdir] [-t templatedir] pages...\n"); + exit(1); +} + +int +main(int argc, char *argv[]) +{ + const char *configfile = "config"; + const char *outputdir = "output"; + const char *templatedir = "templates"; + struct template *templates = NULL, *t, *tc; + struct config *c; + DIR *dirp, *dirt; + struct dirent *dp, *dt; + char *p, *content, *base, *basefile, file[PATH_MAX], dir[PATH_MAX]; + char outputfile[PATH_MAX]; + int i, r; + +#ifdef __OpenBSD__ + if (pledge("stdio cpath rpath wpath", NULL) == -1) { + fprintf(stderr, "pledge: %s\n", strerror(errno)); + return 1; + } +#endif + + ARGBEGIN { + case 'c': + configfile = EARGF(usage()); + break; + case 'o': + outputdir = EARGF(usage()); + break; + case 't': + templatedir = EARGF(usage()); + break; + default: + usage(); + } ARGEND; + + if (!(dirp = opendir(templatedir))) { + fprintf(stderr, "opendir: %s: %s\n", templatedir, strerror(errno)); + exit(1); + } + + while ((dp = readdir(dirp))) { + /* ignore hidden or file */ + if (dp->d_name[0] == '.' || !(dp->d_type & DT_DIR)) + continue; + + t = calloc(1, sizeof(*t)); + t->name = strdup(dp->d_name); + + /* TODO: truncate check */ + snprintf(dir, sizeof(dir), "%s/%s", templatedir, dp->d_name); + if (!(dirt = opendir(dir))) { + fprintf(stderr, "opendir: %s: %s\n", dir, strerror(errno)); + exit(1); + } + while ((dt = readdir(dirt))) { + /* ignore hidden or dir */ + if (dt->d_name[0] == '.' || (dt->d_type & DT_DIR)) + continue; + /* page is a special case for now */ + if (strcmp(dt->d_name, "page")) { + snprintf(file, sizeof(file), "%s/%s", outputdir, dp->d_name); + t->fp = efopen(file, "wb"); + } + + snprintf(file, sizeof(file), "%s/%s/%s", templatedir, dp->d_name, dt->d_name); + + if (!strcmp(dt->d_name, "item") || strstr(dt->d_name, "item.") == dt->d_name) + t->item = readfile(file); + else if (!strcmp(dt->d_name, "header") || strstr(dt->d_name, "header.") == dt->d_name) + t->header = readfile(file); /* TODO: non-fatal */ + else if (!strcmp(dt->d_name, "footer") || strstr(dt->d_name, "footer.") == dt->d_name) + t->footer = readfile(file); /* TODO: non-fatal */ + } + closedir(dirt); + + if (!templates) + templates = t; + else + tc->next = t; + tc = t; + } + closedir(dirp); + + /* global config */ + /* TODO: make optional */ + global = readconfig(configfile); + + for (t = templates; t; t = t->next) { + if (t->fp) + writepage(t->fp, "", NULL, t->header); + } + + /* pages */ + for (i = 1; i < argc; i++) { + if ((p = strrchr(argv[i], '.'))) + base = estrndup(argv[i], p - argv[i]); + else + base = estrdup(argv[i]); + + if ((p = strrchr(base, '/'))) + basefile = estrdup(&base[p - base + 1]); + else + basefile = estrdup(base); + + /* read config */ + r = snprintf(file, sizeof(file), "%s.cfg", base); + if (r < 0 || (size_t)r >= sizeof(file)) { + fprintf(stderr, "path truncated: '%s'\n", argv[i]); + exit(1); + } + c = readconfig(file); + + /* read file data */ + r = snprintf(file, sizeof(file), "%s.html", base); + if (r < 0 || (size_t)r >= sizeof(file)) { + fprintf(stderr, "path truncated: '%s'\n", argv[i]); + exit(1); + } + content = readfile(file); + + /* set HTML filename */ + if ((p = strrchr(file, '/'))) + setvar(&c->vars, newvar("file", &file[p - file + 1])); + else + setvar(&c->vars, newvar("file", file)); + + /* set content */ + setvar(&c->vars, newvar("content", content)); + free(content); + + for (t = templates; t; t = t->next) { + /* TODO: page is a special case for now */ + if (!strcmp(t->name, "page")) { + /* output file */ + r = snprintf(outputfile, sizeof(outputfile), "%s/%s.html", outputdir, basefile); + if (r < 0 || (size_t)r >= sizeof(file)) { + fprintf(stderr, "path truncated: '%s/%s.html'\n", outputdir, basefile); + exit(1); + } + + t->fp = efopen(outputfile, "wb"); + writepage(t->fp, file, c, t->header); + writepage(t->fp, file, c, t->item); + writepage(t->fp, file, c, t->footer); + fclose(t->fp); + t->fp = NULL; + } else { + writepage(t->fp, file, c, t->item); + } + } + + /* TODO: check using valgrind */ + free(base); + free(basefile); + freevars(c->vars); + free(c); + } + + for (t = templates; t; t = t->next) { + if (t->fp) { + writepage(t->fp, "", NULL, t->footer); + fclose(t->fp); + } + free(t->name); + free(t->header); + free(t->item); + free(t->footer); + } + + /* cleanup */ + freevars(global->vars); + free(global); + + return 0; +} diff --git a/templates/atom.xml/footer.xml b/templates/atom.xml/footer.xml @@ -0,0 +1 @@ +</feed> diff --git a/templates/atom.xml/header.xml b/templates/atom.xml/header.xml @@ -0,0 +1,8 @@ +<?xml version="1.0" encoding="UTF-8"?> +<feed xmlns="http://www.w3.org/2005/Atom" xml:lang="${lang}"> + <title type="text">${sitetitle}</title> + <subtitle type="text">${description}</subtitle> + <updated>${siteupdated@%Y-%m-%dT%H:%M:%S%z}</updated> + <link rel="alternate" type="text/html" href="${siteurl}" /> + <id>${siteurl}/atom.xml</id> + <link rel="self" type="application/atom+xml" href="${siteurl}/atom.xml" /> diff --git a/templates/atom.xml/item.xml b/templates/atom.xml/item.xml @@ -0,0 +1,12 @@ +<entry> + <title type="html">${title}</title> + <link rel="alternate" type="text/html" href="${siteurl}/${file}" /> + <id>${siteurl}/${file}</id> + <updated>${updated@%Y-%m-%dT%H:%M:%S%z}</updated> + <published>${created@%Y-%m-%dT%H:%M:%S%z}</published> + <author> + <name>${author}</name> + <uri>${siteurl}</uri> + </author> + <summary type="html">${description}</summary> +</entry> diff --git a/templates/index.html/footer.html b/templates/index.html/footer.html @@ -0,0 +1,5 @@ + </table> + </div> + </div> + </body> +</html> diff --git a/templates/index.html/header.html b/templates/index.html/header.html @@ -0,0 +1,37 @@ +<!DOCTYPE html> +<html dir="ltr" lang="${lang}"> + <head> + <title>${title} - ${sitetitle}</title> + <link rel="stylesheet" href="style.css" type="text/css" media="screen" /> + <link rel="stylesheet" href="print.css" type="text/css" media="print" /> + <link rel="alternate" type="application/rss+xml" title="${sitetitle} RSS Feed" href="rss.xml" /> + <link rel="alternate" type="application/atom+xml" title="${sitetitle} Atom Feed" href="atom.xml" /> + <link rel="icon" type="image/png" href="/favicon.png" /> + <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" /> + <meta http-equiv="Content-Language" content="${lang}" /> + <meta content="width=device-width" name="viewport" /> + <meta content="${keywords}" name="keywords" /> + <meta content="${description}" name="description" /> + <meta content="${author}" name="author" /> + </head> + <body> + <div id="menuwrap"> + <div id="menu"> + <span id="links"> + <a href="index.html" title="Blog">Blog</a> | + <a href="http://git.codemadness.org/" title="Some of my projects">Git</a> | + <a href="https://github.com/hiltjo/" title="Some of my projects on github">Github</a> + </span> + <span id="links-contact"> + <span class="hidden"> | </span> + <a href="rss.xml" title="RSS feed">RSS</a> | + <a href="atom.xml" title="Atom feed">Atom</a> | + <a href="mailto:${sitemail}" title="Mail me">Mail</a> + </span> + </div> + </div> + <hr class="hidden" /> + <div id="mainwrap"> + <div id="main"> + <h1>Posts</h1> + <table> diff --git a/templates/index.html/item.html b/templates/index.html/item.html @@ -0,0 +1,4 @@ +<tr> + <td>${updated@%Y-%m-%d}</td> + <td><a href="${file}" title="${description}">${title}</a></td> +</tr> diff --git a/templates/page/footer.html b/templates/page/footer.html @@ -0,0 +1,4 @@ + </div> + </div> + </body> +</html> diff --git a/templates/page/header.html b/templates/page/header.html @@ -0,0 +1,35 @@ +<!DOCTYPE html> +<html dir="ltr" lang="${lang}"> + <head> + <title>${title} - ${sitetitle}</title> + <link rel="stylesheet" href="style.css" type="text/css" media="screen" /> + <link rel="stylesheet" href="print.css" type="text/css" media="print" /> + <link rel="alternate" type="application/rss+xml" title="${sitetitle} RSS Feed" href="rss.xml" /> + <link rel="alternate" type="application/atom+xml" title="${sitetitle} Atom Feed" href="atom.xml" /> + <link rel="icon" type="image/png" href="/favicon.png" /> + <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" /> + <meta http-equiv="Content-Language" content="${lang}" /> + <meta content="width=device-width" name="viewport" /> + <meta content="${keywords}" name="keywords" /> + <meta content="${description}" name="description" /> + <meta content="${author}" name="author" /> + </head> + <body> + <div id="menuwrap"> + <div id="menu"> + <span id="links"> + <a href="index.html" title="Blog">Blog</a> | + <a href="http://git.codemadness.org/" title="Some of my projects">Git</a> | + <a href="https://github.com/hiltjo/" title="Some of my projects on github">Github</a> + </span> + <span id="links-contact"> + <span class="hidden"> | </span> + <a href="rss.xml" title="RSS feed">RSS</a> | + <a href="atom.xml" title="Atom feed">Atom</a> | + <a href="mailto:${sitemail}" title="Mail me">Mail</a> + </span> + </div> + </div> + <hr class="hidden" /> + <div id="mainwrap"> + <div id="main"> diff --git a/templates/page/item.html b/templates/page/item.html @@ -0,0 +1,3 @@ + <h1><a href="">${title}</a></h1> + <em><strong>Written:</strong> ${created@%Y-%m-%d}</em> + #{content} diff --git a/templates/rss.xml/footer.xml b/templates/rss.xml/footer.xml @@ -0,0 +1,2 @@ + </channel> +</rss> diff --git a/templates/rss.xml/header.xml b/templates/rss.xml/header.xml @@ -0,0 +1,7 @@ +<?xml version="1.0" encoding="UTF-8"?> +<rss version="2.0"> + <channel> + <title>${sitetitle}</title> + <link>${siteurl}</link> + <description>${description}</description> + <language>${lang}</language> diff --git a/templates/rss.xml/item.xml b/templates/rss.xml/item.xml @@ -0,0 +1,8 @@ +<item> + <title>${title}</title> + <link>${siteurl}/${file}</link> + <pubDate>${updated@%a, %d %b %Y %H:%M:%S %z}</pubDate> + <author>${author}</author> + <guid isPermaLink="false">${siteurl}/${file}</guid> + <description><![CDATA[${description}]]></description> +</item> diff --git a/templates/sitemap.xml/footer.xml b/templates/sitemap.xml/footer.xml @@ -0,0 +1 @@ +</urlset> diff --git a/templates/sitemap.xml/header.xml b/templates/sitemap.xml/header.xml @@ -0,0 +1,2 @@ +<?xml version="1.0" encoding="UTF-8"?> +<urlset> diff --git a/templates/sitemap.xml/item.xml b/templates/sitemap.xml/item.xml @@ -0,0 +1 @@ +<url><loc>${siteurl}/${file}</loc></url> diff --git a/templates/urllist.txt/footer.txt b/templates/urllist.txt/footer.txt diff --git a/templates/urllist.txt/header.txt b/templates/urllist.txt/header.txt diff --git a/templates/urllist.txt/item.txt b/templates/urllist.txt/item.txt @@ -0,0 +1 @@ +${siteurl}/${file}