├── description ├── minpkg └── vendor │ ├── pcre │ ├── linux │ │ └── libpcre.a │ ├── macosx │ │ └── libpcre.a │ └── windows │ │ └── libpcre.a │ ├── openssl │ ├── linux │ │ ├── libssl.a │ │ └── libcrypto.a │ ├── macosx │ │ ├── libssl.a │ │ └── libcrypto.a │ └── windows │ │ ├── libssl.a │ │ └── libcrypto.a │ └── aes │ ├── aes.nim │ ├── aes.h │ └── libaes.c ├── site ├── assets │ ├── fonts │ │ ├── fa-brands-400.woff │ │ ├── fa-solid-900.woff │ │ ├── SourceCodePro-It.woff │ │ ├── SourceCodePro-Bold.woff │ │ ├── SourceSansPro-Light.woff │ │ ├── SourceCodePro-BoldIt.woff │ │ ├── SourceCodePro-Regular.woff │ │ ├── SourceSansPro-LightIt.woff │ │ ├── SourceSansPro-Semibold.woff │ │ └── SourceSansPro-SemiboldIt.woff │ └── styles │ │ ├── site.css │ │ ├── hastysite.css │ │ ├── fonts.css │ │ └── luxbar.css ├── contents │ ├── news.md │ ├── posts │ │ ├── v140-released.md │ │ ├── v133-released.md │ │ ├── v121-released.md │ │ ├── v131-released.md │ │ ├── v1310-released.md │ │ ├── v122-released.md │ │ ├── v130-released.md │ │ ├── v120-released.md │ │ ├── v137-released.md │ │ ├── v138-released.md │ │ ├── v132-released.md │ │ ├── v134-released.md │ │ ├── v136-released.md │ │ ├── v135-released.md │ │ ├── v101-released.md │ │ ├── v110-released.md │ │ ├── v139-released.md │ │ ├── hastysite-article.md │ │ ├── v100-released.md │ │ └── v010-released.md │ ├── _site-structure_.md │ ├── _default-rules_.md │ ├── home.md │ ├── about.md │ ├── getting-started.md │ ├── usage.md │ ├── customization.md │ └── reference.md ├── scripts │ ├── clean.min │ ├── build.min │ ├── page.min │ └── post.min ├── settings.json ├── templates │ ├── page.mustache │ ├── post.mustache │ ├── news.mustache │ ├── _head.mustache │ ├── _footer.mustache │ └── _header.mustache └── rules.min ├── hastyscribepkg └── vendor │ └── markdown │ ├── linux │ └── libmarkdown.a │ ├── macosx │ └── libmarkdown.a │ └── windows │ └── libmarkdown.a ├── .gitignore ├── hastysitepkg └── config.nim ├── hastysite.nims ├── LICENSE ├── README.md ├── .github └── workflows │ ├── ci.yml │ └── add-artifacts-to-current-releases.yml ├── hastysite.nimble ├── HastySite_UserGuide.md └── hastysite.nim /description: -------------------------------------------------------------------------------- 1 | A high-performance static site generator. 2 | -------------------------------------------------------------------------------- /minpkg/vendor/pcre/linux/libpcre.a: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/h3rald/hastysite/master/minpkg/vendor/pcre/linux/libpcre.a -------------------------------------------------------------------------------- /minpkg/vendor/openssl/linux/libssl.a: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/h3rald/hastysite/master/minpkg/vendor/openssl/linux/libssl.a -------------------------------------------------------------------------------- /minpkg/vendor/openssl/macosx/libssl.a: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/h3rald/hastysite/master/minpkg/vendor/openssl/macosx/libssl.a -------------------------------------------------------------------------------- /minpkg/vendor/pcre/macosx/libpcre.a: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/h3rald/hastysite/master/minpkg/vendor/pcre/macosx/libpcre.a -------------------------------------------------------------------------------- /minpkg/vendor/pcre/windows/libpcre.a: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/h3rald/hastysite/master/minpkg/vendor/pcre/windows/libpcre.a -------------------------------------------------------------------------------- /site/assets/fonts/fa-brands-400.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/h3rald/hastysite/master/site/assets/fonts/fa-brands-400.woff -------------------------------------------------------------------------------- /site/assets/fonts/fa-solid-900.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/h3rald/hastysite/master/site/assets/fonts/fa-solid-900.woff -------------------------------------------------------------------------------- /minpkg/vendor/openssl/linux/libcrypto.a: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/h3rald/hastysite/master/minpkg/vendor/openssl/linux/libcrypto.a -------------------------------------------------------------------------------- /minpkg/vendor/openssl/windows/libssl.a: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/h3rald/hastysite/master/minpkg/vendor/openssl/windows/libssl.a -------------------------------------------------------------------------------- /site/assets/fonts/SourceCodePro-It.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/h3rald/hastysite/master/site/assets/fonts/SourceCodePro-It.woff -------------------------------------------------------------------------------- /minpkg/vendor/openssl/macosx/libcrypto.a: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/h3rald/hastysite/master/minpkg/vendor/openssl/macosx/libcrypto.a -------------------------------------------------------------------------------- /minpkg/vendor/openssl/windows/libcrypto.a: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/h3rald/hastysite/master/minpkg/vendor/openssl/windows/libcrypto.a -------------------------------------------------------------------------------- /site/assets/fonts/SourceCodePro-Bold.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/h3rald/hastysite/master/site/assets/fonts/SourceCodePro-Bold.woff -------------------------------------------------------------------------------- /site/assets/fonts/SourceSansPro-Light.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/h3rald/hastysite/master/site/assets/fonts/SourceSansPro-Light.woff -------------------------------------------------------------------------------- /site/assets/fonts/SourceCodePro-BoldIt.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/h3rald/hastysite/master/site/assets/fonts/SourceCodePro-BoldIt.woff -------------------------------------------------------------------------------- /site/assets/fonts/SourceCodePro-Regular.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/h3rald/hastysite/master/site/assets/fonts/SourceCodePro-Regular.woff -------------------------------------------------------------------------------- /site/assets/fonts/SourceSansPro-LightIt.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/h3rald/hastysite/master/site/assets/fonts/SourceSansPro-LightIt.woff -------------------------------------------------------------------------------- /site/assets/fonts/SourceSansPro-Semibold.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/h3rald/hastysite/master/site/assets/fonts/SourceSansPro-Semibold.woff -------------------------------------------------------------------------------- /site/assets/fonts/SourceSansPro-SemiboldIt.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/h3rald/hastysite/master/site/assets/fonts/SourceSansPro-SemiboldIt.woff -------------------------------------------------------------------------------- /hastyscribepkg/vendor/markdown/linux/libmarkdown.a: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/h3rald/hastysite/master/hastyscribepkg/vendor/markdown/linux/libmarkdown.a -------------------------------------------------------------------------------- /hastyscribepkg/vendor/markdown/macosx/libmarkdown.a: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/h3rald/hastysite/master/hastyscribepkg/vendor/markdown/macosx/libmarkdown.a -------------------------------------------------------------------------------- /hastyscribepkg/vendor/markdown/windows/libmarkdown.a: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/h3rald/hastysite/master/hastyscribepkg/vendor/markdown/windows/libmarkdown.a -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | nimcache/ 2 | packages/ 3 | site/temp 4 | site/output 5 | hastysite*.zip 6 | nakefile 7 | hastysite 8 | hastysite.exe 9 | HastySite_UserGuide.htm 10 | .DS_Store 11 | -------------------------------------------------------------------------------- /site/contents/news.md: -------------------------------------------------------------------------------- 1 | ----- 2 | id: news 3 | title: "Latest News" 4 | content-type: news 5 | ----- 6 | This page contains announcements related to HastySite releases and milestones. 7 | -------------------------------------------------------------------------------- /hastysitepkg/config.nim: -------------------------------------------------------------------------------- 1 | const 2 | pkgName* = "HastySite" 3 | pkgVersion* = "1.4.0" 4 | pkgDescription* = "A small but powerful static site generator" 5 | pkgAuthor* = "Fabio Cevasco" 6 | -------------------------------------------------------------------------------- /site/contents/posts/v140-released.md: -------------------------------------------------------------------------------- 1 | ----- 2 | id: v140-released 3 | title: "v1.4.0 released" 4 | content-type: post 5 | date: "19 October 2024" 6 | timestamp: 1729372536 7 | ----- 8 | 9 | Upgraded min to v0.45.0. 10 | -------------------------------------------------------------------------------- /site/contents/posts/v133-released.md: -------------------------------------------------------------------------------- 1 | ----- 2 | id: v133-released 3 | title: "Version 1.3.3 released" 4 | content-type: post 5 | date: "29 December 2019" 6 | timestamp: 1577621894 7 | ----- 8 | * Fixed cross-compilation error. 9 | -------------------------------------------------------------------------------- /site/contents/posts/v121-released.md: -------------------------------------------------------------------------------- 1 | ----- 2 | id: v121-released 3 | title: "Version 1.2.1 released" 4 | content-type: post 5 | date: "12 August 2018" 6 | timestamp: 1534073404 7 | ----- 8 | 9 | * Upgraded to min v0.19.0. 10 | -------------------------------------------------------------------------------- /site/scripts/clean.min: -------------------------------------------------------------------------------- 1 | ;Deletes all temporary and output files. 2 | 'hastysite import 3 | 4 | "Cleaning temporary folder..." io.notice! 5 | clean-temp 6 | "Cleaning output folder..." io.notice! 7 | clean-output 8 | "All done." io.notice! 9 | -------------------------------------------------------------------------------- /site/assets/styles/site.css: -------------------------------------------------------------------------------- 1 | #page-getting-started .unstyled li a::before { 2 | content: none; 3 | } 4 | 5 | footer p { 6 | font-size: 0.8em; 7 | line-height: 1em; 8 | margin: 5px 0; 9 | } 10 | 11 | main { 12 | padding: 0 2em; 13 | } 14 | -------------------------------------------------------------------------------- /site/contents/posts/v131-released.md: -------------------------------------------------------------------------------- 1 | ----- 2 | id: v131-released 3 | title: "Version 1.3.1 released" 4 | content-type: post 5 | date: "25 August 2019" 6 | timestamp: 1566752624 7 | ----- 8 | * Upgraded min to v0.19.4; tested on Nim v0.20.2. 9 | 10 | -------------------------------------------------------------------------------- /site/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "contents": "contents", 3 | "assets": "assets", 4 | "templates": "templates", 5 | "temp": "temp", 6 | "output": "output", 7 | "scripts": "scripts", 8 | "title": "HastySite", 9 | "rules": "rules.min" 10 | } 11 | -------------------------------------------------------------------------------- /site/contents/posts/v1310-released.md: -------------------------------------------------------------------------------- 1 | ----- 2 | id: v1310-released 3 | title: "v1.3.10 released" 4 | content-type: post 5 | date: "8 October 2023" 6 | timestamp: 1696769749 7 | ----- 8 | * Upgraded min to v0.40.0. 9 | * Upgraded hastyscribe to v2.1.0. 10 | 11 | -------------------------------------------------------------------------------- /site/contents/posts/v122-released.md: -------------------------------------------------------------------------------- 1 | ----- 2 | id: v122-released 3 | title: "Version 1.2.2 released" 4 | content-type: post 5 | date: "24 November 2018" 6 | timestamp: 1543060501 7 | ----- 8 | 9 | * Upgraded to min v0.19.2 10 | * Upgraded to HastyScribe v1.11.0 11 | -------------------------------------------------------------------------------- /site/contents/posts/v130-released.md: -------------------------------------------------------------------------------- 1 | ----- 2 | id: v130-released 3 | title: "Version 1.3.0 released" 4 | content-type: post 5 | date: "9 December 2018" 6 | timestamp: 1544370271 7 | ----- 8 | 9 | * Implemented basic support for SCSS-like partial imports. 10 | 11 | -------------------------------------------------------------------------------- /site/scripts/build.min: -------------------------------------------------------------------------------- 1 | ;Builds a site by processing contents and assets. 2 | 'hastysite import 3 | 4 | "Preprocessing..." io.notice! 5 | preprocess 6 | "Processing rules..." io.notice! 7 | process-rules 8 | "Postprocessing..." io.notice! 9 | postprocess 10 | "All done." io.notice! 11 | -------------------------------------------------------------------------------- /site/contents/posts/v120-released.md: -------------------------------------------------------------------------------- 1 | ----- 2 | id: v120-released 3 | title: "Version 1.2.0 released" 4 | content-type: post 5 | date: "29 April 2018" 6 | timestamp: 1525011732 7 | ----- 8 | 9 | * Upgraded HastyScribe to v1.10.0. 10 | * Upgraded min to v0.15.2. 11 | * Improved stylesheet. 12 | -------------------------------------------------------------------------------- /site/contents/posts/v137-released.md: -------------------------------------------------------------------------------- 1 | ----- 2 | id: v137-release 3 | title: "Version 1.3.7 released" 4 | content-type: post 5 | date: "25 April 2021" 6 | timestamp: 1619352320 7 | ----- 8 | 9 | * Updated min to v0.35.1 10 | * Updated HastyScribe to v1.12.4 11 | * Compiled with Nim v1.4.6 12 | -------------------------------------------------------------------------------- /site/contents/posts/v138-released.md: -------------------------------------------------------------------------------- 1 | ----- 2 | id: v138-released 3 | title: "Version 1.3.8 released" 4 | content-type: post 5 | date: "15 August 2023" 6 | timestamp: 1692066608 7 | ----- 8 | 9 | * Updated min to v0.39.1 10 | * Updated HastyScribe to v1.12.5 11 | * Compiled with Nim v2.0.0 12 | -------------------------------------------------------------------------------- /site/contents/posts/v132-released.md: -------------------------------------------------------------------------------- 1 | ----- 2 | id: v132-released 3 | title: "Version 1.3.2 released" 4 | content-type: post 5 | date: "30 September 2019" 6 | timestamp: 1569848401 7 | ----- 8 | * Fixed processing of metadata fields. 9 | * Upgraded to min v0.19.5. 10 | * Tested on Nim v1.0.0 11 | 12 | -------------------------------------------------------------------------------- /site/contents/posts/v134-released.md: -------------------------------------------------------------------------------- 1 | ----- 2 | id: v134-released 3 | title: "v1.3.4 Released" 4 | content-type: post 5 | date: "11 September 2020" 6 | timestamp: 1599853513 7 | ----- 8 | * Fixed Linux cross-compilation 9 | * Updated to min v0.20.1 10 | * Updated to hastyscribe v1.12.1 11 | 12 | 13 | -------------------------------------------------------------------------------- /site/contents/posts/v136-released.md: -------------------------------------------------------------------------------- 1 | ----- 2 | id: v136-released 3 | title: "Version 1.3.6 released" 4 | content-type: post 5 | date: "20 March 2021" 6 | timestamp: 1616231032 7 | ----- 8 | * Updated min to v0.35.0 (contains breaking changes to scripts and rules syntax) 9 | * Compiled with Nim v1.4.4 10 | -------------------------------------------------------------------------------- /site/templates/page.mustache: -------------------------------------------------------------------------------- 1 | 2 | 3 | {{> _head}} 4 | 5 | {{> _header}} 6 |
7 |
8 |

{{title}}

9 | {{{contents}}} 10 |
11 |
12 | {{> _footer}} 13 | 14 | 15 | -------------------------------------------------------------------------------- /site/contents/posts/v135-released.md: -------------------------------------------------------------------------------- 1 | ----- 2 | id: v135-released 3 | title: "Version 1.3.5 released" 4 | content-type: post 5 | date: "31 October 2020" 6 | timestamp: 1604182470 7 | ----- 8 | * Updated to min v0.21.1 9 | * Updated to hastyscribe v1.12.2 10 | * Fixed post/clean scripts 11 | * Compiled with Nim v.1.4.0 12 | 13 | -------------------------------------------------------------------------------- /site/contents/posts/v101-released.md: -------------------------------------------------------------------------------- 1 | ----- 2 | id: v101-released 3 | title: "Version 1.0.1 released" 4 | content-type: post 5 | date: "19 November 2017" 6 | timestamp: 1511100000 7 | ----- 8 | 9 | Just a bugfix release, containing minor fixes to the default CSS stylesheet, and upgrades to HastyScribe v1.7.1 and min v0.13.0. 10 | -------------------------------------------------------------------------------- /site/templates/post.mustache: -------------------------------------------------------------------------------- 1 | 2 | 3 | {{> _head}} 4 | 5 | {{> _header}} 6 |
7 |
8 |

{{title}}

9 |

{{date}}

