5
5
# https://github.com/carlesfe/bashblog/contributors
6
6
# Check out README.md for more details
7
7
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
+
8
20
# Global variables
9
21
# It is recommended to perform a 'rebuild' after changing any of this in the code
10
22
@@ -252,6 +264,14 @@ get_html_file_content() {
252
264
}"
253
265
}
254
266
267
+ # Invoke the editor specified by the $EDITOR environment variable. Use a
268
+ # function for this as we need to locally word-split $EDITOR on spaces
269
+ # (in case it contains arguments, like EDITOR=' joe -nobackups) .
270
+ invoke_editor () {
271
+ local IFS=$' \t\n '
272
+ $EDITOR " $1 "
273
+ }
274
+
255
275
# Edit an existing, published .html file while keeping its original timestamp
256
276
# Please note that this function does not automatically republish anything, as
257
277
# it is usually called from 'main'.
@@ -270,7 +290,7 @@ edit() {
270
290
touch_timestamp=$( LC_ALL=C date -r " ${1%% .* } .html" +' %Y%m%d%H%M' )
271
291
tags_before=$( tags_in_post " ${1%% .* } .html" )
272
292
if [[ $2 == full ]]; then
273
- $EDITOR "$1"
293
+ invoke_editor " $1 "
274
294
filename=$1
275
295
else
276
296
if [[ ${1##* .} == md ]]; then
@@ -280,7 +300,7 @@ edit() {
280
300
exit
281
301
fi
282
302
# editing markdown file
283
- $EDITOR "$1"
303
+ invoke_editor " $1 "
284
304
TMPFILE=$( markdown " $1 " )
285
305
filename=${1%% .* } .html
286
306
else
@@ -290,7 +310,7 @@ edit() {
290
310
get_post_title " $1 " > " $TMPFILE "
291
311
# Post text with plaintext tags
292
312
get_html_file_content ' text' ' text' < " $1 " | sed " /^<p>$template_tags_line_header /s|<a href='$prefix_tags \([^']*\).html'>\\ 1</a>|\\ 1|g" >> " $TMPFILE "
293
- $EDITOR " $TMPFILE "
313
+ invoke_editor " $TMPFILE "
294
314
filename=$1
295
315
fi
296
316
rm " $filename "
@@ -306,10 +326,10 @@ edit() {
306
326
chmod 644 " $filename "
307
327
echo " Posted $filename "
308
328
tags_after=$( tags_in_post " $filename " )
309
- relevant_tags=$( echo " $tags_before $tags_after " | tr ' , ' ' ' | tr ' ' ' \n ' | sort -u | tr ' \n' ' ' )
310
- if [[ ! -z $relevant_tags ]]; then
311
- relevant_posts=" $( posts_with_tags $relevant_tags ) $ filename"
312
- rebuild_tags " $relevant_posts " " $relevant_tags "
329
+ relevant_tags=$( sort -u <<< " $tags_before " $ '\n ' " $tags_after " )
330
+ if [[ -n $relevant_tags ]]; then
331
+ relevant_posts=$( posts_with_tags $relevant_tags ) $' \n ' $ filename
332
+ rebuild_tags $relevant_posts --tags $relevant_tags
313
333
fi
314
334
}
315
335
@@ -475,10 +495,11 @@ create_html_page() {
475
495
parse_file () {
476
496
# Read for the title and check that the filename is ok
477
497
title=" "
478
- while IFS= ' ' read -r line; do
498
+ while read -r line; do
479
499
if [[ -z $title ]]; then
480
500
# remove extra <p> and </p> added by markdown
481
- title=$( echo " $line " | sed ' s/<\/*p>//g' )
501
+ title=${line# <p>}
502
+ title=${title% </ p>}
482
503
if [[ -n $3 ]]; then
483
504
filename=$3
484
505
else
@@ -498,13 +519,14 @@ parse_file() {
498
519
content=$filename .tmp
499
520
# Parse possible tags
500
521
elif [[ $line == " <p>$template_tags_line_header " * ]]; then
501
- tags=$( echo " $line " | cut -d " :" -f 2- | sed -e ' s/<\/p>//g' -e ' s/^ *//' -e ' s/ *$//' -e ' s/, /,/g' )
502
- IFS=, read -r -a array <<< " $tags"
503
-
504
522
echo -n " <p>$template_tags_line_header " >> " $content "
505
- for item in " ${array[@]} " ; do
506
- echo -n " <a href='$prefix_tags$item .html'>$item </a>, "
507
- done | sed ' s/, $/<\/p>/g' >> " $content "
523
+ sed " s%</p>%%g
524
+ s/^.*:[[:blank:]]*//
525
+ s/[[:blank:]]\$ //
526
+ s/[[:blank:]]*,[[:blank:]]*/,/g
527
+ s%\([^,]*\),%<a href='$prefix_tags \1.html'>\1</a>, %g
528
+ s%, \([^,]*\)\$ %, <a href='$prefix_tags \1.html'>\1</a></p>%
529
+ " <<< " $line" >> " $content "
508
530
else
509
531
echo " $line " >> " $content "
510
532
fi
565
587
filename=" "
566
588
while [[ $post_status != " p" && $post_status != " P" ]]; do
567
589
[[ -n $filename ]] && rm " $filename " # Delete the generated html file, if any
568
- $EDITOR " $TMPFILE "
590
+ invoke_editor " $TMPFILE "
569
591
if [[ $fmt == md ]]; then
570
592
html_from_md=$( markdown " $TMPFILE " )
571
593
parse_file " $html_from_md "
607
629
echo " Posted $filename "
608
630
relevant_tags=$( tags_in_post $filename )
609
631
if [[ -n $relevant_tags ]]; then
610
- relevant_posts=" $( posts_with_tags $relevant_tags ) $ filename"
611
- rebuild_tags " $relevant_posts " " $relevant_tags "
632
+ relevant_posts=$( posts_with_tags $relevant_tags ) $' \n ' $ filename
633
+ rebuild_tags $relevant_posts --tags $relevant_tags
612
634
fi
613
635
}
614
636
@@ -623,7 +645,7 @@ all_posts() {
623
645
{
624
646
echo " <h3>$template_archive_title </h3>"
625
647
prev_month=" "
626
- while IFS= ' ' read -r i ; do
648
+ for i in $( set +f ; ls -t ./ * .html ) ; do
627
649
is_boilerplate_file " $i " && continue
628
650
echo -n " ." 1>&3
629
651
# Month headers
@@ -640,7 +662,7 @@ all_posts() {
640
662
# Date
641
663
date=$( LC_ALL=$date_locale date -r " $i " +" $date_format " )
642
664
echo " $date </li>"
643
- done < <( ls -t ./ * .html )
665
+ done
644
666
echo " " 1>&3
645
667
echo " </ul>"
646
668
echo " <div id=\" all_posts\" ><a href=\" ./$index_file \" >$template_archive_index_page </a></div>"
@@ -663,7 +685,7 @@ all_tags() {
663
685
{
664
686
echo " <h3>$template_tags_title </h3>"
665
687
echo " <ul>"
666
- for i in $prefix_tags * .html; do
688
+ for i in $( set +f ; printf ' %s\n ' $ prefix_tags* .html) ; do
667
689
[[ -f " $i " ]] || break
668
690
echo -n " ." 1>&3
669
691
nposts=$( grep -c " <\!-- text begin -->" " $i " )
@@ -696,7 +718,8 @@ rebuild_index() {
696
718
# Create the content file
697
719
{
698
720
n=0
699
- while IFS=' ' read -r i; do
721
+ for i in $( set +f; ls -t ./* .html) # sort by date, newest first
722
+ do
700
723
is_boilerplate_file " $i " && continue ;
701
724
if (( n >= number_of_index_articles)) ; then break ; fi
702
725
if [[ -n $cut_do ]]; then
@@ -706,7 +729,7 @@ rebuild_index() {
706
729
fi
707
730
echo -n " ." 1>&3
708
731
n=$(( n + 1 ))
709
- done < <( ls -t ./ * .html ) # sort by date, newest first
732
+ done
710
733
711
734
feed=$blog_feed
712
735
if [[ -n $global_feedburner ]]; then feed=$global_feedburner ; fi
@@ -723,9 +746,18 @@ rebuild_index() {
723
746
724
747
# Finds all tags referenced in one post.
725
748
# Accepts either filename as first argument, or post content at stdin
726
- # Prints one line with space-separated tags to stdout
749
+ # Prints tags to stdout, one per line.
750
+ # (Since we're doing global IFS word splitting on newline only,
751
+ # something like 'for tag in $(tags_in_post $i)' will work.)
727
752
tags_in_post () {
728
- sed -n " /^<p>$template_tags_line_header /{s/^<p>$template_tags_line_header //;s/<[^>]*>//g;s/[ ,]\+/ /g;p;}" " $1 " | tr ' , ' ' '
753
+ local newline=$' \n '
754
+ sed -n " /^<p>$template_tags_line_header / {
755
+ s/^<p>$template_tags_line_header [[:blank:]]*//
756
+ s/[[:blank:]]*<[^>]*>[[:blank:]]*//g
757
+ s/[[:blank:]]*,[[:blank:]]*/,/g
758
+ s/,\+/\\ $newline /g
759
+ p
760
+ }" " $1 "
729
761
}
730
762
731
763
# Finds all posts referenced in a number of tags.
@@ -741,17 +773,15 @@ posts_with_tags() {
741
773
# Rebuilds tag_*.html files
742
774
# if no arguments given, rebuilds all of them
743
775
# if arguments given, they should have this format:
744
- # " FILE1 [FILE2 [...]]" " TAG1 [TAG2 [...]]"
776
+ # FILE1 [FILE2 [...]] --tags TAG1 [TAG2 [...]]
745
777
# where FILEn are files with posts which should be used for rebuilding tags,
746
778
# and TAGn are names of tags which should be rebuilt.
747
779
# example:
748
- # rebuild_tags "one_post.html another_article.html" "example-tag another-tag"
749
- # mind the quotes!
780
+ # rebuild_tags one_post.html another_article.html --tags example-tag another-tag
750
781
rebuild_tags () {
751
- local IFS=$' \n ' # word splitting only on newline; make $* expand with newline as separator
752
782
if (( $# < 1 )) ; then
753
783
# will process all files and tags
754
- files=( $( ls -t ./* .html) )
784
+ files=( $( set +f ; ls -t ./* .html) )
755
785
all_tags=yes
756
786
else
757
787
# will process only given files and tags
@@ -765,7 +795,7 @@ rebuild_tags() {
765
795
echo -n " Rebuilding tag pages "
766
796
n=0
767
797
if [[ -n $all_tags ]]; then
768
- rm -f ./" $prefix_tags " * .html
798
+ ( set +f ; rm -f ./" $prefix_tags " * .html )
769
799
else
770
800
for i in " ${tags[@]} " ; do
771
801
rm -f " ./$prefix_tags$i .html"
@@ -792,12 +822,12 @@ rebuild_tags() {
792
822
done
793
823
rm " $tmpfile "
794
824
# Now generate the tag files with headers, footers, etc
795
- while IFS= ' ' read -r i ; do
825
+ for i in $( set +f ; ls -t ./ " $prefix_tags " * .tmp.html 2> /dev/null ) ; do
796
826
tagname=${i# ./ " $prefix_tags " }
797
827
tagname=${tagname% .tmp.html}
798
828
create_html_page " $i " " $prefix_tags$tagname .html" yes " $global_title — $template_tag_title \" $tagname \" " " $global_author "
799
829
rm " $i "
800
- done < <( ls -t ./ " $prefix_tags " * .tmp.html 2> /dev/null )
830
+ done
801
831
echo
802
832
}
803
833
@@ -821,11 +851,12 @@ get_post_author() {
821
851
list_tags () {
822
852
if [[ $2 == -n ]]; then do_sort=1; else do_sort=0; fi
823
853
824
- ls ./$prefix_tags * .html & > /dev/null
825
- (( $? != 0 )) && echo " No posts yet. Use 'bb.sh post' to create one" && return
854
+ if ! (set +f; set -- $prefix_tags * .html; [[ -e $1 ]]); then
855
+ echo " No posts yet. Use 'bb.sh post' to create one"
856
+ return
857
+ fi
826
858
827
- lines=" "
828
- for i in $prefix_tags * .html; do
859
+ for i in $( set +f; printf ' %s\n' $prefix_tags * .html) ; do
829
860
[[ -f " $i " ]] || break
830
861
nposts=$( grep -c " <\!-- text begin -->" " $i " )
831
862
tagname=${i# " $prefix_tags " }
@@ -844,17 +875,19 @@ list_tags() {
844
875
845
876
# Displays a list of the posts
846
877
list_posts () {
847
- ls ./* .html & > /dev/null
848
- (( $? != 0 )) && echo " No posts yet. Use 'bb.sh post' to create one" && return
878
+ if ! (set +f; set -- * .html; [[ -e $1 ]]); then
879
+ echo " No posts yet. Use 'bb.sh post' to create one"
880
+ return
881
+ fi
849
882
850
883
lines=" "
851
884
n=1
852
- while IFS= ' ' read -r i ; do
885
+ for i in $( set +f ; ls -t ./ * .html ) ; do
853
886
is_boilerplate_file " $i " && continue
854
887
line=" $n # $( get_post_title " $i " ) # $( LC_ALL=$date_locale date -r " $i " +" $date_format " ) "
855
888
lines+=$line \\ n
856
889
n=$(( n + 1 ))
857
- done < <( ls -t ./ * .html )
890
+ done
858
891
859
892
echo -e " $lines " | column -t -s " #"
860
893
}
@@ -877,7 +910,7 @@ make_rss() {
877
910
echo " <atom:link href=\" $global_url /$blog_feed \" rel=\" self\" type=\" application/rss+xml\" />"
878
911
879
912
n=0
880
- while IFS= ' ' read -r i ; do
913
+ for i in $( set +f ; ls -t ./ * .html ) ; do
881
914
is_boilerplate_file " $i " && continue
882
915
(( n >= number_of_feed_articles)) && break # max 10 items
883
916
echo -n " ." 1>&3
@@ -891,7 +924,7 @@ make_rss() {
891
924
echo " <pubDate>$( LC_ALL=C date -r " $i " +" %a, %d %b %Y %H:%M:%S %z" ) </pubDate></item>"
892
925
893
926
n=$(( n + 1 ))
894
- done < <( ls -t ./ * .html )
927
+ done
895
928
896
929
echo ' </channel></rss>'
897
930
} 3>&1 > " $rssfile "
@@ -989,7 +1022,8 @@ create_css() {
989
1022
rebuild_all_entries () {
990
1023
echo -n " Rebuilding all entries "
991
1024
992
- for i in ./* .html; do # no need to sort
1025
+ for i in $( set +f; printf ' %s\n' * .html) # no need to sort
1026
+ do
993
1027
is_boilerplate_file " $i " && continue ;
994
1028
contentfile=.tmp.$RANDOM
995
1029
while [[ -f $contentfile ]]; do contentfile=.tmp.$RANDOM ; done
@@ -1042,7 +1076,7 @@ reset() {
1042
1076
echo " Are you sure you want to delete all blog entries? Please write \" Yes, I am!\" "
1043
1077
read -r line
1044
1078
if [[ $line == " Yes, I am!" ]]; then
1045
- rm .* .html ./* .html ./* .css ./* .rss & > /dev/null
1079
+ (set +f ; rm -f .* .html ./* .html ./* .css ./* .rss)
1046
1080
echo
1047
1081
echo " Deleted all posts, stylesheets and feeds."
1048
1082
echo " Kept your old '.backup.tar.gz' just in case, please delete it manually if needed."
@@ -1114,9 +1148,9 @@ do_main() {
1114
1148
fi
1115
1149
1116
1150
# Test for existing html files
1117
- if ls ./ * .html & > /dev/null ; then
1151
+ if (set +f ; set -- * .html; [[ -e $1 ]]) ; then
1118
1152
# We're going to back up just in case
1119
- tar -c -z -f " .backup.tar.gz" -- * .html &&
1153
+ (set +f ; tar -c -z -f " .backup.tar.gz" -- * .html) &&
1120
1154
chmod 600 " .backup.tar.gz"
1121
1155
elif [[ $1 == rebuild ]]; then
1122
1156
echo " Can't find any html files, nothing to rebuild"
0 commit comments