|
@@ -5,6 +5,18 @@
|
|
|
# https://github.com/carlesfe/bashblog/contributors
|
|
|
# Check out README.md for more details
|
|
|
|
|
|
+# Some shell settings for robustness by default. These help eliminate
|
|
|
+# unexpected snags and security vulnerabilities in case someone forgets to
|
|
|
+# quote a variable somewhere. They do require a few coding adaptations.
|
|
|
+
|
|
|
+IFS=$'\n' # Globally, we do word splitting only on newline (which also
|
|
|
+ # makes "$*" expand with newline separator instead of space).
|
|
|
+
|
|
|
+set -f # Disable globbing (pathname expansion). It can be re-enabled
|
|
|
+ # locally using 'set +f'; it's handy to do this in a subshell,
|
|
|
+ # for example in $(command substitution), as the globbing will
|
|
|
+ # be local to the subshell.
|
|
|
+
|
|
|
# Global variables
|
|
|
# It is recommended to perform a 'rebuild' after changing any of this in the code
|
|
|
|
|
@@ -252,6 +264,14 @@ get_html_file_content() {
|
|
|
}"
|
|
|
}
|
|
|
|
|
|
+# Invoke the editor specified by the $EDITOR environment variable. Use a
|
|
|
+# function for this as we need to locally word-split $EDITOR on spaces
|
|
|
+# (in case it contains arguments, like EDITOR='joe -nobackups).
|
|
|
+invoke_editor() {
|
|
|
+ local IFS=$' \t\n'
|
|
|
+ $EDITOR "$1"
|
|
|
+}
|
|
|
+
|
|
|
# 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'.
|
|
@@ -270,7 +290,7 @@ edit() {
|
|
|
touch_timestamp=$(LC_ALL=C date -r "${1%%.*}.html" +'%Y%m%d%H%M')
|
|
|
tags_before=$(tags_in_post "${1%%.*}.html")
|
|
|
if [[ $2 == full ]]; then
|
|
|
- $EDITOR "$1"
|
|
|
+ invoke_editor "$1"
|
|
|
filename=$1
|
|
|
else
|
|
|
if [[ ${1##*.} == md ]]; then
|
|
@@ -280,7 +300,7 @@ edit() {
|
|
|
exit
|
|
|
fi
|
|
|
# editing markdown file
|
|
|
- $EDITOR "$1"
|
|
|
+ invoke_editor "$1"
|
|
|
TMPFILE=$(markdown "$1")
|
|
|
filename=${1%%.*}.html
|
|
|
else
|
|
@@ -290,7 +310,7 @@ edit() {
|
|
|
get_post_title "$1" > "$TMPFILE"
|
|
|
# Post text with plaintext tags
|
|
|
get_html_file_content 'text' 'text' <"$1" | sed "/^<p>$template_tags_line_header/s|<a href='$prefix_tags\([^']*\).html'>\\1</a>|\\1|g" >> "$TMPFILE"
|
|
|
- $EDITOR "$TMPFILE"
|
|
|
+ invoke_editor "$TMPFILE"
|
|
|
filename=$1
|
|
|
fi
|
|
|
rm "$filename"
|
|
@@ -306,10 +326,10 @@ edit() {
|
|
|
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 [[ ! -z $relevant_tags ]]; then
|
|
|
- relevant_posts="$(posts_with_tags $relevant_tags) $filename"
|
|
|
- rebuild_tags "$relevant_posts" "$relevant_tags"
|
|
|
+ relevant_tags=$(sort -u <<< "$tags_before"$'\n'"$tags_after")
|
|
|
+ if [[ -n $relevant_tags ]]; then
|
|
|
+ relevant_posts=$(posts_with_tags $relevant_tags)$'\n'$filename
|
|
|
+ rebuild_tags $relevant_posts --tags $relevant_tags
|
|
|
fi
|
|
|
}
|
|
|
|
|
@@ -475,10 +495,11 @@ create_html_page() {
|
|
|
parse_file() {
|
|
|
# Read for the title and check that the filename is ok
|
|
|
title=""
|
|
|
- while IFS='' read -r line; do
|
|
|
+ while read -r line; do
|
|
|
if [[ -z $title ]]; then
|
|
|
# remove extra <p> and </p> added by markdown
|
|
|
- title=$(echo "$line" | sed 's/<\/*p>//g')
|
|
|
+ title=${line#<p>}
|
|
|
+ title=${title%</p>}
|
|
|
if [[ -n $3 ]]; then
|
|
|
filename=$3
|
|
|
else
|
|
@@ -498,13 +519,14 @@ parse_file() {
|
|
|
content=$filename.tmp
|
|
|
# Parse possible tags
|
|
|
elif [[ $line == "<p>$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 "<p>$template_tags_line_header " >> "$content"
|
|
|
- for item in "${array[@]}"; do
|
|
|
- echo -n "<a href='$prefix_tags$item.html'>$item</a>, "
|
|
|
- done | sed 's/, $/<\/p>/g' >> "$content"
|
|
|
+ sed "s%</p>%%g
|
|
|
+ s/^.*:[[:blank:]]*//
|
|
|
+ s/[[:blank:]]\$//
|
|
|
+ s/[[:blank:]]*,[[:blank:]]*/,/g
|
|
|
+ s%\([^,]*\),%<a href='$prefix_tags\1.html'>\1</a>, %g
|
|
|
+ s%, \([^,]*\)\$%, <a href='$prefix_tags\1.html'>\1</a></p>%
|
|
|
+ " <<< "$line" >> "$content"
|
|
|
else
|
|
|
echo "$line" >> "$content"
|
|
|
fi
|
|
@@ -565,7 +587,7 @@ EOF
|
|
|
filename=""
|
|
|
while [[ $post_status != "p" && $post_status != "P" ]]; do
|
|
|
[[ -n $filename ]] && rm "$filename" # Delete the generated html file, if any
|
|
|
- $EDITOR "$TMPFILE"
|
|
|
+ invoke_editor "$TMPFILE"
|
|
|
if [[ $fmt == md ]]; then
|
|
|
html_from_md=$(markdown "$TMPFILE")
|
|
|
parse_file "$html_from_md"
|
|
@@ -607,8 +629,8 @@ EOF
|
|
|
echo "Posted $filename"
|
|
|
relevant_tags=$(tags_in_post $filename)
|
|
|
if [[ -n $relevant_tags ]]; then
|
|
|
- relevant_posts="$(posts_with_tags $relevant_tags) $filename"
|
|
|
- rebuild_tags "$relevant_posts" "$relevant_tags"
|
|
|
+ relevant_posts=$(posts_with_tags $relevant_tags)$'\n'$filename
|
|
|
+ rebuild_tags $relevant_posts --tags $relevant_tags
|
|
|
fi
|
|
|
}
|
|
|
|
|
@@ -623,7 +645,7 @@ all_posts() {
|
|
|
{
|
|
|
echo "<h3>$template_archive_title</h3>"
|
|
|
prev_month=""
|
|
|
- while IFS='' read -r i; do
|
|
|
+ for i in $(set +f; ls -t ./*.html); do
|
|
|
is_boilerplate_file "$i" && continue
|
|
|
echo -n "." 1>&3
|
|
|
# Month headers
|
|
@@ -640,7 +662,7 @@ all_posts() {
|
|
|
# Date
|
|
|
date=$(LC_ALL=$date_locale date -r "$i" +"$date_format")
|
|
|
echo " $date</li>"
|
|
|
- done < <(ls -t ./*.html)
|
|
|
+ done
|
|
|
echo "" 1>&3
|
|
|
echo "</ul>"
|
|
|
echo "<div id=\"all_posts\"><a href=\"./$index_file\">$template_archive_index_page</a></div>"
|
|
@@ -663,7 +685,7 @@ all_tags() {
|
|
|
{
|
|
|
echo "<h3>$template_tags_title</h3>"
|
|
|
echo "<ul>"
|
|
|
- for i in $prefix_tags*.html; do
|
|
|
+ for i in $(set +f; printf '%s\n' $prefix_tags*.html); do
|
|
|
[[ -f "$i" ]] || break
|
|
|
echo -n "." 1>&3
|
|
|
nposts=$(grep -c "<\!-- text begin -->" "$i")
|
|
@@ -696,7 +718,8 @@ rebuild_index() {
|
|
|
# Create the content file
|
|
|
{
|
|
|
n=0
|
|
|
- while IFS='' read -r i; do
|
|
|
+ for i in $(set +f; ls -t ./*.html) # sort by date, newest first
|
|
|
+ do
|
|
|
is_boilerplate_file "$i" && continue;
|
|
|
if ((n >= number_of_index_articles)); then break; fi
|
|
|
if [[ -n $cut_do ]]; then
|
|
@@ -706,7 +729,7 @@ rebuild_index() {
|
|
|
fi
|
|
|
echo -n "." 1>&3
|
|
|
n=$(( n + 1 ))
|
|
|
- done < <(ls -t ./*.html) # sort by date, newest first
|
|
|
+ done
|
|
|
|
|
|
feed=$blog_feed
|
|
|
if [[ -n $global_feedburner ]]; then feed=$global_feedburner; fi
|
|
@@ -723,9 +746,18 @@ rebuild_index() {
|
|
|
|
|
|
# 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
|
|
|
+# Prints tags to stdout, one per line.
|
|
|
+# (Since we're doing global IFS word splitting on newline only,
|
|
|
+# something like 'for tag in $(tags_in_post $i)' will work.)
|
|
|
tags_in_post() {
|
|
|
- sed -n "/^<p>$template_tags_line_header/{s/^<p>$template_tags_line_header//;s/<[^>]*>//g;s/[ ,]\+/ /g;p;}" "$1" | tr ', ' ' '
|
|
|
+ local newline=$'\n'
|
|
|
+ sed -n "/^<p>$template_tags_line_header/ {
|
|
|
+ s/^<p>$template_tags_line_header[[:blank:]]*//
|
|
|
+ s/[[:blank:]]*<[^>]*>[[:blank:]]*//g
|
|
|
+ s/[[:blank:]]*,[[:blank:]]*/,/g
|
|
|
+ s/,\+/\\$newline/g
|
|
|
+ p
|
|
|
+ }" "$1"
|
|
|
}
|
|
|
|
|
|
# Finds all posts referenced in a number of tags.
|
|
@@ -741,17 +773,15 @@ posts_with_tags() {
|
|
|
# Rebuilds tag_*.html files
|
|
|
# if no arguments given, rebuilds all of them
|
|
|
# if arguments given, they should have this format:
|
|
|
-# "FILE1 [FILE2 [...]]" "TAG1 [TAG2 [...]]"
|
|
|
+# FILE1 [FILE2 [...]] --tags 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 one_post.html another_article.html --tags example-tag another-tag
|
|
|
rebuild_tags() {
|
|
|
- local IFS=$'\n' # word splitting only on newline; make $* expand with newline as separator
|
|
|
if (($# < 1)); then
|
|
|
# will process all files and tags
|
|
|
- files=( $(ls -t ./*.html) )
|
|
|
+ files=( $(set +f; ls -t ./*.html) )
|
|
|
all_tags=yes
|
|
|
else
|
|
|
# will process only given files and tags
|
|
@@ -765,7 +795,7 @@ rebuild_tags() {
|
|
|
echo -n "Rebuilding tag pages "
|
|
|
n=0
|
|
|
if [[ -n $all_tags ]]; then
|
|
|
- rm -f ./"$prefix_tags"*.html
|
|
|
+ ( set +f; rm -f ./"$prefix_tags"*.html )
|
|
|
else
|
|
|
for i in "${tags[@]}"; do
|
|
|
rm -f "./$prefix_tags$i.html"
|
|
@@ -792,12 +822,12 @@ rebuild_tags() {
|
|
|
done
|
|
|
rm "$tmpfile"
|
|
|
# Now generate the tag files with headers, footers, etc
|
|
|
- while IFS='' read -r i; do
|
|
|
+ for i in $(set +f; ls -t ./"$prefix_tags"*.tmp.html 2>/dev/null); 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)
|
|
|
+ done
|
|
|
echo
|
|
|
}
|
|
|
|
|
@@ -821,11 +851,12 @@ get_post_author() {
|
|
|
list_tags() {
|
|
|
if [[ $2 == -n ]]; then do_sort=1; else do_sort=0; fi
|
|
|
|
|
|
- ls ./$prefix_tags*.html &> /dev/null
|
|
|
- (($? != 0)) && echo "No posts yet. Use 'bb.sh post' to create one" && return
|
|
|
+ if ! (set +f; set -- $prefix_tags*.html; [[ -e $1 ]]); then
|
|
|
+ echo "No posts yet. Use 'bb.sh post' to create one"
|
|
|
+ return
|
|
|
+ fi
|
|
|
|
|
|
- lines=""
|
|
|
- for i in $prefix_tags*.html; do
|
|
|
+ for i in $(set +f; printf '%s\n' $prefix_tags*.html); do
|
|
|
[[ -f "$i" ]] || break
|
|
|
nposts=$(grep -c "<\!-- text begin -->" "$i")
|
|
|
tagname=${i#"$prefix_tags"}
|
|
@@ -844,17 +875,19 @@ list_tags() {
|
|
|
|
|
|
# Displays a list of the posts
|
|
|
list_posts() {
|
|
|
- ls ./*.html &> /dev/null
|
|
|
- (($? != 0)) && echo "No posts yet. Use 'bb.sh post' to create one" && return
|
|
|
+ if ! (set +f; set -- *.html; [[ -e $1 ]]); then
|
|
|
+ echo "No posts yet. Use 'bb.sh post' to create one"
|
|
|
+ return
|
|
|
+ fi
|
|
|
|
|
|
lines=""
|
|
|
n=1
|
|
|
- while IFS='' read -r i; do
|
|
|
+ for i in $(set +f; ls -t ./*.html); do
|
|
|
is_boilerplate_file "$i" && continue
|
|
|
line="$n # $(get_post_title "$i") # $(LC_ALL=$date_locale date -r "$i" +"$date_format")"
|
|
|
lines+=$line\\n
|
|
|
n=$(( n + 1 ))
|
|
|
- done < <(ls -t ./*.html)
|
|
|
+ done
|
|
|
|
|
|
echo -e "$lines" | column -t -s "#"
|
|
|
}
|
|
@@ -877,7 +910,7 @@ make_rss() {
|
|
|
echo "<atom:link href=\"$global_url/$blog_feed\" rel=\"self\" type=\"application/rss+xml\" />"
|
|
|
|
|
|
n=0
|
|
|
- while IFS='' read -r i; do
|
|
|
+ for i in $(set +f; ls -t ./*.html); do
|
|
|
is_boilerplate_file "$i" && continue
|
|
|
((n >= number_of_feed_articles)) && break # max 10 items
|
|
|
echo -n "." 1>&3
|
|
@@ -891,7 +924,7 @@ make_rss() {
|
|
|
echo "<pubDate>$(LC_ALL=C date -r "$i" +"%a, %d %b %Y %H:%M:%S %z")</pubDate></item>"
|
|
|
|
|
|
n=$(( n + 1 ))
|
|
|
- done < <(ls -t ./*.html)
|
|
|
+ done
|
|
|
|
|
|
echo '</channel></rss>'
|
|
|
} 3>&1 >"$rssfile"
|
|
@@ -989,7 +1022,8 @@ create_css() {
|
|
|
rebuild_all_entries() {
|
|
|
echo -n "Rebuilding all entries "
|
|
|
|
|
|
- for i in ./*.html; do # no need to sort
|
|
|
+ for i in $(set +f; printf '%s\n' *.html) # no need to sort
|
|
|
+ do
|
|
|
is_boilerplate_file "$i" && continue;
|
|
|
contentfile=.tmp.$RANDOM
|
|
|
while [[ -f $contentfile ]]; do contentfile=.tmp.$RANDOM; done
|
|
@@ -1042,7 +1076,7 @@ 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
|
|
|
+ (set +f; rm -f .*.html ./*.html ./*.css ./*.rss)
|
|
|
echo
|
|
|
echo "Deleted all posts, stylesheets and feeds."
|
|
|
echo "Kept your old '.backup.tar.gz' just in case, please delete it manually if needed."
|
|
@@ -1114,9 +1148,9 @@ do_main() {
|
|
|
fi
|
|
|
|
|
|
# Test for existing html files
|
|
|
- if ls ./*.html &> /dev/null; then
|
|
|
+ if (set +f; set -- *.html; [[ -e $1 ]]); then
|
|
|
# We're going to back up just in case
|
|
|
- tar -c -z -f ".backup.tar.gz" -- *.html &&
|
|
|
+ (set +f; tar -c -z -f ".backup.tar.gz" -- *.html) &&
|
|
|
chmod 600 ".backup.tar.gz"
|
|
|
elif [[ $1 == rebuild ]]; then
|
|
|
echo "Can't find any html files, nothing to rebuild"
|