10 | {{{contents}}} 11 |
12 |
13 | {{> _footer}} 14 | 15 | 16 | -------------------------------------------------------------------------------- /site/contents/posts/v110-released.md: -------------------------------------------------------------------------------- 1 | ----- 2 | id: v110-released 3 | title: "Version 1.1.0 released" 4 | content-type: post 5 | date: "11 March 2018" 6 | timestamp: 1520776800 7 | ----- 8 | 9 | * Upgraded HastyScribe to v1.8.0. 10 | * Upgraded min to v0.15.0. 11 | * Fixed compilation warnings with Nim 0.18.0. 12 | * Now including Font Awesome 5. 13 | -------------------------------------------------------------------------------- /site/contents/posts/v139-released.md: -------------------------------------------------------------------------------- 1 | ----- 2 | id: v139-released 3 | title: "Version 1.3.9 released" 4 | content-type: post 5 | date: "1 September 2023" 6 | timestamp: 1693572989 7 | ----- 8 | 9 | * Updated HastyScribe 2.0.0. 10 | * Now using [nim-mustache](https://github.com/soasme/nim-mustache/tree/master) for Mustache processing. 11 | * Simplified installation. 12 | -------------------------------------------------------------------------------- /site/assets/styles/hastysite.css: -------------------------------------------------------------------------------- 1 | .fa { 2 | font-family: 'Font Awesome 5 Free', 'Font Awesome 5 Brands'; 3 | } 4 | 5 | i.fa { 6 | font-style: normal; 7 | font-weight: normal; 8 | } 9 | 10 | main { 11 | margin-top: 60px; 12 | } 13 | 14 | footer { 15 | margin: auto; 16 | text-align: center; 17 | } 18 | 19 | footer a::before { 20 | content: none !important; 21 | } 22 | -------------------------------------------------------------------------------- /site/contents/posts/hastysite-article.md: -------------------------------------------------------------------------------- 1 | ----- 2 | id: hastysite-article 3 | title: "How HastySite was born" 4 | content-type: post 5 | date: "27 December 2017" 6 | timestamp: 1514373489 7 | ----- 8 | 9 | An article on how HastySite was born has been published on [H3RALD.com](https://h3rald.com): 10 | 11 | 12 | 13 | This article explains the main technological choices behind HastySite and what differentiates it from other static site generators. 14 | 15 | -------------------------------------------------------------------------------- /site/templates/news.mustache: -------------------------------------------------------------------------------- 1 | 2 | 3 | {{> _head}} 4 | 5 | {{> _header}} 6 |
7 |
8 |

{{title}}

9 | {{{contents}}} 10 |
    11 | {{#posts}} 12 |
  • {{date}}{{title}}
  • 13 | {{/posts}} 14 |
15 |
16 |
17 | {{> _footer}} 18 | 19 | 20 | -------------------------------------------------------------------------------- /site/contents/posts/v100-released.md: -------------------------------------------------------------------------------- 1 | ----- 2 | id: v100-released 3 | title: "Version 1.0.0 released" 4 | content-type: post 5 | date: "12 November 2017" 6 | timestamp: 1510504875 7 | ----- 8 | 9 | This is the first public release of HastySite. There are not many things changed compared to the previous release, except for the brand new HastySite web site available at . 10 | 11 | The contents of the site are also available in a standalone HTML document, [here](https://h3rald.com/hastysite/HastySitee_UserGuide.htm). 12 | -------------------------------------------------------------------------------- /site/contents/posts/v010-released.md: -------------------------------------------------------------------------------- 1 | ----- 2 | id: v010-released 3 | title: "Version 0.1.0 released" 4 | content-type: post 5 | date: "15 October 2017" 6 | timestamp: 1508065200 7 | ----- 8 | 9 | The first internal release of HastySite is out and available [on Github](https://github.com/h3rald/hastysite/releases/tag/0.1.0). This is intented to be only an internal pre-release, but as a matter of fact everything works as expected. 10 | 11 | This first release has already been tested in production on the following web sites: 12 | 13 | * 14 | * 15 | 16 | -------------------------------------------------------------------------------- /site/templates/_head.mustache: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | {{site-title}} - {{title}} 6 | 7 | 8 | 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /site/contents/_site-structure_.md: -------------------------------------------------------------------------------- 1 | * assets/ — _Your site assets_ 2 | * fonts/ 3 | * styles/ 4 | * contents/ — _Your site contents._ 5 | * output/ — _Your static web site._ 6 | * rules.min — _Rules to process your contents and assets._ 7 | * scripts/ — _Scripts to manage your site._ 8 | * settings.json — _Your site configuration._ 9 | * temp/ — _Temporary files and folders will be placed here._ 10 | * templates/ — _Mustache templates._ 11 | 12 | 13 | > %tip% 14 | > Tip 15 | > 16 | > Default folder paths can be configured in your [settings.json](class:file) file. 17 | 18 | -------------------------------------------------------------------------------- /site/templates/_footer.mustache: -------------------------------------------------------------------------------- 1 | 16 | -------------------------------------------------------------------------------- /site/contents/_default-rules_.md: -------------------------------------------------------------------------------- 1 | * Ignores contents and assets starting with [.](class:kwd) or [\_](class:kwd). 2 | * Pre-processes [CSS variables](https://developer.mozilla.org/en-US/docs/Web/CSS/Using_CSS_variables) in all [.css](class:ext) files. 3 | * Processes text as [HastyScribe](https://h3rald.com/hastysite)-compatible Markdown in all [.md](class:ext) content files. 4 | * Associates contents to [mustache](https://mustache.github.io/) templates based on the value of the [content-type](class:kwd) metadata property. 5 | * Copies each asset file "as-is" to the [output](class:dir) directory, respecting the source directory structure in the [asset](class:dir) directory. 6 | * Copies each content file to a directory within the [output](class:dir) named after the source content ID, in an [index.html](class:file) file (to easily obtain "pretty URLs" ending with no extension). 7 | 8 | -------------------------------------------------------------------------------- /hastysite.nims: -------------------------------------------------------------------------------- 1 | # https://blog.filippo.io/easy-windows-and-linux-cross-compilers-for-macos/ 2 | 3 | switch("amd64.windows.gcc.path", "/usr/local/bin") 4 | switch("amd64.windows.gcc.exe", "x86_64-w64-mingw32-gcc") 5 | switch("amd64.windows.gcc.linkerexe", "x86_64-w64-mingw32-gcc") 6 | 7 | switch("amd64.linux.gcc.path", "/usr/local/bin") 8 | switch("amd64.linux.gcc.exe", "x86_64-linux-musl-gcc") 9 | switch("amd64.linux.gcc.linkerexe", "x86_64-linux-musl-gcc") 10 | 11 | switch("opt", "size") 12 | switch("mm", "refc") 13 | switch("threadAnalysis", "off") 14 | 15 | when not defined(dev): 16 | switch("define", "release") 17 | 18 | if findExe("musl-gcc") != "": 19 | switch("gcc.exe", "musl-gcc") 20 | switch("gcc.linkerexe", "musl-gcc") 21 | 22 | when defined(windows): 23 | switch("dynlibOverride", "pcre64") 24 | when defined(freebsd): 25 | switch("dynlibOverride", "pcre2") 26 | else: 27 | switch("dynlibOverride", "pcre") 28 | 29 | -------------------------------------------------------------------------------- /site/scripts/page.min: -------------------------------------------------------------------------------- 1 | ;Creates a new empty page. 2 | "" :ident 3 | "" :title 4 | false :valid-id 5 | 6 | ( 7 | (str) expect first :ident 8 | ident "^[a-z0-9-]+$" match? :valid-regexp 9 | (sys.pwd "contents") => "/" join sys.ls :filelist 10 | filelist (fs.filename "(.+)\\..+$" search 1 get) map ident in? not :valid-file 11 | valid-regexp valid-file and 12 | ) ^validate 13 | 14 | 15 | (valid-id not) ( 16 | "ID" io.ask @ident 17 | ident validate @valid-id 18 | (valid-id not) ("ID must not be already used and it must contain only lowercase letters, numbers, or -" io.warn!) when 19 | ) while 20 | 21 | "Title" io.ask @title 22 | 23 | "-----\nid: $1\ntitle: \"$2\"\ncontent-type: page\n-----\n\n" (ident title) => % :metadata 24 | 25 | metadata puts! 26 | ("Create page?" io.confirm) 27 | ( 28 | (ident ".md") => "" join :fn 29 | (sys.pwd "contents") => "/" join :dirpath 30 | (dirpath fn) => "/" join :path 31 | metadata path fs.write 32 | ) when 33 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2014-2023 Fabio Cevasco 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /site/scripts/post.min: -------------------------------------------------------------------------------- 1 | ;Creates a new empty post. 2 | "" :ident 3 | "" :title 4 | time.stamp :ts 5 | ts "d MMMM yyyy" time.format :date 6 | false :valid-id 7 | 8 | ( 9 | (str) expect first :ident 10 | ident "^[a-z0-9-]+$" match? :valid-regexp 11 | (sys.pwd "contents" "posts") => "/" join sys.ls :filelist 12 | filelist (fs.filename "(.+)\\..+$" search 1 get) map ident in? not :valid-file 13 | valid-regexp valid-file and 14 | ) ^validate 15 | 16 | 17 | (valid-id not) ( 18 | "ID" io.ask @ident 19 | ident validate @valid-id 20 | (valid-id not) ("ID must not be already used and it must contain only lowercase letters, numbers, or -" io.warn!) when 21 | ) while 22 | 23 | "Title" io.ask @title 24 | 25 | "-----\nid: $1\ntitle: \"$2\"\ncontent-type: post\ndate: \"$3\"\ntimestamp: $4\n-----\n\n" (ident title date ts) => % :metadata 26 | 27 | metadata puts! 28 | ("Create post?" io.confirm) 29 | ( 30 | (ident ".md") => "" join :fn 31 | (sys.pwd "contents" "posts") => "/" join :dirpath 32 | (dirpath fs.exists? not) (dirpath sys.mkdir) when 33 | (dirpath fn) => "/" join :path 34 | metadata path fs.write 35 | ) when 36 | -------------------------------------------------------------------------------- /site/templates/_header.mustache: -------------------------------------------------------------------------------- 1 |
2 | 3 |
4 | 17 |
18 |
19 | -------------------------------------------------------------------------------- /site/contents/home.md: -------------------------------------------------------------------------------- 1 | ----- 2 | id: "news" 3 | content-type: "page" 4 | title: "Welcome to HastySite" 5 | ----- 6 | 7 | HastySite is a minimalist but powerful static site generator written in [Nim](https://nim-lang.org) which aims to be fast at processing content and highly configurable to suit your own needs. 8 | 9 | ## Key Features 10 | 11 | > %unstyled% 12 | > * [](class:check) Built-in rich markdown support via [HastyScribe](https://h3rald.com/hastyscribe). 13 | > * [](class:check) Built-in [mustache](https://mustache.github.io/) support for page templates. 14 | > * [](class:check) Limited support for standard [CSS variables](https://developer.mozilla.org/en-US/docs/Web/CSS/Using_CSS_variables). 15 | > * [](class:check) Fully configurable content and asset processing pipeline, using the [min](https://min-lang.org) programming language. 16 | > * [](class:check) Custom script definition, using the [min](https://min-lang.org) programming language. 17 | > * [](class:check) Default stylesheet and fonts from [HastyScribe](https://h3rald.com/hastyscribe). 18 | > * [](class:check) Default scripts and rules to get started quickly. 19 | > * [](class:check) All packed in a single executable file, with no dependencies, available for the most common desktop platforms. 20 | 21 | 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | [![Nimble](https://raw.githubusercontent.com/yglukhov/nimble-tag/master/nimble.png)](https://nimble.directory/pkg/hastysite) 2 | 3 | ![release](https://img.shields.io/github/release/h3rald/hastysite.svg) 4 | ![license](https://img.shields.io/github/license/h3rald/hastysite.svg) 5 | 6 | # HastySite 7 | 8 | HastySite is a minimalist but powerful static site generator written in [Nim](https://nim-lang.org) which aims to be fast at processing content and highly configurable to suit your own needs. 9 | 10 | ## Key Features 11 | 12 | HastySite provides the following features: 13 | 14 | * Built-in rich markdown support via [HastyScribe](https://h3rald.com/hastyscribe). 15 | * Built-in [mustache](https://mustache.github.io/) support for page templates. 16 | * Limited support for standard [CSS variables](https://developer.mozilla.org/en-US/docs/Web/CSS/Using_CSS_variables). 17 | * Fully configurable content and asset processing pipeline, using the [min](https://min-lang.org) programming language. 18 | * Custom script definition, using the [min](https://min-lang.org) programming language. 19 | * Default stylesheet and fonts from [HastyScribe](https://h3rald.com/hastyscribe). 20 | * Default scripts and rules to get started quickly. 21 | * All packed in a single executable file, with no dependencies, available for the most common desktop platforms. 22 | 23 | For more information, see the [HastySite Web Site](https://hastysite.h3rald.com) or the [HastySite User Guide](https://h3rald.com/hastysite/HastySite_UserGuide.htm) 24 | -------------------------------------------------------------------------------- /.github/workflows/ci.yml: -------------------------------------------------------------------------------- 1 | name: CI 2 | 3 | # Controls when the action will run. 4 | on: 5 | # Triggers the workflow on push or pull request events but only for the master branch 6 | push: 7 | branches: [master] 8 | pull_request: 9 | branches: [master] 10 | 11 | # Allows you to run this workflow manually from the Actions tab 12 | workflow_dispatch: 13 | 14 | # A workflow run is made up of one or more jobs that can run sequentially or in parallel 15 | jobs: 16 | # This workflow contains a single job called "ci" 17 | ci: 18 | # The type of runner that the job will run on 19 | runs-on: ubuntu-20.04 20 | env: 21 | CHOOSENIM_CHOOSE_VERSION: stable 22 | CHOOSENIM_NO_ANALYTICS: 1 23 | 24 | # Steps represent a sequence of tasks that will be executed as part of the job 25 | steps: 26 | # Checks-out your repository under $GITHUB_WORKSPACE, so your job can access it 27 | - uses: actions/checkout@v2 28 | 29 | - name: install musl-gcc 30 | run: sudo apt-get install -y musl-tools 31 | 32 | - name: Update $PATH 33 | run: echo "$HOME/.nimble/bin" >> $GITHUB_PATH 34 | 35 | - name: Install Nim 36 | run: | 37 | curl https://nim-lang.org/choosenim/init.sh -sSf > init.sh 38 | sh init.sh -y 39 | 40 | - name: Build 41 | run: nimble build -y --mm:refc -d:release --opt:size --gcc.exe:musl-gcc --gcc.linkerexe:musl-gcc --cpu:amd64 --os:linux 42 | 43 | - name: Build Site 44 | run: | 45 | cd site 46 | ../hastysite build 47 | -------------------------------------------------------------------------------- /hastysite.nimble: -------------------------------------------------------------------------------- 1 | import hastysitepkg/config 2 | 3 | # Package 4 | version = pkgVersion 5 | author = pkgAuthor 6 | description = pkgDescription 7 | license = "MIT" 8 | bin = @["hastysite"] 9 | installDirs = @["minpkg", "hastysitepkg"] 10 | 11 | # Deps 12 | requires "nim >= 2.2.0 & <= 3.0.0" 13 | requires "min >= 0.45.0" 14 | requires "checksums >= 0.2.1" 15 | requires "hastyscribe >= 2.1.0" 16 | requires "mustache >= 0.4.3" 17 | 18 | # Tasks 19 | const 20 | compile = "nim c -d:release" 21 | linux_x64 = "--cpu:amd64 --os:linux" 22 | windows_x64 = "--cpu:amd64 --os:windows" 23 | macosx_x64 = "" 24 | #parallel = "--parallelBuild:1 --verbosity:3" 25 | hs = "hastysite" 26 | hs_file = "hastysite.nim" 27 | zip = "zip -X" 28 | 29 | proc shell(command, args: string, dest = "") = 30 | exec command & " " & args & " " & dest 31 | 32 | proc filename_for(os: string, arch: string): string = 33 | return "hastysite" & "_v" & version & "_" & os & "_" & arch & ".zip" 34 | 35 | task windows_x64_build, "Build hastysite for Windows (x64)": 36 | shell compile, windows_x64, hs_file 37 | 38 | task linux_x64_build, "Build hastysite for Linux (x64)": 39 | shell compile, linux_x64, hs_file 40 | 41 | task macosx_x64_build, "Build hastysite for Mac OS X (x64)": 42 | shell compile, macosx_x64, hs_file 43 | 44 | task release, "Release hastysite": 45 | echo "\n\n\n WINDOWS - x64:\n\n" 46 | windows_x64_buildTask() 47 | shell zip, filename_for("windows", "x64"), hs & ".exe" 48 | shell "rm", hs & ".exe" 49 | echo "\n\n\n LINUX - x64:\n\n" 50 | linux_x64_buildTask() 51 | shell zip, filename_for("linux", "x64"), hs 52 | shell "rm", hs 53 | echo "\n\n\n MAC OS X - x64:\n\n" 54 | macosx_x64_buildTask() 55 | shell zip, filename_for("macosx", "x64"), hs 56 | shell "rm", hs 57 | echo "\n\n\n ALL DONE!" 58 | -------------------------------------------------------------------------------- /HastySite_UserGuide.md: -------------------------------------------------------------------------------- 1 | % HastySite User Guide 2 | % Fabio Cevasco 3 | % - 4 | 5 | ## Overview 6 | 7 | {@ site/contents/home.md || 1 @} 8 | 9 | ## Why HastySite? 10 | 11 | {@ site/contents/about.md || 1 @} 12 | 13 | ## Getting Started 14 | 15 | {@ site/contents/getting-started.md || 1 @} 16 | 17 | ## Usage 18 | 19 | {@ site/contents/usage.md || 1 @} 20 | 21 | ## Customization 22 | 23 | {@ site/contents/customization.md || 1 @} 24 | 25 | ## Reference 26 | 27 | {@ site/contents/reference.md || 1 @} 28 | 29 | ## Changelog 30 | 31 | ### v1.3.9 32 | 33 | {@ site/contents/posts/v139-released.md || 1 @} 34 | 35 | ### v1.3.8 36 | 37 | {@ site/contents/posts/v138-released.md || 1 @} 38 | 39 | ### v1.3.7 40 | 41 | {@ site/contents/posts/v137-released.md || 1 @} 42 | 43 | ### v1.3.6 44 | 45 | {@ site/contents/posts/v136-released.md || 1 @} 46 | 47 | ### v1.3.5 48 | 49 | {@ site/contents/posts/v135-released.md || 1 @} 50 | 51 | ### v1.3.4 52 | 53 | {@ site/contents/posts/v134-released.md || 1 @} 54 | 55 | ### v1.3.3 56 | 57 | {@ site/contents/posts/v133-released.md || 1 @} 58 | 59 | ### v1.3.2 60 | 61 | {@ site/contents/posts/v132-released.md || 1 @} 62 | 63 | ### v1.3.1 64 | 65 | {@ site/contents/posts/v131-released.md || 1 @} 66 | 67 | ### v1.3.0 68 | 69 | {@ site/contents/posts/v130-released.md || 1 @} 70 | 71 | ### v1.2.2 72 | 73 | {@ site/contents/posts/v122-released.md || 1 @} 74 | 75 | ### v1.2.1 76 | 77 | {@ site/contents/posts/v121-released.md || 1 @} 78 | 79 | ### v1.2.0 80 | 81 | {@ site/contents/posts/v120-released.md || 1 @} 82 | 83 | ### v1.1.0 84 | 85 | {@ site/contents/posts/v110-released.md || 1 @} 86 | 87 | ### v1.0.1 88 | 89 | {@ site/contents/posts/v101-released.md || 1 @} 90 | 91 | ### v1.0.0 92 | 93 | {@ site/contents/posts/v100-released.md || 1 @} 94 | 95 | ### v0.1.0 96 | 97 | {@ site/contents/posts/v010-released.md || 1 @} 98 | -------------------------------------------------------------------------------- /site/rules.min: -------------------------------------------------------------------------------- 1 | 'hastysite import 2 | 3 | ;Routing 4 | ( 5 | (dict) expect -> :meta 6 | meta "id" dict.get :id 7 | meta "ext" dict.get :ext 8 | ( 9 | ((id "home" ==) ( 10 | meta ( 11 | ("index" "id" dict.set) 12 | (".html" "ext" dict.set) 13 | ) tap 14 | )) 15 | ((true) ( 16 | meta ( 17 | (".html" "ext" dict.set) 18 | ("$1/index" (id) => % "id" dict.set) 19 | ) tap 20 | )) 21 | ) case 22 | ) ^set-destination 23 | 24 | ;Process Markdown content 25 | ( 26 | (dict) expect -> :meta 27 | "" :page 28 | "" :contents 29 | meta "content-type" dict.get :tpl 30 | meta ( 31 | (input-fread @contents meta) 32 | (settings "title" dict.get "site-title" dict.set) 33 | (:temp contents temp markdown @contents temp) 34 | (contents "contents" dict.set) 35 | (:temp tpl temp mustache @page temp) 36 | (page "contents" dict.set) 37 | ) tap 38 | ) ^process-content 39 | 40 | ;Process CSS asset 41 | ( 42 | (dict) expect -> :meta 43 | "" :contents 44 | meta ( 45 | (input-fread @contents meta) 46 | (:temp contents preprocess-css @contents temp) 47 | (contents "contents" dict.set) 48 | ) tap 49 | output-fwrite 50 | ) ^process-css-asset 51 | 52 | ;;; Main ;;; 53 | 54 | ;;Filter and sort posts by timestamp 55 | contents 56 | ('content-type dict.has?) filter 57 | ("content-type" dict.get "post" ==) filter 58 | (:a :b a "timestamp" dict.get b "timestamp" dict.get >) sort :posts 59 | 60 | ;;Process contents 61 | contents ( 62 | (dict) expect -> :content 63 | content ("id" dict.get "news" ==) (content posts "posts" dict.set @content) when 64 | ( 65 | ((content "id" dict.get "/" split last "^[._]" match?) ()) ;;Ignore files starting with a dot or underscore 66 | ((true) (content process-content set-destination output-fwrite)) 67 | ) case 68 | ) foreach 69 | 70 | ;;Process assets 71 | assets ( 72 | (dict) expect -> 73 | stack.dup 74 | ( 75 | (("ext" dict.get ".css" match?) (process-css-asset)) 76 | (("id" dict.get "/" split last "^[._]" match?) ()) ;;Ignore files starting with a dot or underscore 77 | ((true) (output-cp)) 78 | ) case 79 | ) foreach 80 | -------------------------------------------------------------------------------- /site/assets/styles/fonts.css: -------------------------------------------------------------------------------- 1 | @font-face { 2 | font-family: "Font Awesome 5 Free"; 3 | src: url("../fonts/fa-solid-900.woff") format("woff"); 4 | -webkit-font-smoothing: antialiased; 5 | -moz-osx-font-smoothing: grayscale; 6 | } 7 | 8 | @font-face { 9 | font-family: "Font Awesome 5 Brands"; 10 | src: url("../fonts/fa-brands-400.woff") format("woff"); 11 | -webkit-font-smoothing: antialiased; 12 | -moz-osx-font-smoothing: grayscale; 13 | } 14 | 15 | @font-face { 16 | font-family: "Source Sans Pro"; 17 | src: url("../fonts/SourceSansPro-Light.woff") format("woff"); 18 | font-weight: 300; 19 | -webkit-font-smoothing: antialiased; 20 | -moz-osx-font-smoothing: grayscale; 21 | } 22 | 23 | @font-face { 24 | font-family: "Source Sans Pro"; 25 | src: url("../fonts/SourceSansPro-LightIt.woff") format("woff"); 26 | -webkit-font-smoothing: antialiased; 27 | -moz-osx-font-smoothing: grayscale; 28 | font-weight: 300; 29 | font-style: italic; 30 | } 31 | 32 | @font-face { 33 | font-family: "Source Sans Pro"; 34 | src: url("../fonts/SourceSansPro-Semibold.woff") format("woff"); 35 | -webkit-font-smoothing: antialiased; 36 | -moz-osx-font-smoothing: grayscale; 37 | font-weight: 600; 38 | } 39 | 40 | @font-face { 41 | font-family: "Source Sans Pro"; 42 | src: url("../fonts/SourceSansPro-SemiboldIt.woff") format("woff"); 43 | -webkit-font-smoothing: antialiased; 44 | -moz-osx-font-smoothing: grayscale; 45 | font-weight: 600; 46 | font-style: italic; 47 | } 48 | 49 | @font-face { 50 | font-family: "Source Code Pro"; 51 | src: url("../fonts/SourceCodePro-Regular.woff") format("woff"); 52 | -webkit-font-smoothing: antialiased; 53 | -moz-osx-font-smoothing: grayscale; 54 | } 55 | 56 | @font-face { 57 | font-family: "Source Code Pro"; 58 | src: url("../fonts/SourceCodePro-Bold.woff") format("woff"); 59 | -webkit-font-smoothing: antialiased; 60 | -moz-osx-font-smoothing: grayscale; 61 | font-weight: bold; 62 | } 63 | 64 | @font-face { 65 | font-family: "Source Code Pro"; 66 | src: url("../fonts/SourceCodePro-It.woff") format("woff"); 67 | -webkit-font-smoothing: antialiased; 68 | -moz-osx-font-smoothing: grayscale; 69 | font-style: italic; 70 | } 71 | 72 | @font-face { 73 | font-family: "Source Code Pro"; 74 | src: url("../fonts/SourceCodePro-BoldIt.woff") format("woff"); 75 | -webkit-font-smoothing: antialiased; 76 | -moz-osx-font-smoothing: grayscale; 77 | font-weight: bold; 78 | font-style: italic; 79 | } 80 | -------------------------------------------------------------------------------- /site/contents/about.md: -------------------------------------------------------------------------------- 1 | ----- 2 | id: about 3 | title: "About" 4 | content-type: page 5 | ----- 6 | 7 | HastySite is a static-site generator, similar to [hundreds](https://www.staticgen.com/) of others. Why bother with yet another one then? 8 | 9 | Because HastySite: 10 | 11 | * has been designed with minimalism in mind. It does not provide many features on its own, but it can be extended do do almost anything you'd want it to do. 12 | * is only comprised by a single executable file, available pre-compiled for all major desktop platforms, and it can be compiled to run on even more via [Nim](https://nim-lang.org). 13 | * embeds a concatenative programming language in it, that can be used to customize almost every aspect of it. 14 | * can be extended, from the way it processes files to creating custom commands to do literally what you want. 15 | * provides a simple but functional fully-working site template out-of-the-box, which is also the same template used for its [web site](https://hastysite.h3rald.com). 16 | * provides out-of-the-box Markdown support. But not just any markdown, [HastyScribe](https://h3rald.com/hastyscribe)-compatible markdown, which extends the alredy-amazing and powerful [Discount](https://www.pell.portland.or.us/~orc/Code/discount/) engine with more useful features such as snippets, macros, fields and transclusion. 17 | * provides a robust logic-less templating engine based on [mustache](https://mustache.github.io/). 18 | * provides support for SCSS-like partials and [CSS variables](https://developer.mozilla.org/en-US/docs/Web/CSS/Using_CSS_variables), which don't substitute a full fledged CSS preprocessor like LESS or SASS, but they do help. 19 | 20 | 21 | ## Technology and Credits 22 | 23 | HastySite has been built leveraging the following open source projects: 24 | 25 | * The [min](https://min-lang.org) programming language. 26 | * The [HastyScribe](https://h3rald.com/hastyscribe) markdown compiler. 27 | * The [moustachu](https://github.com/fenekku/moustachu) mustache template engine. 28 | 29 | Special thanks also to the creators and maintainers of the following projects, that made HastySite possible: 30 | 31 | * The [Nim](https://nim-lang.org) programming language, used to develop HastySite and all the above-mentioned projects. 32 | * The [Discount](https://www.pell.portland.or.us/~orc/Code/discount/) markdown compiler, used as the basis for HastyScribe. 33 | 34 | ## Sites Using HastySite 35 | 36 | HastySite powers the following web sites: 37 | 38 | * 39 | * 40 | * 41 | -------------------------------------------------------------------------------- /minpkg/vendor/aes/aes.nim: -------------------------------------------------------------------------------- 1 | when not(defined(AES_H)): 2 | const 3 | AES_H* = true 4 | # #define the macros below to 1/0 to enable/disable the mode of operation. 5 | # 6 | # CBC enables AES encryption in CBC-mode of operation. 7 | # CTR enables encryption in counter-mode. 8 | # ECB enables the basic ECB 16-byte block algorithm. All can be enabled simultaneously. 9 | # The #ifndef-guard allows it to be configured before #include'ing or at compile time. 10 | const 11 | CBC* = 1 12 | ECB* = 1 13 | CTR* = 1 14 | AES128* = 1 15 | AES192* = 1 16 | AES256* = 1 17 | const 18 | AES_BLOCKLEN* = 16 19 | when defined(AES256) and (AES256 == 1): 20 | const 21 | AES_KEYLEN* = 32 22 | AES_keyExpSize* = 240 23 | elif defined(AES192) and (AES192 == 1): 24 | const 25 | AES_KEYLEN* = 24 26 | AES_keyExpSize* = 208 27 | else: 28 | const 29 | AES_KEYLEN* = 16 30 | AES_keyExpSize* = 176 31 | type 32 | AES_ctx* = object 33 | RoundKey*: array[AES_keyExpSize, uint8] 34 | Iv*: array[AES_BLOCKLEN, uint8] 35 | 36 | {.push importc, cdecl.} 37 | proc AES_init_ctx*(ctx: ptr AES_ctx; key: ptr uint8) 38 | proc AES_init_ctx_iv*(ctx: ptr AES_ctx; key: ptr uint8; iv: ptr uint8) 39 | proc AES_ctx_set_iv*(ctx: ptr AES_ctx; iv: ptr uint8) 40 | when defined(ECB) and (ECB == 1): 41 | # buffer size is exactly AES_BLOCKLEN bytes; 42 | # you need only AES_init_ctx as IV is not used in ECB 43 | # NB: ECB is considered insecure for most uses 44 | proc AES_ECB_encrypt*(ctx: ptr AES_ctx; buf: ptr uint8) 45 | proc AES_ECB_decrypt*(ctx: ptr AES_ctx; buf: ptr uint8) 46 | when defined(CBC) and (CBC == 1): 47 | # buffer size MUST be mutile of AES_BLOCKLEN; 48 | # Suggest https://en.wikipedia.org/wiki/Padding_(cryptography)#PKCS7 for padding scheme 49 | # NOTES: you need to set IV in ctx via AES_init_ctx_iv() or AES_ctx_set_iv() 50 | # no IV should ever be reused with the same key 51 | proc AES_CBC_encrypt_buffer*(ctx: ptr AES_ctx; buf: ptr uint8; 52 | length: uint32_t) 53 | proc AES_CBC_decrypt_buffer*(ctx: ptr AES_ctx; buf: ptr uint8; 54 | length: uint32_t) 55 | # Same function for encrypting as for decrypting. 56 | # IV is incremented for every block, and used after encryption as XOR-compliment for output 57 | # Suggesting https://en.wikipedia.org/wiki/Padding_(cryptography)#PKCS7 for padding scheme 58 | # NOTES: you need to set IV in ctx with AES_init_ctx_iv() or AES_ctx_set_iv() 59 | # no IV should ever be reused with the same key 60 | proc AES_CTR_xcrypt_buffer*(ctx: ptr AES_ctx; buf: ptr uint8; 61 | length: uint32) 62 | {.pop.} 63 | -------------------------------------------------------------------------------- /minpkg/vendor/aes/aes.h: -------------------------------------------------------------------------------- 1 | #ifndef _AES_H_ 2 | #define _AES_H_ 3 | 4 | #include 5 | 6 | // #define the macros below to 1/0 to enable/disable the mode of operation. 7 | // 8 | // CBC enables AES encryption in CBC-mode of operation. 9 | // CTR enables encryption in counter-mode. 10 | // ECB enables the basic ECB 16-byte block algorithm. All can be enabled simultaneously. 11 | 12 | // The #ifndef-guard allows it to be configured before #include'ing or at compile time. 13 | #ifndef CBC 14 | #define CBC 1 15 | #endif 16 | 17 | #ifndef ECB 18 | #define ECB 1 19 | #endif 20 | 21 | #ifndef CTR 22 | #define CTR 1 23 | #endif 24 | 25 | 26 | #define AES128 1 27 | //#define AES192 1 28 | //#define AES256 1 29 | 30 | #define AES_BLOCKLEN 16 //Block length in bytes AES is 128b block only 31 | 32 | #if defined(AES256) && (AES256 == 1) 33 | #define AES_KEYLEN 32 34 | #define AES_keyExpSize 240 35 | #elif defined(AES192) && (AES192 == 1) 36 | #define AES_KEYLEN 24 37 | #define AES_keyExpSize 208 38 | #else 39 | #define AES_KEYLEN 16 // Key length in bytes 40 | #define AES_keyExpSize 176 41 | #endif 42 | 43 | struct AES_ctx 44 | { 45 | uint8_t RoundKey[AES_keyExpSize]; 46 | uint8_t Iv[AES_BLOCKLEN]; 47 | }; 48 | 49 | void AES_init_ctx(struct AES_ctx* ctx, const uint8_t* key); 50 | void AES_init_ctx_iv(struct AES_ctx* ctx, const uint8_t* key, const uint8_t* iv); 51 | void AES_ctx_set_iv(struct AES_ctx* ctx, const uint8_t* iv); 52 | 53 | #if defined(ECB) && (ECB == 1) 54 | // buffer size is exactly AES_BLOCKLEN bytes; 55 | // you need only AES_init_ctx as IV is not used in ECB 56 | // NB: ECB is considered insecure for most uses 57 | void AES_ECB_encrypt(struct AES_ctx* ctx, const uint8_t* buf); 58 | void AES_ECB_decrypt(struct AES_ctx* ctx, const uint8_t* buf); 59 | 60 | #endif // #if defined(ECB) && (ECB == !) 61 | 62 | 63 | #if defined(CBC) && (CBC == 1) 64 | // buffer size MUST be mutile of AES_BLOCKLEN; 65 | // Suggest https://en.wikipedia.org/wiki/Padding_(cryptography)#PKCS7 for padding scheme 66 | // NOTES: you need to set IV in ctx via AES_init_ctx_iv() or AES_ctx_set_iv() 67 | // no IV should ever be reused with the same key 68 | void AES_CBC_encrypt_buffer(struct AES_ctx* ctx, uint8_t* buf, uint32_t length); 69 | void AES_CBC_decrypt_buffer(struct AES_ctx* ctx, uint8_t* buf, uint32_t length); 70 | 71 | #endif // #if defined(CBC) && (CBC == 1) 72 | 73 | 74 | #if defined(CTR) && (CTR == 1) 75 | 76 | // Same function for encrypting as for decrypting. 77 | // IV is incremented for every block, and used after encryption as XOR-compliment for output 78 | // Suggesting https://en.wikipedia.org/wiki/Padding_(cryptography)#PKCS7 for padding scheme 79 | // NOTES: you need to set IV in ctx with AES_init_ctx_iv() or AES_ctx_set_iv() 80 | // no IV should ever be reused with the same key 81 | void AES_CTR_xcrypt_buffer(struct AES_ctx* ctx, uint8_t* buf, uint32_t length); 82 | 83 | #endif // #if defined(CTR) && (CTR == 1) 84 | 85 | 86 | #endif //_AES_H_ 87 | -------------------------------------------------------------------------------- /site/contents/getting-started.md: -------------------------------------------------------------------------------- 1 | ----- 2 | id: getting-started 3 | title: "Getting Started" 4 | content-type: page 5 | ----- 6 | 7 | {{version => 1.3.10}} 8 | 9 | ## Download 10 | 11 | You can download one of the following pre-built HastySite binaries: 12 | 13 | > %unstyled% 14 | > * {#release||{{version}}||macosx||macOS||x64||apple#} 15 | > * {#release||{{version}}||windows||Windows||x64||windows#} 16 | > * {#release||{{version}}||linux||Linux||x64||linux#} 17 | 18 | {#release -> [](class:$5)[hastysite v$1 for $3 ($4)](https://github.com/h3rald/hastysite/releases/download/v$1/hastysite\_v$1\_$2\_$4.zip) #} 19 | 20 | ## Building from Source 21 | 22 | Alternatively, you can build HastySite from source as follows: 23 | 24 | 1. Download and install [nim](https://nim-lang.org). 25 | 2. Download and build [Nifty](https://github.com/h3rald/nifty), and put the nifty executable somewhere in your [$PATH](class:kwd). 26 | 3. Clone the HastySite [repository](https://github.com/h3rald/hastysite). 27 | 4. Navigate to the HastySite repository local folder. 28 | 5. Run the following command to download HastySite's dependencies. 29 | > %terminal% 30 | > nifty install 31 | 7. Run the following command to compile HastySite: 32 | > %terminal% 33 | > nim c -d:release hastysite.nim 34 | 35 | > %tip% 36 | > Tip 37 | > 38 | > You should put the compiled HastySite executable somewhere in yout [$PATH](class:kwd). 39 | 40 | ## Running HastySite 41 | 42 | To create a new site, run the following command in an empty directory: 43 | 44 | > %terminal% 45 | > hastysite init 46 | 47 | This will create the following default directory structure: 48 | 49 | {@ _site-structure_.md || 1 @} 50 | 51 | Then, create your first page by running the following command and specifying the page ID and Title: 52 | 53 | > %terminal% 54 | > hastysite page 55 | > ID: home 56 | > Title: Home Page 57 | > \-\-\-\-\- 58 | > id: home 59 | > title: "Home Page" 60 | > content-type: page 61 | > \-\-\-\-\- 62 | > Create page? [yes/no]: y 63 | 64 | Finally, run the following command to generate your site contents (just an empty home page for now) and copy the default assets. 65 | 66 | > %terminal% 67 | > hastysite build 68 | > Preprocessing\.\.\. 69 | > Processing rules\.\.\. 70 | > - Writing file: output/index.html 71 | > - Copying: assets/fonts/SourceSansPro-Regular.woff -> output/fonts/SourceSansPro-Regular.woff 72 | > - Copying: assets/fonts/SourceSansPro-It.woff -> output/fonts/SourceSansPro-It.woff 73 | > - Copying: assets/fonts/fontawesome-webfont.woff -> output/fonts/fontawesome-webfont.woff 74 | > - Copying: assets/fonts/SourceCodePro-Regular.woff -> output/fonts/SourceCodePro-Regular.woff 75 | > - Copying: assets/fonts/SourceSansPro-Bold.woff -> output/fonts/SourceSansPro-Bold.woff 76 | > - Copying: assets/fonts/SourceSansPro-BoldIt.woff -> output/fonts/SourceSansPro-BoldIt.woff 77 | > - Writing file: output/styles/hastysite.css 78 | > - Writing file: output/styles/site.css 79 | > - Writing file: output/styles/luxbar.css 80 | > - Writing file: output/styles/hastyscribe.css 81 | > - Writing file: output/styles/fonts.css 82 | > Postprocessing\.\.\. 83 | > All done. 84 | 85 | That's it! You can view the result by serving the [output](class:dir) directory from any web server. 86 | -------------------------------------------------------------------------------- /site/contents/usage.md: -------------------------------------------------------------------------------- 1 | ----- 2 | id: usage 3 | title: "Usage" 4 | content-type: page 5 | ----- 6 | 7 | HastySite is a simple command-line program that supports some default commands and options, described in the following sections. 8 | 9 | ## Syntax 10 | 11 | 12 | **hastysite** *command* [ *options* ] 13 | 14 | 15 | 16 | ## Default Commands 17 | 18 | The following sections define the default commands provided by HastySite. All of them except for [init](class:kwd) can be modified, and you can also configure your own commands by creating your own [min](https://min-lang.org) scripts and placing them in the [scripts](class:dir) directory. 19 | 20 | {#customizable-command => 21 | > %note% 22 | > Note 23 | > 24 | > This command can be customized by modifying the [scripts/$1.min](class:file) file within your site directory. 25 | #} 26 | 27 | ### build 28 | 29 | Builds the site by preprocessing contents and assets, processing rules defined in the [rules.min](class:file) file, and creating a temporary file containing the checksums of all newly-generated files. By doing so, the next time this command is executed, only the files that have actually been modified will be copied to the [output](class:dir) directory. 30 | 31 | The [rules.min](class:file) file processed by this command: 32 | 33 | {@ _default-rules_.md || 1 @} 34 | 35 | {#customizable-command||build#} 36 | 37 | ### clean 38 | 39 | Deletes all files and directories in the [output](class:dir) and [temp](class:dir) directories. 40 | 41 | {#customizable-command||clean#} 42 | 43 | ### init 44 | 45 | Initializes a new HastySite site directory, by creating the following directory structure: 46 | 47 | {@ _site-structure_.md || 1 @} 48 | 49 | 50 | ### page 51 | 52 | Generates an empty page content file containing initial metadata. This command asks the user for the following information: 53 | 54 | * A valid ID composed only by letters, numbers, and dashes that has not yet been used for another page. 55 | * The title of the page. 56 | 57 | After information has been provided, a new content will be created in the [contents](class:dir) directory containing the following metadata properties: 58 | 59 | * id 60 | * title 61 | * content-type (set to **page**) 62 | 63 | {#customizable-command||page#} 64 | 65 | ### post 66 | 67 | Generates an empty post content file containing initial metadata. This command asks the user for the following information: 68 | 69 | * A valid ID composed only by letters, numbers, and dashes that has not yet been used for another post. 70 | * The title of the post. 71 | 72 | After information has been provided, a new content will be created in the [contents/posts](class:dir) directory containing the following metadata properties: 73 | 74 | * id 75 | * title 76 | * content-type (set to **post**) 77 | * timestamp (set to the Unix timestamp of the creation of the content) 78 | * data (set to a date string corresponding to the creation of the content) 79 | 80 | {#customizable-command||post#} 81 | 82 | ## Options 83 | 84 | By default, HastySite provides the following options to display information about the program or alter its behavior. 85 | 86 | ### -h, \-\-help 87 | 88 | Displays the description of all HastySite commands and options. 89 | 90 | 91 | ### -l=_level_, \-\-loglevel=_level_ 92 | 93 | Sets the log level to one the following values: 94 | 95 | * debug 96 | * info 97 | * notice (default) 98 | * warn 99 | * error 100 | * fatal 101 | 102 | ### -v, \-\-version 103 | 104 | Displays the HastySite version string. 105 | -------------------------------------------------------------------------------- /.github/workflows/add-artifacts-to-current-releases.yml: -------------------------------------------------------------------------------- 1 | name: Add artifacts to current release 2 | 3 | # Controls when the action will run. 4 | on: 5 | # Allows you to run this workflow manually from the Actions tab 6 | workflow_dispatch: 7 | 8 | jobs: 9 | release: 10 | name: "Build and upload artifacts" 11 | runs-on: ${{ matrix.os }} 12 | strategy: 13 | matrix: 14 | os: 15 | - ubuntu-latest 16 | - macos-13 17 | - windows-2019 18 | 19 | env: 20 | CHOOSENIM_CHOOSE_VERSION: stable 21 | CHOOSENIM_NO_ANALYTICS: 1 22 | 23 | steps: 24 | # Cancel other actions of the same type that might be already running 25 | - name: "Cancel similar actions in progress" 26 | uses: styfle/cancel-workflow-action@0.6.0 27 | with: 28 | access_token: ${{ github.token }} 29 | 30 | # Detects OS and provide Nim-friendly OS identifiers 31 | - name: Detect current OS 32 | id: os 33 | run: echo "::set-output name=id::${{matrix.os == 'ubuntu-latest' && 'linux' || matrix.os == 'macos-13' && 'macosx' || matrix.os == 'windows-2019' && 'windows'}}" 34 | 35 | # Checks out the repository 36 | - uses: actions/checkout@v2 37 | 38 | # Installs libraries 39 | - name: install musl-gcc 40 | run: sudo apt-get install -y musl-tools 41 | if: matrix.os == 'ubuntu-latest' 42 | 43 | # Sets path (Linux, macOS) 44 | - name: Update $PATH 45 | run: echo "$HOME/.nimble/bin" >> $GITHUB_PATH 46 | if: matrix.os == 'macos-13' || matrix.os == 'ubuntu-latest' 47 | 48 | # Sets path (Windows) 49 | - name: Update %PATH% 50 | run: | 51 | echo "${HOME}/.nimble/bin" | Out-File -FilePath $env:GITHUB_PATH -Encoding utf8 -Append 52 | echo "${GITHUB_WORKSPACE}" | Out-File -FilePath $env:GITHUB_PATH -Encoding utf8 -Append 53 | echo "C:\msys64\mingw64\bin" | Out-File -FilePath $env:GITHUB_PATH -Encoding utf8 -Append 54 | if: matrix.os == 'windows-2019' 55 | 56 | - name: Setup Msys2 57 | if: matrix.os == 'windows-2019' 58 | uses: msys2/setup-msys2@v2 59 | with: 60 | msystem: MINGW64 61 | release: true 62 | update: true 63 | path-type: inherit 64 | install: >- 65 | base-devel 66 | autotools 67 | mingw-w64-x86_64-perl-locale-maketext 68 | mingw-w64-x86_64-toolchain 69 | mingw-w64-x86_64-autotools 70 | 71 | # Install the Nim compiler and dependencies 72 | - name: Install Nim and deps 73 | run: | 74 | curl https://nim-lang.org/choosenim/init.sh -sSf > init.sh 75 | sh init.sh -y 76 | 77 | # Build for Linux 78 | - name: Build (Linux) 79 | run: nimble build -y -d:release --gcc.exe:musl-gcc --gcc.linkerexe:musl-gcc --mm:refc --opt:size 80 | if: matrix.os == 'ubuntu-latest' 81 | 82 | # Build for macOS 83 | - name: Build (macOs) 84 | run: nimble build -y -d:release --opt:size --mm:refc 85 | if: matrix.os == 'macos-13' 86 | 87 | # Build for Windows 88 | - name: Build (Windows) 89 | run: nimble build -v -y -d:release --mm:refc --opt:size --gcc.exe:x86_64-w64-mingw32-gcc --gcc.linkerexe:x86_64-w64-mingw32-gcc 90 | if: matrix.os == 'windows-2019' 91 | 92 | # UPX compress (Linux) 93 | - name: UPX 94 | uses: svenstaro/upx-action@v2 95 | with: 96 | files: | 97 | hastysite 98 | args: --best --force 99 | if: matrix.os == 'ubuntu-latest' 100 | 101 | # UPX compress (Linux) 102 | - name: UPX 103 | uses: svenstaro/upx-action@v2 104 | with: 105 | files: | 106 | hastysite.exe 107 | args: --best --force 108 | if: matrix.os == 'windows-2019' 109 | 110 | # Retrieve ID and Name of the current (draft) release 111 | - name: "Get current release" 112 | id: current-release 113 | uses: InsonusK/get-latest-release@v1.0.1 114 | with: 115 | myToken: ${{ github.token }} 116 | exclude_types: "release" 117 | view_top: 1 118 | 119 | # Package the resulting Linux/macOS binary 120 | - name: Create artifact (Linux, macOS) 121 | run: zip hastysite_${{steps.current-release.outputs.tag_name}}_${{steps.os.outputs.id}}_x64.zip hastysite 122 | if: matrix.os == 'ubuntu-latest' || matrix.os == 'macos-13' 123 | 124 | # Package the resulting Windows binary 125 | - name: Create artifact (Windows) 126 | run: Compress-Archive -Path hastysite.exe -DestinationPath hastysite_${{steps.current-release.outputs.tag_name}}_windows_x64.zip 127 | if: matrix.os == 'windows-2019' 128 | 129 | # Upload artifacts to current draft release 130 | - name: "Upload to current release" 131 | uses: xresloader/upload-to-github-release@v1 132 | env: 133 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 134 | with: 135 | file: "hastysite_v*.zip" 136 | overwrite: true 137 | tag_name: ${{steps.current-release.outputs.tag_name}} 138 | release_id: ${{steps.current-release.outputs.id }} 139 | verbose: true 140 | -------------------------------------------------------------------------------- /site/contents/customization.md: -------------------------------------------------------------------------------- 1 | ----- 2 | id: customization 3 | title: "Customization" 4 | content-type: page 5 | ----- 6 | 7 | By default, HastySite provide all the scripts and rules necessary to build a simple blog with static pages and timestamped blog posts. While this may work for a simple blogging site, you may need additional features like support for tags, or maybe group articles by months, or project pages with custom metadata, and so on. 8 | 9 | HastySite can be customized to your heart content essentially in the following ways: 10 | 11 | * By modifying the [rules.min](class:file) file and tweak the build pipeline. 12 | * By creating custom scripts to create generators for custom content types, or any other task you deem useful for your use case. 13 | * By leveraging HastyScribe advanced features for content reuse, like snippets, fields, macros, and transclusions. 14 | 15 | ## Default Build Pipeline 16 | 17 | Before diving into customization techniques, you should be familiar with the way HastySite builds your site out-of-the-box. If you have a look at the [scripts/build.min](class:file) file, it looks like this: 18 | 19 | ``` 20 | ;Builds a site by processing contents and assets. 21 | 'hastysite import 22 | 23 | "Preprocessing..." notice 24 | preprocess 25 | "Processing rules..." notice 26 | process-rules 27 | "Postprocessing..." notice 28 | postprocess 29 | "All done." notice 30 | ``` 31 | 32 | Even if you are not familiar with the [min](https://minl-lang.org) programming language, this code looks straightforward enough. as part of the build process, three actions are performed: 33 | 34 | 1. preprocess 35 | 2. process-rules 36 | 3. post-process 37 | 38 | ### Preprocess 39 | 40 | During this phase, the following operations are performed: 41 | 42 | 1. Some maintenance of temporary files is performed, e.g. the [temp](class:dir) directory is created and populated with a [checksums.json](class:file) file if needed, temporary contents from the previous build are deleted, and so on. 43 | 2. All contents are loaded, and metadata in the content header is processed and saved in memory. 44 | 3. All assets are loaded and bare-bones metadata is generated for them and saved in memory, as they have no header. 45 | 46 | If you are wondering what this content header is, it's a section at the start of a content file delimited by five dashes, and containing metadata properties, like this: 47 | 48 | ``` 49 | ----- 50 | id: getting-started 51 | title: "Getting Started" 52 | content-type: page 53 | ----- 54 | ``` 55 | 56 | Now, this *looks* like [YAML](http://yaml.org/), but it is actually Nim's own [configuration file format](https://nim-lang.org/docs/parsecfg.html), which is a bit more limited, but it does the job. Just remember to wrap strings with spaces in double quotes and everything will be good. 57 | 58 | Internally, after the preprocessing phase all contents and assets will have the following metadata: 59 | 60 | id 61 | : An identifier for the content or asset, corresponding to the path to the file relative to the [contents](class:dir) or [assets](class:dir) folder, without extension. 62 | path 63 | : The path to the file relative to the [contents](class:dir) or [assets](class:dir) folder, *including* extension. 64 | type 65 | : Either **content** or **asset**. 66 | ext 67 | : The file extension (including the leading [.](class:kwd)). 68 | 69 | ### Process Rules 70 | 71 | In this phase, the control of the build process is passed to the [rules.min](class:file) script. It is important to point out that in case of an empty [rules.min](class:file) file, *nothing* will be done and no output file will be generated. 72 | 73 | Luckily, a default [rules.min](class:file) file is provided, which: 74 | 75 | {@ _default-rules_.md || 1 @} 76 | 77 | Typically, you only need to modify this file to change how HastySite builds your site. 78 | 79 | ### Postprocess 80 | 81 | In this phase, the [temp/checksums.json](class:file) is updated with the latest checksums of the generated output files. 82 | 83 | ## Modifying the [rules.min](class:kwd) File 84 | 85 | The [rules.min](class:kwd) file is used to build your site. This file is nothing but a [min](https://min-lang-org) script, and therefore you should have at least a basic understanding of the min programming language before diving in and modifying it. 86 | 87 | In particular, you should get acquainted with the following min modules, whose symbols and sigils are typically used to create [rules.min](class:kwd) files: 88 | 89 | * [lang](https://min-lang.org/reference-lang/) 90 | * [seq](https://min-seq.org/reference-seq/) 91 | * [str](https://min-str.org/reference-str/) 92 | * [time](https://min-time.org/reference-time/) 93 | 94 | Additionally, a dedicated [hastysite](class:kwd) module is also provided specifically for HastySite, which is described in the Reference section. 95 | 96 | ## Creating Commands 97 | 98 | Once you are comfortable with witing min scripts and maybe after you modified the [rules.min](class:file) file, you could try modifying or adding new HastySite commands. To do so, you must: 99 | 100 | 1. Create a new [.min](class:ext) file named after your command (e.g. [project.min](class:file)) and place it in the [scripts](class:dir) folder of your site. 101 | 2. On the first line, enter a min comment corresponding to the description of your command. This description will be used by HastySite help system. 102 | 3. On the subsequent lines, write the logic of your command in min. 103 | 104 | For more information and examples, have a look at the default scripts generated by the hastysite init command. 105 | 106 | ## Leveraging HastyScribe Advanced Features 107 | 108 | In some cases, you may not even need to edit [.min](class:kwd) files to customize the way your site pages are rendered. In particular, [HastyScribe](https://h3rald.com/hastyscribe), the Markdown engine that powers HastySite, provide a lot of extra feature aimed at improving content reuse, such as: 109 | 110 | * [Transclusion](https://h3rald.com/hastyscribe/HastyScribe_UserGuide.htm#Transclusion), to basically load the contents of a Markdown file into another. 111 | * [Snippets](https://h3rald.com/hastyscribe/HastyScribe_UserGuide.htm#Snippets), to reuse chunks of text in the same file (and transcluded files). 112 | * [Fields](https://h3rald.com/hastyscribe/HastyScribe_UserGuide.htm#Fields), to specify things like current date and time, but also custom properties defined at run time. 113 | * [Macros](https://h3rald.com/hastyscribe/HastyScribe_UserGuide.htm#Macros), to create chunks of reusable text with placeholders. 114 | 115 | -------------------------------------------------------------------------------- /site/contents/reference.md: -------------------------------------------------------------------------------- 1 | ----- 2 | id: reference 3 | title: "Reference" 4 | content-type: page 5 | ----- 6 | 7 | This section provides some reference information related to the default metadata of contents and assets, as well as the full documentation of the [hastysite](class:kwd) min Module. 8 | 9 | ## Default Content and Asset Metadata 10 | 11 | The following table lists all the default metadata properties available for contents and assets, and also after which build phase they become available. 12 | 13 | > %note% 14 | > Note 15 | > 16 | > You can define your own custom metadata for contents in a header section delimited by five dashes, at the start of each content file. 17 | 18 | > %responsive% 19 | > Property | Content | Asset 20 | > ---------------|-----------------------|----------------- 21 | > id | [](class:check) | [](class:check) 22 | > path | [](class:check) | [](class:check) 23 | > ext | [](class:check) | [](class:check) 24 | > type | [](class:check) | [](class:check) 25 | > title | [](class:check)^1 | [](class:square) 26 | > content-type | [](class:check)^1 | [](class:square) 27 | > date | [](class:check)^(1,2) | [](class:square) 28 | > timestamp | [](class:check)^(1,2) | [](class:square) 29 | > contents | [](class:check)^3 | [](class:check)^3 30 | 31 | > %unstyled% 32 | > * 1 This property is defined for all **page** and **post** contents created using the default [page](class:kwd) and [post](class:kwd) commands. 33 | > * 2 This property is defined for all **post** contents created using the default [post](class:kwd) command. 34 | > * 3 This property *must* be added to contents and assets before they can be written to the output folder. This can be done implicitly using symbols provided with the [hastysite](class:kwd) min module. 35 | 36 | ## [hastysite](class:kwd) min Module 37 | 38 | This [min](https://min-lang.org) module can be imported in the [rules.min](class:kwd) file or in any script file and can be used to perform common operations such as ready and writing files, and interact with HastySite data at build time. 39 | 40 | {{null => ∅}} 41 | {{d => [dict](class:kwd)}} 42 | {{q => [quot](class:kwd)}} 43 | {{m => [meta](class:kwd)}} 44 | {{qm => [(meta\*)](class:kwd)}} 45 | {{q1 => [quot1](class:kwd)}} 46 | {{q2 => [quot2](class:kwd)}} 47 | {{s => [string](class:kwd)}} 48 | {{s1 => [string1](class:kwd)}} 49 | {{s2 => [string2](class:kwd)}} 50 | 51 | {#op => 52 | 53 | ### [$1](class:kwd) 54 | 55 | > %operator% 56 | > [ $2 **⇒** $3](class:kwd) 57 | > 58 | > $4 59 | #} 60 | 61 | {#op||assets||{{null}}||{{qm}}|| 62 | Returns a quotation of metadata dictionaries {{qm}} containing the metadata of each asset file. 63 | #} 64 | 65 | {#op||clean-output||{{null}}||{{null}}|| 66 | Deletes all the contents of the [output](class:dir) directory. 67 | #} 68 | 69 | {#op||clean-temp||{{null}}||{{null}}|| 70 | Deletes all the contents of the [temp](class:dir) directory. 71 | #} 72 | 73 | {#op||contents||{{null}}||{{qm}}|| 74 | Returns a quotation of metadata dictionaries {{qm}} containing the metadata of each content file. 75 | #} 76 | 77 | {#op||input-fread||{{m}}||{{s}}|| 78 | Reads the contents of the file identified by the metadata dictionary {{m}} (such as those returned by the [contents](class:kwd) and [assets](class:kwd) parameters). 79 | 80 | Note that: 81 | 82 | * The source directory is determined by the value of the [type](class:kwd) metadata property. 83 | * The path within the source directory is determined by the value of the [path](class:kwd) metadata property. 84 | #} 85 | 86 | {#op||markdown||{{s1}} {{d}}||{{s2}}|| 87 | Converts the [HastyScribe](https://h3rald.com/hastyscribe) Markdown string {{s1}} into the HTML fragment {{s2}}, using the properties of {{d}} as custom fields (accessible therefore via HastyScribe's [\{\{$<field-name>\}\}](class:kwd) syntax). 88 | #} 89 | 90 | {#op||mustache||{{s1}} {{d}}||{{s2}}|| 91 | Renders mustache template {{s1}} into {{s2}}, using dictionary {{d}} as context. 92 | 93 | > %note% 94 | > Note 95 | > 96 | > {{s1}} is the path to the mustache template file, relative to the [templates](class:dir) directory and without [.mustache](class:ext). 97 | #} 98 | 99 | {#op||output||{{null}}||{{s}}|| 100 | Returns the full path to the [output](class:dir) directory. 101 | #} 102 | 103 | {#op||output-cp||{{m}}||{{null}}|| 104 | Copies a file from the source directory ([contents](class:dir) or [assets](class:dir) depending on its [type](class:kwd)) to the [output](class:dir) directory. 105 | 106 | Note that: 107 | 108 | * The source directory is determined by the value of the [type](class:kwd) metadata property. 109 | * The path within the source directory is determined by the value of the [path](class:kwd) metadata property. 110 | * The destination path within the output directory is determined by the value of concatenation of the [id](class:kwd) and [ext](class:kwd) metadata properties. 111 | * The contents of the file are retrieved from the [contents](class:kwd) metadata property (in case of contents) or the contents of the original file (in case of assets). 112 | 113 | #} 114 | 115 | {#op||output-fwrite||{{m}}||{{null}}|| 116 | Writes the contents of the file identified by the metadata dictionary {{m}} (such as those returned by the [contents](class:kwd) and [assets](class:kwd) parameters). 117 | 118 | Note that: 119 | 120 | * The destination path within the output directory is determined by the value of concatenation of the [id](class:kwd) and [ext](class:kwd) metadata properties. 121 | * The contents of the file are retrieved from the [contents](class:kwd) metadata property (in case of contents) or the contents of the original file (in case of assets). 122 | 123 | #} 124 | 125 | {#op||postprocess||{{null}}||{{null}}|| 126 | Starts the postprocessing phase of the build. 127 | #} 128 | 129 | {#op||preprocess||{{null}}||{{null}}|| 130 | Starts the preprocessing phase of the build. 131 | #} 132 | 133 | {#op||preprocess-css||{{s1}}||{{s2}}|| 134 | Pre-process CSS contents within {{s1}}, i.e.: 135 | 136 | 1. Pseudo-SCSS partial imports. Partial CSS files must start with an underscore and be placed in the `css-partials` directory (set in your [settings.json](class:file), or [assets/styles](class:dir) if not specified). 137 | 2. [CSS variable](https://developer.mozilla.org/en-US/docs/Web/CSS/Using_CSS_variables) declarations 138 | 139 | Returns the resulting CSS code {{s2}}. 140 | 141 | For example, the following CSS code in two different files: 142 | 143 | 144 | \_vars.css: 145 | : ``` 146 | :root { 147 | --standard-gray: #cccccc; 148 | } 149 | ``` 150 | 151 | style.css: 152 | : ``` 153 | @import 'vars'; 154 | .note { 155 | background-color: var(--standard-gray); 156 | } 157 | ``` 158 | 159 | Will be converted into the following: 160 | 161 | ``` 162 | :root { 163 | --standard-gray: #cccccc; 164 | } 165 | 166 | .note { 167 | background-color: #cccccc; 168 | } 169 | ``` 170 | 171 | > %warning% 172 | > Limitation 173 | > 174 | > Only basic support for CSS variables is provided, e.g. no fallback values are supported. 175 | #} 176 | 177 | {#op||process-rules||{{null}}||{{null}}|| 178 | Starts the processing phase of the build, and interprets the [rules.min](class:file) file. 179 | #} 180 | 181 | {#op||settings||{{null}}||{{d}}|| 182 | Returns a dictionary {{d}} containing all the settings defined in the [settings.json](class:file) file. 183 | #} 184 | 185 | -------------------------------------------------------------------------------- /site/assets/styles/luxbar.css: -------------------------------------------------------------------------------- 1 | .luxbar-default { 2 | width: 100%; 3 | position: relative; 4 | box-shadow: 0 1px 3px rgba(0, 0, 0, 0.12), 0 1px 2px rgba(0, 0, 0, 0.24); } 5 | 6 | .luxbar-static { 7 | box-shadow: 0 1px 3px rgba(0, 0, 0, 0.12), 0 1px 2px rgba(0, 0, 0, 0.24); 8 | width: 100%; 9 | position: absolute; 10 | top: 0; 11 | left: 0; } 12 | .luxbar-static .luxbar-checkbox:checked ~ .luxbar-menu { 13 | position: absolute; } 14 | 15 | .luxbar-fixed { 16 | width: 100%; 17 | position: fixed; 18 | top: 0; 19 | left: 0; 20 | z-index: 1000; 21 | box-shadow: 0 1px 3px rgba(0, 0, 0, 0.12), 0 1px 2px rgba(0, 0, 0, 0.24); } 22 | 23 | .luxbar-fixed-bottom { 24 | width: 100%; 25 | position: fixed; 26 | bottom: 0; 27 | left: 0; 28 | z-index: 1000; 29 | box-shadow: 0 1px 3px rgba(0, 0, 0, 0.12), 0 1px 2px rgba(0, 0, 0, 0.24); } 30 | 31 | .luxbar-hamburger span, .luxbar-hamburger span::before, .luxbar-hamburger span::after { 32 | display: block; 33 | height: 2px; 34 | width: 26px; 35 | transition: 0.6s ease; } 36 | 37 | .luxbar-checkbox:checked ~ .luxbar-menu li .luxbar-hamburger span { 38 | background-color: transparent; } 39 | .luxbar-checkbox:checked ~ .luxbar-menu li .luxbar-hamburger span::before, .luxbar-checkbox:checked ~ .luxbar-menu li .luxbar-hamburger span::after { 40 | margin-top: 0; } 41 | 42 | .luxbar-header { 43 | display: flex; 44 | flex-direction: row; 45 | justify-content: space-between; 46 | align-items: center; 47 | height: 58px; } 48 | 49 | .luxbar-menu-left .luxbar-navigation, .luxbar-menu-left .luxbar-header { 50 | justify-content: flex-start; } 51 | 52 | .luxbar-menu-right .luxbar-hamburger { 53 | margin-left: auto; } 54 | 55 | .luxbar-brand { 56 | font-size: 1.6em; 57 | padding: 18px 24px 18px 24px; } 58 | 59 | .luxbar-menu { 60 | min-height: 58px; 61 | transition: 0.6s ease; 62 | width: 100%; } 63 | 64 | .luxbar-navigation { 65 | display: flex; 66 | flex-direction: column; 67 | list-style: none; 68 | padding-left: 0; 69 | margin: 0; } 70 | 71 | .luxbar-menu a, 72 | .luxbar-item a { 73 | text-decoration: none; 74 | color: inherit; 75 | cursor: pointer; } 76 | 77 | .luxbar-item { 78 | height: 58px; } 79 | .luxbar-item a { 80 | padding: 18px 24px 18px 24px; 81 | display: block; } 82 | 83 | .luxbar-hamburger { 84 | padding: 18px 24px 18px 24px; 85 | position: relative; 86 | cursor: pointer; } 87 | .luxbar-hamburger span::before, .luxbar-hamburger span::after { 88 | content: ''; 89 | position: absolute; } 90 | .luxbar-hamburger span::before { 91 | margin-top: -8px; } 92 | .luxbar-hamburger span::after { 93 | margin-top: 8px; } 94 | 95 | .luxbar-checkbox { 96 | display: none; } 97 | .luxbar-checkbox:not(:checked) ~ .luxbar-menu { 98 | overflow: hidden; 99 | height: 58px; } 100 | .luxbar-checkbox:checked ~ .luxbar-menu { 101 | transition: height 0.6s ease; 102 | height: 100vh; 103 | overflow: auto; } 104 | 105 | .dropdown { 106 | position: relative; 107 | height: auto; 108 | min-height: 58px; } 109 | .dropdown:hover > ul { 110 | position: relative; 111 | display: block; 112 | min-width: 100%; } 113 | .dropdown > a::after { 114 | position: absolute; 115 | content: ''; 116 | right: 10px; 117 | top: 25px; 118 | border-width: 5px 5px 0; 119 | border-color: transparent; 120 | border-style: solid; } 121 | .dropdown > ul { 122 | display: block; 123 | overflow-x: hidden; 124 | list-style: none; 125 | padding: 0; } 126 | .dropdown > ul .luxbar-item { 127 | min-width: 100%; 128 | height: 29px; 129 | padding: 5px 10px 5px 40px; } 130 | .dropdown > ul .luxbar-item a { 131 | min-height: 29px; 132 | line-height: 29px; 133 | padding: 0; } 134 | 135 | @media screen and (min-width: 800px) { 136 | .luxbar-navigation { 137 | flex-flow: row; 138 | justify-content: flex-end; } 139 | 140 | .luxbar-hamburger { 141 | display: none; } 142 | 143 | .luxbar-checkbox:not(:checked) ~ .luxbar-menu { 144 | overflow: visible; } 145 | .luxbar-checkbox:checked ~ .luxbar-menu { 146 | height: 58px; } 147 | 148 | .luxbar-menu .luxbar-item { 149 | border-top: 0; } 150 | 151 | .luxbar-menu-right .luxbar-header { 152 | margin-right: auto; } 153 | 154 | .dropdown { 155 | height: 58px; } 156 | .dropdown:hover > ul { 157 | position: absolute; 158 | left: 0; 159 | top: 58px; 160 | padding: 0; } 161 | .dropdown > ul { 162 | display: none; } 163 | .dropdown > ul .luxbar-item { 164 | padding: 5px 10px; } 165 | .dropdown > ul .luxbar-item a { 166 | white-space: nowrap; } } 167 | .luxbar-checkbox:checked + .luxbar-menu .luxbar-hamburger-doublespin span::before { 168 | transform: rotate(225deg); } 169 | .luxbar-checkbox:checked + .luxbar-menu .luxbar-hamburger-doublespin span::after { 170 | transform: rotate(-225deg); } 171 | 172 | .luxbar-checkbox:checked + .luxbar-menu .luxbar-hamburger-spin span::before { 173 | transform: rotate(45deg); } 174 | .luxbar-checkbox:checked + .luxbar-menu .luxbar-hamburger-spin span::after { 175 | transform: rotate(-45deg); } 176 | 177 | /******* color variables *******/ 178 | /******* default dark *******/ 179 | .luxbar-menu-dark, 180 | .luxbar-menu-dark .dropdown ul { 181 | background-color: #212121; 182 | color: #fff; } 183 | 184 | .luxbar-menu-dark .active, 185 | .luxbar-menu-dark .luxbar-item:hover { 186 | background-color: #424242; } 187 | .luxbar-menu-dark .luxbar-hamburger span, 188 | .luxbar-menu-dark .luxbar-hamburger span::before, 189 | .luxbar-menu-dark .luxbar-hamburger span::after { 190 | background-color: #fff; } 191 | 192 | /******* default light *******/ 193 | .luxbar-menu-light, 194 | .luxbar-menu-light .dropdown ul { 195 | background-color: #e0e0e0; 196 | color: #212121; } 197 | 198 | .luxbar-menu-light .active, 199 | .luxbar-menu-light .luxbar-item:hover { 200 | background-color: #bdbdbd; } 201 | .luxbar-menu-light .luxbar-hamburger span, 202 | .luxbar-menu-light .luxbar-hamburger span::before, 203 | .luxbar-menu-light .luxbar-hamburger span::after { 204 | background-color: #212121; } 205 | 206 | /******* default material-red *******/ 207 | .luxbar-menu-material-red, 208 | .luxbar-menu-material-red .dropdown ul { 209 | background-color: #b71c1c; 210 | color: #fff; } 211 | 212 | .luxbar-menu-material-red .active, 213 | .luxbar-menu-material-red .luxbar-item:hover { 214 | background-color: #c62828; } 215 | .luxbar-menu-material-red .luxbar-hamburger span, 216 | .luxbar-menu-material-red .luxbar-hamburger span::before, 217 | .luxbar-menu-material-red .luxbar-hamburger span::after { 218 | background-color: #fff; } 219 | 220 | /******* default material-indigo *******/ 221 | .luxbar-menu-material-indigo, 222 | .luxbar-menu-material-indigo .dropdown ul { 223 | background-color: #1a237e; 224 | color: #fff; } 225 | 226 | .luxbar-menu-material-indigo .active, 227 | .luxbar-menu-material-indigo .luxbar-item:hover { 228 | background-color: #283593; } 229 | .luxbar-menu-material-indigo .luxbar-hamburger span, 230 | .luxbar-menu-material-indigo .luxbar-hamburger span::before, 231 | .luxbar-menu-material-indigo .luxbar-hamburger span::after { 232 | background-color: #fff; } 233 | 234 | /******* default material-green *******/ 235 | .luxbar-menu-material-green, 236 | .luxbar-menu-material-green .dropdown ul { 237 | background-color: #1b5e20; 238 | color: #fff; } 239 | 240 | .luxbar-menu-material-green .active, 241 | .luxbar-menu-material-green .luxbar-item:hover { 242 | background-color: #2e7d32; } 243 | .luxbar-menu-material-green .luxbar-hamburger span, 244 | .luxbar-menu-material-green .luxbar-hamburger span::before, 245 | .luxbar-menu-material-green .luxbar-hamburger span::after { 246 | background-color: #fff; } 247 | 248 | /******* default material-amber *******/ 249 | .luxbar-menu-material-amber, 250 | .luxbar-menu-material-amber .dropdown ul { 251 | background-color: #ff6f00; 252 | color: #fff; } 253 | 254 | .luxbar-menu-material-amber .active, 255 | .luxbar-menu-material-amber .luxbar-item:hover { 256 | background-color: #ff8f00; } 257 | .luxbar-menu-material-amber .luxbar-hamburger span, 258 | .luxbar-menu-material-amber .luxbar-hamburger span::before, 259 | .luxbar-menu-material-amber .luxbar-hamburger span::after { 260 | background-color: #fff; } 261 | 262 | /******* default material-brown *******/ 263 | .luxbar-menu-material-brown, 264 | .luxbar-menu-material-brown .dropdown ul { 265 | background-color: #3e2723; 266 | color: #fff; } 267 | 268 | .luxbar-menu-material-brown .active, 269 | .luxbar-menu-material-brown .luxbar-item:hover { 270 | background-color: #4e342e; } 271 | .luxbar-menu-material-brown .luxbar-hamburger span, 272 | .luxbar-menu-material-brown .luxbar-hamburger span::before, 273 | .luxbar-menu-material-brown .luxbar-hamburger span::after { 274 | background-color: #fff; } 275 | 276 | /******* default material-bluegrey *******/ 277 | .luxbar-menu-material-bluegrey, 278 | .luxbar-menu-material-bluegrey .dropdown ul { 279 | background-color: #263238; 280 | color: #fff; } 281 | 282 | .luxbar-menu-material-bluegrey .active, 283 | .luxbar-menu-material-bluegrey .luxbar-item:hover { 284 | background-color: #37474f; } 285 | .luxbar-menu-material-bluegrey .luxbar-hamburger span, 286 | .luxbar-menu-material-bluegrey .luxbar-hamburger span::before, 287 | .luxbar-menu-material-bluegrey .luxbar-hamburger span::after { 288 | background-color: #fff; } 289 | 290 | /******* default material-cyan *******/ 291 | .luxbar-menu-material-cyan, 292 | .luxbar-menu-material-cyan .dropdown ul { 293 | background-color: #006064; 294 | color: #fff; } 295 | 296 | .luxbar-menu-material-cyan .active, 297 | .luxbar-menu-material-cyan .luxbar-item:hover { 298 | background-color: #00838f; } 299 | .luxbar-menu-material-cyan .luxbar-hamburger span, 300 | .luxbar-menu-material-cyan .luxbar-hamburger span::before, 301 | .luxbar-menu-material-cyan .luxbar-hamburger span::after { 302 | background-color: #fff; } 303 | 304 | /*# sourceMappingURL=luxbar.css.map */ 305 | -------------------------------------------------------------------------------- /hastysite.nim: -------------------------------------------------------------------------------- 1 | import 2 | std/json, 3 | std/strutils, 4 | std/os, 5 | std/sequtils, 6 | std/tables, 7 | std/critbits, 8 | std/streams, 9 | std/parsecfg, 10 | checksums/sha1, 11 | std/logging, 12 | std/pegs 13 | 14 | when defined(linux): 15 | {.passL:"-static".} 16 | 17 | import 18 | min, 19 | hastyscribe, 20 | mustache 21 | 22 | import 23 | hastysitepkg/config 24 | 25 | type 26 | HastyDirs = object 27 | assets*: string 28 | contents*: string 29 | templates*: string 30 | output*: string 31 | temp*: string 32 | tempContents: string 33 | scripts*: string 34 | HastyFiles = object 35 | rules*: string 36 | checksums: string 37 | contents: seq[JsonNode] 38 | assets: seq[JsonNode] 39 | HastySite* = object 40 | settings*: JsonNode 41 | checksums*: JsonNode 42 | scripts*: JsonNode 43 | dirs*: HastyDirs 44 | files*: HastyFiles 45 | NoMetadataException* = ref Exception 46 | DictionaryRequiredException* = ref Exception 47 | MetadataRequiredException* = ref Exception 48 | 49 | const SCRIPT_BUILD = "./site/scripts/build.min".slurp 50 | const SCRIPT_CLEAN = "./site/scripts/clean.min".slurp 51 | const SCRIPT_POST = "./site/scripts/post.min".slurp 52 | const SCRIPT_PAGE = "./site/scripts/page.min".slurp 53 | const TEMPLATE_HEAD = "./site/templates/_head.mustache".slurp 54 | const TEMPLATE_HEADER = "./site/templates/_header.mustache".slurp 55 | const TEMPLATE_FOOTER = "./site/templates/_footer.mustache".slurp 56 | const TEMPLATE_NEWS = "./site/templates/news.mustache".slurp 57 | const TEMPLATE_PAGE = "./site/templates/page.mustache".slurp 58 | const TEMPLATE_POST = "./site/templates/post.mustache".slurp 59 | const FONT_SCP_R = "./site/assets/fonts/SourceCodePro-Regular.woff".slurp 60 | const FONT_SCP_I = "./site/assets/fonts/SourceCodePro-It.woff".slurp 61 | const FONT_SCP_B = "./site/assets/fonts/SourceCodePro-Bold.woff".slurp 62 | const FONT_SCP_BI = "./site/assets/fonts/SourceCodePro-BoldIt.woff".slurp 63 | const FONT_SSP_R = "./site/assets/fonts/SourceSansPro-Light.woff".slurp 64 | const FONT_SSP_I = "./site/assets/fonts/SourceSansPro-LightIt.woff".slurp 65 | const FONT_SSP_B = "./site/assets/fonts/SourceSansPro-Semibold.woff".slurp 66 | const FONT_SSP_BI = "./site/assets/fonts/SourceSansPro-SemiboldIt.woff".slurp 67 | const FONT_FAS = "./site/assets/fonts/fa-solid-900.woff".slurp 68 | const FONT_FAB = "./site/assets/fonts/fa-brands-400.woff".slurp 69 | const STYLE_FONTS = "./site/assets/styles/fonts.css".slurp 70 | const STYLE_HASTYSITE = "./site/assets/styles/hastysite.css".slurp 71 | const STYLE_HASTYSCRIBE = "./site/assets/styles/hastyscribe.css".slurp 72 | const STYLE_LUXBAR = "./site/assets/styles/luxbar.css".slurp 73 | const STYLE_SITE = "./site/assets/styles/site.css".slurp 74 | const RULES = "./site/rules.min".slurp 75 | 76 | var PEG_CSS_VAR_DEF {.threadvar.} : Peg 77 | PEG_CSS_VAR_DEF = peg"""'--' {[a-zA-Z0-9_-]+} ':' {@} ';'""" 78 | 79 | var PEG_CSS_VAR_INSTANCE {.threadvar.} : Peg 80 | PEG_CSS_VAR_INSTANCE = peg""" 81 | instance <- 'var(--' {id} ')' 82 | id <- [a-zA-Z0-9_-]+ 83 | """ 84 | var PEG_CSS_IMPORT {.threadvar.} : Peg 85 | PEG_CSS_IMPORT = peg""" 86 | import <- '@import' \s+ '\'' {partial} '\';' 87 | partial <- [a-zA-Z0-9_-]+ 88 | """ 89 | 90 | var CSS_VARS {.threadvar.} : Table[string, string] 91 | CSS_VARS = initTable[string, string]() 92 | 93 | #### Helper Functions 94 | 95 | proc processCssVariables(text: string): string = 96 | result = text 97 | for def in result.findAll(PEG_CSS_VAR_DEF): 98 | var matches: array[0..1, string] 99 | discard def.match(PEG_CSS_VAR_DEF, matches) 100 | let id = matches[0].strip 101 | let value = matches[1].strip 102 | CSS_VARS[id] = value 103 | for instance in result.findAll(PEG_CSS_VAR_INSTANCE): 104 | var matches: array[0..1, string] 105 | discard instance.match(PEG_CSS_VAR_INSTANCE, matches) 106 | let id = matches[0].strip 107 | if CSS_VARS.hasKey(id): 108 | result = result.replace(instance, CSS_VARS[id]) 109 | else: 110 | stderr.writeLine("CSS variable '$1' is not defined." % ["--" & id]) 111 | 112 | proc processCssImportPartials(text: string, hs: HastySite): string = 113 | result = text 114 | var folder = "assets/styles" 115 | if hs.settings.hasKey("css-partials"): 116 | folder = hs.settings["css-partials"].getStr 117 | for def in result.findAll(PEG_CSS_IMPORT): 118 | var matches: array[0..1, string] 119 | discard def.match(PEG_CSS_IMPORT, matches) 120 | let partial = folder/"_" & matches[0].strip & ".css" 121 | var contents = "" 122 | if partial.fileExists: 123 | contents = partial.readFile 124 | result = result.replace(def, contents) 125 | else: 126 | stderr.writeLine("@import: partial '$1' does not exist" % [partial]) 127 | 128 | proc preprocessContent(file, dir: string, obj: var JsonNode): string = 129 | let fileid = file.replace(dir, "") 130 | var f: File 131 | discard f.open(file) 132 | var s, cfg = "" 133 | result = "" 134 | var delimiter = 0 135 | try: 136 | while f.readLine(s): 137 | if delimiter >= 2: 138 | result &= s&"\n" 139 | else: 140 | if s.startsWith("----"): 141 | delimiter.inc 142 | else: 143 | cfg &= s&"\n" 144 | except CatchableError: 145 | discard 146 | if not obj.hasKey("contents"): 147 | obj["contents"] = newJObject() 148 | var meta = newJObject(); 149 | if delimiter < 2: 150 | result = cfg 151 | else: 152 | try: 153 | let ss = newStringStream(cfg) 154 | var p: CfgParser 155 | p.open(ss, file) 156 | while true: 157 | var e = next(p) 158 | case e.kind 159 | of cfgEof: 160 | break 161 | of cfgKeyValuePair: 162 | meta[e.key] = newJString(e.value) 163 | of cfgError: 164 | warn e.msg 165 | else: 166 | discard 167 | p.close() 168 | except CatchableError: 169 | meta = newJObject() 170 | meta["path"] = %fileid 171 | meta["id"] = %fileid.changeFileExt("") 172 | meta["ext"] = %fileid.splitFile.ext 173 | obj["contents"][fileid] = meta 174 | f.close() 175 | 176 | proc get(json: JsonNode, key, default: string): string = 177 | if json.hasKey(key): 178 | return json[key].getStr 179 | else: 180 | return default 181 | 182 | proc contentMetadata(f, dir: string, meta: JsonNode): JsonNode = 183 | result = newJObject() 184 | let fdata = f.splitFile 185 | let path = f.replace(dir & DirSep, "") 186 | if meta.hasKey("contents") and meta["contents"].hasKey(path): 187 | for key, value in meta["contents"][path].pairs: 188 | result[key] = value 189 | result["path"] = %path # source path relative to input 190 | result["type"] = %"content" 191 | result["ext"] = %fdata.ext # output extension 192 | if fdata.ext == "": 193 | result["id"] = %path 194 | else: 195 | result["id"] = %path.changeFileExt("") # output path relative to output without extension 196 | 197 | proc assetMetadata(f, dir: string): JsonNode = 198 | result = newJObject() 199 | let fdata = f.splitFile 200 | let path = f.replace(dir & DirSep, "") 201 | result["path"] = %path # source path relative to input 202 | result["type"] = %"asset" 203 | result["ext"] = %fdata.ext # output extension 204 | if fdata.ext == "": 205 | result["id"] = %path 206 | else: 207 | result["id"] = %path.changeFileExt("") # output path relative to output without extension 208 | 209 | proc hastysite_module*(i: In, hs1: HastySite) 210 | 211 | proc interpret(hs: HastySite, file: string) = 212 | var i = newMinInterpreter(file, file.parentDir) 213 | i.hastysite_module(hs) 214 | i.interpret(newFileStream(file, fmRead)) 215 | 216 | #### Main Functions 217 | 218 | proc newHastySite*(file: string): HastySite = 219 | let json = file.parseFile() 220 | result.settings = json 221 | result.dirs.assets = json.get("assets", "assets") 222 | result.dirs.contents = json.get("contents", "contents") 223 | result.dirs.templates = json.get("templates", "templates") 224 | result.dirs.output = json.get("output", "output") 225 | result.dirs.temp = json.get("temp", "temp") 226 | result.dirs.tempContents = result.dirs.temp / result.dirs.contents 227 | result.dirs.scripts = json.get("scripts", "scripts") 228 | result.files.rules = json.get("rules", "rules.min") 229 | result.files.checksums = result.dirs.temp / "checksums.json" 230 | result.scripts = newJObject() 231 | for f in result.dirs.scripts.walkDir(true): 232 | let path = result.dirs.scripts/f.path 233 | let file = path.open() 234 | let desc = file.readLine.replace(";", "") 235 | let key = f.path.replace(".min", "") 236 | file.close() 237 | result.scripts[key] = %desc 238 | 239 | proc preprocess*(hs: var HastySite) = 240 | if hs.dirs.tempContents.dirExists: 241 | hs.dirs.tempContents.removeDir 242 | var meta = newJObject() 243 | for f in hs.dirs.contents.walkDirRec(): 244 | if f.isHidden: 245 | continue 246 | info("Preprocessing: " & f); 247 | let content = f.preprocessContent(hs.dirs.contents & DirSep, meta) 248 | let dest = hs.dirs.temp/f 249 | dest.parentDir.createDir 250 | dest.writeFile(content) 251 | if not hs.dirs.temp.dirExists: 252 | hs.dirs.temp.createDir 253 | if not hs.files.checksums.fileExists: 254 | let checksums = newJObject() 255 | hs.files.checksums.writeFile(checksums.pretty) 256 | hs.checksums = hs.files.checksums.parseFile 257 | let contents = toSeq(hs.dirs.tempContents.walkDirRec()) 258 | let assets = toSeq(hs.dirs.assets.walkDirRec()) 259 | let contentDir = hs.dirs.tempContents 260 | let assetDir = hs.dirs.assets 261 | hs.files.contents = contents.map(proc (f: string): JsonNode = return contentMetadata(f, contentDir, meta)) 262 | info("Total Contents: " & $hs.files.contents.len) 263 | hs.files.assets = assets.map(proc (f: string): JsonNode = return assetMetadata(f, assetDir)) 264 | info("Total Assets: " & $hs.files.assets.len) 265 | 266 | proc init*(dir: string) = 267 | var json = newJObject() 268 | json["contents"] = %"contents" 269 | json["assets"] = %"assets" 270 | json["templates"] = %"templates" 271 | json["temp"] = %"temp" 272 | json["output"] = %"output" 273 | json["scripts"] = %"scripts" 274 | json["css-partials"] = %"assets/styles" 275 | for key, value in json.pairs: 276 | createDir(dir/value.getStr) 277 | createDir(dir/"assets/fonts") 278 | createDir(dir/"assets/styles") 279 | json["title"] = %"My Web Site" 280 | json["rules"] = %"rules.min" 281 | writeFile(dir/"rules.min", RULES) 282 | writeFile(dir/"settings.json", json.pretty) 283 | writeFile(dir/"scripts/build.min", SCRIPT_BUILD) 284 | writeFile(dir/"scripts/clean.min", SCRIPT_CLEAN) 285 | writeFile(dir/"scripts/page.min", SCRIPT_PAGE) 286 | writeFile(dir/"scripts/post.min", SCRIPT_POST) 287 | writeFile(dir/"templates/_head.mustache", TEMPLATE_HEAD) 288 | writeFile(dir/"templates/_header.mustache", TEMPLATE_HEADER) 289 | writeFile(dir/"templates/_footer.mustache", TEMPLATE_FOOTER) 290 | writeFile(dir/"templates/page.mustache", TEMPLATE_PAGE) 291 | writeFile(dir/"templates/news.mustache", TEMPLATE_NEWS) 292 | writeFile(dir/"templates/post.mustache", TEMPLATE_POST) 293 | writeFile(dir/"assets/fonts/SourceCodePro-Regular.woff", FONT_SCP_R) 294 | writeFile(dir/"assets/fonts/SourceCodePro-It.woff", FONT_SCP_I) 295 | writeFile(dir/"assets/fonts/SourceCodePro-Bold.woff", FONT_SCP_B) 296 | writeFile(dir/"assets/fonts/SourceCodePro-BoldIt.woff", FONT_SCP_BI) 297 | writeFile(dir/"assets/fonts/SourceSansPro-Light.woff", FONT_SSP_R) 298 | writeFile(dir/"assets/fonts/SourceSansPro-Semibold.woff", FONT_SSP_B) 299 | writeFile(dir/"assets/fonts/SourceSansPro-LightIt.woff", FONT_SSP_I) 300 | writeFile(dir/"assets/fonts/SourceSansPro-SemiboldIt.woff", FONT_SSP_BI) 301 | writeFile(dir/"assets/fonts/fa-solid-900.woff", FONT_FAS) 302 | writeFile(dir/"assets/fonts/fa-brands-400.woff", FONT_FAB) 303 | writeFile(dir/"assets/styles/fonts.css", STYLE_FONTS) 304 | writeFile(dir/"assets/styles/hastyscribe.css", STYLE_HASTYSCRIBE) 305 | writeFile(dir/"assets/styles/hastysite.css", STYLE_HASTYSITE) 306 | writeFile(dir/"assets/styles/luxbar.css", STYLE_LUXBAR) 307 | writeFile(dir/"assets/styles/site.css", STYLE_SITE) 308 | 309 | proc wasModified(hs: HastySite, sha1: string, outfile: string): bool = 310 | return (not hs.checksums.hasKey(outfile) or hs.checksums[outfile] != %sha1) 311 | 312 | proc updateSHA1(hs: HastySite, sha1: string, outfile: string) = 313 | hs.checksums[outfile] = %sha1 314 | 315 | proc postprocess(hs: HastySite) = 316 | hs.files.checksums.writeFile(hs.checksums.pretty) 317 | 318 | #### min Library 319 | 320 | proc hastysite_module*(i: In, hs1: HastySite) = 321 | var hs = hs1 322 | let def = i.define() 323 | 324 | def.symbol("preprocess") do (i: In): 325 | hs.preprocess() 326 | 327 | def.symbol("postprocess") do (i: In): 328 | hs.postprocess() 329 | 330 | def.symbol("process-rules") do (i: In): 331 | hs.interpret(hs.files.rules) 332 | 333 | def.symbol("clean-output") do (i: In): 334 | hs.dirs.output.removeDir 335 | 336 | def.symbol("clean-temp") do (i: In): 337 | hs.dirs.temp.removeDir 338 | 339 | def.symbol("settings") do (i: In): 340 | i.push i.fromJson(hs.settings) 341 | 342 | def.symbol("contents") do (i: In): 343 | var contents = newSeq[MinValue](0) 344 | debug("JSON Contents requested") 345 | for j in hs.files.contents: 346 | debug(j) 347 | contents.add i.fromJson(j) 348 | i.push contents.newVal() 349 | 350 | def.symbol("assets") do (i: In): 351 | var assets = newSeq[MinValue](0) 352 | for j in hs.files.assets: 353 | assets.add i.fromJson(j) 354 | i.push assets.newVal() 355 | 356 | def.symbol("output") do (i: In): 357 | i.push hs.dirs.output.newVal 358 | 359 | def.symbol("input-fread") do (i: In): 360 | var vals = i.expect(["dict"]) 361 | var d = vals[0] 362 | let t = i.dget(d, "type").getString 363 | let path = i.dget(d, "path").getString 364 | var contents = "" 365 | if t == "content": 366 | contents = readFile(hs.dirs.tempContents/path) 367 | else: 368 | contents = readFile(hs.dirs.assets/path) 369 | i.push contents.newVal 370 | 371 | def.symbol("output-fwrite") do (i: In): 372 | var vals = i.expect(["dict"]) 373 | var d = vals[0] 374 | let id = i.dget(d, "id").getString 375 | let ext = i.dget(d, "ext").getString 376 | var contents = "" 377 | try: 378 | contents = i.dget(d, "contents").getString 379 | except CatchableError: 380 | raise MetadataRequiredException(msg: "Metadata key 'contents' not found in dictionary.") 381 | let outname = id&ext 382 | let outfile = hs.dirs.output/outname 383 | outfile.parentDir.createDir 384 | let sha1 = $secureHash(contents) 385 | if hs.wasModified(sha1, outname): 386 | notice " - Writing file: ", outfile 387 | hs.updateSHA1(sha1, outname) 388 | writeFile(outfile, contents) 389 | 390 | def.symbol("output-cp") do (i: In): 391 | var vals = i.expect(["dict"]) 392 | var d = vals[0] 393 | let t = i.dget(d, "type").getString 394 | let path = i.dget(d, "path").getString 395 | let id = i.dget(d, "id").getString 396 | let ext = i.dget(d, "ext").getString 397 | var infile, outfile: string 398 | let outname = id&ext 399 | if t == "content": 400 | infile = hs.dirs.tempContents/path 401 | outfile = hs.dirs.output/outname 402 | else: 403 | infile = hs.dirs.assets/path 404 | outfile = hs.dirs.output/outname 405 | let sha1 = $secureHash(infile.readFile) 406 | if hs.wasModified(sha1, outname): 407 | hs.updateSHA1(sha1, outname) 408 | notice " - Copying: ", infile, " -> ", outfile 409 | outfile.parentDir.createDir 410 | copyFileWithPermissions(infile, outfile) 411 | 412 | def.symbol("preprocess-css") do (i: In): 413 | var vals = i.expect("str") 414 | let css = vals[0] 415 | var res = css.getString.processCssImportPartials(hs) 416 | res = res.processCssVariables() 417 | i.push res.newVal() 418 | 419 | def.symbol("mustache") do (i: In): 420 | var vals = i.expect(["dict", "str"]) 421 | let c = vals[0] 422 | let t = vals[1] 423 | let json = i%c 424 | let ctx = newContext(searchDirs = @[hs.dirs.templates]) 425 | for key, val in json: 426 | ctx[key] = val.castValue 427 | let tplname = t.getString & ".mustache" 428 | let tpl = readFile(hs.dirs.templates/tplname) 429 | i.push tpl.render(ctx).newval 430 | 431 | def.symbol("markdown") do (i: In): 432 | var vals = i.expect(["dict", "str"]) 433 | let c = vals[0] 434 | let t = vals[1] 435 | let options = HastyOptions(toc: false, output: "", css: "", watermark: "", fragment: true) 436 | var fields = initTable[string, string]() 437 | for key, v in c.dVal: 438 | fields[key] = $$v.val 439 | var hastyscribe = newHastyScribe(options, fields) 440 | let file = t.getString() 441 | i.push hastyscribe.compileFragment(file, hs.dirs.contents).newVal 442 | 443 | def.finalize("hastysite") 444 | 445 | 446 | when isMainModule: 447 | 448 | import 449 | parseopt 450 | 451 | if logging.getHandlers().len == 0: 452 | newNiftyLogger().addHandler() 453 | setLogFilter(lvlNotice) 454 | 455 | proc usage(scripts: bool, hs: HastySite): string = 456 | var text = """ $1 v$2 - a tiny static site generator 457 | (c) 2016-2021 Fabio Cevasco 458 | 459 | Usage: 460 | hastysite command 461 | 462 | Commands: 463 | init - Initializes a new site in the current directory. 464 | """ % [pkgName, pkgVersion] 465 | if scripts: 466 | for key, value in hs.scripts.pairs: 467 | text &= " " & key & " - " & value.getStr & "\n" 468 | text &= """ Options: 469 | -h, --help Print this help 470 | -l, --loglevel Sets the log level (one of: debug, info, notice, 471 | warn, error, fatal). Default: notice 472 | -v, --version Print the program version""" 473 | return text 474 | 475 | let pwd = getCurrentDir() 476 | let cfg = pwd/"settings.json" 477 | var hs: HastySite 478 | var scripts = false 479 | 480 | if cfg.fileExists: 481 | hs = newHastySite(cfg) 482 | scripts = true 483 | 484 | for kind, key, val in getopt(): 485 | case kind: 486 | of cmdArgument: 487 | case key: 488 | of "init": 489 | pwd.init() 490 | else: 491 | if scripts: 492 | if hs.scripts.hasKey(key): 493 | hs.interpret(hs.dirs.scripts/key & ".min") 494 | else: 495 | fatal "Script '$1' not found" % key 496 | else: 497 | fatal "This directory does not contain a valid HastySite site" 498 | of cmdLongOption, cmdShortOption: 499 | case key: 500 | of "loglevel", "l": 501 | var v = val 502 | setLogLevel(v) 503 | of "help", "h": 504 | echo usage(scripts, hs) 505 | quit(0) 506 | of "version", "v": 507 | echo pkgVersion 508 | quit(0) 509 | else: 510 | discard 511 | else: 512 | discard 513 | -------------------------------------------------------------------------------- /minpkg/vendor/aes/libaes.c: -------------------------------------------------------------------------------- 1 | /* 2 | 3 | This is an implementation of the AES algorithm, specifically ECB, CTR and CBC mode. 4 | Block size can be chosen in aes.h - available choices are AES128, AES192, AES256. 5 | 6 | The implementation is verified against the test vectors in: 7 | National Institute of Standards and Technology Special Publication 800-38A 2001 ED 8 | 9 | ECB-AES128 10 | ---------- 11 | 12 | plain-text: 13 | 6bc1bee22e409f96e93d7e117393172a 14 | ae2d8a571e03ac9c9eb76fac45af8e51 15 | 30c81c46a35ce411e5fbc1191a0a52ef 16 | f69f2445df4f9b17ad2b417be66c3710 17 | 18 | key: 19 | 2b7e151628aed2a6abf7158809cf4f3c 20 | 21 | resulting cipher 22 | 3ad77bb40d7a3660a89ecaf32466ef97 23 | f5d3d58503b9699de785895a96fdbaaf 24 | 43b1cd7f598ece23881b00e3ed030688 25 | 7b0c785e27e8ad3f8223207104725dd4 26 | 27 | 28 | NOTE: String length must be evenly divisible by 16byte (str_len % 16 == 0) 29 | You should pad the end of the string with zeros if this is not the case. 30 | For AES192/256 the key size is proportionally larger. 31 | 32 | */ 33 | 34 | 35 | /*****************************************************************************/ 36 | /* Includes: */ 37 | /*****************************************************************************/ 38 | #include 39 | #include // CBC mode, for memset 40 | #include "aes.h" 41 | 42 | /*****************************************************************************/ 43 | /* Defines: */ 44 | /*****************************************************************************/ 45 | // The number of columns comprising a state in AES. This is a constant in AES. Value=4 46 | #define Nb 4 47 | 48 | #if defined(AES256) && (AES256 == 1) 49 | #define Nk 8 50 | #define Nr 14 51 | #elif defined(AES192) && (AES192 == 1) 52 | #define Nk 6 53 | #define Nr 12 54 | #else 55 | #define Nk 4 // The number of 32 bit words in a key. 56 | #define Nr 10 // The number of rounds in AES Cipher. 57 | #endif 58 | 59 | // jcallan@github points out that declaring Multiply as a function 60 | // reduces code size considerably with the Keil ARM compiler. 61 | // See this link for more information: https://github.com/kokke/tiny-AES-C/pull/3 62 | #ifndef MULTIPLY_AS_A_FUNCTION 63 | #define MULTIPLY_AS_A_FUNCTION 0 64 | #endif 65 | 66 | 67 | 68 | 69 | /*****************************************************************************/ 70 | /* Private variables: */ 71 | /*****************************************************************************/ 72 | // state - array holding the intermediate results during decryption. 73 | typedef uint8_t state_t[4][4]; 74 | 75 | 76 | 77 | // The lookup-tables are marked const so they can be placed in read-only storage instead of RAM 78 | // The numbers below can be computed dynamically trading ROM for RAM - 79 | // This can be useful in (embedded) bootloader applications, where ROM is often limited. 80 | static const uint8_t sbox[256] = { 81 | //0 1 2 3 4 5 6 7 8 9 A B C D E F 82 | 0x63, 0x7c, 0x77, 0x7b, 0xf2, 0x6b, 0x6f, 0xc5, 0x30, 0x01, 0x67, 0x2b, 0xfe, 0xd7, 0xab, 0x76, 83 | 0xca, 0x82, 0xc9, 0x7d, 0xfa, 0x59, 0x47, 0xf0, 0xad, 0xd4, 0xa2, 0xaf, 0x9c, 0xa4, 0x72, 0xc0, 84 | 0xb7, 0xfd, 0x93, 0x26, 0x36, 0x3f, 0xf7, 0xcc, 0x34, 0xa5, 0xe5, 0xf1, 0x71, 0xd8, 0x31, 0x15, 85 | 0x04, 0xc7, 0x23, 0xc3, 0x18, 0x96, 0x05, 0x9a, 0x07, 0x12, 0x80, 0xe2, 0xeb, 0x27, 0xb2, 0x75, 86 | 0x09, 0x83, 0x2c, 0x1a, 0x1b, 0x6e, 0x5a, 0xa0, 0x52, 0x3b, 0xd6, 0xb3, 0x29, 0xe3, 0x2f, 0x84, 87 | 0x53, 0xd1, 0x00, 0xed, 0x20, 0xfc, 0xb1, 0x5b, 0x6a, 0xcb, 0xbe, 0x39, 0x4a, 0x4c, 0x58, 0xcf, 88 | 0xd0, 0xef, 0xaa, 0xfb, 0x43, 0x4d, 0x33, 0x85, 0x45, 0xf9, 0x02, 0x7f, 0x50, 0x3c, 0x9f, 0xa8, 89 | 0x51, 0xa3, 0x40, 0x8f, 0x92, 0x9d, 0x38, 0xf5, 0xbc, 0xb6, 0xda, 0x21, 0x10, 0xff, 0xf3, 0xd2, 90 | 0xcd, 0x0c, 0x13, 0xec, 0x5f, 0x97, 0x44, 0x17, 0xc4, 0xa7, 0x7e, 0x3d, 0x64, 0x5d, 0x19, 0x73, 91 | 0x60, 0x81, 0x4f, 0xdc, 0x22, 0x2a, 0x90, 0x88, 0x46, 0xee, 0xb8, 0x14, 0xde, 0x5e, 0x0b, 0xdb, 92 | 0xe0, 0x32, 0x3a, 0x0a, 0x49, 0x06, 0x24, 0x5c, 0xc2, 0xd3, 0xac, 0x62, 0x91, 0x95, 0xe4, 0x79, 93 | 0xe7, 0xc8, 0x37, 0x6d, 0x8d, 0xd5, 0x4e, 0xa9, 0x6c, 0x56, 0xf4, 0xea, 0x65, 0x7a, 0xae, 0x08, 94 | 0xba, 0x78, 0x25, 0x2e, 0x1c, 0xa6, 0xb4, 0xc6, 0xe8, 0xdd, 0x74, 0x1f, 0x4b, 0xbd, 0x8b, 0x8a, 95 | 0x70, 0x3e, 0xb5, 0x66, 0x48, 0x03, 0xf6, 0x0e, 0x61, 0x35, 0x57, 0xb9, 0x86, 0xc1, 0x1d, 0x9e, 96 | 0xe1, 0xf8, 0x98, 0x11, 0x69, 0xd9, 0x8e, 0x94, 0x9b, 0x1e, 0x87, 0xe9, 0xce, 0x55, 0x28, 0xdf, 97 | 0x8c, 0xa1, 0x89, 0x0d, 0xbf, 0xe6, 0x42, 0x68, 0x41, 0x99, 0x2d, 0x0f, 0xb0, 0x54, 0xbb, 0x16 }; 98 | 99 | static const uint8_t rsbox[256] = { 100 | 0x52, 0x09, 0x6a, 0xd5, 0x30, 0x36, 0xa5, 0x38, 0xbf, 0x40, 0xa3, 0x9e, 0x81, 0xf3, 0xd7, 0xfb, 101 | 0x7c, 0xe3, 0x39, 0x82, 0x9b, 0x2f, 0xff, 0x87, 0x34, 0x8e, 0x43, 0x44, 0xc4, 0xde, 0xe9, 0xcb, 102 | 0x54, 0x7b, 0x94, 0x32, 0xa6, 0xc2, 0x23, 0x3d, 0xee, 0x4c, 0x95, 0x0b, 0x42, 0xfa, 0xc3, 0x4e, 103 | 0x08, 0x2e, 0xa1, 0x66, 0x28, 0xd9, 0x24, 0xb2, 0x76, 0x5b, 0xa2, 0x49, 0x6d, 0x8b, 0xd1, 0x25, 104 | 0x72, 0xf8, 0xf6, 0x64, 0x86, 0x68, 0x98, 0x16, 0xd4, 0xa4, 0x5c, 0xcc, 0x5d, 0x65, 0xb6, 0x92, 105 | 0x6c, 0x70, 0x48, 0x50, 0xfd, 0xed, 0xb9, 0xda, 0x5e, 0x15, 0x46, 0x57, 0xa7, 0x8d, 0x9d, 0x84, 106 | 0x90, 0xd8, 0xab, 0x00, 0x8c, 0xbc, 0xd3, 0x0a, 0xf7, 0xe4, 0x58, 0x05, 0xb8, 0xb3, 0x45, 0x06, 107 | 0xd0, 0x2c, 0x1e, 0x8f, 0xca, 0x3f, 0x0f, 0x02, 0xc1, 0xaf, 0xbd, 0x03, 0x01, 0x13, 0x8a, 0x6b, 108 | 0x3a, 0x91, 0x11, 0x41, 0x4f, 0x67, 0xdc, 0xea, 0x97, 0xf2, 0xcf, 0xce, 0xf0, 0xb4, 0xe6, 0x73, 109 | 0x96, 0xac, 0x74, 0x22, 0xe7, 0xad, 0x35, 0x85, 0xe2, 0xf9, 0x37, 0xe8, 0x1c, 0x75, 0xdf, 0x6e, 110 | 0x47, 0xf1, 0x1a, 0x71, 0x1d, 0x29, 0xc5, 0x89, 0x6f, 0xb7, 0x62, 0x0e, 0xaa, 0x18, 0xbe, 0x1b, 111 | 0xfc, 0x56, 0x3e, 0x4b, 0xc6, 0xd2, 0x79, 0x20, 0x9a, 0xdb, 0xc0, 0xfe, 0x78, 0xcd, 0x5a, 0xf4, 112 | 0x1f, 0xdd, 0xa8, 0x33, 0x88, 0x07, 0xc7, 0x31, 0xb1, 0x12, 0x10, 0x59, 0x27, 0x80, 0xec, 0x5f, 113 | 0x60, 0x51, 0x7f, 0xa9, 0x19, 0xb5, 0x4a, 0x0d, 0x2d, 0xe5, 0x7a, 0x9f, 0x93, 0xc9, 0x9c, 0xef, 114 | 0xa0, 0xe0, 0x3b, 0x4d, 0xae, 0x2a, 0xf5, 0xb0, 0xc8, 0xeb, 0xbb, 0x3c, 0x83, 0x53, 0x99, 0x61, 115 | 0x17, 0x2b, 0x04, 0x7e, 0xba, 0x77, 0xd6, 0x26, 0xe1, 0x69, 0x14, 0x63, 0x55, 0x21, 0x0c, 0x7d }; 116 | 117 | // The round constant word array, Rcon[i], contains the values given by 118 | // x to the power (i-1) being powers of x (x is denoted as {02}) in the field GF(2^8) 119 | static const uint8_t Rcon[11] = { 120 | 0x8d, 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80, 0x1b, 0x36 }; 121 | 122 | /* 123 | * Jordan Goulder points out in PR #12 (https://github.com/kokke/tiny-AES-C/pull/12), 124 | * that you can remove most of the elements in the Rcon array, because they are unused. 125 | * 126 | * From Wikipedia's article on the Rijndael key schedule @ https://en.wikipedia.org/wiki/Rijndael_key_schedule#Rcon 127 | * 128 | * "Only the first some of these constants are actually used – up to rcon[10] for AES-128 (as 11 round keys are needed), 129 | * up to rcon[8] for AES-192, up to rcon[7] for AES-256. rcon[0] is not used in AES algorithm." 130 | */ 131 | 132 | 133 | /*****************************************************************************/ 134 | /* Private functions: */ 135 | /*****************************************************************************/ 136 | /* 137 | static uint8_t getSBoxValue(uint8_t num) 138 | { 139 | return sbox[num]; 140 | } 141 | */ 142 | #define getSBoxValue(num) (sbox[(num)]) 143 | /* 144 | static uint8_t getSBoxInvert(uint8_t num) 145 | { 146 | return rsbox[num]; 147 | } 148 | */ 149 | #define getSBoxInvert(num) (rsbox[(num)]) 150 | 151 | // This function produces Nb(Nr+1) round keys. The round keys are used in each round to decrypt the states. 152 | static void KeyExpansion(uint8_t* RoundKey, const uint8_t* Key) 153 | { 154 | unsigned i, j, k; 155 | uint8_t tempa[4]; // Used for the column/row operations 156 | 157 | // The first round key is the key itself. 158 | for (i = 0; i < Nk; ++i) 159 | { 160 | RoundKey[(i * 4) + 0] = Key[(i * 4) + 0]; 161 | RoundKey[(i * 4) + 1] = Key[(i * 4) + 1]; 162 | RoundKey[(i * 4) + 2] = Key[(i * 4) + 2]; 163 | RoundKey[(i * 4) + 3] = Key[(i * 4) + 3]; 164 | } 165 | 166 | // All other round keys are found from the previous round keys. 167 | for (i = Nk; i < Nb * (Nr + 1); ++i) 168 | { 169 | { 170 | k = (i - 1) * 4; 171 | tempa[0]=RoundKey[k + 0]; 172 | tempa[1]=RoundKey[k + 1]; 173 | tempa[2]=RoundKey[k + 2]; 174 | tempa[3]=RoundKey[k + 3]; 175 | 176 | } 177 | 178 | if (i % Nk == 0) 179 | { 180 | // This function shifts the 4 bytes in a word to the left once. 181 | // [a0,a1,a2,a3] becomes [a1,a2,a3,a0] 182 | 183 | // Function RotWord() 184 | { 185 | k = tempa[0]; 186 | tempa[0] = tempa[1]; 187 | tempa[1] = tempa[2]; 188 | tempa[2] = tempa[3]; 189 | tempa[3] = k; 190 | } 191 | 192 | // SubWord() is a function that takes a four-byte input word and 193 | // applies the S-box to each of the four bytes to produce an output word. 194 | 195 | // Function Subword() 196 | { 197 | tempa[0] = getSBoxValue(tempa[0]); 198 | tempa[1] = getSBoxValue(tempa[1]); 199 | tempa[2] = getSBoxValue(tempa[2]); 200 | tempa[3] = getSBoxValue(tempa[3]); 201 | } 202 | 203 | tempa[0] = tempa[0] ^ Rcon[i/Nk]; 204 | } 205 | #if defined(AES256) && (AES256 == 1) 206 | if (i % Nk == 4) 207 | { 208 | // Function Subword() 209 | { 210 | tempa[0] = getSBoxValue(tempa[0]); 211 | tempa[1] = getSBoxValue(tempa[1]); 212 | tempa[2] = getSBoxValue(tempa[2]); 213 | tempa[3] = getSBoxValue(tempa[3]); 214 | } 215 | } 216 | #endif 217 | j = i * 4; k=(i - Nk) * 4; 218 | RoundKey[j + 0] = RoundKey[k + 0] ^ tempa[0]; 219 | RoundKey[j + 1] = RoundKey[k + 1] ^ tempa[1]; 220 | RoundKey[j + 2] = RoundKey[k + 2] ^ tempa[2]; 221 | RoundKey[j + 3] = RoundKey[k + 3] ^ tempa[3]; 222 | } 223 | } 224 | 225 | void AES_init_ctx(struct AES_ctx* ctx, const uint8_t* key) 226 | { 227 | KeyExpansion(ctx->RoundKey, key); 228 | } 229 | #if (defined(CBC) && (CBC == 1)) || (defined(CTR) && (CTR == 1)) 230 | void AES_init_ctx_iv(struct AES_ctx* ctx, const uint8_t* key, const uint8_t* iv) 231 | { 232 | KeyExpansion(ctx->RoundKey, key); 233 | memcpy (ctx->Iv, iv, AES_BLOCKLEN); 234 | } 235 | void AES_ctx_set_iv(struct AES_ctx* ctx, const uint8_t* iv) 236 | { 237 | memcpy (ctx->Iv, iv, AES_BLOCKLEN); 238 | } 239 | #endif 240 | 241 | // This function adds the round key to state. 242 | // The round key is added to the state by an XOR function. 243 | static void AddRoundKey(uint8_t round,state_t* state,uint8_t* RoundKey) 244 | { 245 | uint8_t i,j; 246 | for (i = 0; i < 4; ++i) 247 | { 248 | for (j = 0; j < 4; ++j) 249 | { 250 | (*state)[i][j] ^= RoundKey[(round * Nb * 4) + (i * Nb) + j]; 251 | } 252 | } 253 | } 254 | 255 | // The SubBytes Function Substitutes the values in the 256 | // state matrix with values in an S-box. 257 | static void SubBytes(state_t* state) 258 | { 259 | uint8_t i, j; 260 | for (i = 0; i < 4; ++i) 261 | { 262 | for (j = 0; j < 4; ++j) 263 | { 264 | (*state)[j][i] = getSBoxValue((*state)[j][i]); 265 | } 266 | } 267 | } 268 | 269 | // The ShiftRows() function shifts the rows in the state to the left. 270 | // Each row is shifted with different offset. 271 | // Offset = Row number. So the first row is not shifted. 272 | static void ShiftRows(state_t* state) 273 | { 274 | uint8_t temp; 275 | 276 | // Rotate first row 1 columns to left 277 | temp = (*state)[0][1]; 278 | (*state)[0][1] = (*state)[1][1]; 279 | (*state)[1][1] = (*state)[2][1]; 280 | (*state)[2][1] = (*state)[3][1]; 281 | (*state)[3][1] = temp; 282 | 283 | // Rotate second row 2 columns to left 284 | temp = (*state)[0][2]; 285 | (*state)[0][2] = (*state)[2][2]; 286 | (*state)[2][2] = temp; 287 | 288 | temp = (*state)[1][2]; 289 | (*state)[1][2] = (*state)[3][2]; 290 | (*state)[3][2] = temp; 291 | 292 | // Rotate third row 3 columns to left 293 | temp = (*state)[0][3]; 294 | (*state)[0][3] = (*state)[3][3]; 295 | (*state)[3][3] = (*state)[2][3]; 296 | (*state)[2][3] = (*state)[1][3]; 297 | (*state)[1][3] = temp; 298 | } 299 | 300 | static uint8_t xtime(uint8_t x) 301 | { 302 | return ((x<<1) ^ (((x>>7) & 1) * 0x1b)); 303 | } 304 | 305 | // MixColumns function mixes the columns of the state matrix 306 | static void MixColumns(state_t* state) 307 | { 308 | uint8_t i; 309 | uint8_t Tmp, Tm, t; 310 | for (i = 0; i < 4; ++i) 311 | { 312 | t = (*state)[i][0]; 313 | Tmp = (*state)[i][0] ^ (*state)[i][1] ^ (*state)[i][2] ^ (*state)[i][3] ; 314 | Tm = (*state)[i][0] ^ (*state)[i][1] ; Tm = xtime(Tm); (*state)[i][0] ^= Tm ^ Tmp ; 315 | Tm = (*state)[i][1] ^ (*state)[i][2] ; Tm = xtime(Tm); (*state)[i][1] ^= Tm ^ Tmp ; 316 | Tm = (*state)[i][2] ^ (*state)[i][3] ; Tm = xtime(Tm); (*state)[i][2] ^= Tm ^ Tmp ; 317 | Tm = (*state)[i][3] ^ t ; Tm = xtime(Tm); (*state)[i][3] ^= Tm ^ Tmp ; 318 | } 319 | } 320 | 321 | // Multiply is used to multiply numbers in the field GF(2^8) 322 | #if MULTIPLY_AS_A_FUNCTION 323 | static uint8_t Multiply(uint8_t x, uint8_t y) 324 | { 325 | return (((y & 1) * x) ^ 326 | ((y>>1 & 1) * xtime(x)) ^ 327 | ((y>>2 & 1) * xtime(xtime(x))) ^ 328 | ((y>>3 & 1) * xtime(xtime(xtime(x)))) ^ 329 | ((y>>4 & 1) * xtime(xtime(xtime(xtime(x)))))); 330 | } 331 | #else 332 | #define Multiply(x, y) \ 333 | ( ((y & 1) * x) ^ \ 334 | ((y>>1 & 1) * xtime(x)) ^ \ 335 | ((y>>2 & 1) * xtime(xtime(x))) ^ \ 336 | ((y>>3 & 1) * xtime(xtime(xtime(x)))) ^ \ 337 | ((y>>4 & 1) * xtime(xtime(xtime(xtime(x)))))) \ 338 | 339 | #endif 340 | 341 | // MixColumns function mixes the columns of the state matrix. 342 | // The method used to multiply may be difficult to understand for the inexperienced. 343 | // Please use the references to gain more information. 344 | static void InvMixColumns(state_t* state) 345 | { 346 | int i; 347 | uint8_t a, b, c, d; 348 | for (i = 0; i < 4; ++i) 349 | { 350 | a = (*state)[i][0]; 351 | b = (*state)[i][1]; 352 | c = (*state)[i][2]; 353 | d = (*state)[i][3]; 354 | 355 | (*state)[i][0] = Multiply(a, 0x0e) ^ Multiply(b, 0x0b) ^ Multiply(c, 0x0d) ^ Multiply(d, 0x09); 356 | (*state)[i][1] = Multiply(a, 0x09) ^ Multiply(b, 0x0e) ^ Multiply(c, 0x0b) ^ Multiply(d, 0x0d); 357 | (*state)[i][2] = Multiply(a, 0x0d) ^ Multiply(b, 0x09) ^ Multiply(c, 0x0e) ^ Multiply(d, 0x0b); 358 | (*state)[i][3] = Multiply(a, 0x0b) ^ Multiply(b, 0x0d) ^ Multiply(c, 0x09) ^ Multiply(d, 0x0e); 359 | } 360 | } 361 | 362 | 363 | // The SubBytes Function Substitutes the values in the 364 | // state matrix with values in an S-box. 365 | static void InvSubBytes(state_t* state) 366 | { 367 | uint8_t i, j; 368 | for (i = 0; i < 4; ++i) 369 | { 370 | for (j = 0; j < 4; ++j) 371 | { 372 | (*state)[j][i] = getSBoxInvert((*state)[j][i]); 373 | } 374 | } 375 | } 376 | 377 | static void InvShiftRows(state_t* state) 378 | { 379 | uint8_t temp; 380 | 381 | // Rotate first row 1 columns to right 382 | temp = (*state)[3][1]; 383 | (*state)[3][1] = (*state)[2][1]; 384 | (*state)[2][1] = (*state)[1][1]; 385 | (*state)[1][1] = (*state)[0][1]; 386 | (*state)[0][1] = temp; 387 | 388 | // Rotate second row 2 columns to right 389 | temp = (*state)[0][2]; 390 | (*state)[0][2] = (*state)[2][2]; 391 | (*state)[2][2] = temp; 392 | 393 | temp = (*state)[1][2]; 394 | (*state)[1][2] = (*state)[3][2]; 395 | (*state)[3][2] = temp; 396 | 397 | // Rotate third row 3 columns to right 398 | temp = (*state)[0][3]; 399 | (*state)[0][3] = (*state)[1][3]; 400 | (*state)[1][3] = (*state)[2][3]; 401 | (*state)[2][3] = (*state)[3][3]; 402 | (*state)[3][3] = temp; 403 | } 404 | 405 | 406 | // Cipher is the main function that encrypts the PlainText. 407 | static void Cipher(state_t* state, uint8_t* RoundKey) 408 | { 409 | uint8_t round = 0; 410 | 411 | // Add the First round key to the state before starting the rounds. 412 | AddRoundKey(0, state, RoundKey); 413 | 414 | // There will be Nr rounds. 415 | // The first Nr-1 rounds are identical. 416 | // These Nr-1 rounds are executed in the loop below. 417 | for (round = 1; round < Nr; ++round) 418 | { 419 | SubBytes(state); 420 | ShiftRows(state); 421 | MixColumns(state); 422 | AddRoundKey(round, state, RoundKey); 423 | } 424 | 425 | // The last round is given below. 426 | // The MixColumns function is not here in the last round. 427 | SubBytes(state); 428 | ShiftRows(state); 429 | AddRoundKey(Nr, state, RoundKey); 430 | } 431 | 432 | static void InvCipher(state_t* state,uint8_t* RoundKey) 433 | { 434 | uint8_t round = 0; 435 | 436 | // Add the First round key to the state before starting the rounds. 437 | AddRoundKey(Nr, state, RoundKey); 438 | 439 | // There will be Nr rounds. 440 | // The first Nr-1 rounds are identical. 441 | // These Nr-1 rounds are executed in the loop below. 442 | for (round = (Nr - 1); round > 0; --round) 443 | { 444 | InvShiftRows(state); 445 | InvSubBytes(state); 446 | AddRoundKey(round, state, RoundKey); 447 | InvMixColumns(state); 448 | } 449 | 450 | // The last round is given below. 451 | // The MixColumns function is not here in the last round. 452 | InvShiftRows(state); 453 | InvSubBytes(state); 454 | AddRoundKey(0, state, RoundKey); 455 | } 456 | 457 | 458 | /*****************************************************************************/ 459 | /* Public functions: */ 460 | /*****************************************************************************/ 461 | #if defined(ECB) && (ECB == 1) 462 | 463 | 464 | void AES_ECB_encrypt(struct AES_ctx *ctx,const uint8_t* buf) 465 | { 466 | // The next function call encrypts the PlainText with the Key using AES algorithm. 467 | Cipher((state_t*)buf, ctx->RoundKey); 468 | } 469 | 470 | void AES_ECB_decrypt(struct AES_ctx* ctx,const uint8_t* buf) 471 | { 472 | // The next function call decrypts the PlainText with the Key using AES algorithm. 473 | InvCipher((state_t*)buf, ctx->RoundKey); 474 | } 475 | 476 | 477 | #endif // #if defined(ECB) && (ECB == 1) 478 | 479 | 480 | 481 | 482 | 483 | #if defined(CBC) && (CBC == 1) 484 | 485 | 486 | static void XorWithIv(uint8_t* buf, uint8_t* Iv) 487 | { 488 | uint8_t i; 489 | for (i = 0; i < AES_BLOCKLEN; ++i) // The block in AES is always 128bit no matter the key size 490 | { 491 | buf[i] ^= Iv[i]; 492 | } 493 | } 494 | 495 | void AES_CBC_encrypt_buffer(struct AES_ctx *ctx,uint8_t* buf, uint32_t length) 496 | { 497 | uintptr_t i; 498 | uint8_t *Iv = ctx->Iv; 499 | for (i = 0; i < length; i += AES_BLOCKLEN) 500 | { 501 | XorWithIv(buf, Iv); 502 | Cipher((state_t*)buf, ctx->RoundKey); 503 | Iv = buf; 504 | buf += AES_BLOCKLEN; 505 | //printf("Step %d - %d", i/16, i); 506 | } 507 | /* store Iv in ctx for next call */ 508 | memcpy(ctx->Iv, Iv, AES_BLOCKLEN); 509 | } 510 | 511 | void AES_CBC_decrypt_buffer(struct AES_ctx* ctx, uint8_t* buf, uint32_t length) 512 | { 513 | uintptr_t i; 514 | uint8_t storeNextIv[AES_BLOCKLEN]; 515 | for (i = 0; i < length; i += AES_BLOCKLEN) 516 | { 517 | memcpy(storeNextIv, buf, AES_BLOCKLEN); 518 | InvCipher((state_t*)buf, ctx->RoundKey); 519 | XorWithIv(buf, ctx->Iv); 520 | memcpy(ctx->Iv, storeNextIv, AES_BLOCKLEN); 521 | buf += AES_BLOCKLEN; 522 | } 523 | 524 | } 525 | 526 | #endif // #if defined(CBC) && (CBC == 1) 527 | 528 | 529 | 530 | #if defined(CTR) && (CTR == 1) 531 | 532 | /* Symmetrical operation: same function for encrypting as for decrypting. Note any IV/nonce should never be reused with the same key */ 533 | void AES_CTR_xcrypt_buffer(struct AES_ctx* ctx, uint8_t* buf, uint32_t length) 534 | { 535 | uint8_t buffer[AES_BLOCKLEN]; 536 | 537 | unsigned i; 538 | int bi; 539 | for (i = 0, bi = AES_BLOCKLEN; i < length; ++i, ++bi) 540 | { 541 | if (bi == AES_BLOCKLEN) /* we need to regen xor compliment in buffer */ 542 | { 543 | 544 | memcpy(buffer, ctx->Iv, AES_BLOCKLEN); 545 | Cipher((state_t*)buffer,ctx->RoundKey); 546 | 547 | /* Increment Iv and handle overflow */ 548 | for (bi = (AES_BLOCKLEN - 1); bi >= 0; --bi) 549 | { 550 | /* inc will owerflow */ 551 | if (ctx->Iv[bi] == 255) 552 | { 553 | ctx->Iv[bi] = 0; 554 | continue; 555 | } 556 | ctx->Iv[bi] += 1; 557 | break; 558 | } 559 | bi = 0; 560 | } 561 | 562 | buf[i] = (buf[i] ^ buffer[bi]); 563 | } 564 | } 565 | 566 | #endif // #if defined(CTR) && (CTR == 1) 567 | 568 | --------------------------------------------------------------------------------