Skip to content

Commit 5fc037f

Browse files
committed
Merge branch 'master' of github.com:cfenollosa/bashblog
2 parents 2f598c1 + c6a9bef commit 5fc037f

File tree

1 file changed

+94
-55
lines changed

1 file changed

+94
-55
lines changed

bb.sh

Lines changed: 94 additions & 55 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,18 @@
55
# https://github.com/carlesfe/bashblog/contributors
66
# Check out README.md for more details
77

8+
# Some shell settings for robustness by default. These help eliminate
9+
# unexpected snags and security vulnerabilities in case someone forgets to
10+
# quote a variable somewhere. They do require a few coding adaptations.
11+
12+
IFS=$'\n' # Globally, we do word splitting only on newline (which also
13+
# makes "$*" expand with newline separator instead of space).
14+
15+
set -f # Disable globbing (pathname expansion). It can be re-enabled
16+
# locally using 'set +f'; it's handy to do this in a subshell,
17+
# for example in $(command substitution), as the globbing will
18+
# be local to the subshell.
19+
820
# Global variables
921
# It is recommended to perform a 'rebuild' after changing any of this in the code
1022

@@ -257,6 +269,14 @@ get_html_file_content() {
257269
}"
258270
}
259271
272+
# Invoke the editor specified by the $EDITOR environment variable. Use a
273+
# function for this as we need to locally word-split $EDITOR on spaces
274+
# (in case it contains arguments, like EDITOR='joe -nobackups).
275+
invoke_editor() {
276+
local IFS=$' \t\n'
277+
$EDITOR "$1"
278+
}
279+
260280
# Edit an existing, published .html file while keeping its original timestamp
261281
# Please note that this function does not automatically republish anything, as
262282
# it is usually called from 'main'.
@@ -276,7 +296,8 @@ edit() {
276296
touch_timestamp=$(LC_ALL=C date -r "${1%%.*}.html" +"$date_format_timestamp")
277297
tags_before=$(tags_in_post "${1%%.*}.html")
278298
if [[ $2 == full ]]; then
279-
$EDITOR "$1"
299+
invoke_editor "$1"
300+
touch -t "$touch_timestamp" "$1"
280301
filename=$1
281302
else
282303
if [[ ${1##*.} == md ]]; then
@@ -286,7 +307,8 @@ edit() {
286307
exit
287308
fi
288309
# editing markdown file
289-
$EDITOR "$1"
310+
invoke_editor "$1"
311+
touch -t "$touch_timestamp" "$1"
290312
TMPFILE=$(markdown "$1")
291313
filename=${1%%.*}.html
292314
else
@@ -296,27 +318,29 @@ edit() {
296318
get_post_title "$1" > "$TMPFILE"
297319
# Post text with plaintext tags
298320
get_html_file_content 'text' 'text' <"$1" | sed "/^<p>$template_tags_line_header/s|<a href='$prefix_tags\([^']*\).html'>\\1</a>|\\1|g" >> "$TMPFILE"
299-
$EDITOR "$TMPFILE"
321+
invoke_editor "$TMPFILE"
300322
filename=$1
301323
fi
302324
rm "$filename"
303325
if [[ $2 == keep ]]; then
326+
old_filename=''
304327
parse_file "$TMPFILE" "$edit_timestamp" "$filename"
305328
else
329+
old_filename=$filename # save old filename to exclude it from $relevant_posts
306330
parse_file "$TMPFILE" "$edit_timestamp" # this command sets $filename as the html processed file
307331
[[ ${1##*.} == md ]] && mv "$1" "${filename%%.*}.md" 2>/dev/null
308332
fi
309333
rm "$TMPFILE"
334+
touch -t "$touch_timestamp" "$filename"
310335
fi
311-
touch -t "$touch_timestamp" "$filename"
312-
touch -t "$touch_timestamp" "$1"
313336
chmod 644 "$filename"
314337
echo "Posted $filename"
315338
tags_after=$(tags_in_post "$filename")
316-
relevant_tags=$(echo "$tags_before $tags_after" | tr ',' ' ' | tr ' ' '\n' | sort -u | tr '\n' ' ')
317-
if [[ ! -z $relevant_tags ]]; then
318-
relevant_posts="$(posts_with_tags $relevant_tags) $filename"
319-
rebuild_tags "$relevant_posts" "$relevant_tags"
339+
relevant_tags=$(sort -u <<< "$tags_before"$'\n'"$tags_after")
340+
if [[ -n $relevant_tags ]]; then
341+
relevant_posts=$(posts_with_tags $relevant_tags)$'\n'$filename
342+
[[ -n $old_filename ]] && relevant_posts=$(grep -vFx "$old_filename" <<<"$relevant_posts")
343+
rebuild_tags $relevant_posts --tags $relevant_tags
320344
fi
321345
}
322346

@@ -488,10 +512,11 @@ create_html_page() {
488512
parse_file() {
489513
# Read for the title and check that the filename is ok
490514
title=""
491-
while IFS='' read -r line; do
515+
while read -r line; do
492516
if [[ -z $title ]]; then
493517
# remove extra <p> and </p> added by markdown
494-
title=$(echo "$line" | sed 's/<\/*p>//g')
518+
title=${line#<p>}
519+
title=${title%</p>}
495520
if [[ -n $3 ]]; then
496521
filename=$3
497522
else
@@ -513,7 +538,6 @@ parse_file() {
513538
elif [[ $line == "<p>$template_tags_line_header"* ]]; then
514539
tags=$(echo "$line" | cut -d ":" -f 2- | sed -e 's/<\/p>//g' -e 's/^ *//' -e 's/ *$//' -e 's/, /,/g')
515540
IFS=, read -r -a array <<< "$tags"
516-
517541
echo -n "<p>$template_tags_line_header " >> "$content"
518542
for item in "${array[@]}"; do
519543
echo -n "<a href='$prefix_tags$item.html'>$item</a>, "
@@ -578,7 +602,7 @@ EOF
578602
filename=""
579603
while [[ $post_status != "p" && $post_status != "P" ]]; do
580604
[[ -n $filename ]] && rm "$filename" # Delete the generated html file, if any
581-
$EDITOR "$TMPFILE"
605+
invoke_editor "$TMPFILE"
582606
if [[ $fmt == md ]]; then
583607
html_from_md=$(markdown "$TMPFILE")
584608
parse_file "$html_from_md"
@@ -620,8 +644,8 @@ EOF
620644
echo "Posted $filename"
621645
relevant_tags=$(tags_in_post $filename)
622646
if [[ -n $relevant_tags ]]; then
623-
relevant_posts="$(posts_with_tags $relevant_tags) $filename"
624-
rebuild_tags "$relevant_posts" "$relevant_tags"
647+
relevant_posts=$(posts_with_tags $relevant_tags)$'\n'$filename
648+
rebuild_tags $relevant_posts --tags $relevant_tags
625649
fi
626650
}
627651

@@ -636,7 +660,7 @@ all_posts() {
636660
{
637661
echo "<h3>$template_archive_title</h3>"
638662
prev_month=""
639-
while IFS='' read -r i; do
663+
for i in $(set +f; ls -t ./*.html); do
640664
is_boilerplate_file "$i" && continue
641665
echo -n "." 1>&3
642666
# Month headers
@@ -653,7 +677,7 @@ all_posts() {
653677
# Date
654678
date=$(LC_ALL=$date_locale date -r "$i" +"$date_format")
655679
echo " $date</li>"
656-
done < <(ls -t ./*.html)
680+
done
657681
echo "" 1>&3
658682
echo "</ul>"
659683
echo "<div id=\"all_posts\"><a href=\"./$index_file\">$template_archive_index_page</a></div>"
@@ -676,7 +700,7 @@ all_tags() {
676700
{
677701
echo "<h3>$template_tags_title</h3>"
678702
echo "<ul>"
679-
for i in $prefix_tags*.html; do
703+
for i in $(set +f; printf '%s\n' $prefix_tags*.html); do
680704
[[ -f "$i" ]] || break
681705
echo -n "." 1>&3
682706
nposts=$(grep -c "<\!-- text begin -->" "$i")
@@ -713,7 +737,8 @@ rebuild_index() {
713737
# Create the content file
714738
{
715739
n=0
716-
while IFS='' read -r i; do
740+
for i in $(set +f; ls -t ./*.html) # sort by date, newest first
741+
do
717742
is_boilerplate_file "$i" && continue;
718743
if ((n >= number_of_index_articles)); then break; fi
719744
if [[ -n $cut_do ]]; then
@@ -723,7 +748,7 @@ rebuild_index() {
723748
fi
724749
echo -n "." 1>&3
725750
n=$(( n + 1 ))
726-
done < <(ls -t ./*.html) # sort by date, newest first
751+
done
727752

728753
feed=$blog_feed
729754
if [[ -n $global_feedburner ]]; then feed=$global_feedburner; fi
@@ -740,14 +765,22 @@ rebuild_index() {
740765

741766
# Finds all tags referenced in one post.
742767
# Accepts either filename as first argument, or post content at stdin
743-
# Prints one line with space-separated tags to stdout
768+
# Prints tags to stdout, one per line.
769+
# (Since we're doing global IFS word splitting on newline only,
770+
# something like 'for tag in $(tags_in_post $i)' will work.)
744771
tags_in_post() {
745-
sed -n "/^<p>$template_tags_line_header/{s/^<p>$template_tags_line_header//;s/<[^>]*>//g;s/[ ,]\+/ /g;p;}" "$1" | tr ', ' ' '
772+
local newline=$'\n'
773+
sed -n "/^<p>$template_tags_line_header/ {
774+
s/^<p>$template_tags_line_header[[:blank:]]*//
775+
s/[[:blank:]]*<[^>]*>[[:blank:]]*//g
776+
s/[[:blank:]]*,[[:blank:]]*/\\$newline/g
777+
p
778+
}" "$1"
746779
}
747780

748781
# Finds all posts referenced in a number of tags.
749-
# Arguments are tags
750-
# Prints one line with space-separated tags to stdout
782+
# Arguments are tags.
783+
# Prints file names to stdout, one per line.
751784
posts_with_tags() {
752785
(($# < 1)) && return
753786
set -- "${@/#/$prefix_tags}"
@@ -758,58 +791,61 @@ posts_with_tags() {
758791
# Rebuilds tag_*.html files
759792
# if no arguments given, rebuilds all of them
760793
# if arguments given, they should have this format:
761-
# "FILE1 [FILE2 [...]]" "TAG1 [TAG2 [...]]"
794+
# FILE1 [FILE2 [...]] --tags TAG1 [TAG2 [...]]
762795
# where FILEn are files with posts which should be used for rebuilding tags,
763796
# and TAGn are names of tags which should be rebuilt.
764797
# example:
765-
# rebuild_tags "one_post.html another_article.html" "example-tag another-tag"
766-
# mind the quotes!
798+
# rebuild_tags one_post.html another_article.html --tags example-tag another-tag
767799
rebuild_tags() {
768-
if (($# < 2)); then
800+
if (($# < 1)); then
769801
# will process all files and tags
770-
files=$(ls -t ./*.html)
802+
files=( $(set +f; ls -t ./*.html) )
771803
all_tags=yes
772804
else
773805
# will process only given files and tags
774-
files=$(printf '%s\n' $1 | sort -u)
775-
files=$(ls -t $files)
776-
tags=$2
806+
for ((i=1; i<=$#; i++)); do
807+
[[ ${!i} == --tags ]] && break
808+
done
809+
files=( $(ls -t $(sort -u <<< "${*:1:$((i-1))}")) )
810+
tags=( "${@:$((i+1)):$#}" )
811+
all_tags=''
777812
fi
778813
echo -n "Rebuilding tag pages "
779814
n=0
780815
if [[ -n $all_tags ]]; then
781-
rm ./"$prefix_tags"*.html &> /dev/null
816+
( set +f; rm -f ./"$prefix_tags"*.html )
782817
else
783-
for i in $tags; do
784-
rm "./$prefix_tags$i.html" &> /dev/null
818+
for i in "${tags[@]}"; do
819+
rm -f "./$prefix_tags$i.html"
785820
done
786821
fi
787822
# First we will process all files and create temporal tag files
788823
# with just the content of the posts
789824
tmpfile=tmp.$RANDOM
790825
while [[ -f $tmpfile ]]; do tmpfile=tmp.$RANDOM; done
791-
while IFS='' read -r i; do
792-
is_boilerplate_file "$i" && continue;
826+
for i in "${files[@]}"; do
827+
is_boilerplate_file "$i" && continue
793828
echo -n "."
794829
if [[ -n $cut_do ]]; then
795830
get_html_file_content 'entry' 'entry' 'cut' <"$i" | awk "/$cut_line/ { print \"<p class=\\\"readmore\\\"><a href=\\\"$i\\\">$template_read_more</a></p>\" ; next } 1"
796831
else
797832
get_html_file_content 'entry' 'entry' <"$i"
798833
fi >"$tmpfile"
799834
for tag in $(tags_in_post "$i"); do
800-
if [[ -n $all_tags || " $tags " == *" $tag "* ]]; then
835+
# if either all tags or array tags[] contains $tag...
836+
if [[ -n $all_tags || $'\n'"${tags[*]}"$'\n' == *$'\n'"$tag"$'\n'* ]]; then
801837
cat "$tmpfile" >> "$prefix_tags$tag".tmp.html
802838
fi
803839
done
804-
done <<< "$files"
840+
done
805841
rm "$tmpfile"
806842
# Now generate the tag files with headers, footers, etc
807-
while IFS='' read -r i; do
843+
for i in $(set +f; ls -t ./"$prefix_tags"*.tmp.html 2>/dev/null); do
808844
tagname=${i#./"$prefix_tags"}
809845
tagname=${tagname%.tmp.html}
810846
create_html_page "$i" "$prefix_tags$tagname.html" yes "$global_title &mdash; $template_tag_title \"$tagname\"" "$global_author"
811847
rm "$i"
812-
done < <(ls -t ./"$prefix_tags"*.tmp.html 2>/dev/null)
848+
done
813849
echo
814850
}
815851

@@ -833,11 +869,12 @@ get_post_author() {
833869
list_tags() {
834870
if [[ $2 == -n ]]; then do_sort=1; else do_sort=0; fi
835871

836-
ls ./$prefix_tags*.html &> /dev/null
837-
(($? != 0)) && echo "No posts yet. Use 'bb.sh post' to create one" && return
872+
if ! (set +f; set -- $prefix_tags*.html; [[ -e $1 ]]); then
873+
echo "No posts yet. Use 'bb.sh post' to create one"
874+
return
875+
fi
838876

839-
lines=""
840-
for i in $prefix_tags*.html; do
877+
for i in $(set +f; printf '%s\n' $prefix_tags*.html); do
841878
[[ -f "$i" ]] || break
842879
nposts=$(grep -c "<\!-- text begin -->" "$i")
843880
tagname=${i#"$prefix_tags"}
@@ -856,17 +893,19 @@ list_tags() {
856893

857894
# Displays a list of the posts
858895
list_posts() {
859-
ls ./*.html &> /dev/null
860-
(($? != 0)) && echo "No posts yet. Use 'bb.sh post' to create one" && return
896+
if ! (set +f; set -- *.html; [[ -e $1 ]]); then
897+
echo "No posts yet. Use 'bb.sh post' to create one"
898+
return
899+
fi
861900

862901
lines=""
863902
n=1
864-
while IFS='' read -r i; do
903+
for i in $(set +f; ls -t ./*.html); do
865904
is_boilerplate_file "$i" && continue
866905
line="$n # $(get_post_title "$i") # $(LC_ALL=$date_locale date -r "$i" +"$date_format")"
867906
lines+=$line\\n
868907
n=$(( n + 1 ))
869-
done < <(ls -t ./*.html)
908+
done
870909

871910
echo -e "$lines" | column -t -s "#"
872911
}
@@ -889,7 +928,7 @@ make_rss() {
889928
echo "<atom:link href=\"$global_url/$blog_feed\" rel=\"self\" type=\"application/rss+xml\" />"
890929

891930
n=0
892-
while IFS='' read -r i; do
931+
for i in $(set +f; ls -t ./*.html); do
893932
is_boilerplate_file "$i" && continue
894933
((n >= number_of_feed_articles)) && break # max 10 items
895934
echo -n "." 1>&3
@@ -903,7 +942,7 @@ make_rss() {
903942
echo "<pubDate>$(LC_ALL=C date -r "$i" +"$date_format_full")</pubDate></item>"
904943

905944
n=$(( n + 1 ))
906-
done < <(ls -t ./*.html)
945+
done
907946

908947
echo '</channel></rss>'
909948
} 3>&1 >"$rssfile"
@@ -1002,7 +1041,7 @@ create_css() {
10021041
rebuild_all_entries() {
10031042
echo -n "Rebuilding all entries "
10041043

1005-
for i in ./*.html; do
1044+
for i in $(set +f; printf '%s\n' *.html); do
10061045
is_boilerplate_file "$i" && continue;
10071046
contentfile=.tmp.$RANDOM
10081047
while [[ -f $contentfile ]]; do contentfile=.tmp.$RANDOM; done
@@ -1058,7 +1097,7 @@ reset() {
10581097
echo "Are you sure you want to delete all blog entries? Please write \"Yes, I am!\" "
10591098
read -r line
10601099
if [[ $line == "Yes, I am!" ]]; then
1061-
rm .*.html ./*.html ./*.css ./*.rss &> /dev/null
1100+
(set +f; rm -f .*.html ./*.html ./*.css ./*.rss)
10621101
echo
10631102
echo "Deleted all posts, stylesheets and feeds."
10641103
echo "Kept your old '.backup.tar.gz' just in case, please delete it manually if needed."
@@ -1130,9 +1169,9 @@ do_main() {
11301169
fi
11311170

11321171
# Test for existing html files
1133-
if ls ./*.html &> /dev/null; then
1172+
if (set +f; set -- *.html; [[ -e $1 ]]); then
11341173
# We're going to back up just in case
1135-
tar -c -z -f ".backup.tar.gz" -- *.html &&
1174+
(set +f; tar -c -z -f ".backup.tar.gz" -- *.html) &&
11361175
chmod 600 ".backup.tar.gz"
11371176
elif [[ $1 == rebuild ]]; then
11381177
echo "Can't find any html files, nothing to rebuild"

0 commit comments

Comments
 (0)