From 6136b8a79a7f0ce5e35dc07cd88965953bd0e1af Mon Sep 17 00:00:00 2001 From: Paul Buetow Date: Sun, 1 Mar 2026 17:39:53 +0200 Subject: Extract generate::extract_title helper to eliminate duplication Title extraction ($SED pattern + quote sanitization) was duplicated in generate, atomfeed, gemfeed, notes, and template modules. Now centralized with unit tests for heading extraction and empty files. Co-Authored-By: Claude Opus 4.6 --- lib/atomfeed.source.sh | 2 +- lib/gemfeed.source.sh | 3 +-- lib/generate.source.sh | 18 +++++++++++++++++- lib/notes.source.sh | 3 +-- lib/template.source.sh | 2 +- 5 files changed, 21 insertions(+), 7 deletions(-) (limited to 'lib') diff --git a/lib/atomfeed.source.sh b/lib/atomfeed.source.sh index 3fe46be..20a87d0 100644 --- a/lib/atomfeed.source.sh +++ b/lib/atomfeed.source.sh @@ -121,7 +121,7 @@ atomfeed::_entry () { assert::not_empty content "$content" # Extract first heading as post title. - local title=$($SED -n '/^# / { s/# //; p; q; }' "$gemfeed_dir/$gmi_file" | tr '"' "'") + local title=$(generate::extract_title "$gemfeed_dir/$gmi_file") assert::not_empty title "$title" # Extract first paragraph from Gemtext as the summary. diff --git a/lib/gemfeed.source.sh b/lib/gemfeed.source.sh index 40792cc..8700ac4 100644 --- a/lib/gemfeed.source.sh +++ b/lib/gemfeed.source.sh @@ -47,8 +47,7 @@ GEMFEED while read -r gmi_file; do # Extract first heading as post title. - local title=$($SED -n '/^# / { s/# //; p; q; }' \ - "$gemfeed_dir/$gmi_file" | tr '"' "'") + local title=$(generate::extract_title "$gemfeed_dir/$gmi_file") # Extract the date from the file name, and also get the word count. local filename_date=$(basename "$gemfeed_dir/$gmi_file" | cut -d- -f1,2,3) diff --git a/lib/generate.source.sh b/lib/generate.source.sh index 544ae08..17322e7 100644 --- a/lib/generate.source.sh +++ b/lib/generate.source.sh @@ -53,6 +53,13 @@ generate::safe_overwrite () { fi } +# Extract the first heading from a .gmi file as a title, with double quotes +# replaced by single quotes for safe embedding in feeds and HTML attributes. +generate::extract_title () { + local -r file="$1"; shift + $SED -n '/^# / { s/# //; p; q; }' "$file" | tr '"' "'" +} + # Add other docs (e.g. images, videos) from Gemtext to output format. # Skips copying if the output file already exists and is newer than the source. generate::fromgmi_add_docs () { @@ -119,7 +126,7 @@ generate::_to_output_format () { dest=${dest/.gmi/.$format} local dest_dir=$(dirname "$dest") - local title=$($SED -n '/^# / { s/# //; p; q; }' "$src" | tr '"' "'") + local title=$(generate::extract_title "$src") if [[ -z "$title" ]]; then title="$SUBTITLE" fi @@ -337,5 +344,14 @@ generate::test () { assert::equals "$(cat "$tmp_dir/file")" 'different content' assert::equals "$(test -f "$tmp_dir/file.tmp" && echo exists || echo gone)" 'gone' + # Test generate::extract_title: extracts first heading and sanitizes quotes + echo '# My "Great" Title' > "$tmp_dir/test.gmi" + echo '## Not this one' >> "$tmp_dir/test.gmi" + assert::equals "$(generate::extract_title "$tmp_dir/test.gmi")" "My 'Great' Title" + + # Test generate::extract_title: file with no heading returns empty + echo 'Just a paragraph' > "$tmp_dir/noheading.gmi" + assert::equals "$(generate::extract_title "$tmp_dir/noheading.gmi")" '' + rm -rf "$tmp_dir" } diff --git a/lib/notes.source.sh b/lib/notes.source.sh index dc62caf..64625b4 100644 --- a/lib/notes.source.sh +++ b/lib/notes.source.sh @@ -27,8 +27,7 @@ NOTES while read -r gmi_file; do # Extract first heading as post title. - local title=$($SED -n '/^# / { s/# //; p; q; }' \ - "$notes_dir/$gmi_file" | tr '"' "'") + local title=$(generate::extract_title "$notes_dir/$gmi_file") echo "=> ./$gmi_file $title" >> "$notes_dir/index.gmi.tmp" done < <(notes::_get_notes) diff --git a/lib/template.source.sh b/lib/template.source.sh index b93303e..0e34a87 100644 --- a/lib/template.source.sh +++ b/lib/template.source.sh @@ -143,7 +143,7 @@ template::inline::_index () { for topic in "$@"; do while read -r gmi_file; do local date=$(cut -d- -f1,2,3 <<< "$gmi_file") - local title=$($SED -n "/^# / { s/# //; p; q; }" "$gmi_file") + local title=$(generate::extract_title "$gmi_file") local current='' if [ "$gmi_file" = "$CURRENT_GMI" ]; then -- cgit v1.2.3