7 |
8 | '
9 | if (( WSH_CSS == 1 )); then
10 | print -r -- " "
11 | fi
12 | if [[ -n $WSH_TITLE ]]; then
13 | print -r -- " ${WSH_TITLE}${WSH_GENFILE_TITLE:+ | $WSH_GENFILE_TITLE}"
14 | else
15 | print -r -- " ${WSH_GENFILE_TITLE}"
16 | fi
17 | print -r -- '
18 | '
19 | }
20 |
21 | function page_footer {
22 | print -r -- '
23 |
24 | '
25 | }
26 |
--------------------------------------------------------------------------------
/wswsh.conf.default:
--------------------------------------------------------------------------------
1 | # wswsh.conf
2 | # Ypnose - http://ywstd.fr
3 | # One config per website.
4 |
5 | # Site title.
6 | #WSH_TITLE=""
7 |
8 | # CSS style. Pretty obvious, right?
9 | # Write entire location from the src directory.
10 | #WSH_CSSFILE=""
11 |
12 | # Default extension is txt, when WSH_EXT isn't specified.
13 | # If you want to write articles with HTML fragments, WSH_EXT should be
14 | # wshtml or wtfhtml.
15 | # DON'T WRITE html (redundant) or .txt (you don't need the dot).
16 | # For example, if you choose txt, it'll "use" files with the following
17 | # extensions: txt,TXT,txT,TxT,tXt and so on.
18 | #WSH_EXT=""
19 |
20 | # Write the other interpreter
21 | # You can leave this option if you only concatenate files with cat(1).
22 | #WSH_INTERP=""
23 |
24 | # Tag to match to extract page title, it defaults to
25 | #WSH_PATTERN=""
26 |
27 | # Regex to clear the HTML tags
28 | # Read the source code to see the default regex
29 | #WSH_REGEX=""
30 |
31 | # Enable source file copy.
32 | # The file in src/ used for the converting, will be copied to dest/
33 | # Valid value: 1. Disable it by commenting out the line.
34 | #WSH_COPY="1"
35 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | Modified BSD License
2 |
3 | Copyright (c) 2013-2020, Ypnose
4 | All rights reserved.
5 |
6 | Redistribution and use in source and binary forms, with or without modification,
7 | are permitted provided that the following conditions are met:
8 |
9 | Redistributions of source code must retain the above copyright notice, this
10 | list of conditions and the following disclaimer.
11 |
12 | Redistributions in binary form must reproduce the above copyright notice, this
13 | list of conditions and the following disclaimer in the documentation and/or
14 | other materials provided with the distribution.
15 |
16 | Neither the name of the {organization} nor the names of its
17 | contributors may be used to endorse or promote products derived from
18 | this software without specific prior written permission.
19 |
20 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
21 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
22 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
23 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR
24 | ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
25 | (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
26 | LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
27 | ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
28 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
29 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
30 |
--------------------------------------------------------------------------------
/wswsh:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env mksh
2 | # Script by Ypnose - http://ywstd.fr
3 | set -e
4 |
5 | WSH_BASEDIR="$1"
6 | WSH_CSS="0"
7 | WSH_COPY="0"
8 |
9 | function p_err {
10 | print -ru2 -- "$1"
11 | exit 1
12 | }
13 |
14 | function get_config {
15 | if [[ ! -r ${WSH_BASEDIR}/wswsh.conf ]]; then
16 | p_err "Missing wswsh.conf in $WSH_BASEDIR"
17 | fi
18 | . "${WSH_BASEDIR}/wswsh.conf"
19 | # NOT FINAL (SYMLINKS?)...
20 | if [[ -f ${WSH_BASEDIR}/includes/layout ]]; then
21 | . "${WSH_BASEDIR}/includes/layout"
22 | fi
23 | }
24 |
25 | function get_recdirs {
26 | typeset WSH_DESTDIR
27 |
28 | if [[ ! -d ${WSH_BASEDIR}/src ]]; then
29 | p_err "src directory is missing"
30 | fi
31 | # Create src/ hierarchy in dest/
32 | ( cd "${WSH_BASEDIR}/src"; find . -type d ) | while IFS= read -r WSH_DESTDIR; do
33 | mkdir -p "${WSH_BASEDIR}/dest/${WSH_DESTDIR#*/}"
34 | done
35 | }
36 |
37 | function run_hook {
38 | # Run hooks before & after generating the page
39 | # Hooks can use all variables defined inside wswsh
40 | typeset WSH_HOOK
41 |
42 | WSH_HOOK="$1"
43 | if [[ -f ${WSH_BASEDIR}/includes/${WSH_HOOK} ]]; then
44 | . "${WSH_BASEDIR}/includes/${WSH_HOOK}"
45 | fi
46 | }
47 |
48 | function gen_page {
49 | typeset WSH_GENFILE WSH_GENFILE_NOSRC WSH_GENFILE_FINAL WSH_GENFILE_TITLE
50 |
51 | if [[ -n $WSH_INTERP && ! -x $(whence -p $WSH_INTERP) ]]; then
52 | p_err "$WSH_INTERP not found in PATH"
53 | fi
54 |
55 | if [[ -n $WSH_CSSFILE ]]; then
56 | if [[ -r ${WSH_BASEDIR}/src/${WSH_CSSFILE} ]]; then
57 | # WSH_CSS variable can be used in layout to trigger actions
58 | WSH_CSS="1"
59 | cp -a "${WSH_BASEDIR}/src/${WSH_CSSFILE}" "${WSH_BASEDIR}/dest/${WSH_CSSFILE}"
60 | else
61 | p_err "${WSH_BASEDIR}/src/${WSH_CSSFILE} is missing"
62 | fi
63 | fi
64 |
65 | cd "$WSH_BASEDIR"
66 | find src -type f -iname "*.${WSH_EXT:-txt}" | while IFS= read -r WSH_GENFILE; do
67 | WSH_GENFILE_NOSRC="${WSH_GENFILE#*/}"
68 | WSH_GENFILE_FINAL="dest/${WSH_GENFILE_NOSRC%%.*}.html"
69 | # WSH_GENFILE_TITLE can be used in layout
70 | WSH_GENFILE_TITLE="$(awk '/'"${WSH_PATTERN:-
}"'/{gsub(/'"${WSH_REGEX:-^[\t ]*
[\t ]*|[\t ]*<\/h1>.*$}"'/,"");print $0;exit}' "$WSH_GENFILE")"
71 |
72 | run_hook pre_genpage
73 | page_header >"$WSH_GENFILE_FINAL"
74 | run_hook post_header
75 | if ! "${WSH_INTERP:-cat}" "$WSH_GENFILE" >>"$WSH_GENFILE_FINAL"; then
76 | p_err "Generating $WSH_GENFILE failed"
77 | fi
78 | run_hook pre_footer
79 | page_footer >>"$WSH_GENFILE_FINAL"
80 | run_hook post_genpage
81 |
82 | if (( WSH_COPY == 1 )); then
83 | cp -a "src/${WSH_GENFILE_NOSRC}" "dest/${WSH_GENFILE_NOSRC}"
84 | fi
85 | done
86 | }
87 |
88 | function copy_html {
89 | typeset WSH_HTML
90 |
91 | # We copy existing *.html files from src to dest.
92 | find "src" -type f -iname "*.html" | while IFS= read -r WSH_HTML; do
93 | if [[ -f dest/${WSH_HTML#*/} ]]; then
94 | print "dest/${WSH_HTML#*/} already exists"
95 | continue
96 | fi
97 | cp -a -- "$WSH_HTML" "dest/${WSH_HTML#*/}"
98 | done
99 | }
100 |
101 | if [[ -z $1 || $1 = "-h" ]]; then
102 | print "usage: ${0##*/} [DIR]"
103 | exit
104 | fi
105 |
106 | get_config
107 | get_recdirs
108 | gen_page
109 | copy_html
110 |
111 | for f in robots.txt sitemap.xml; do
112 | if [[ -r ${WSH_BASEDIR}/src/${f} ]]; then
113 | cp -a "${WSH_BASEDIR}/src/${f}" "${WSH_BASEDIR}/dest/${f}"
114 | fi
115 | done
116 |
117 | WSH_TOT="$(find "${WSH_BASEDIR}/dest" -type f | wc -l)"
118 | print "$WSH_TOT file(s) in ${WSH_BASEDIR}/dest"
119 |
120 | exit
121 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | wswsh - [w]eb [s]ucks [w]ithout [sh]ell
2 | ---------------------------------------
3 |
4 | wswsh is a static website script using shell. It means [w]eb [s]ucks
5 | [w]ithout [sh]ell. Simple name for a simple script.
6 | It has many advantages:
7 |
8 | * Lightweight
9 | * Only requires a shell + UNIX utilities
10 | * Compatible with [ahrf](https://github.com/Ypnose/ahrf)
11 | * Easily "hackable" with external scripts / interpreters
12 | * Less than 140 LOC (without external layouts)
13 | * Human readable configuration
14 |
15 | *You can read another howto with examples [here](http://ywstd.fr/blog/2013/blogging-shell.html) (might be a good intro).*
16 |
17 | How to use it?
18 | --------------
19 |
20 | Create a directory including the following files:
21 |
22 | includes/layout
23 | wswsh
24 | wswsh.conf.default
25 |
26 | You'll need a config file. Copy the file `wswsh.conf.default` to
27 | `wswsh.conf`. Edit it according to your needs. The comments explain
28 | almost everything.
29 |
30 | A typical hierarchy contains a `src` directory, with your website inside
31 | it.
32 |
33 | .
34 | ├── includes/
35 | │ └── layout
36 | ├── src/
37 | │ ├── css/
38 | │ │ └── style.css
39 | │ ├── blog/
40 | │ │ └── my_post.txt
41 | │ ├── me/
42 | │ │ └── john_doe.txt
43 | │ └── foo/
44 | │ └── baz/
45 | │ └── this_is_a_test.txt
46 | ├── wswsh
47 | └── wswsh.conf
48 |
49 | Each directory in `src` will be reproduced in a new directory `dest`.
50 |
51 | There is no default interpreter, only `cat` is called. It involves posts
52 | written in HTML.
53 | **wswsh** also supports [ahrf](https://github.com/Ypnose/ahrf).
54 |
55 | When you're ready, launch `./wswsh "$PWD"`. Using the previous example,
56 | we now have:
57 |
58 | .
59 | ├── includes/
60 | │ └── layout
61 | ├── dest/
62 | │ ├── css/
63 | │ │ └── style.css
64 | │ ├── blog/
65 | │ │ └── my_post.html
66 | │ ├── me/
67 | │ │ └── john_doe.html
68 | │ └── foo/
69 | │ └── baz/
70 | │ └── this_is_a_test.html
71 | ├── src/
72 | │ ├── css/
73 | │ │ └── style.css
74 | │ ├── blog/
75 | │ │ └── my_post.txt
76 | │ ├── me/
77 | │ │ └── john_doe.txt
78 | │ └── foo/
79 | │ └── baz/
80 | │ └── this_is_a_test.txt
81 | ├── wswsh
82 | └── wswsh.conf
83 |
84 | `dest` is your generated website. You can upload it anywhere.
85 |
86 | Note(s)
87 | -------
88 |
89 | If you want to have the same `wswsh` executable for all your blogs
90 | (assuming you have several websites), it's possible to put `wswsh` in
91 | your `PATH`, instead of having a "redundant" file, in every directory.
92 |
93 | The default behavior allows you to modify `wswsh` for your websites. So,
94 | it's possible to write custom modifications per site.
95 |
96 | The true "power" of `wswsh` resides in hooks launched at many different
97 | steps. Those hooks are sourced inside `wswsh` env allowing you to use
98 | inherited variables to launch all kind of specific actions.
99 |
100 | An "interpreter" can be run if it's placed ouside your `PATH`. Write the
101 | full path to the executable, within `wswsh.conf`:
102 |
103 | WSH_INTERP="/home/foo/my_exec"
104 |
105 | Why not provide a script sh compliant (or even bash)?
106 | -----------------------------------------------------
107 |
108 | Few months ago, I still wanted to write a `sh` compliant version but I
109 | decided to drop that idea. At the moment, we are less than 5 people who
110 | use it. Creating a second version would be a waste of time, especially
111 | when the users already switched to `mksh`.
112 | Maintaining two redundant versions isn't easy and I do not want to work
113 | for nothing. If you're still interested, you're free to adapt to `sh`.
114 | It shouldn't be complicated.
115 |
116 | `awk` compatibility
117 | -------------------
118 |
119 | When I added awk regexes, one of my goal was to support `nawk`, `mawk`
120 | and `gawk`. `gawk` is very common among Linux distributions, so I didn't
121 | have the choice. The regexes were "created" on `mawk`. I tested the
122 | compatibility and it worked flawlessly with the required implementations.
123 | So, you can gain some precious seconds if you're brave enough to use
124 | `nawk` or `mawk`.
125 |
126 | Copyright
127 | ---------
128 |
129 | Code created by Ypnose, under BSD (3-Clause) License.
130 |
131 | Website(s)
132 | ----------
133 |
134 | Powering:
135 | * http://ywstd.fr/blog/
136 | * http://savoirvivre.intraaktion.de
137 |
138 | You decided to adopt wswsh for your website(s)? Please contact me. I
139 | would be happy to add it in the README.
140 |
--------------------------------------------------------------------------------