diff --git a/bob b/bob index d503dbb..d479a13 100755 --- a/bob +++ b/bob @@ -55,29 +55,36 @@ init() # _index "$blog" "$lang" "$dark" } +get_git_dates() +{ + local mdfile="$1" + created_fmt=$(git log --diff-filter=A --format=%cd --date=format:'%Y-%m-%d %H:%M:%S' -- "$mdfile" | tail -1) + created_epoch=$(git log --diff-filter=A --format=%ct -- "$mdfile" | tail -1) + updated_fmt=$(git log -1 --format=%cd --date=format:'%Y-%m-%d %H:%M:%S' -- "$mdfile") + updated_epoch=$(git log -1 --format=%ct -- "$mdfile") +} + update_index() { # Build lines with creation/modification epochs and formatted timestamps, safely handling spaces. sorted_posts=$( find ./posts -maxdepth 1 -type f -name '*.html' -print0 | while IFS= read -r -d '' file; do - # Try birth time; fallback to mtime if birth not available - creation_epoch=$(stat -c %W "$file" 2>/dev/null) - if [ -z "$creation_epoch" ] || [ "$creation_epoch" -le 0 ]; then - creation_epoch=$(stat -c %Y "$file") - fi - - modification_epoch=$(stat -c %Y "$file") - - # Human-readable timestamps - created_fmt=$(date -d @"$creation_epoch" '+%Y-%m-%d %H:%M:%S') - modified_fmt=$(date -d @"$modification_epoch" '+%Y-%m-%d %H:%M:%S') + # Getting only the file name without the preceding path name=$(basename "$file") + # Remove extension + title="$name" + title=${title%.html} + + mdfile="./drafts/published/${title}.md" + + get_git_dates "$mdfile" + # Use pipe as delimiter (filename already basename so cannot contain '/'; could contain '|', but that's rare; # if paranoid, choose NUL and handle differently). This is simple and readable. - printf '%s|%s|%s|%s|%s\n' "$creation_epoch" "$created_fmt" "$modification_epoch" "$modified_fmt" "$name" + printf '%s|%s|%s|%s|%s\n' "$created_epoch" "$created_fmt" "$updated_epoch" "$updated_fmt" "$name" done | # sort by numeric creation epoch descending (field 1) sort -t'|' -k1,1nr | @@ -90,7 +97,7 @@ update_index() sub(/\.html$/, "", display) gsub(/[_-]/, " ", display) # escape filename/display if needed (not done here); keep simple - printf "
  • %s

    created at %s

    updated at %s

  • \n", filename, display, $2, $4 + printf "
  • %s

    created at %s

    updated at %s

  • \n", filename, display, $2, $4 } END { print "" }' ) @@ -105,34 +112,6 @@ update_index() }' "$template" > "./index.html" } - -publish() -{ - # Storing the path of the post/article to publish - article_path=$1 - - # from the relative path, only retrieving the name of the article (without file extension) - article_name=$(echo $article_path | cut -d '/' -f 2 | cut -d '.' -f 1) - - # Convert the markdown draft into an html article and storing it locally - post=$(awk -f ${BOB_LIB}/markdown.awk ./$article_path) - - # Retrieving the html article template - template="${BOB_LIB}/template/post.html" - - # Escaping the & for next step to not confuse awk - escaped_post=$(echo "$post" | sed 's/&/\\&/g') - - # In the template, replacing the string {{article}} by the actual content parsed above - awk -v content="$escaped_post" '{gsub(/\{\{article\}\}/, content); print}' "$template" > "./posts/$article_name.html" - - # moving the draft into the "published" folder - mv ./$article_path ./drafts/published/$article_name.md - - # updating the index.html as a new article shall appear in the list - update_index -} - publish_one() { # Storing the path of the post/article to publish @@ -145,14 +124,25 @@ publish_one() # Convert the markdown draft into an html article and storing it locally post=$(awk -f ${BOB_LIB}/markdown.awk ./$article_path) + get_git_dates "$article_path" + # Retrieving the html article template template="${BOB_LIB}/template/post.html" # Escaping the & for next step to not confuse awk - escaped_post=$(echo "$post" | sed 's/&/\\&/g') + escaped_post=$(echo "$post" | sed 's/&/\\\\&/g') # In the template, replacing the string {{article}} by the actual content parsed above - awk -v content="$escaped_post" '{gsub(/\{\{article\}\}/, content); print}' "$template" > "./posts/$article_name.html" + awk \ + -v content="$escaped_post" \ + -v created="$created_fmt" \ + -v updated="$updated_fmt" \ + '{ + gsub(/{{article}}/, content); + gsub(/{{created}}/, created); + gsub(/{{updated}}/, updated); + print + }' "$template" > "./posts/$article_name.html" } publish_all() diff --git a/lib/markdown.awk b/lib/markdown.awk index c6802c6..a590828 100755 --- a/lib/markdown.awk +++ b/lib/markdown.awk @@ -61,16 +61,24 @@ function replaceInline(line, result, start, end) { line = substr(line, 1, start-1) "" matched_link "" substr(line, end+1) } - while (match(line, /`([^*]+)`/)) { + while (match(line, /`([^`]+)`/)) { start = RSTART end = RSTART + RLENGTH - 1 # Build the result: before match, , content, , after match - line = substr(line, 1, start-1) "" substr(line, start+1, RLENGTH-2) "" substr(line, end+1) + line = substr(line, 1, start-1) "" escape_html(substr(line, start+1, RLENGTH-2)) "" substr(line, end+1) } return line } +function escape_html(str) { + # gsub(/&/, "\\&", str) # Must be done first! + gsub(//, "\\>", str) + gsub(/*/, "\\*", str) + gsub(/\\/, "\\\\", str) + return str +} function closeOne() { env = pop() @@ -131,34 +139,71 @@ function closeOne() { n = RLENGTH; if (env == "code") { # In a code block, print a new item - print substr($0, n+1) + print escape_html(substr($0, n+1)) } else { # Otherwise, init the code block push("pre") push("code") - print "
    " substr($0, n+1)
    +        print "
    " escape_html(substr($0, n+1))
         }
    +    next
     }
     
     
     # Matching a simple paragraph
    -!/^(#|\*|-|\+|>|`|$|\t|    )/ {
    -    env = last() 
    -    if (env == "none") {
    -        # If no block, print a paragraph
    -        print "

    " replaceInline($0) "

    " +!/^(#|*|-|+|>|`|$|\t| )/ { +env = last() + + # NEW LOGIC: If we hit normal text, close any open code, pre, lists, or quotes. + while (env != "none") { + # Lists, blockquotes, and code must be closed by un-indented text + if (env == "ul" || env == "ol" || env == "blockquote" || env == "code" || env == "pre") { + env = pop() + print "" + env = last() + } else { + # If we hit a 'p' or other non-container tag, stop closing + break + } + } + + # Now that we've closed the containing blocks, check if we need a new paragraph + if (env == "none" || env == "p") { + # Start a new paragraph or continue the existing one + if (env == "none") { + push("p") + printf "

    " + } else { + # If env == "p", we are continuing it, so add a newline + printf "\n" + } + + # Print the line content + printf "%s", replaceInline($0) + } else if (env == "blockquote") { + # If the environment is blockquote, we continue the quote but need to + # explicitly close the current paragraph if the rule above didn't catch it print $0 } } $0 == "" { env = last() - while (env != "none") { - env = pop() - print "" + while (env != "none" && env !="code") { + # Check if the environment is one we want to close (ul, ol, blockquote) + if (env == "ul" || env == "ol" || env == "blockquote" || env == "p") { + env = pop() + print "" + } else { + # If we hit an environment we don't want to close (like 'pre' under 'code'), + # we need to break the loop or handle it. Since 'code' is the only + # multi-level block you have, just break if it's not a closable one. + break + } env = last() } + next } diff --git a/lib/template/post.html b/lib/template/post.html index d9ddf6e..83fe0d1 100644 --- a/lib/template/post.html +++ b/lib/template/post.html @@ -12,6 +12,10 @@

    simpet

    +
    +

    Created at:

    +

    Updated at:

    +
    {{article}}