bb.sh 43 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092
  1. #!/usr/bin/env bash
  2. # BashBlog, a simple blog system written in a single bash script
  3. # Copyright: Carlos Fenollosa <carlos.fenollosa@gmail.com>, 2011-2014
  4. # With contributions from many others:
  5. # https://github.com/carlesfe/bashblog/contributors
  6. #########################################################################################
  7. #
  8. # README
  9. #
  10. #########################################################################################
  11. #
  12. # This is a very basic blog system
  13. #
  14. # Basically it asks the user to create a text file, then converts it into a .html file
  15. # and then rebuilds the index.html and feed.rss.
  16. #
  17. # Comments are supported via external service (Disqus).
  18. # Markdown syntax is supported via third party library (e.g. Gruber's Markdown.pl)
  19. #
  20. # This script is standalone, it doesn't require any other file to run
  21. #
  22. # Files that this script generates:
  23. # - main.css (inherited from my web page) and blog.css (blog-specific stylesheet)
  24. # - one .html for each post
  25. # - one tag_*.html file for each tag
  26. # - index.html (regenerated each run)
  27. # - feed.rss (idem)
  28. # - all_posts.html (idem)
  29. # - all_tags.html (idem)
  30. # - it also generates temporal files, which are removed afterwards
  31. #
  32. # It generates valid html and rss files, so keep care to use valid xhtml when editing a post
  33. #
  34. # There are many loops which iterate on '*.html' so make sure not to manually put other
  35. # html files on this folder.
  36. #
  37. # Read more: https://github.com/cfenollosa/bashblog
  38. #########################################################################################
  39. #
  40. # LICENSE
  41. #
  42. #########################################################################################
  43. #
  44. # This program is free software: you can redistribute it and/or modify
  45. # it under the terms of the GNU General Public License as published by
  46. # the Free Software Foundation, either version 3 of the License, or
  47. # (at your option) any later version.
  48. #
  49. # This program is distributed in the hope that it will be useful,
  50. # but WITHOUT ANY WARRANTY; without even the implied warranty of
  51. # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  52. # GNU General Public License for more details.
  53. #
  54. # You should have received a copy of the GNU General Public License
  55. # along with this program. If not, see <http://www.gnu.org/licenses/>.
  56. #########################################################################################
  57. #
  58. # CHANGELOG
  59. #
  60. #########################################################################################
  61. #
  62. # 2.1 Support for tags/categories
  63. # 'delete' command
  64. # 2.0.3 Support for other analytics code, via external file
  65. # 2.0.2 Fixed bug when $body_begin_file was empty
  66. # Added extra line in the footer linking to the github project
  67. # 2.0.1 Allow personalized header/footer files
  68. # 2.0 Added Markdown support
  69. # Fully support BSD date
  70. # 1.6.4 Fixed bug in localized dates
  71. # 1.6.3 Now supporting BSD date
  72. # 1.6.2 Simplified some functions and variables to avoid duplicated information
  73. # 1.6.1 'date' fix when hours are 1 digit.
  74. # 1.6.0 Disqus comments. External configuration file. Check of 'date' command version.
  75. # 1.5.1 Misc bugfixes and parameter checks
  76. # 1.5 Đurađ Radojičić (djura-san) refactored some code and added flexibility and i18n
  77. # 1.4.2 Now issues are handled at Github
  78. # 1.4.1 Some code refactoring
  79. # 1.4 Using twitter for comments, improved 'rebuild' command
  80. # 1.3 'edit' command
  81. # 1.2.2 Feedburner support
  82. # 1.2.1 Fixed the timestamps bug
  83. # 1.2 'list' command
  84. # 1.1 Draft and preview support
  85. # 1.0 Read http://is.gd/Bkdoru
  86. #########################################################################################
  87. #
  88. # CODE
  89. #
  90. #########################################################################################
  91. #
  92. # As usual with bash scripts, scroll all the way to the bottom for the main routine
  93. # All other functions are declared above main.
  94. # Global variables
  95. # It is recommended to perform a 'rebuild' after changing any of this in the code
  96. # Config file. Any settings "key=value" written there will override the
  97. # global_variables defaults. Useful to avoid editing bb.sh and having to deal
  98. # with merges in VCS
  99. global_config=".config"
  100. # This function will load all the variables defined here. They might be overriden
  101. # by the 'global_config' file contents
  102. global_variables() {
  103. global_software_name="BashBlog"
  104. global_software_version="2.1"
  105. # Blog title
  106. global_title="My fancy blog"
  107. # The typical subtitle for each blog
  108. global_description="A blog about turtles and carrots"
  109. # The public base URL for this blog
  110. global_url="http://example.com/blog"
  111. # Your name
  112. global_author="John Smith"
  113. # You can use twitter or facebook or anything for global_author_url
  114. global_author_url="http://twitter.com/example"
  115. # Your email
  116. global_email="john@smith.com"
  117. # CC by-nc-nd is a good starting point, you can change this to "&copy;" for Copyright
  118. global_license="CC by-nc-nd"
  119. # If you have a Google Analytics ID (UA-XXXXX) and wish to use the standard
  120. # embedding code, put it on global_analytics
  121. # If you have custom analytics code (i.e. non-google) or want to use the Universal
  122. # code, leave global_analytics empty and specify a global_analytics_file
  123. global_analytics=""
  124. global_analytics_file=""
  125. # Leave this empty (i.e. "") if you don't want to use feedburner,
  126. # or change it to your own URL
  127. global_feedburner=""
  128. # Change this to your username if you want to use twitter for comments
  129. global_twitter_username=""
  130. # Change this to your disqus username to use disqus for comments
  131. global_disqus_username=""
  132. # Blog generated files
  133. # index page of blog (it is usually good to use "index.html" here)
  134. index_file="index.html"
  135. number_of_index_articles="8"
  136. # global archive
  137. archive_index="all_posts.html"
  138. tags_index="all_tags.html"
  139. # feed file (rss in this case)
  140. blog_feed="feed.rss"
  141. number_of_feed_articles="10"
  142. # "cut" blog entry when putting it to index page
  143. # i.e. include only up to first <hr> (---- in markdown)
  144. # possible values: "cut", ""
  145. cut_do="cut"
  146. # Regexp matching the HTML line where to do the cut
  147. # note that slash is regexp separator so you need to prepend it with backslash
  148. cut_line='<hr ?\/?>'
  149. # save markdown file when posting with "bb post -m"
  150. # possible values: "yes", ""
  151. save_markdown="yes"
  152. # prefix for tags/categories files
  153. # please make sure that no other html file starts with this prefix
  154. prefix_tags="tag_"
  155. # personalized header and footer (only if you know what you're doing)
  156. # DO NOT name them .header.html, .footer.html or they will be overwritten
  157. # leave blank to generate them, recommended
  158. header_file=""
  159. footer_file=""
  160. # extra content to add just after we open the <body> tag
  161. # and before the actual blog content
  162. body_begin_file=""
  163. # CSS files to include on every page, f.ex. css_include=('main.css' 'blog.css')
  164. # leave empty to use generated
  165. css_include=()
  166. # Localization and i18n
  167. # "Comments?" (used in twitter link after every post)
  168. template_comments="Comments?"
  169. # "Read more..." (link under cut article on index page)
  170. template_read_more="Read more..."
  171. # "View more posts" (used on bottom of index page as link to archive)
  172. template_archive="View more posts"
  173. # "All posts" (title of archive page)
  174. template_archive_title="All posts"
  175. # "All tags"
  176. template_tags_title="All tags"
  177. # "posts" (on "All tags" page, text at the end of each tag line, like "2. Music - 15 posts")
  178. template_tags_posts="posts"
  179. # "Posts tagged" (text on a title of a page with index of one tag, like "My Blog - Posts tagged "Music"")
  180. template_tag_title="Posts tagged"
  181. # "Tags:" (beginning of line in HTML file with list of all tags for this article)
  182. template_tags_line_header="Tags:"
  183. # "Back to the index page" (used on archive page, it is link to blog index)
  184. template_archive_index_page="Back to the index page"
  185. # "Subscribe" (used on bottom of index page, it is link to RSS feed)
  186. template_subscribe="Subscribe"
  187. # "Subscribe to this page..." (used as text for browser feed button that is embedded to html)
  188. template_subscribe_browser_button="Subscribe to this page..."
  189. # "Tweet" (used as twitter text button for posting to twitter)
  190. template_twitter_button="Tweet"
  191. template_twitter_comment="&lt;Type your comment here but please leave the URL so that other people can follow the comments&gt;"
  192. # The locale to use for the dates displayed on screen (not for the timestamps)
  193. date_format="%B %d, %Y"
  194. date_locale="C"
  195. # Markdown location. Trying to autodetect by default.
  196. # The invocation must support the signature 'markdown_bin in.html > out.md'
  197. markdown_bin="$(which Markdown.pl)"
  198. }
  199. # Check for the validity of some variables
  200. # DO NOT EDIT THIS FUNCTION unless you know what you're doing
  201. global_variables_check() {
  202. [[ "$header_file" == ".header.html" ]] &&
  203. echo "Please check your configuration. '.header.html' is not a valid value for the setting 'header_file'" &&
  204. exit
  205. [[ "$footer_file" == ".footer.html" ]] &&
  206. echo "Please check your configuration. '.footer.html' is not a valid value for the setting 'footer_file'" &&
  207. exit
  208. }
  209. # Test if the markdown script is working correctly
  210. test_markdown() {
  211. [[ -z "$markdown_bin" ]] && return 1
  212. [[ -z "$(which diff)" ]] && return 1
  213. in="/tmp/md-in-$(echo $RANDOM).md"
  214. out="/tmp/md-out-$(echo $RANDOM).html"
  215. good="/tmp/md-good-$(echo $RANDOM).html"
  216. echo -e "line 1\n\nline 2" > $in
  217. echo -e "<p>line 1</p>\n\n<p>line 2</p>" > $good
  218. $markdown_bin $in > $out 2> /dev/null
  219. diff $good $out &> /dev/null # output is irrelevant, we'll check $?
  220. if [[ $? -ne 0 ]]; then
  221. rm -f $in $good $out
  222. return 1
  223. fi
  224. rm -f $in $good $out
  225. return 0
  226. }
  227. # Parse a Markdown file into HTML and return the generated file
  228. markdown() {
  229. out="$(echo $1 | sed 's/md$/html/g')"
  230. while [ -f "$out" ]; do out="$(echo $out | sed 's/\.html$/\.'$RANDOM'\.html/')"; done
  231. $markdown_bin $1 > $out
  232. echo $out
  233. }
  234. # Prints the required google analytics code
  235. google_analytics() {
  236. [[ -z "$global_analytics" ]] && [[ -z "$global_analytics_file" ]] && return
  237. if [[ -z "$global_analytics_file" ]]; then
  238. echo "<script type=\"text/javascript\">
  239. var _gaq = _gaq || [];
  240. _gaq.push(['_setAccount', '"$global_analytics"']);
  241. _gaq.push(['_trackPageview']);
  242. (function() {
  243. var ga = document.createElement('script'); ga.type = 'text/javascript'; ga.async = true;
  244. ga.src = ('https:' == document.location.protocol ? 'https://ssl' : 'http://www') + '.google-analytics.com/ga.js';
  245. var s = document.getElementsByTagName('script')[0]; s.parentNode.insertBefore(ga, s);
  246. })();
  247. </script>"
  248. else
  249. cat "$global_analytics_file"
  250. fi
  251. }
  252. # Prints the required code for disqus comments
  253. disqus_body() {
  254. [[ -z "$global_disqus_username" ]] && return
  255. echo '<div id="disqus_thread"></div>
  256. <script type="text/javascript">
  257. /* * * CONFIGURATION VARIABLES: EDIT BEFORE PASTING INTO YOUR WEBPAGE * * */
  258. var disqus_shortname = '\'$global_disqus_username\''; // required: replace example with your forum shortname
  259. /* * * DONT EDIT BELOW THIS LINE * * */
  260. (function() {
  261. var dsq = document.createElement("script"); dsq.type = "text/javascript"; dsq.async = true;
  262. dsq.src = "//" + disqus_shortname + ".disqus.com/embed.js";
  263. (document.getElementsByTagName("head")[0] || document.getElementsByTagName("body")[0]).appendChild(dsq);
  264. })();
  265. </script>
  266. <noscript>Please enable JavaScript to view the <a href="http://disqus.com/?ref_noscript">comments powered by Disqus.</a></noscript>
  267. <a href="http://disqus.com" class="dsq-brlink">comments powered by <span class="logo-disqus">Disqus</span></a>'
  268. }
  269. # Prints the required code for disqus in the footer
  270. disqus_footer() {
  271. [[ -z "$global_disqus_username" ]] && return
  272. echo '<script type="text/javascript">
  273. /* * * CONFIGURATION VARIABLES: EDIT BEFORE PASTING INTO YOUR WEBPAGE * * */
  274. var disqus_shortname = '\'$global_disqus_username\''; // required: replace example with your forum shortname
  275. /* * * DONT EDIT BELOW THIS LINE * * */
  276. (function () {
  277. var s = document.createElement("script"); s.async = true;
  278. s.type = "text/javascript";
  279. s.src = "//" + disqus_shortname + ".disqus.com/count.js";
  280. (document.getElementsByTagName("HEAD")[0] || document.getElementsByTagName("BODY")[0]).appendChild(s);
  281. }());
  282. </script>'
  283. }
  284. # Reads HTML file from stdin, prints its content to stdout
  285. # $1 where to start ("text" or "entry")
  286. # $2 where to stop ("text" or "entry")
  287. # $3 "cut" to remove text from <hr /> to <!-- text end -->
  288. # note that this does not remove <hr /> line itself,
  289. # so you can see if text was cut or not
  290. get_html_file_content() {
  291. awk '/<!-- '$1' begin -->/, /<!-- '$2' end -->/{
  292. if (!/<!-- '$1' begin -->/ && !/<!-- '$2' end -->/) print
  293. if ("'$3'" == "cut" && /'"$cut_line"'/){
  294. if ("'$2'" == "text") exit # no need to read further
  295. while (getline > 0 && !/<!-- text end -->/) {}
  296. }
  297. }'
  298. }
  299. # Edit an existing, published .html file while keeping its original timestamp
  300. # Please note that this function does not automatically republish anything, as
  301. # it is usually called from 'main'.
  302. #
  303. # Note that it edits HTML file, even if you wrote the post as markdown originally
  304. # Note that if you edit title then filename might also change
  305. #
  306. # $1 the file to edit
  307. # $2 (optional) edit mode:
  308. # "keep" to keep old filename
  309. # "full" to edit full HTML, and not only text part (keeps old filename)
  310. # leave empty for default behavior (edit only text part and change name)
  311. edit() {
  312. # Original post timestamp
  313. edit_timestamp="$(LC_ALL=$date_locale date -r "${1%%.*}.html" +"%a, %d %b %Y %H:%M:%S %z" )"
  314. touch_timestamp="$(LC_ALL=$date_locale date -r "${1%%.*}.html" +'%Y%m%d%H%M')"
  315. if [ "$2" = "full" ]; then
  316. $EDITOR "$1"
  317. filename="$1"
  318. else
  319. if [[ "${1##*.}" == "md" ]]; then
  320. # editing markdown file
  321. $EDITOR "$1"
  322. TMPFILE="$(markdown "$1")"
  323. filename="${1%%.*}.html"
  324. else
  325. # Create the content file
  326. TMPFILE="$(basename $1).$RANDOM.html"
  327. # Title
  328. echo "$(get_post_title $1)" > "$TMPFILE"
  329. # Post text with plaintext tags
  330. get_html_file_content 'text' 'text' <$1 | sed "/^<p>$template_tags_line_header/s|<a href='$prefix_tags\([^']*\).html'>\\1</a>|\\1|g" >> "$TMPFILE"
  331. $EDITOR "$TMPFILE"
  332. filename="$1"
  333. fi
  334. rm "$filename"
  335. if [ "$2" = "keep" ]; then
  336. parse_file "$TMPFILE" "$edit_timestamp" "$filename"
  337. else
  338. parse_file "$TMPFILE" "$edit_timestamp" # this command sets $filename as the html processed file
  339. [[ "${1##*.}" == "md" ]] && mv "$1" "${filename%%.*}.md" 2>/dev/null
  340. fi
  341. rm "$TMPFILE"
  342. fi
  343. touch -t "$touch_timestamp" "$filename"
  344. chmod 644 "$filename"
  345. echo "Posted $filename"
  346. }
  347. # Adds the code needed by the twitter button
  348. #
  349. # $1 the post URL
  350. twitter() {
  351. [[ -z "$global_twitter_username" ]] && return
  352. if [[ -z "$global_disqus_username" ]]; then
  353. echo "<p id='twitter'>$template_comments&nbsp;"
  354. else
  355. echo "<p id='twitter'><a href=\"$1#disqus_thread\">$template_comments</a> &nbsp;"
  356. fi
  357. echo "<a href=\"https://twitter.com/share\" class=\"twitter-share-button\" data-text=\"$template_twitter_comment\" data-url=\"$1\""
  358. echo " data-via=\"$global_twitter_username\""
  359. echo ">$template_twitter_button</a> <script>!function(d,s,id){var js,fjs=d.getElementsByTagName(s)[0];if(!d.getElementById(id)){js=d.createElement(s);js.id=id;js.src=\"//platform.twitter.com/widgets.js\";fjs.parentNode.insertBefore(js,fjs);}}(document,\"script\",\"twitter-wjs\");</script>"
  360. echo "</p>"
  361. }
  362. # Check if the file is a 'boilerplate' (i.e. not a post)
  363. # The return values are designed to be used like this inside a loop:
  364. # is_boilerplate_file <file> && continue
  365. #
  366. # $1 the file
  367. #
  368. # Return 0 (bash return value 'true') if the input file is am index, feed, etc
  369. # or 1 (bash return value 'false') if it is a blogpost
  370. is_boilerplate_file() {
  371. if [[ "$1" == "$index_file" ]] || [[ "$1" == "$archive_index" ]] || [[ "$1" == "$tags_index" ]] || [[ "$1" == "$footer_file" ]] || [[ "$1" == "$header_file" ]] || [[ "$1" == "$global_analytics_file" ]] || [[ "$1" = "$prefix_tags"* ]] ; then return 0
  372. else return 1
  373. fi
  374. }
  375. # Adds all the bells and whistles to format the html page
  376. # Every blog post is marked with a <!-- entry begin --> and <!-- entry end -->
  377. # which is parsed afterwards in the other functions. There is also a marker
  378. # <!-- text begin --> to determine just the beginning of the text body of the post
  379. #
  380. # $1 a file with the body of the content
  381. # $2 the output file
  382. # $3 "yes" if we want to generate the index.html,
  383. # "no" to insert new blog posts
  384. # $4 title for the html header
  385. # $5 original blog timestamp
  386. create_html_page() {
  387. content="$1"
  388. filename="$2"
  389. index="$3"
  390. title="$4"
  391. timestamp="$5"
  392. # Create the actual blog post
  393. # html, head
  394. cat ".header.html" > "$filename"
  395. echo "<title>$title</title>" >> "$filename"
  396. google_analytics >> "$filename"
  397. echo "</head><body>" >> "$filename"
  398. # stuff to add before the actual body content
  399. [[ -n "$body_begin_file" ]] && cat "$body_begin_file" >> "$filename"
  400. # body divs
  401. echo '<div id="divbodyholder">' >> "$filename"
  402. echo '<div class="headerholder"><div class="header">' >> "$filename"
  403. # blog title
  404. echo '<div id="title">' >> "$filename"
  405. cat .title.html >> "$filename"
  406. echo '</div></div></div>' >> "$filename" # title, header, headerholder
  407. echo '<div id="divbody"><div class="content">' >> "$filename"
  408. file_url="$(sed 's/.rebuilt//g' <<< $filename)" # Get the correct URL when rebuilding
  409. # one blog entry
  410. if [[ "$index" == "no" ]]; then
  411. echo '<!-- entry begin -->' >> "$filename" # marks the beginning of the whole post
  412. echo '<h3><a class="ablack" href="'$file_url'">' >> "$filename"
  413. # remove possible <p>'s on the title because of markdown conversion
  414. echo "$(echo "$title" | sed 's/<\/*p>//g')" >> "$filename"
  415. echo '</a></h3>' >> "$filename"
  416. if [[ "$timestamp" == "" ]]; then
  417. echo '<div class="subtitle">'$(LC_ALL=$date_locale date +"$date_format")' &mdash; ' >> "$filename"
  418. else
  419. echo '<div class="subtitle">'$(LC_ALL=$date_locale date +"$date_format" --date="$timestamp") ' &mdash; ' >> "$filename"
  420. fi
  421. echo "$global_author</div>" >> "$filename"
  422. echo '<!-- text begin -->' >> "$filename" # This marks the text body, after the title, date...
  423. fi
  424. cat "$content" >> "$filename" # Actual content
  425. if [[ "$index" == "no" ]]; then
  426. echo '<!-- text end -->' >> "$filename"
  427. twitter "$global_url/$file_url" >> "$filename"
  428. echo '<!-- entry end -->' >> "$filename" # absolute end of the post
  429. fi
  430. echo '</div>' >> "$filename" # content
  431. # Add disqus commments except for index and all_posts pages
  432. if [[ ${filename%.*.*} != "index" && ${filename%.*.*} != "all_posts" ]]; then
  433. disqus_body >> "$filename"
  434. fi
  435. # page footer
  436. cat .footer.html >> "$filename"
  437. # close divs
  438. echo '</div></div>' >> "$filename" # divbody and divbodyholder
  439. disqus_footer >> "$filename"
  440. echo '</body></html>' >> "$filename"
  441. }
  442. # Parse the plain text file into an html file
  443. #
  444. # $1 source file name
  445. # $2 (optional) timestamp for the file
  446. # $3 (optional) destination file name
  447. # note that although timestamp is optional, something must be provided at its
  448. # place if destination file name is provided, i.e:
  449. # parse_file source.txt "" destination.html
  450. parse_file() {
  451. # Read for the title and check that the filename is ok
  452. title=""
  453. while IFS='' read -r line; do
  454. if [[ "$title" == "" ]]; then
  455. # set title and
  456. # remove extra <p> and </p> added by markdown
  457. title=$(echo "$line" | sed 's/<\/*p>//g')
  458. if [ "$3" ]; then
  459. filename=$3
  460. else
  461. filename="$(echo $title | tr [:upper:] [:lower:])"
  462. filename="$(echo $filename | sed 's/\ /-/g')"
  463. filename="$(echo $filename | sed 'y/йцукенгшщзхъфывапролджэячсмитьбю/jcukengsszh-fyvaproldzeahsmit-by/')"
  464. filename="$(echo $filename | sed 'y/ЙЦУКЕНГШЩЗХЪФЫВАПРОЛДЖЭЯЧСМИТЬБЮ/jcukengsszh-fyvaproldzeahsmit-by/')"
  465. filename="$(echo $filename | tr -dc '[:alnum:]-')" # html likes alphanumeric
  466. filename="$(echo $filename | sed 's/^-*//')" # unix utilities are unhappy if filename starts with -
  467. [ "$filename" ] || filename=$RANDOM # if filename gets empty, put something in it
  468. filename="$filename.html"
  469. # Check for duplicate file names
  470. while [ -f "$filename" ]; do
  471. suffix="$RANDOM"
  472. filename="$(echo $filename | sed 's/\.html/'$suffix'\.html/g')"
  473. done
  474. fi
  475. content="$filename.tmp"
  476. # Parse possible tags
  477. elif [[ "$line" = "<p>$template_tags_line_header"* ]]; then
  478. tags="$(echo "$line" | cut -d ":" -f 2- | sed -e 's/<\/p>//g' -e 's/^ *//' -e 's/ *$//' -e 's/, /,/g')"
  479. IFS=, read -r -a array <<< "$tags"
  480. echo -n "<p>$template_tags_line_header " >> "$content"
  481. (for item in "${array[@]}"; do
  482. echo -n "<a href='$prefix_tags$item.html'>$item</a>, "
  483. done ) | sed 's/, $//g' >> "$content"
  484. echo -e "</p>" >> "$content"
  485. else
  486. echo "$line" >> "$content"
  487. fi
  488. done < "$1"
  489. # Create the actual html page
  490. create_html_page "$content" "$filename" no "$title" "$2"
  491. rm "$content"
  492. }
  493. # Manages the creation of the text file and the parsing to html file
  494. # also the drafts
  495. write_entry() {
  496. fmt="html"; f="$2"
  497. [[ "$2" == "-m" ]] && fmt="md" && f="$3"
  498. if [[ "$fmt" == "md" ]]; then
  499. test_markdown
  500. if [[ "$?" -ne 0 ]]; then
  501. echo "Markdown is not working, please use HTML. Press a key to continue..."
  502. fmt="html"
  503. read
  504. fi
  505. fi
  506. if [[ "$f" != "" ]]; then
  507. TMPFILE="$f"
  508. if [[ ! -f "$TMPFILE" ]]; then
  509. echo "The file doesn't exist"
  510. delete_includes
  511. exit
  512. fi
  513. # check if TMPFILE is markdown even though the user didn't specify it
  514. extension="${TMPFILE##*.}"
  515. [[ "$extension" == "md" ]] && fmt="md"
  516. else
  517. TMPFILE=".entry-$RANDOM.$fmt"
  518. echo -e "Title on this line\n" >> "$TMPFILE"
  519. [[ "$fmt" == "html" ]] && cat << EOF >> "$TMPFILE"
  520. <p>The rest of the text file is an <b>html</b> blog post. The process will continue as soon
  521. as you exit your editor.</p>
  522. <p>$template_tags_line_header keep-this-tag-format, tags-are-optional, example</p>
  523. EOF
  524. [[ "$fmt" == "md" ]] && cat << EOF >> "$TMPFILE"
  525. The rest of the text file is a **Markdown** blog post. The process will continue
  526. as soon as you exit your editor.
  527. $template_tags_line_header keep-this-tag-format, tags-are-optional, beware-with-underscores-in-markdown, example
  528. EOF
  529. fi
  530. chmod 600 "$TMPFILE"
  531. post_status="E"
  532. filename=""
  533. while [ "$post_status" != "p" ] && [ "$post_status" != "P" ]; do
  534. [ "$filename" ] && rm "$filename" # Delete the generated html file, if any
  535. $EDITOR "$TMPFILE"
  536. if [[ "$fmt" == "md" ]]; then
  537. html_from_md="$(markdown "$TMPFILE")"
  538. parse_file "$html_from_md"
  539. rm "$html_from_md"
  540. else
  541. parse_file "$TMPFILE" # this command sets $filename as the html processed file
  542. fi
  543. chmod 600 "$filename"
  544. echo -n "Preview? (Y/n) "
  545. read p
  546. if [[ "$p" != "n" ]] && [[ "$p" != "N" ]]; then
  547. chmod 644 "$filename"
  548. echo "Open $global_url/$filename in your browser"
  549. fi
  550. echo -n "[P]ost this entry, [E]dit again, [D]raft for later? (p/E/d) "
  551. read post_status
  552. if [[ "$post_status" == "d" ]] || [[ "$post_status" == "D" ]]; then
  553. mkdir -p "drafts/"
  554. chmod 700 "drafts/"
  555. title="$(head -n 1 $TMPFILE)"
  556. title="$(echo $title | tr [:upper:] [:lower:])"
  557. title="$(echo $title | sed 's/\ /-/g')"
  558. title="$(echo $title | tr -dc '[:alnum:]-')"
  559. draft="drafts/$title.$fmt"
  560. while [ -f "$draft" ]; do draft="drafts/$title-$RANDOM.$fmt"; done
  561. mv "$TMPFILE" "$draft"
  562. chmod 600 "$draft"
  563. rm "$filename"
  564. delete_includes
  565. echo "Saved your draft as '$draft'"
  566. exit
  567. fi
  568. done
  569. if [[ "$fmt" == "md" && "$save_markdown" ]]; then
  570. mv "$TMPFILE" "${filename%%.*}.md"
  571. else
  572. rm "$TMPFILE"
  573. fi
  574. chmod 644 "$filename"
  575. echo "Posted $filename"
  576. }
  577. # Create an index page with all the posts
  578. all_posts() {
  579. echo -n "Creating an index page with all the posts "
  580. contentfile="$archive_index.$RANDOM"
  581. while [ -f "$contentfile" ]; do
  582. contentfile="$archive_index.$RANDOM"
  583. done
  584. echo "<h3>$template_archive_title</h3>" >> "$contentfile"
  585. echo "<ul>" >> "$contentfile"
  586. for i in $(ls -t *.html); do
  587. is_boilerplate_file "$i" && continue
  588. echo -n "."
  589. # Title
  590. title="$(get_post_title "$i")"
  591. echo -n '<li><a href="'$i'">'$title'</a> &mdash;' >> "$contentfile"
  592. # Date
  593. date="$(LC_ALL=$date_locale date -r "$i" +"$date_format")"
  594. echo " $date</li>" >> "$contentfile"
  595. done
  596. echo ""
  597. echo "</ul>" >> "$contentfile"
  598. echo '<div id="all_posts"><a href="'./'">'$template_archive_index_page'</a></div>' >> "$contentfile"
  599. create_html_page "$contentfile" "$archive_index.tmp" yes "$global_title &mdash; All posts"
  600. mv "$archive_index.tmp" "$archive_index"
  601. chmod 644 "$archive_index"
  602. rm "$contentfile"
  603. }
  604. # Create an index page with all the tags
  605. all_tags() {
  606. echo -n "Creating an index page with all the tags "
  607. contentfile="$tags_index.$RANDOM"
  608. while [ -f "$contentfile" ]; do
  609. contentfile="$tags_index.$RANDOM"
  610. done
  611. echo "<h3>$template_tags_title</h3>" >> "$contentfile"
  612. echo "<ul>" >> "$contentfile"
  613. for i in ./$prefix_tags*.html; do
  614. echo -n "."
  615. nposts="$(grep -c "<\!-- text begin -->" $i)"
  616. tagname="$(echo $i | cut -c $((${#prefix_tags}+1))- | sed 's/\.html//g')"
  617. echo "<li><a href="$i">$tagname</a> &mdash; $nposts $template_tags_posts</li>" >> "$contentfile"
  618. done
  619. echo ""
  620. echo "</ul>" >> "$contentfile"
  621. echo '<div id="all_posts"><a href="'./'">'$template_archive_index_page'</a></div>' >> "$contentfile"
  622. create_html_page "$contentfile" "$tags_index.tmp" yes "$global_title &mdash; $template_tags_title"
  623. mv "$tags_index.tmp" "$tags_index"
  624. chmod 644 "$tags_index"
  625. rm "$contentfile"
  626. }
  627. # Generate the index.html with the content of the latest posts
  628. rebuild_index() {
  629. echo -n "Rebuilding the index "
  630. newindexfile="$index_file.$RANDOM"
  631. contentfile="$newindexfile.content"
  632. while [ -f "$newindexfile" ]; do
  633. newindexfile="$index_file.$RANDOM"
  634. contentfile="$newindexfile.content"
  635. done
  636. # Create the content file
  637. n=0
  638. for i in $(ls -t ./*.html); do # sort by date, newest first
  639. is_boilerplate_file "$i" && continue;
  640. if [[ "$n" -ge "$number_of_index_articles" ]]; then break; fi
  641. if [ "$cut_do" ]; then
  642. 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' >> "$contentfile"
  643. else
  644. get_html_file_content 'entry' 'entry' <$i >> "$contentfile"
  645. fi
  646. echo -n "."
  647. n=$(( $n + 1 ))
  648. done
  649. feed="$blog_feed"
  650. if [[ "$global_feedburner" != "" ]]; then feed="$global_feedburner"; fi
  651. echo '<div id="all_posts"><a href="'$archive_index'">'$template_archive'</a> &mdash; <a href="'$tags_index'">'$template_tags_title'</a> &mdash; <a href="'$feed'">'$template_subscribe'</a></div>' >> "$contentfile"
  652. echo ""
  653. create_html_page "$contentfile" "$newindexfile" yes "$global_title"
  654. rm "$contentfile"
  655. mv "$newindexfile" "$index_file"
  656. chmod 644 "$index_file"
  657. }
  658. # Rebuilds all tag_*.html files
  659. rebuild_tags() {
  660. echo -n "Rebuilding tag pages "
  661. n=0
  662. rm $prefix_tags*.html &> /dev/null
  663. # First we will process all files and create temporal tag files
  664. # with just the content of the posts
  665. for i in $(ls -t ./*.html); do
  666. is_boilerplate_file "$i" && continue;
  667. echo -n "."
  668. tmpfile="$(mktemp tmp.XXX)"
  669. if [ "$cut_do" ]; then
  670. 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' >> "$tmpfile"
  671. else
  672. get_html_file_content 'entry' 'entry' <$i >> "$tmpfile"
  673. fi
  674. while IFS='' read line; do
  675. if [[ "$line" = "<p>$template_tags_line_header"* ]]; then
  676. # 'split' tags by commas
  677. echo "$line" | cut -c 10- | while IFS="," read -a tags; do
  678. for dirty_tag in "${tags[@]}"; do # extract html around it
  679. tag="$(expr "$dirty_tag" : ".*>\(.*\)</a" | tr " " "_")"
  680. # Add the content of this post to the tag file
  681. cat "$tmpfile" >> "$prefix_tags$tag".tmp.html
  682. done
  683. done
  684. fi
  685. done < "$i"
  686. rm "$tmpfile"
  687. done
  688. # Now generate the tag files with headers, footers, etc
  689. for i in $(ls -t ./$prefix_tags*.tmp.html); do
  690. tagname="$(echo $i | cut -c $((${#prefix_tags}+1))- | sed 's/\.tmp\.html//g')"
  691. create_html_page "$i" "$prefix_tags$tagname.html" yes "$global_title &mdash; $template_tag_title \"$tagname\""
  692. rm "$i"
  693. done
  694. echo
  695. }
  696. # Return the post title
  697. #
  698. # $1 the html file
  699. get_post_title() {
  700. awk '/<h3><a class="ablack" href=".+">/, /<\/a><\/h3>/{if (!/<h3><a class="ablack" href=".+">/ && !/<\/a><\/h3>/) print}' "$1"
  701. }
  702. # Displays a list of the posts
  703. list_posts() {
  704. ls *.html &> /dev/null
  705. [[ $? -ne 0 ]] && echo "No posts yet. Use 'bb.sh post' to create one" && return
  706. lines=""
  707. n=1
  708. for i in $(ls -t ./*.html); do
  709. is_boilerplate_file "$i" && continue
  710. line="$n # $(get_post_title "$i") # $(LC_ALL=$date_locale date -r $i +"$date_format")"
  711. lines="${lines}""$line""\n" # Weird stuff needed for the newlines
  712. n=$(( $n + 1 ))
  713. done
  714. echo -e "$lines" | column -t -s "#"
  715. }
  716. # Generate the feed file
  717. make_rss() {
  718. echo -n "Making RSS "
  719. rssfile="$blog_feed.$RANDOM"
  720. while [ -f "$rssfile" ]; do rssfile="$blog_feed.$RANDOM"; done
  721. echo '<?xml version="1.0" encoding="UTF-8" ?>' >> "$rssfile"
  722. echo '<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">' >> "$rssfile"
  723. echo '<channel><title>'$global_title'</title><link>'$global_url'</link>' >> "$rssfile"
  724. echo '<description>'$global_description'</description><language>en</language>' >> "$rssfile"
  725. echo '<lastBuildDate>'$(LC_ALL=$date_locale date +"%a, %d %b %Y %H:%M:%S %z")'</lastBuildDate>' >> "$rssfile"
  726. echo '<pubDate>'$(LC_ALL=$date_locale date +"%a, %d %b %Y %H:%M:%S %z")'</pubDate>' >> "$rssfile"
  727. echo '<atom:link href="'$global_url/$blog_feed'" rel="self" type="application/rss+xml" />' >> "$rssfile"
  728. n=0
  729. for i in $(ls -t ./*.html); do
  730. is_boilerplate_file "$i" && continue
  731. [[ "$n" -ge "$number_of_feed_articles" ]] && break # max 10 items
  732. echo -n "."
  733. echo '<item><title>' >> "$rssfile"
  734. echo "$(get_post_title "$i")" >> "$rssfile"
  735. echo '</title><description><![CDATA[' >> "$rssfile"
  736. echo "$(get_html_file_content 'text' 'entry' $cut_do <$i)" >> "$rssfile"
  737. echo "]]></description><link>$global_url/$i</link>" >> "$rssfile"
  738. echo "<guid>$global_url/$i</guid>" >> "$rssfile"
  739. echo "<dc:creator>$global_author</dc:creator>" >> "$rssfile"
  740. echo '<pubDate>'$(LC_ALL=$date_locale date -r "$i" +"%a, %d %b %Y %H:%M:%S %z")'</pubDate></item>' >> "$rssfile"
  741. n=$(( $n + 1 ))
  742. done
  743. echo '</channel></rss>' >> "$rssfile"
  744. echo ""
  745. mv "$rssfile" "$blog_feed"
  746. chmod 644 "$blog_feed"
  747. }
  748. # generate headers, footers, etc
  749. create_includes() {
  750. echo '<h1 class="nomargin"><a class="ablack" href="'$global_url'">'$global_title'</a></h1>' > ".title.html"
  751. echo '<div id="description">'$global_description'</div>' >> ".title.html"
  752. if [[ -f "$header_file" ]]; then cp "$header_file" .header.html
  753. else
  754. echo '<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">' > ".header.html"
  755. echo '<html xmlns="http://www.w3.org/1999/xhtml"><head>' >> ".header.html"
  756. echo '<meta http-equiv="Content-type" content="text/html;charset=UTF-8" />' >> ".header.html"
  757. for css_file in ${css_include[*]}; do
  758. echo '<link rel="stylesheet" href="'$css_file'" type="text/css" />' >> ".header.html"
  759. done
  760. if [[ "$global_feedburner" == "" ]]; then
  761. echo '<link rel="alternate" type="application/rss+xml" title="'$template_subscribe_browser_button'" href="'$blog_feed'" />' >> ".header.html"
  762. else
  763. echo '<link rel="alternate" type="application/rss+xml" title="'$template_subscribe_browser_button'" href="'$global_feedburner'" />' >> ".header.html"
  764. fi
  765. fi
  766. if [[ -f "$footer_file" ]]; then cp "$footer_file" .footer.html
  767. else
  768. protected_mail="$(echo "$global_email" | sed 's/@/\&#64;/g' | sed 's/\./\&#46;/g')"
  769. echo '<div id="footer">'$global_license '<a href="'$global_author_url'">'$global_author'</a> &mdash; <a href="mailto:'$protected_mail'">'$protected_mail'</a><br/>' >> ".footer.html"
  770. echo 'Generated with <a href="https://github.com/cfenollosa/bashblog">bashblog</a>, a single bash script to easily create blogs like this one</div>' >> ".footer.html"
  771. fi
  772. }
  773. # Delete the temporarily generated include files
  774. delete_includes() {
  775. rm ".title.html" ".footer.html" ".header.html"
  776. }
  777. # Create the css file from scratch
  778. create_css() {
  779. # To avoid overwriting manual changes. However it is recommended that
  780. # this function is modified if the user changes the blog.css file
  781. [ $css_include ] && return || css_include=('main.css' 'blog.css')
  782. if [[ ! -f "blog.css" ]]; then
  783. # blog.css directives will be loaded after main.css and thus will prevail
  784. echo '#title{font-size: x-large;}
  785. a.ablack{color:black !important;}
  786. li{margin-bottom:8px;}
  787. ul,ol{margin-left:24px;margin-right:24px;}
  788. #all_posts{margin-top:24px;text-align:center;}
  789. .subtitle{font-size:small;margin:12px 0px;}
  790. .content p{margin-left:24px;margin-right:24px;}
  791. h1{margin-bottom:12px !important;}
  792. #description{font-size:large;margin-bottom:12px;}
  793. h3{margin-top:42px;margin-bottom:8px;}
  794. h4{margin-left:24px;margin-right:24px;}
  795. #twitter{line-height:20px;vertical-align:top;text-align:right;font-style:italic;color:#333;margin-top:24px;font-size:14px;}' > blog.css
  796. fi
  797. # If there is a style.css from the parent page (i.e. some landing page)
  798. # then use it. This directive is here for compatibility with my own
  799. # home page. Feel free to edit it out, though it doesn't hurt
  800. if [[ -f "../style.css" ]] && [[ ! -f "main.css" ]]; then
  801. ln -s "../style.css" "main.css"
  802. elif [[ ! -f "main.css" ]]; then
  803. echo 'body{font-family:Georgia,"Times New Roman",Times,serif;margin:0;padding:0;background-color:#F3F3F3;}
  804. #divbodyholder{padding:5px;background-color:#DDD;width:874px;margin:24px auto;}
  805. #divbody{width:776px;border:solid 1px #ccc;background-color:#fff;padding:0px 48px 24px 48px;top:0;}
  806. .headerholder{background-color:#f9f9f9;border-top:solid 1px #ccc;border-left:solid 1px #ccc;border-right:solid 1px #ccc;}
  807. .header{width:800px;margin:0px auto;padding-top:24px;padding-bottom:8px;}
  808. .content{margin-bottom:45px;}
  809. .nomargin{margin:0;}
  810. .description{margin-top:10px;border-top:solid 1px #666;padding:10px 0;}
  811. h3{font-size:20pt;width:100%;font-weight:bold;margin-top:32px;margin-bottom:0;}
  812. .clear{clear:both;}
  813. #footer{padding-top:10px;border-top:solid 1px #666;color:#333333;text-align:center;font-size:small;font-family:"Courier New","Courier",monospace;}
  814. a{text-decoration:none;color:#003366 !important;}
  815. a:visited{text-decoration:none;color:#336699 !important;}
  816. blockquote{background-color:#f9f9f9;border-left:solid 4px #e9e9e9;margin-left:12px;padding:12px 12px 12px 24px;}
  817. blockquote img{margin:12px 0px;}
  818. blockquote iframe{margin:12px 0px;}' > main.css
  819. fi
  820. }
  821. # Regenerates all the single post entries, keeping the post content but modifying
  822. # the title, html structure, etc
  823. rebuild_all_entries() {
  824. echo -n "Rebuilding all entries "
  825. for i in ./*.html; do # no need to sort
  826. is_boilerplate_file "$i" && continue;
  827. contentfile=".tmp.$RANDOM"
  828. while [ -f "$contentfile" ]; do contentfile=".tmp.$RANDOM"; done
  829. echo -n "."
  830. # Get the title and entry, and rebuild the html structure from scratch (divs, title, description...)
  831. title="$(get_post_title "$i")"
  832. get_html_file_content 'text' 'text' <$i >> "$contentfile"
  833. # Original post timestamp
  834. timestamp="$(LC_ALL=$date_locale date -r $i +"%a, %d %b %Y %H:%M:%S %z" )"
  835. create_html_page "$contentfile" "$i.rebuilt" no "$title" "$timestamp"
  836. # keep the original timestamp!
  837. timestamp="$(LC_ALL=$date_locale date -r $i +'%Y%m%d%H%M')"
  838. mv "$i.rebuilt" "$i"
  839. chmod 644 "$i"
  840. touch -t $timestamp "$i"
  841. rm "$contentfile"
  842. done
  843. echo ""
  844. }
  845. # Displays the help
  846. function usage() {
  847. echo "$global_software_name v$global_software_version"
  848. echo "Usage: $0 command [filename]"
  849. echo ""
  850. echo "Commands:"
  851. echo " post [-m] [filename] insert a new blog post, or the filename of a draft to continue editing it"
  852. echo " use '-m' to edit the post as Markdown text"
  853. echo " edit [-n|-f] [filename] edit an already published .html or .md file. **NEVER** edit manually a published .html file,"
  854. echo " always use this function as it keeps internal data and rebuilds the blog"
  855. echo " use '-n' to give the file a new name, if title was changed"
  856. echo " use '-f' to edit full html file, instead of just text part (also preserves name)"
  857. echo " delete [filename] deletes the post and rebuilds the blog"
  858. echo " rebuild regenerates all the pages and posts, preserving the content of the entries"
  859. echo " reset deletes everything except this script. Use with a lot of caution and back up first!"
  860. echo " list list all posts"
  861. echo ""
  862. echo "For more information please open $0 in a code editor and read the header and comments"
  863. }
  864. # Delete all generated content, leaving only this script
  865. reset() {
  866. echo "Are you sure you want to delete all blog entries? Please write \"Yes, I am!\" "
  867. read line
  868. if [[ "$line" == "Yes, I am!" ]]; then
  869. rm .*.html *.html *.css *.rss &> /dev/null
  870. echo
  871. echo "Deleted all posts, stylesheets and feeds."
  872. echo "Kept your old '.backup.tar.gz' just in case, please delete it manually if needed."
  873. else
  874. echo "Phew! You dodged a bullet there. Nothing was modified."
  875. fi
  876. }
  877. # Detects if GNU date is installed
  878. date_version_detect() {
  879. date --version >/dev/null 2>&1
  880. if [[ $? -ne 0 ]]; then
  881. # date utility is BSD. Test if gdate is installed
  882. if gdate --version >/dev/null 2>&1 ; then
  883. date() {
  884. gdate "$@"
  885. }
  886. else
  887. # BSD date
  888. date() {
  889. if [[ "$1" == "-r" ]]; then
  890. # Fall back to using stat for 'date -r'
  891. format=$(echo $3 | sed 's/\+//g')
  892. stat -f "%Sm" -t "$format" "$2"
  893. elif [[ $(echo $@ | grep '\-\-date') ]]; then
  894. # convert between dates using BSD date syntax
  895. /bin/date -j -f "%a, %d %b %Y %H:%M:%S %z" "$(echo $2 | sed 's/\-\-date\=//g')" "$1"
  896. else
  897. # acceptable format for BSD date
  898. /bin/date -j "$@"
  899. fi
  900. }
  901. fi
  902. fi
  903. }
  904. # Main function
  905. # Encapsulated on its own function for readability purposes
  906. #
  907. # $1 command to run
  908. # $2 file name of a draft to continue editing (optional)
  909. do_main() {
  910. # Detect if using BSD date or GNU date
  911. date_version_detect
  912. # Load default configuration, then override settings with the config file
  913. global_variables
  914. [[ -f "$global_config" ]] && source "$global_config" &> /dev/null
  915. global_variables_check
  916. # Check for $EDITOR
  917. [[ -z "$EDITOR" ]] &&
  918. echo "Please set your \$EDITOR environment variable" && exit
  919. # Check for validity of argument
  920. [[ "$1" != "reset" ]] && [[ "$1" != "post" ]] && [[ "$1" != "rebuild" ]] && [[ "$1" != "list" ]] && [[ "$1" != "edit" ]] && [[ "$1" != "delete" ]] &&
  921. usage && exit
  922. [[ "$1" == "list" ]] &&
  923. list_posts && exit
  924. if [[ "$1" == "edit" ]]; then
  925. if [[ $# -lt 2 ]] || [[ ! -f "${!#}" ]]; then
  926. echo "Please enter a valid html file to edit"
  927. exit
  928. fi
  929. fi
  930. # Test for existing html files
  931. ls *.html &> /dev/null
  932. [[ $? -ne 0 ]] && [[ "$1" == "rebuild" ]] &&
  933. echo "Can't find any html files, nothing to rebuild" && exit
  934. # We're going to back up just in case
  935. ls *.html &> /dev/null
  936. [[ $? -eq 0 ]] &&
  937. tar cfz ".backup.tar.gz" *.html &&
  938. chmod 600 ".backup.tar.gz"
  939. # Keep first backup of this day containing yesterday's version of the blog
  940. [[ ! -f .yesterday.tar.gz ]] || [ "$(LC_ALL=$date_locale date -r .yesterday.tar.gz +'%d')" != "$(LC_ALL=$date_locale date +'%d')" ] &&
  941. cp .backup.tar.gz .yesterday.tar.gz &> /dev/null
  942. [[ "$1" == "reset" ]] &&
  943. reset && exit
  944. create_css
  945. create_includes
  946. [[ "$1" == "post" ]] && write_entry "$@"
  947. [[ "$1" == "rebuild" ]] && rebuild_all_entries
  948. [[ "$1" == "delete" ]] && rm "$2" &> /dev/null
  949. if [[ "$1" == "edit" ]]; then
  950. if [[ "$2" == "-n" ]]; then
  951. edit "$3"
  952. elif [[ "$2" == "-f" ]]; then
  953. edit "$3" full
  954. else
  955. edit "$2" keep
  956. fi
  957. fi
  958. rebuild_index
  959. all_posts
  960. rebuild_tags
  961. all_tags
  962. make_rss
  963. delete_includes
  964. }
  965. #
  966. # MAIN
  967. # Do not change anything here. If you want to modify the code, edit do_main()
  968. #
  969. do_main $*
  970. # vim: set shiftwidth=4 tabstop=4 expandtab: