line 1
\n\nline 2
' - local c2=$'line 1
\nline 2
' - # shellcheck disable=SC2235 # must enforce order of operations - [[ "$m1" == "$c1" ]] || [[ "$m2" == "$c2" ]] - else - return 1 - fi -} - - -# Parse a Markdown file into HTML and return the generated file -mrkdwn() { - out=${1%.md}.html - while [[ -f $out ]]; do out=${out%.html}.$RANDOM.html; done - $markdown_bin "$1" > "$out" - echo "$out" -} - -# Reads HTML file from stdin, prints its content to stdout -# $1 where to start ("text" or "entry") -# $2 where to stop ("text" or "entry") -# $3 "cut" to remove text from$template_tags_line_header/ ) print
- }
- }
- }"
-}
-
-# Edit an existing, published .html file while keeping its original timestamp
-# Please note that this function does not automatically republish anything, as
-# it is usually called from 'main'.
-#
-# Note that it edits HTML file, even if you wrote the post as markdown originally
-# Note that if you edit title then filename might also change
-#
-# $1 the file to edit
-# $2 (optional) edit mode:
-# "keep" to keep old filename
-# "full" to edit full HTML, and not only text part (keeps old filename)
-# leave empty for default behavior (edit only text part and change name)
-edit() {
- [[ ! -f "${1%%.*}.html" ]] && \
- printf "Can't edit post \"%s.html\", did you mean to use \"bb.sh post $template_tags_line_header/s|\\1|\\1|g" >> "$TMPFILE"
- $EDITOR "$TMPFILE"
- filename=$1
- fi
- rm "$filename"
- if [[ $2 == keep ]]; then
- parse_file "$TMPFILE" "$edit_timestamp" "$filename"
- else
- parse_file "$TMPFILE" "$edit_timestamp" # this command sets $filename as the html processed file
- [[ ${1##*.} == md ]] && mv "$1" "${filename%%.*}.md" 2>/dev/null
- fi
- rm "$TMPFILE"
- fi
- touch -t "$touch_timestamp" "$filename"
- touch -t "$touch_timestamp" "$1"
- chmod 644 "$filename"
- echo "Posted $filename"
- tags_after=$(tags_in_post "$filename")
- relevant_tags=$(echo "$tags_before $tags_after" | tr ',' ' ' | tr ' ' '\n' | sort -u | tr '\n' ' ')
- if [[ -n $relevant_tags ]]; then
- # shellcheck disable=SC2086 # Intended splitting of $relevant_tags
- relevant_posts="$(posts_with_tags $relevant_tags) $filename"
- rebuild_tags "$relevant_posts" "$relevant_tags"
- fi
-}
-
-# Create a Twitter summary (twitter "card") for the post
-#
-# $1 the post file
-# $2 the title
-twitter_card() {
- [[ -z $global_twitter_username ]] && return
-
- echo ""
- echo ""
- echo "" # Twitter truncates at 70 char
- description=$(grep -v "^ $template_tags_line_header" "$1" | sed -e 's/<[^>]*>//g' | head -c 250 | tr '\n' ' ' | sed "s/\"/'/g")
- echo ""
- image=$(sed -n 's/.* $template_comments $template_twitter_button "
- echo " $template_comments ";
- fi
-
- echo "$template_twitter_button "
- echo " 's on the title because of markdown conversion
- title=${title// /}
- title=${title//<\/p>/}
- echo "$title"
- echo ' and $template_tags_line_header"* ]]; then
- tags=$(echo "$line" | cut -d ":" -f 2- | sed -e 's/<\/p>//g' -e 's/^ *//' -e 's/ *$//' -e 's/, /,/g')
- IFS=, read -r -a array <<< "$tags"
-
- echo -n " $template_tags_line_header " >> "$content"
- for item in "${array[@]}"; do
- echo -n "$item, "
- done | sed 's/, $/<\/p>/g' >> "$content"
- else
- echo "$line" >> "$content"
- fi
- done < "$1"
-
- # Create the actual html page
- create_html_page "$content" "$filename" no "$title" "$2" "$global_author"
- rm "$content"
-}
-
-# Manages the creation of the text file and the parsing to html file
-# also the drafts
-write_entry() {
- test_markdown && fmt=md || fmt=html
- f=$2
- [[ $2 == -html ]] && fmt=html && f=$3
-
- if [[ -n $f ]]; then
- TMPFILE=$f
- if [[ ! -f $TMPFILE ]]; then
- echo "The file doesn't exist"
- delete_includes
- exit
- fi
- # guess format from TMPFILE
- extension=${TMPFILE##*.}
- [[ $extension == md || $extension == html ]] && fmt=$extension
- # but let user override it (`bb.sh post -html file.md`)
- [[ $2 == -html ]] && fmt=html
- # Test if Markdown is working before re-posting a .md file
- if [[ $extension == md ]]; then
- if test_markdown; then
- echo "Markdown is not working, please edit HTML file directly."
- exit
- fi
- fi
- else
- TMPFILE=.entry-$RANDOM.$fmt
- echo -e "Title on this line\n" >> "$TMPFILE"
-
- [[ $fmt == html ]] && cat << EOF >> "$TMPFILE"
- The rest of the text file is an html blog post. The process will continue as soon
-as you exit your editor. $template_tags_line_header keep-this-tag-format, tags-are-optional, example $template_tags_line_header/{s/^ $template_tags_line_header//;s/<[^>]*>//g;s/[ ,]\+/ /g;p;}" "$1" | tr ', ' ' '
-}
-
-# Finds all posts referenced in a number of tags.
-# Arguments are tags
-# Prints one line with space-separated tags to stdout
-posts_with_tags() {
- (($# < 1)) && return
- set -- "${@/#/$prefix_tags}"
- set -- "${@/%/.html}"
- sed -n '/^ 's on the title because of markdown conversion
+ title=${title// /}
+ title=${title//<\/p>/}
+ echo "$title"
+ echo ' $template_tags_line_header/s|\\1|\\1|g" >> "$TMPFILE"
+ $EDITOR "$TMPFILE"
+ filename=$1
+ fi
+ rm "$filename"
+ if [[ $2 == keep ]]; then
+ parse_file "$TMPFILE" "$edit_timestamp" "$filename"
+ else
+ parse_file "$TMPFILE" "$edit_timestamp" # this command sets $filename as the html processed file
+ [[ ${1##*.} == md ]] && mv "$1" "${filename%%.*}.md" 2>/dev/null
+ fi
+ rm "$TMPFILE"
+ fi
+ touch -t "$touch_timestamp" "$filename"
+ touch -t "$touch_timestamp" "$1"
+ chmod 644 "$filename"
+ echo "Posted $filename"
+ tags_after=$(tags_in_post "$filename")
+ relevant_tags=$(echo "$tags_before $tags_after" | tr ',' ' ' | tr ' ' '\n' | sort -u | tr '\n' ' ')
+ if [[ -n $relevant_tags ]]; then
+ # shellcheck disable=SC2086 # Intended splitting of $relevant_tags
+ relevant_posts="$(posts_with_tags $relevant_tags) $filename"
+ rebuild_tags "$relevant_posts" "$relevant_tags"
+ fi
+}
diff --git a/lib/get_html_file_content.sh b/lib/get_html_file_content.sh
new file mode 100644
index 0000000..28cf6b3
--- /dev/null
+++ b/lib/get_html_file_content.sh
@@ -0,0 +1,21 @@
+#!/usr/bin/env bash
+
+# Reads HTML file from stdin, prints its content to stdout
+# $1 where to start ("text" or "entry")
+# $2 where to stop ("text" or "entry")
+# $3 "cut" to remove text from $template_tags_line_header/ ) print
+ }
+ }
+ }"
+}
\ No newline at end of file
diff --git a/lib/get_post_author.sh b/lib/get_post_author.sh
new file mode 100644
index 0000000..a086bd9
--- /dev/null
+++ b/lib/get_post_author.sh
@@ -0,0 +1,8 @@
+#!/usr/bin/env bash
+
+# Return the post author
+#
+# $1 the html file
+get_post_author() {
+ awk '/ and $template_tags_line_header"* ]]; then
+ tags=$(echo "$line" | cut -d ":" -f 2- | sed -e 's/<\/p>//g' -e 's/^ *//' -e 's/ *$//' -e 's/, /,/g')
+ IFS=, read -r -a array <<< "$tags"
+
+ echo -n " $template_tags_line_header " >> "$content"
+ for item in "${array[@]}"; do
+ echo -n "$item, "
+ done | sed 's/, $/<\/p>/g' >> "$content"
+ else
+ echo "$line" >> "$content"
+ fi
+ done < "$1"
+
+ # Create the actual html page
+ create_html_page "$content" "$filename" no "$title" "$2" "$global_author"
+ rm "$content"
+}
\ No newline at end of file
diff --git a/lib/posts_with_tags.sh b/lib/posts_with_tags.sh
new file mode 100644
index 0000000..9dd90c2
--- /dev/null
+++ b/lib/posts_with_tags.sh
@@ -0,0 +1,11 @@
+#!/usr/bin/env bash
+
+# Finds all posts referenced in a number of tags.
+# Arguments are tags
+# Prints one line with space-separated tags to stdout
+posts_with_tags() {
+ (($# < 1)) && return
+ set -- "${@/#/$prefix_tags}"
+ set -- "${@/%/.html}"
+ sed -n '/^ $template_tags_line_header/{s/^ $template_tags_line_header//;s/<[^>]*>//g;s/[ ,]\+/ /g;p;}" "$1" | tr ', ' ' '
+}
\ No newline at end of file
diff --git a/lib/test_markdown.sh b/lib/test_markdown.sh
new file mode 100644
index 0000000..1ad49ef
--- /dev/null
+++ b/lib/test_markdown.sh
@@ -0,0 +1,16 @@
+#!/usr/bin/env bash
+
+# Test if the markdown script is working correctly
+test_markdown() {
+ declare markdown_bin
+ if [[ -n $markdown_bin ]]; then
+ local m1; m1=$("$markdown_bin" <<< "$'line 1\n\nline 2'")
+ local m2; m2=$("$markdown_bin" <<< "$'line 1\n\nline 2'")
+ local c1=$' line 1 line 2 line 1 line 2 $template_tags_line_header" "$1" | sed -e 's/<[^>]*>//g' | head -c 250 | tr '\n' ' ' | sed "s/\"/'/g")
+ echo ""
+ image=$(sed -n 's/.* $template_comments $template_twitter_button "
+ echo " $template_comments ";
+ fi
+
+ echo "$template_twitter_button "
+ echo " The rest of the text file is an html blog post. The process will continue as soon
+as you exit your editor. $template_tags_line_header keep-this-tag-format, tags-are-optional, example"
- # remove possible
'
- if [[ -z $timestamp ]]; then
- echo ""
- else
- echo ""
- fi
- if [[ -z $timestamp ]]; then
- echo -n "$template_archive_title
"
- prev_month=""
- while IFS='' read -r i; do
- is_boilerplate_file "$i" && continue
- echo -n "." 1>&3
- # Month headers
- month=$(LC_ALL=$date_locale date -r "$i" +"$date_allposts_header")
- if [[ $month != "$prev_month" ]]; then
- [[ -n $prev_month ]] && echo "" # Don't close ul before first header
- echo "$month
"
- echo ""
- prev_month=$month
- fi
- # Title
- title=$(get_post_title "$i")
- echo -n "
"
- echo ""
- } 3>&1 >"$contentfile"
-
- create_html_page "$contentfile" "$archive_index.tmp" yes "$global_title — $template_archive_title" "$global_author"
- mv "$archive_index.tmp" "$archive_index"
- chmod 644 "$archive_index"
- rm "$contentfile"
-}
-
-# Create an index page with all the tags
-all_tags() {
- echo -n "Creating an index page with all the tags "
- contentfile=$tags_index.$RANDOM
- while [[ -f $contentfile ]]; do
- contentfile=$tags_index.$RANDOM
- done
-
- {
- echo "$template_tags_title
"
- echo ""
- # shellcheck disable=SC2231 # Intended splitting of $prefix_tags
- for i in $prefix_tags*.html; do
- [[ -f "$i" ]] || break
- echo -n "." 1>&3
- nposts=$(grep -c "<\!-- text begin -->" "$i")
- tagname=${i#"$prefix_tags"}
- tagname=${tagname%.html}
- case $nposts in
- 1) word=$template_tags_posts_singular;;
- 2|3|4) word=$template_tags_posts_2_4;;
- *) word=$template_tags_posts;;
- esac
- echo "
"
- echo ""
- } 3>&1 > "$contentfile"
-
- create_html_page "$contentfile" "$tags_index.tmp" yes "$global_title — $template_tags_title" "$global_author"
- mv "$tags_index.tmp" "$tags_index"
- chmod 644 "$tags_index"
- rm "$contentfile"
-}
-
-# Generate the index.html with the content of the latest posts
-rebuild_index() {
- echo -n "Rebuilding the index "
- newindexfile=$index_file.$RANDOM
- contentfile=$newindexfile.content
- while [[ -f $newindexfile ]]; do
- newindexfile=$index_file.$RANDOM
- contentfile=$newindexfile.content
- done
-
- # Create the content file
- {
- n=0
- while IFS='' read -r i; do
- is_boilerplate_file "$i" && continue;
- if ((n >= number_of_index_articles)); then break; fi
- if [[ -n $cut_do ]]; then
- get_html_file_content 'entry' 'entry' 'cut' <"$i" | awk "/$cut_line/ { print \"\" ; next } 1"
- else
- get_html_file_content 'entry' 'entry' <"$i"
- fi
- echo -n "." 1>&3
- n=$(( n + 1 ))
- done < <(ls -t ./*.html) # sort by date, newest first
-
- feed=$blog_feed
- if [[ -n $global_feedburner ]]; then feed=$global_feedburner; fi
- echo ""
- } 3>&1 >"$contentfile"
-
- echo ""
-
- create_html_page "$contentfile" "$newindexfile" yes "$global_title" "$global_author"
- rm "$contentfile"
- mv "$newindexfile" "$index_file"
- chmod 644 "$index_file"
-}
-
-# Finds all tags referenced in one post.
-# Accepts either filename as first argument, or post content at stdin
-# Prints one line with space-separated tags to stdout
-tags_in_post() {
- sed -n "/^/{s/.*href="\([^"]*\)">.*/\1/;p;}' "$@" 2> /dev/null
-}
-
-# Rebuilds tag_*.html files
-# if no arguments given, rebuilds all of them
-# if arguments given, they should have this format:
-# "FILE1 [FILE2 [...]]" "TAG1 [TAG2 [...]]"
-# where FILEn are files with posts which should be used for rebuilding tags,
-# and TAGn are names of tags which should be rebuilt.
-# example:
-# rebuild_tags "one_post.html another_article.html" "example-tag another-tag"
-# mind the quotes!
-rebuild_tags() {
- if (($# < 2)); then
- # will process all files and tags
- files=$(ls -t ./*.html)
- all_tags=yes
- else
- # will process only given files and tags
- files=$(printf '%s\n' "$1" | sort -u)
- # shellcheck disable=SC2086 # Intended splitting of $files
- files=$(ls -t $files)
- tags=$2
- fi
- echo -n "Rebuilding tag pages "
- n=0
- if [[ -n $all_tags ]]; then
- rm ./"$prefix_tags"*.html &> /dev/null
- else
- for i in $tags; do
- rm "./$prefix_tags$i.html" &> /dev/null
- done
- fi
- # First we will process all files and create temporal tag files
- # with just the content of the posts
- tmpfile=tmp.$RANDOM
- while [[ -f $tmpfile ]]; do tmpfile=tmp.$RANDOM; done
- while IFS='' read -r i; do
- is_boilerplate_file "$i" && continue;
- echo -n "."
- if [[ -n $cut_do ]]; then
- get_html_file_content 'entry' 'entry' 'cut' <"$i" | awk "/$cut_line/ { print \"\" ; next } 1"
- else
- get_html_file_content 'entry' 'entry' <"$i"
- fi >"$tmpfile"
- for tag in $(tags_in_post "$i"); do
- if [[ -n $all_tags || " $tags " == *" $tag "* ]]; then
- cat "$tmpfile" >> "$prefix_tags$tag".tmp.html
- fi
- done
- done <<< "$files"
- rm "$tmpfile"
- # Now generate the tag files with headers, footers, etc
- while IFS='' read -r i; do
- tagname=${i#./"$prefix_tags"}
- tagname=${tagname%.tmp.html}
- create_html_page "$i" "$prefix_tags$tagname.html" yes "$global_title — $template_tag_title \"$tagname\"" "$global_author"
- rm "$i"
- done < <(ls -t ./"$prefix_tags"*.tmp.html 2>/dev/null)
- echo
-}
-
-# Return the post title
-#
-# $1 the html file
-get_post_title() {
- awk '/
/, /<\/a><\/h3>/{if (!/
/ && !/<\/a><\/h3>/) print}' "$1"
-}
-
-# Return the post author
-#
-# $1 the html file
-get_post_author() {
- awk '/
$global_title
"
- echo "$template_archive_title
"
+ prev_month=""
+ while IFS='' read -r i; do
+ is_boilerplate_file "$i" && continue
+ echo -n "." 1>&3
+ # Month headers
+ month=$(LC_ALL=$date_locale date -r "$i" +"$date_allposts_header")
+ if [[ $month != "$prev_month" ]]; then
+ [[ -n $prev_month ]] && echo "" # Don't close ul before first header
+ echo "$month
"
+ echo ""
+ prev_month=$month
+ fi
+ # Title
+ title=$(get_post_title "$i")
+ echo -n "
"
+ echo ""
+ } 3>&1 >"$contentfile"
+
+ create_html_page "$contentfile" "$archive_index.tmp" yes "$global_title — $template_archive_title" "$global_author"
+ mv "$archive_index.tmp" "$archive_index"
+ chmod 644 "$archive_index"
+ rm "$contentfile"
+}
\ No newline at end of file
diff --git a/lib/all_tags.sh b/lib/all_tags.sh
new file mode 100644
index 0000000..0090c17
--- /dev/null
+++ b/lib/all_tags.sh
@@ -0,0 +1,47 @@
+#!/usr/bin/env bash
+
+# Create an index page with all the tags
+all_tags() {
+ declare tags_index
+ declare template_tags_posts
+ declare template_tags_posts_singular
+ declare template_tags_posts_2_4
+ declare template_tags_title
+ declare prefix_tags
+ declare index_file
+ declare template_archive_index_page
+ declare global_author
+ declare global_title
+ echo -n "Creating an index page with all the tags "
+ contentfile=$tags_index.$RANDOM
+ while [[ -f $contentfile ]]; do
+ contentfile=$tags_index.$RANDOM
+ done
+
+ {
+ echo "$template_tags_title
"
+ echo ""
+ # shellcheck disable=SC2231 # Intended splitting of $prefix_tags
+ for i in $prefix_tags*.html; do
+ [[ -f "$i" ]] || break
+ echo -n "." 1>&3
+ nposts=$(grep -c "<\!-- text begin -->" "$i")
+ tagname=${i#"$prefix_tags"}
+ tagname=${tagname%.html}
+ case $nposts in
+ 1) word=$template_tags_posts_singular;;
+ 2|3|4) word=$template_tags_posts_2_4;;
+ *) word=$template_tags_posts;;
+ esac
+ echo "
"
+ echo ""
+ } 3>&1 > "$contentfile"
+
+ create_html_page "$contentfile" "$tags_index.tmp" yes "$global_title — $template_tags_title" "$global_author"
+ mv "$tags_index.tmp" "$tags_index"
+ chmod 644 "$tags_index"
+ rm "$contentfile"
+}
\ No newline at end of file
diff --git a/lib/create_css.sh b/lib/create_css.sh
new file mode 100644
index 0000000..506f6c6
--- /dev/null
+++ b/lib/create_css.sh
@@ -0,0 +1,48 @@
+#!/usr/bin/env bash
+
+# Create the css file from scratch
+create_css() {
+ # To avoid overwriting manual changes. However it is recommended that
+ # this function is modified if the user changes the blog.css file
+ (( ${#css_include[@]} > 0 )) && return || css_include=('main.css' 'blog.css')
+ if [[ ! -f blog.css ]]; then
+ # blog.css directives will be loaded after main.css and thus will prevail
+ echo '#title{font-size: x-large;}
+ a.ablack{color:black !important;}
+ li{margin-bottom:8px;}
+ ul,ol{margin-left:24px;margin-right:24px;}
+ #all_posts{margin-top:24px;text-align:center;}
+ .subtitle{font-size:small;margin:12px 0px;}
+ .content p{margin-left:24px;margin-right:24px;}
+ h1{margin-bottom:12px !important;}
+ #description{font-size:large;margin-bottom:12px;}
+ h3{margin-top:42px;margin-bottom:8px;}
+ h4{margin-left:24px;margin-right:24px;}
+ img{max-width:100%;}
+ #twitter{line-height:20px;vertical-align:top;text-align:right;font-style:italic;color:#333;margin-top:24px;font-size:14px;}' > blog.css
+ fi
+
+ # If there is a style.css from the parent page (i.e. some landing page)
+ # then use it. This directive is here for compatibility with my own
+ # home page. Feel free to edit it out, though it doesn't hurt
+ if [[ -f ../style.css ]] && [[ ! -f main.css ]]; then
+ ln -s "../style.css" "main.css"
+ elif [[ ! -f main.css ]]; then
+ echo 'body{font-family:Georgia,"Times New Roman",Times,serif;margin:0;padding:0;background-color:#F3F3F3;}
+ #divbodyholder{padding:5px;background-color:#DDD;width:100%;max-width:874px;margin:24px auto;}
+ #divbody{border:solid 1px #ccc;background-color:#fff;padding:0px 48px 24px 48px;top:0;}
+ .headerholder{background-color:#f9f9f9;border-top:solid 1px #ccc;border-left:solid 1px #ccc;border-right:solid 1px #ccc;}
+ .header{width:100%;max-width:800px;margin:0px auto;padding-top:24px;padding-bottom:8px;}
+ .content{margin-bottom:5%;}
+ .nomargin{margin:0;}
+ .description{margin-top:10px;border-top:solid 1px #666;padding:10px 0;}
+ h3{font-size:20pt;width:100%;font-weight:bold;margin-top:32px;margin-bottom:0;}
+ .clear{clear:both;}
+ #footer{padding-top:10px;border-top:solid 1px #666;color:#333333;text-align:center;font-size:small;font-family:"Courier New","Courier",monospace;}
+ a{text-decoration:none;color:#003366 !important;}
+ a:visited{text-decoration:none;color:#336699 !important;}
+ blockquote{background-color:#f9f9f9;border-left:solid 4px #e9e9e9;margin-left:12px;padding:12px 12px 12px 24px;}
+ blockquote img{margin:12px 0px;}
+ blockquote iframe{margin:12px 0px;}' > main.css
+ fi
+}
\ No newline at end of file
diff --git a/lib/create_html_page.sh b/lib/create_html_page.sh
new file mode 100644
index 0000000..51759bd
--- /dev/null
+++ b/lib/create_html_page.sh
@@ -0,0 +1,91 @@
+#!/usr/bin/env bash
+
+# Adds all the bells and whistles to format the html page
+# Every blog post is marked with a and
+# which is parsed afterwards in the other functions. There is also a marker
+# to determine just the beginning of the text body of the post
+#
+# $1 a file with the body of the content
+# $2 the output file
+# $3 "yes" if we want to generate the index.html,
+# "no" to insert new blog posts
+# $4 title for the html header
+# $5 original blog timestamp
+# $6 post author
+create_html_page() {
+ declare body_begin_file
+ declare date_inpost
+ declare date_locale
+ declare date_format
+ declare date_format_timestamp
+ declare global_url
+ declare body_end_file
+ content=$1
+ filename=$2
+ index=$3
+ title=$4
+ timestamp=$5
+ author=$6
+
+ # Create the actual blog post
+ # html, head
+ {
+ cat ".header.html"
+ echo ""
+ # remove possible
'
+ if [[ -z $timestamp ]]; then
+ echo ""
+ else
+ echo ""
+ fi
+ if [[ -z $timestamp ]]; then
+ echo -n "$global_title
"
+ echo "
to
+# note that this does not remove
line itself,
+# so you can see if text was cut or not
+get_html_file_content() {
+ declare cut_line cut_tags
+ declare template_tags_line_header
+ awk "//, //{
+ if (!// && !//) print
+ if (\"$3\" == \"cut\" && /$cut_line/){
+ if (\"$2\" == \"text\") exit # no need to read further
+ while (getline > 0 && !//) {
+ if (\"$cut_tags\" == \"no\" && /^/, /<\/a><\/h3>/{if (!/
/ && !/<\/a><\/h3>/) print}' "$1"
+}
\ No newline at end of file
diff --git a/lib/global_variables.sh b/lib/global_variables.sh
new file mode 100644
index 0000000..0e56d37
--- /dev/null
+++ b/lib/global_variables.sh
@@ -0,0 +1,165 @@
+#!/usr/bin/env bash
+# shellcheck disable=SC2034
+# Global variables
+# It is recommended to perform a 'rebuild' after changing any of this in the code
+
+# Config file. Any settings "key=value" written there will override the
+# global_variables defaults. Useful to avoid editing bb.sh and having to deal
+# with merges in VCS
+global_config=".tildelog"
+
+# This function will load all the variables defined here. They might be overridden
+# by the 'global_config' file contents
+global_variables() {
+ global_software_name="tildelog"
+ global_software_version="0.1"
+
+ # Blog title
+ global_title="my tildelog"
+ # The typical subtitle for each blog
+ global_description="a blog about tildes"
+ # The server base domain
+ global_domain="tilde.team"
+ # The public base URL for this blog
+ global_url="https://${global_domain}/~$USER/blog"
+ # Your name
+ global_author="~$USER"
+ # You can use twitter or facebook or anything for global_author_url
+ global_author_url="https://${global_domain}/~$USER/"
+ # Your email
+ global_email="$USER@${global_domain}"
+
+ # CC by-nc-nd is a good starting point, you can change this to "©" for Copyright
+ global_license="CC by-nc-nd"
+
+
+ # Leave this empty (i.e. "") if you don't want to use feedburner,
+ # or change it to your own URL
+ global_feedburner=""
+
+ # Change this to your username if you want to use twitter for comments
+ global_twitter_username=""
+ # Set this to false for a Twitter button with share count. The cookieless version
+ # is just a link.
+ global_twitter_cookieless="true"
+
+ # Blog generated files
+ # index page of blog (it is usually good to use "index.html" here)
+ index_file="index.html"
+ number_of_index_articles="10"
+ # global archive
+ archive_index="all_posts.html"
+ tags_index="all_tags.html"
+
+ # ignore gophermap file
+ gophermap="gophermap"
+
+ # ignore gemini generation script and gemini index
+ gemini_index="index.gmi"
+
+ # Non blogpost files. Bashblog will ignore these. Useful for static pages and custom content
+ # Add them as a bash array, e.g. non_blogpost_files=("news.html" "test.html")
+ # TODO: Dash have no support for arrays. Get rid of it!
+ non_blogpost_files=""
+
+ # feed file (rss in this case)
+ blog_feed="feed.rss"
+ number_of_feed_articles="50"
+ # "cut" blog entry when putting it to index page. Leave blank for full articles in front page
+ # i.e. include only up to first '
', or '----' in markdown
+ cut_do="cut"
+ # When cutting, cut also tags? If "no", tags will appear in index page for cut articles
+ cut_tags="yes"
+ # Regexp matching the HTML line where to do the cut
+ # note that slash is regexp separator so you need to prepend it with backslash
+ cut_line='
'
+ # save markdown file when posting with "bb post -m". Leave blank to discard it.
+ save_markdown="yes"
+ # prefix for tags/categories files
+ # please make sure that no other html file starts with this prefix
+ prefix_tags="tag_"
+ # personalized header and footer (only if you know what you're doing)
+ # DO NOT name them .header.html, .footer.html or they will be overwritten
+ # leave blank to generate them, recommended
+ header_file=""
+ footer_file=""
+ # extra content to add just after we open the tag
+ # and before the actual blog content
+ body_begin_file=""
+ # extra content to add just before we cloese )
+ body_end_file=""
+ # CSS files to include on every page, f.ex. css_include=('main.css' 'blog.css')
+ # leave empty to use generated
+ # TODO: Dash have no support for arrays. Get rid of it!
+ css_include=""
+ # HTML files to exclude from index, f.ex. post_exclude=('imprint.html 'aboutme.html')
+ # TODO: Dash have no support for arrays. Get rid of it!
+ html_exclude=""
+
+ # Localization and i18n
+ # "Comments?" (used in twitter link after every post)
+ template_comments="comments?"
+ # "Read more..." (link under cut article on index page)
+ template_read_more="read more..."
+ # "View more posts" (used on bottom of index page as link to archive)
+ template_archive="archive"
+ # "All posts" (title of archive page)
+ template_archive_title="all posts"
+ # "All tags"
+ template_tags_title="all tags"
+ # "posts" (on "All tags" page, text at the end of each tag line, like "2. Music - 15 posts")
+ template_tags_posts="posts"
+ template_tags_posts_2_4="posts" # Some slavic languages use a different plural form for 2-4 items
+ template_tags_posts_singular="post"
+ # "Posts tagged" (text on a title of a page with index of one tag, like "My Blog - Posts tagged "Music"")
+ template_tag_title="posts tagged"
+ # "Tags:" (beginning of line in HTML file with list of all tags for this article)
+ template_tags_line_header="tags:"
+ # "Back to the index page" (used on archive page, it is link to blog index)
+ template_archive_index_page="back home"
+ # "Subscribe" (used on bottom of index page, it is link to RSS feed)
+ template_subscribe="rss"
+ # "Subscribe to this page..." (used as text for browser feed button that is embedded to html)
+ template_subscribe_browser_button="subscribe to this page..."
+ # "Tweet" (used as twitter text button for posting to twitter)
+ template_twitter_button="tweet"
+ template_twitter_comment="<type your comment here but please leave the URL so that other people can follow the comments>"
+
+ # The locale to use for the dates displayed on screen
+ date_format="%B %d, %Y"
+ date_locale="C"
+ date_inpost="bashblog_timestamp"
+ # Don't change these dates
+ date_format_full="%a, %d %b %Y %H:%M:%S %z"
+ date_format_timestamp="%Y%m%d%H%M.%S"
+ date_allposts_header="%B %Y"
+
+ # Perform the post title -> filename conversion
+ # Experts only. You may need to tune the locales too
+ # Leave empty for no conversion, which is not recommended
+ # This default filter respects backwards compatibility
+ convert_filename="iconv -f utf-8 -t ascii//translit | sed 's/^-*//' | tr [:upper:] [:lower:] | tr ' ' '-' | tr -dc '[:alnum:]-'"
+
+ # URL where you can view the post while it's being edited
+ # same as global_url by default
+ # You can change it to path on your computer, if you write posts locally
+ # before copying them to the server
+ preview_url=""
+
+ # Markdown location. Trying to autodetect by default.
+ # The invocation must support the signature 'markdown_bin in.md > out.html'
+
+ markdown_bin=$(which md2html.awk)
+}
+
+# Check for the validity of some variables
+# DO NOT EDIT THIS FUNCTION unless you know what you're doing
+global_variables_check() {
+ [[ $header_file == .header.html ]] && \
+ echo "Please check your configuration. '.header.html' is not a valid value for the setting 'header_file'" && \
+ exit
+ [[ $footer_file == .footer.html ]] && \
+ echo "Please check your configuration. '.footer.html' is not a valid value for the setting 'footer_file'" && \
+ exit
+}
\ No newline at end of file
diff --git a/lib/is_boilerplate_file.sh b/lib/is_boilerplate_file.sh
new file mode 100644
index 0000000..a942cd8
--- /dev/null
+++ b/lib/is_boilerplate_file.sh
@@ -0,0 +1,37 @@
+#!/usr/bin/env bash
+
+# Check if the file is a 'boilerplate' (i.e. not a post)
+# The return values are designed to be used like this inside a loop:
+# is_boilerplate_file /{s/.*href="\([^"]*\)">.*/\1/;p;}' "$@" 2> /dev/null
+}
\ No newline at end of file
diff --git a/lib/rebuild_all_entries.sh b/lib/rebuild_all_entries.sh
new file mode 100644
index 0000000..e1ecc37
--- /dev/null
+++ b/lib/rebuild_all_entries.sh
@@ -0,0 +1,37 @@
+#!/usr/bin/env bash
+
+# Regenerates all the single post entries, keeping the post content but modifying
+# the title, html structure, etc
+rebuild_all_entries() {
+ declare date_inpost
+ declare date_format_full
+ declare date_format_timestamp
+ echo -n "Rebuilding all entries "
+
+ for i in ./*.html; do
+ is_boilerplate_file "$i" && continue;
+ contentfile=.tmp.$RANDOM
+ while [[ -f $contentfile ]]; do contentfile=.tmp.$RANDOM; done
+
+ echo -n "."
+ # Get the title and entry, and rebuild the html structure from scratch (divs, title, description...)
+ title=$(get_post_title "$i")
+
+ get_html_file_content 'text' 'text' <"$i" >> "$contentfile"
+
+ # Read timestamp from post, if present, and sync file timestamp
+ timestamp=$(awk '// { print }' "$i" | cut -d '#' -f 2)
+ [[ -n $timestamp ]] && touch -t "$timestamp" "$i"
+ # Read timestamp from file in correct format for 'create_html_page'
+ timestamp=$(LC_ALL=C date -r "$i" +"$date_format_full")
+
+ create_html_page "$contentfile" "$i.rebuilt" no "$title" "$timestamp" "$(get_post_author "$i")"
+ # keep the original timestamp!
+ timestamp=$(LC_ALL=C date -r "$i" +"$date_format_timestamp")
+ mv "$i.rebuilt" "$i"
+ chmod 644 "$i"
+ touch -t "$timestamp" "$i"
+ rm "$contentfile"
+ done
+ echo ""
+}
\ No newline at end of file
diff --git a/lib/rebuild_index.sh b/lib/rebuild_index.sh
new file mode 100644
index 0000000..2c8920a
--- /dev/null
+++ b/lib/rebuild_index.sh
@@ -0,0 +1,53 @@
+#!/usr/bin/env bash
+
+# Generate the index.html with the content of the latest posts
+rebuild_index() {
+ declare index_file
+ declare number_of_index_articles
+ declare cut_do
+ declare cut_line
+ declare template_read_more
+ declare template_archive
+ declare template_subscribe
+ declare template_tags_title
+ declare blog_feed
+ declare global_author
+ declare global_feedburner
+ declare global_title
+ declare archive_index
+ declare tags_index
+ echo -n "Rebuilding the index "
+ newindexfile=$index_file.$RANDOM
+ contentfile=$newindexfile.content
+ while [[ -f $newindexfile ]]; do
+ newindexfile=$index_file.$RANDOM
+ contentfile=$newindexfile.content
+ done
+
+ # Create the content file
+ {
+ n=0
+ while IFS='' read -r i; do
+ is_boilerplate_file "$i" && continue;
+ if ((n >= number_of_index_articles)); then break; fi
+ if [[ -n $cut_do ]]; then
+ get_html_file_content 'entry' 'entry' 'cut' <"$i" | awk "/$cut_line/ { print \"\" ; next } 1"
+ else
+ get_html_file_content 'entry' 'entry' <"$i"
+ fi
+ echo -n "." 1>&3
+ n=$(( n + 1 ))
+ done < <(ls -t ./*.html) # sort by date, newest first
+
+ feed=$blog_feed
+ if [[ -n $global_feedburner ]]; then feed=$global_feedburner; fi
+ echo ""
+ } 3>&1 >"$contentfile"
+
+ echo ""
+
+ create_html_page "$contentfile" "$newindexfile" yes "$global_title" "$global_author"
+ rm "$contentfile"
+ mv "$newindexfile" "$index_file"
+ chmod 644 "$index_file"
+}
\ No newline at end of file
diff --git a/lib/rebuild_tags.sh b/lib/rebuild_tags.sh
new file mode 100644
index 0000000..e1da522
--- /dev/null
+++ b/lib/rebuild_tags.sh
@@ -0,0 +1,67 @@
+#!/usr/bin/env bash
+
+# Rebuilds tag_*.html files
+# if no arguments given, rebuilds all of them
+# if arguments given, they should have this format:
+# "FILE1 [FILE2 [...]]" "TAG1 [TAG2 [...]]"
+# where FILEn are files with posts which should be used for rebuilding tags,
+# and TAGn are names of tags which should be rebuilt.
+# example:
+# rebuild_tags "one_post.html another_article.html" "example-tag another-tag"
+# mind the quotes!
+rebuild_tags() {
+ declare prefix_tags
+ declare cut_do
+ declare cut_line
+ declare template_read_more
+ declare template_tag_title
+ declare global_title
+ declare global_author
+ if (($# < 2)); then
+ # will process all files and tags
+ files=$(ls -t ./*.html)
+ all_tags=yes
+ else
+ # will process only given files and tags
+ files=$(printf '%s\n' "$1" | sort -u)
+ # shellcheck disable=SC2086 # Intended splitting of $files
+ files=$(ls -t $files)
+ tags=$2
+ fi
+ echo -n "Rebuilding tag pages "
+ #n=0
+ if [[ -n $all_tags ]]; then
+ rm ./"$prefix_tags"*.html &> /dev/null
+ else
+ for i in $tags; do
+ rm "./$prefix_tags$i.html" &> /dev/null
+ done
+ fi
+ # First we will process all files and create temporal tag files
+ # with just the content of the posts
+ tmpfile=tmp.$RANDOM
+ while [[ -f $tmpfile ]]; do tmpfile=tmp.$RANDOM; done
+ while IFS='' read -r i; do
+ is_boilerplate_file "$i" && continue;
+ echo -n "."
+ if [[ -n $cut_do ]]; then
+ get_html_file_content 'entry' 'entry' 'cut' <"$i" | awk "/$cut_line/ { print \"\" ; next } 1"
+ else
+ get_html_file_content 'entry' 'entry' <"$i"
+ fi >"$tmpfile"
+ for tag in $(tags_in_post "$i"); do
+ if [[ -n $all_tags || " $tags " == *" $tag "* ]]; then
+ cat "$tmpfile" >> "$prefix_tags$tag".tmp.html
+ fi
+ done
+ done <<< "$files"
+ rm "$tmpfile"
+ # Now generate the tag files with headers, footers, etc
+ while IFS='' read -r i; do
+ tagname=${i#./"$prefix_tags"}
+ tagname=${tagname%.tmp.html}
+ create_html_page "$i" "$prefix_tags$tagname.html" yes "$global_title — $template_tag_title \"$tagname\"" "$global_author"
+ rm "$i"
+ done < <(ls -t ./"$prefix_tags"*.tmp.html 2>/dev/null)
+ echo
+}
\ No newline at end of file
diff --git a/lib/reset.sh b/lib/reset.sh
new file mode 100644
index 0000000..216620f
--- /dev/null
+++ b/lib/reset.sh
@@ -0,0 +1,15 @@
+#!/usr/bin/env bash
+
+# Delete all generated content, leaving only this script
+reset() {
+ echo "Are you sure you want to delete all blog entries? Please write \"Yes, I am!\" "
+ read -r line
+ if [[ $line == "Yes, I am!" ]]; then
+ rm .*.html ./*.html ./*.css ./*.rss &> /dev/null
+ echo
+ echo "Deleted all posts, stylesheets and feeds."
+ echo "Kept your old '.backup.tar.gz' just in case, please delete it manually if needed."
+ else
+ echo "Phew! You dodged a bullet there. Nothing was modified."
+ fi
+}
\ No newline at end of file
diff --git a/lib/tags_in_post.sh b/lib/tags_in_post.sh
new file mode 100644
index 0000000..35c6eda
--- /dev/null
+++ b/lib/tags_in_post.sh
@@ -0,0 +1,9 @@
+#!/usr/bin/env bash
+
+# Finds all tags referenced in one post.
+# Accepts either filename as first argument, or post content at stdin
+# Prints one line with space-separated tags to stdout
+tags_in_post() {
+ declare template_tags_line_header
+ sed -n "/^
";
+ if(ilcode){
+ t1 = eschtml(t1);
+ tag = "
";
+ }
+ ilcode = !ilcode;
+ return t1 tag nextil(t2);
+ }
+ if(tag == "<"){
+ # Autolinks
+ if(match(t2, /^[^ ]+[\.@][^ ]+>/)){
+ url = eschtml(substr(t2, 1, RLENGTH - 1));
+ t2 = substr(t2, RLENGTH + 1);
+ linktext = url;
+ if(match(url, /@/) && !match(url, /^mailto:/))
+ url = "mailto:" url;
+ return t1 "" linktext "" nextil(t2);
+ }
+ # Html tags
+ if(match(t2, /^[A-Za-z\/!][^>]*>/)){
+ tag = tag substr(t2, RSTART, RLENGTH);
+ t2 = substr(t2, RLENGTH + 1);
+ return t1 tag nextil(t2);
+ }
+ return t1 "<" nextil(t2);
+ }
+ # Html special entities
+ if(tag == "&"){
+ if(match(t2, /^#?[A-Za-z0-9]+;/)){
+ tag = tag substr(t2, RSTART, RLENGTH);
+ t2 = substr(t2, RLENGTH + 1);
+ return t1 tag nextil(t2);
+ }
+ return t1 "&" nextil(t2);
+ }
+ # Images
+ if(tag == "!["){
+ if(!match(t2, /(\[.*\])|(\(.*\))/))
+ return t1 tag nextil(t2);
+ match(t2, /^[^\]]*/);
+ alt = substr(t2, 1, RLENGTH);
+ t2 = substr(t2, RLENGTH + 2);
+ if(match(t2, /^\(/)){
+ # Inline
+ sub(/^\(/, "", t2);
+ match(t2, /^[^\)]+/);
+ url = eschtml(substr(t2, 1, RLENGTH));
+ t2 = substr(t2, RLENGTH + 2);
+ title = "";
+ if(match(url, /[ ]+".*"[ ]*$/)) {
+ title = substr(url, RSTART, RLENGTH);
+ url = substr(url, 1, RSTART - 1);
+ match(title, /".*"/);
+ title = " title=\"" substr(title, RSTART + 1, RLENGTH - 2) "\"";
+ }
+ if(match(url, /^<.*>$/))
+ url = substr(url, 2, RLENGTH - 2);
+ return t1 "" nextil(t2);
+ }
+ else{
+ # Referenced
+ sub(/^ ?\[/, "", t2);
+ id = alt;
+ if(match(t2, /^[^\]]+/))
+ id = substr(t2, 1, RLENGTH);
+ t2 = substr(t2, RLENGTH + 2);
+ if(ref[id])
+ r = ref[id];
+ else{
+ r = "<<" id;
+ nr++;
+ }
+ return t1 "" nextil(t2);
+ }
+ }
+ # Footnotes
+ if(tag == "[^"){
+ match(t2, /^[^\]]*(\[[^\]]*\][^\]]*)*/);
+ linktext = substr(t2, 1, RLENGTH);
+ t2 = substr(t2, RLENGTH + 2);
+ return t1 "" linktext "" nextil(t2);
+ }
+ # Links
+ if(tag == "["){
+ if(!match(t2, /(\[.*\])|(\(.*\))/))
+ return t1 tag nextil(t2);
+ match(t2, /^[^\]]*(\[[^\]]*\][^\]]*)*/);
+ linktext = substr(t2, 1, RLENGTH);
+ t2 = substr(t2, RLENGTH + 2);
+ if(match(t2, /^\(/)){
+ # Inline
+ match(t2, /^[^\)]+(\([^\)]+\)[^\)]*)*/);
+ url = substr(t2, 2, RLENGTH - 1);
+ pt2 = substr(t2, RLENGTH + 2);
+ title = "";
+ if(match(url, /[ ]+".*"[ ]*$/)) {
+ title = substr(url, RSTART, RLENGTH);
+ url = substr(url, 1, RSTART - 1);
+ match(title, /".*"/);
+ title = " title=\"" substr(title, RSTART + 1, RLENGTH - 2) "\"";
+ }
+ if(match(url, /^<.*>$/))
+ url = substr(url, 2, RLENGTH - 2);
+ url = eschtml(url);
+ return t1 "" nextil(linktext) "" nextil(pt2);
+ }
+ else{
+ # Referenced
+ sub(/^ ?\[/, "", t2);
+ id = linktext;
+ if(match(t2, /^[^\]]+/))
+ id = substr(t2, 1, RLENGTH);
+ t2 = substr(t2, RLENGTH + 2);
+ if(ref[id])
+ r = ref[id];
+ else{
+ r = "<<" id;
+ nr++;
+ }
+ pt2 = t2;
+ return t1 "" nextil(linktext) "
$)"))
+ html = 1;
+ if(html && match($0, /^
$/ ||
+(hr && />$/)) {
+ html = 0;
+ hr = 0;
+ oprint($0);
+ next;
+}
+
+html {
+ oprint($0);
+ next;
+}
+
+# List and quote blocks
+
+# Remove indentation
+{
+ for(nnl = 0; nnl < nl; nnl++)
+ if((match(block[nnl + 1], /[ou]l/) && !sub(/^( | )/, "")) || \
+ (block[nnl + 1] == "blockquote" && !sub(/^> ?/, "")))
+ break;
+}
+nnl < nl && !blank && text && ! /^ ? ? ?([*+-]|([0-9]+\.)+)( +| )/ { nnl = nl; }
+# Quote blocks
+{
+ while(sub(/^> /, ""))
+ nblock[++nnl] = "blockquote";
+}
+# Horizontal rules
+{ hr = 0; }
+(blank || (!text && !code)) && /^ ? ? ?([-*_][ ]*)([-*_][ ]*)([-*_][ ]*)+$/ {
+ if(code){
+ oprint("");
+ code = 0;
+ }
+ blank = 0;
+ nnl = 0;
+ hr = 1;
+}
+# List items
+block[nl] ~ /[ou]l/ && /^$/ {
+ blank = 1;
+ next;
+}
+{ newli = 0; }
+!hr && (nnl != nl || !text || block[nl] ~ /[ou]l/) && /^ ? ? ?[*+-]( +| )/ {
+ sub(/^ ? ? ?[*+-]( +| )/, "");
+ nnl++;
+ nblock[nnl] = "ul";
+ newli = 1;
+}
+(nnl != nl || !text || block[nl] ~ /[ou]l/) && /^ ? ? ?([0-9]+\.)+( +| )/ {
+ sub(/^ ? ? ?([0-9]+\.)+( +| )/, "");
+ nnl++;
+ nblock[nnl] = "ol";
+ newli = 1;
+}
+newli {
+ if(blank && nnl == nl && !par)
+ par = "p";
+ blank = 0;
+ printp(par);
+ if(nnl == nl && block[nl] == nblock[nl])
+ oprint("
");
+ code = 0;
+}
+
+# Setex-style Headers
+text && /^=+$/ {printp("h1"); next;}
+text && /^-+$/ {printp("h2"); next;}
+
+# Atx-Style headers
+/^#+/ && (!newli || par=="p" || /^##/) {
+ for(n = 0; n < 6 && sub(/^# */, ""); n++)
+ {
+ sub(/#$/, "");
+ }
+ par = "h" n;
+ if (n == 1) {
+ oprint( text lineheader "\n=" centralize($0,2) "=\n" lineheader "\n" )
+ next;
+ }
+ if (n == 2) {
+ oprint("\n" text wrap($0,"c") "\n" lineheader "\n")
+ next;
+ }
+ if (n == 3) {
+ oprint(text centralize($0) "\n" lineheadersmall "\n")
+ next;
+ }
+ if (n > 3) {
+ text = text centralize($0) "\n"
+ next;
+ }
+}
+
+# Paragraph
+/^$/ {
+ printp(par);
+ par = "p";
+ next;
+}
+
+# Add text
+{ text = (text ? text " " : "") $0; }
+
+function alen(a, ix, k) {
+ k = 0
+ for(ix in a) k++
+ return k
+}
+
+END {
+ if(code){
+ oprint("");
+ code = 0;
+ }
+ printp(par);
+ for(; nl > 0; nl--){
+ if(match(block[nl], /[ou]l/))
+ oprint("");
+ code = 1;
+ $0 = eschtml($0);
+ oprint($0);
+ next;
+}
+code {
+ oprint("
";
+ for (i in fnref) print "
";
+ }
+}
diff --git a/md2html.awk b/md2html.awk
new file mode 100755
index 0000000..b981c04
--- /dev/null
+++ b/md2html.awk
@@ -0,0 +1,463 @@
+#!/bin/awk -f
+#
+# by: Josemar Lohn ";
+ if(ilcode){
+ t1 = eschtml(t1);
+ tag = "
";
+ }
+ ilcode = !ilcode;
+ return t1 tag nextil(t2);
+ }
+ if(tag == "<"){
+ # Autolinks
+ if(match(t2, /^[^ ]+[\.@][^ ]+>/)){
+ url = eschtml(substr(t2, 1, RLENGTH - 1));
+ t2 = substr(t2, RLENGTH + 1);
+ linktext = url;
+ if(match(url, /@/) && !match(url, /^mailto:/))
+ url = "mailto:" url;
+ return t1 "" linktext "" nextil(t2);
+ }
+ # Html tags
+ if(match(t2, /^[A-Za-z\/!][^>]*>/)){
+ tag = tag substr(t2, RSTART, RLENGTH);
+ t2 = substr(t2, RLENGTH + 1);
+ return t1 tag nextil(t2);
+ }
+ return t1 "<" nextil(t2);
+ }
+ # Html special entities
+ if(tag == "&"){
+ if(match(t2, /^#?[A-Za-z0-9]+;/)){
+ tag = tag substr(t2, RSTART, RLENGTH);
+ t2 = substr(t2, RLENGTH + 1);
+ return t1 tag nextil(t2);
+ }
+ return t1 "&" nextil(t2);
+ }
+ # Images
+ if(tag == "!["){
+ if(!match(t2, /(\[.*\])|(\(.*\))/))
+ return t1 tag nextil(t2);
+ match(t2, /^[^\]]*/);
+ alt = substr(t2, 1, RLENGTH);
+ t2 = substr(t2, RLENGTH + 2);
+ if(match(t2, /^\(/)){
+ # Inline
+ sub(/^\(/, "", t2);
+ match(t2, /^[^\)]+/);
+ url = eschtml(substr(t2, 1, RLENGTH));
+ t2 = substr(t2, RLENGTH + 2);
+ title = "";
+ if(match(url, /[ ]+".*"[ ]*$/)) {
+ title = substr(url, RSTART, RLENGTH);
+ url = substr(url, 1, RSTART - 1);
+ match(title, /".*"/);
+ title = " title=\"" substr(title, RSTART + 1, RLENGTH - 2) "\"";
+ }
+ if(match(url, /^<.*>$/))
+ url = substr(url, 2, RLENGTH - 2);
+ return t1 "" nextil(t2);
+ }
+ else{
+ # Referenced
+ sub(/^ ?\[/, "", t2);
+ id = alt;
+ if(match(t2, /^[^\]]+/))
+ id = substr(t2, 1, RLENGTH);
+ t2 = substr(t2, RLENGTH + 2);
+ if(ref[id])
+ r = ref[id];
+ else{
+ r = "<<" id;
+ nr++;
+ }
+ return t1 "" nextil(t2);
+ }
+ }
+ # Footnotes
+ if(tag == "[^"){
+ match(t2, /^[^\]]*(\[[^\]]*\][^\]]*)*/);
+ linktext = substr(t2, 1, RLENGTH);
+ t2 = substr(t2, RLENGTH + 2);
+ return t1 "" linktext "" nextil(t2);
+ }
+ # Links
+ if(tag == "["){
+ if(!match(t2, /(\[.*\])|(\(.*\))/))
+ return t1 tag nextil(t2);
+ match(t2, /^[^\]]*(\[[^\]]*\][^\]]*)*/);
+ linktext = substr(t2, 1, RLENGTH);
+ t2 = substr(t2, RLENGTH + 2);
+ if(match(t2, /^\(/)){
+ # Inline
+ match(t2, /^[^\)]+(\([^\)]+\)[^\)]*)*/);
+ url = substr(t2, 2, RLENGTH - 1);
+ pt2 = substr(t2, RLENGTH + 2);
+ title = "";
+ if(match(url, /[ ]+".*"[ ]*$/)) {
+ title = substr(url, RSTART, RLENGTH);
+ url = substr(url, 1, RSTART - 1);
+ match(title, /".*"/);
+ title = " title=\"" substr(title, RSTART + 1, RLENGTH - 2) "\"";
+ }
+ if(match(url, /^<.*>$/))
+ url = substr(url, 2, RLENGTH - 2);
+ url = eschtml(url);
+ return t1 "" nextil(linktext) "" nextil(pt2);
+ }
+ else{
+ # Referenced
+ sub(/^ ?\[/, "", t2);
+ id = linktext;
+ if(match(t2, /^[^\]]+/))
+ id = substr(t2, 1, RLENGTH);
+ t2 = substr(t2, RLENGTH + 2);
+ if(ref[id])
+ r = ref[id];
+ else{
+ r = "<<" id;
+ nr++;
+ }
+ pt2 = t2;
+ return t1 "" nextil(linktext) "
$)"))
+ html = 1;
+ if(html && match($0, /^
$/ ||
+(hr && />$/)) {
+ html = 0;
+ hr = 0;
+ oprint($0);
+ next;
+}
+
+html {
+ oprint($0);
+ next;
+}
+
+# List and quote blocks
+
+# Remove indentation
+{
+ for(nnl = 0; nnl < nl; nnl++)
+ if((match(block[nnl + 1], /[ou]l/) && !sub(/^( | )/, "")) || \
+ (block[nnl + 1] == "blockquote" && !sub(/^> ?/, "")))
+ break;
+}
+nnl < nl && !blank && text && ! /^ ? ? ?([*+-]|([0-9]+\.)+)( +| )/ { nnl = nl; }
+# Quote blocks
+{
+ while(sub(/^> /, ""))
+ nblock[++nnl] = "blockquote";
+}
+# Horizontal rules
+{ hr = 0; }
+(blank || (!text && !code)) && /^ ? ? ?([-*_][ ]*)([-*_][ ]*)([-*_][ ]*)+$/ {
+ if(code){
+ oprint("");
+ code = 0;
+ }
+ blank = 0;
+ nnl = 0;
+ hr = 1;
+}
+# List items
+block[nl] ~ /[ou]l/ && /^$/ {
+ blank = 1;
+ next;
+}
+{ newli = 0; }
+!hr && (nnl != nl || !text || block[nl] ~ /[ou]l/) && /^ ? ? ?[*+-]( +| )/ {
+ sub(/^ ? ? ?[*+-]( +| )/, "");
+ nnl++;
+ nblock[nnl] = "ul";
+ newli = 1;
+}
+(nnl != nl || !text || block[nl] ~ /[ou]l/) && /^ ? ? ?([0-9]+\.)+( +| )/ {
+ sub(/^ ? ? ?([0-9]+\.)+( +| )/, "");
+ nnl++;
+ nblock[nnl] = "ol";
+ newli = 1;
+}
+newli {
+ if(blank && nnl == nl && !par)
+ par = "p";
+ blank = 0;
+ printp(par);
+ if(nnl == nl && block[nl] == nblock[nl])
+ oprint("
");
+ next;
+}
+
+# Code blocks
+code && /^$/ {
+ if(blanK)
+ oprint("");
+ blank = 1;
+ next;
+}
+!text && sub(/^( | )/, "") {
+ if(blanK)
+ oprint("");
+ blank = 0;
+ if(!code)
+ oprint("
");
+ code = 0;
+}
+
+# Setex-style Headers
+text && /^=+$/ {printp("h1"); next;}
+text && /^-+$/ {printp("h2"); next;}
+
+# Atx-Style headers
+/^#+/ && (!newli || par=="p" || /^##/) {
+ for(n = 0; n < 6 && sub(/^# */, ""); n++)
+ sub(/#$/, "");
+ par = "h" n;
+}
+
+# Paragraph
+/^$/ {
+ printp(par);
+ par = "p";
+ next;
+}
+
+# Add text
+{ text = (text ? text " " : "") $0; }
+
+function alen(a, ix, k) {
+ k = 0
+ for(ix in a) k++
+ return k
+}
+
+END {
+ if(code){
+ oprint("");
+ code = 0;
+ }
+ printp(par);
+ for(; nl > 0; nl--){
+ if(match(block[nl], /[ou]l/))
+ oprint("");
+ code = 1;
+ $0 = eschtml($0);
+ oprint($0);
+ next;
+}
+code {
+ oprint("
";
+ for (i in fnref) print "
";
+ }
+}
diff --git a/spec/do_main_spec.sh b/spec/do_main_spec.sh
new file mode 100644
index 0000000..c46c4bb
--- /dev/null
+++ b/spec/do_main_spec.sh
@@ -0,0 +1,13 @@
+# shellcheck shell=bash
+Describe 'do_main()'
+ Include ./lib/date_version_detect.sh
+ Include ./lib/do_main.sh
+ Include ./lib/global_variables.sh
+ Include ./lib/usage.sh
+ It 'Call function do_main without paramenters'
+ When call do_main
+ The line 1 of output should eq "You're not in your blog directory. Moving you there now"
+ #The line 2 of output should eq "tildelog 0.1"
+ #The line 3 of output should eq "usage: ./tildelog.sh command [filename]"
+ End
+End
diff --git a/spec/spec_helper.sh b/spec/spec_helper.sh
new file mode 100644
index 0000000..197e06f
--- /dev/null
+++ b/spec/spec_helper.sh
@@ -0,0 +1,7 @@
+#shellcheck shell=sh
+
+# set -eu
+
+# shellspec_spec_helper_configure() {
+# shellspec_import 'support/custom_matcher'
+# }
diff --git a/spec/tildeblog_spec.sh b/spec/tildeblog_spec.sh
new file mode 100644
index 0000000..d7664c3
--- /dev/null
+++ b/spec/tildeblog_spec.sh
@@ -0,0 +1,10 @@
+# shellcheck shell=bash
+
+Describe 'tildelog.sh'
+ It 'Call script without paramenters'
+ When run script ./tildelog.sh
+ The line 1 of output should eq "You're not in your blog directory. Moving you there now"
+ The line 2 of output should eq "tildelog 0.1"
+ The line 3 of output should eq "usage: ./tildelog.sh command [filename]"
+ End
+End
\ No newline at end of file
diff --git a/spec/usage_spec.sh b/spec/usage_spec.sh
new file mode 100644
index 0000000..853bc27
--- /dev/null
+++ b/spec/usage_spec.sh
@@ -0,0 +1,9 @@
+# shellcheck shell=bash
+Describe 'usage()'
+ Include ./lib/global_variables.sh
+ Include ./lib/usage.sh
+ It 'Call function usage'
+ When call usage
+ The line 2 of output should include "usage:"
+ End
+End
\ No newline at end of file
diff --git a/tildelog.sh b/tildelog.sh
new file mode 100755
index 0000000..091e168
--- /dev/null
+++ b/tildelog.sh
@@ -0,0 +1,50 @@
+#!/usr/bin/env bash
+
+# TildeLog, a not-so-simple blog/gemlog/phlog system made for tilde.team
+# By Josemar Lohn