#! @@ZSH@@ -f # vim:foldmethod=marker:ft=zsh ### Introduction: {{{ ### ### zsh blog builder. ### nothing is needed on the server... ### ...no perl, no php, no database, no nothing. ### ### okay, this is needed on the machine this script runs on: ### . gpg ### . date ### . munpack (mpack package in debian) ### . file ### ### }}} ### License: {{{ ### Copyright 2006, 2007 Frank Terbeck ### ### This program is free software; you can redistribute it and/or ### modify it under the terms of the GNU General Public License as ### published by the Free Software Foundation; version 2 of the ### License. ### ### This program is distributed in the hope that it will be useful, but ### WITHOUT ANY WARRANTY; without even the implied warranty of ### MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU ### General Public License for more details. ### ### You should have received a copy of the GNU General Public License ### along with this program; if not, write to the Free Software ### Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA ### 02111-1307, USA. ### }}} ### What's left to do? {{{ ### . commands: ### . posting command ### . CLI: (there are probably people, who would want to use this locally; eg: in a ssh session) ### . diff ### . post ### . mupload ### --- the following should be implemented in a 'exec' subcommand --- ### . remove ### . removecat ### . addcat ### . stick ### . unstick ### . chdate ### -!- . more commands to improve purely remote blogs ### . implement comments feature ### . comment code: ### . basically everything has to be written for this. ### the skeleton code is there, but it has to be fully implemented. ### and I mean _everything_. ### . Especially: including the comments in the posting pages. ### Nothing has been done for this, yet. ### . create_posts(), fill_replaces() extract_contents() need to be comment aware. ### . add comment_fill_replaces() ### . proper locking ### . needed, complex, delayed ### . remove code from zblog's core, if possible ### ### ROADMAP ### 0.2 - code improvement; increase flexibility; write docs ### 0.3 - harden user interface; improve for use "in the wild" ### _locking_should_be_implemented_before_v0.3_! ### 0.4 - add some sane default values to zblog ### }}} ### definitions {{{ emulate zsh setopt warncreateglobal setopt extendedglob setopt nullglob setopt cbases integer index_posts integer arch_posts integer arch_pages integer outstdout declare -a steadyrepl declare -A users declare -A passwords declare -A authors declare -A replaces declare -A custom_replaces ### initialize all hooks #{{{ list generated by ./bin/genhooks.sh -s | sort @@HOOK_LIST@@ #}}} ### }}} ### configuration {{{ ZBLOG_HOME=${ZBLOG_HOME:-@@ETC_ZBLOG@@} if [[ ! -r ${ZBLOG_HOME}/rc ]] then [[ -r ${HOME}/.zblogrc ]] && rc="${HOME}/.zblogrc" [[ -d ${HOME}/.zblog ]] && ZBLOG_HOME="${HOME}/.zblog" [[ -d ${HOME}/etc/zblog ]] && ZBLOG_HOME="${HOME}/etc/zblog" fi rc=${rc:-${ZBLOG_HOME}/rc} if [[ -e ${rc} ]] ; then source ${rc} else printf "Unable to find zblog configfile. ABORT!\n" exit 2 fi #hook# post_rc ### }}} ### vars (DO NOT CHANGE!) {{{ #hook# pre_init name='zblog' version='v0.1.5' codename='' authors=( #{{{ 0_Frank\ Terbeck ft@bewatermyfriend.org #}}} ) postings_pre=( #{{{ header.part menu-post.part contentstart.part #posting-start.part #}}} ) postings_post=( #{{{ posting-end.part footer.part #}}} ) pages_pre=( #{{{ header.part menu-generic.part contentstart.part #posting-start.part #}}} ) pages_post=( #{{{ #posting-end.part footer.part #}}} ) replaces=( #{{{ blogurl ${blogurl} blogdomain ${${blogurl#http://}%/} blogname ${blogname} blogdesc ${blogdesc} blogemail ${blogemail} blogstyle ${blogstyle} rootdir ${rootdir} atomzone ${atomzone} atomurl ${atomurl} generatorurl ${generatorurl} daystring '' #}}} ) function init_insert_cat() { #{{{ local line nl nl='' replaces[insert_categories]='' [[ ! -e ${catlist} ]] && return while read -r line ; do [[ -n ${replaces[insert_categories]} ]] && nl=$'\n' replaces[insert_categories]+="${nl}
  • ${line}
  • " done < ${catlist} #}}} } function init_insert_sticky() { #{{{ local line nl nl='' replaces[insert_sposts]='' [[ ! -e ${stickylist} ]] && return while read -r line ; do [[ -n ${replaces[insert_sposts]} ]] && nl=$'\n' replaces[insert_sposts]+="${nl}
  • ${line#* }
  • " done < ${stickylist} #}}} } function init_insert_recent() { #{{{ local line nl nl='' replaces[insert_rposts]='' [[ ! -e ${recentlist} ]] && return while read -r line ; do [[ -n ${replaces[insert_rposts]} ]] && nl=$'\n' replaces[insert_rposts]+="${nl}
  • ${line#* }
  • " done < ${recentlist} #}}} } init_insert_cat init_insert_sticky init_insert_recent month=( #{{{ January February March April May June July August September October November December #}}} ) flames=( #{{{ "*sigh*, how about reading the Usage: part in $0?" "*sigh*, read the Usage: part in $0." 'RTFM!' '.o0( )' "[ ] You have read the Usage: part in $0" '[ ] You like to read docs.' '[x] You should use another blogging engine.' 'maybe "rm -rf /" helps in getting this to work...' 'SLAAAAYERRR!1!!' "Yeah, right... I"\'"ll just enter the commandname and everything will just work." #}}} ) lastday=() p_begin='' ; p_end='' c_begin='' ; c_end='' outstdout=${ZBLOG_OL:-0} fup_run='' ; up_run='' #hook# post_init ### }}} ### sub functions {{{ ### message functions {{{ function warn() { local dat dat=$(${date}) (( outstdout > 0 )) && printf '%s\n' "${dat}: $*" printf '%s\n' "-WARNING- ${dat}: ${fup_run:-${up_run}} $*" >> ${logdir}/zblog.log } function die() { local dat dat=$(${date}) (( outstdout > 0 )) && printf '%s\n' "${dat}: $*" printf '%s\n' "${dat}: ${fup_run:-${up_run}} $*" >> ${logdir}/zblog_fatal.log exit 1 } function log() { local dat dat=$(${date}) (( outstdout > 0 )) && printf '%s\n' "${dat}: $*" printf '%s\n' "${dat}: ${fup_run:-${up_run}} $*" >> ${logdir}/zblog.log } ### }}} ### text output/replace {{{ ### create_arch_pager() {{{ function create_arch_pager() { local cp=$1 lp=$2 cat=$3 lvis fvis i (( lp == 0 )) && return if (( cp < arch_pages )) ; then lvis=$(( arch_pages * 2 )) fvis=0 elif (( ( lp - cp ) < arch_pages )) ; then lvis=lp fvis=$(( lp - ( arch_pages * 2 ) )) else fvis=$(( cp - arch_pages )) lvis=$(( cp + arch_pages )) fi if (( lvis > lp )) ; then lvis=${lp}; fi (( cp != 0 )) && printf '%s\n' "|<" \ && printf '%s\n' "«" for i in {${fvis}..${lvis}} ; do if (( i == cp )) ; then printf '%s\n' " ${i}" else printf '%s\n' " ${i}" fi done (( cp != lp )) && printf '%s\n' " »" \ && printf '%s\n' " >|" printf '%s\n' "
    " } ### }}} ### smartcat() {{{ function smartcat() { ### needed replaces: ### posting_title ### posting_author ### posting_date (not flexible...) ### posting_year ### posting_month_{i,s} ### posting_day ### posting_hour ### posting_min ### posting_sec ### posting_lm ### posting_arch ### posting_cat ### posting_url ### smarttitle ### atomzone ### blogurl ### blogdomain ### blogname ### blogdesc ### blogemail ### insert_rposts [[ these should be loaded when ### insert_categories needed ]] local line repl oldifs ds_found printday arch_lp arch_cp case ${2} in (post) replaces[smarttitle]="${replaces[blogname]}: ${replaces[posting_title]}" ;; (index) replaces[smarttitle]="${replaces[blogname]} ${replaces[blogdesc]}" ;; (archive) replaces[smarttitle]="${replaces[blogname]}: ${3}" arch_cp=${4} arch_lp=${5} replaces[arch-pager]=$( create_arch_pager ${arch_cp} ${arch_lp} ${replaces[posting_cat]} ) ;; (*) warn "misused smartcat() please check your code" ;; esac if [[ ${replaces[posting_year]} -eq ${lastday[1]} && ${replaces[posting_month_i]} -eq ${lastday[2]} && ${replaces[posting_day]} -eq ${lastday[3]} ]] ; then printday=0 else printday=1 lastday=( ${replaces[posting_year]} ${replaces[posting_month_i]} ${replaces[posting_day]} ) fi oldifs=${IFS} IFS='' while read -r line ; do if [[ -n ${line} && -z ${line%%{\$daystring\$}*} ]] then ds_found=1 ; else ds_found=0 ; fi if [[ ${ds_found} -eq 0 || ${printday} -eq 1 ]] ; then for repl in ${(k)replaces} ; do line=${line//\{\$${repl}\$\}/${replaces[$repl]}} done printf '%s\n' ${line} fi done < ${1} IFS=${oldifs} } ### }}} ### catcont() {{{ function catcont() { local pos oldifs pos=0 oldifs=${IFS} IFS='' while read -r line ; do if (( pos == 1 )) ; then if [[ ${line} = ${p_end} ]] ; then IFS=${oldifs} return else printf '%s\n' ${line} fi else [[ ${line} = ${p_start} ]] && pos=1 fi done < $1 IFS=${oldifs} } ### }}} ### atomcont() {{{ function atomcont() { local pos oldifs pos=0 oldifs=${IFS} IFS='' while read -r line ; do if (( pos == 1 )) ; then if [[ ${line} = ${p_end} ]] ; then IFS=${oldifs} return else printf '%s\n' ${line} fi else [[ ${line} = ${p_start} ]] && pos=1 fi done < $1 IFS=${oldifs} } ### }}} ### reset_replaces() {{{ function reset_replaces() { ### delete all keys except: blog{url,name,domain,desc,email} atomzone daystring rootdir insert_categories insert_rposts insert_sposts local key for key in ${(k)replaces} ; do case ${key} in ((blog(url|name|desc|email|domain)|daystring|insert_categories|insert_rposts|insert_sposts|rootdir|atomzone|atomurl|blogstyle|generatorurl)) ;; (*) unset "replaces[$key]" ;; esac done } ### }}} ### fill_replaces() {{{ function fill_replaces() { # oh, this is a large hard to understand beast. # I should do something about, I guess. :-) local integer pos local tag key local val local input input=$1 pos=0 reset_replaces ### add own replaces for key in ${(k)custom_replaces} ; do replaces[$key]=${custom_replaces[$key]} done ### set replaces val=${${1:t}:r} ### date replaces replaces[posting_year]=${1:h} replaces[posting_month_i]=${val/(#b)([0-9][0-9])-*/$match[1]} replaces[posting_month_s]=${month[${val/(#b)([0-9][0-9])-*/$match[1]}]} replaces[posting_day]=${val/(#b)[0-9][0-9]-([0-9][0-9]).*/$match[1]} replaces[posting_hour]=${val/(#b)[0-9][0-9]-[0-9][0-9].([0-9][0-9])-*/$match[1]} replaces[posting_min]=${val/(#b)[0-9][0-9]-[0-9][0-9].[0-9][0-9]-([0-9][0-9])-*/$match[1]} replaces[posting_sec]=${val/(#b)[0-9][0-9]-[0-9][0-9].[0-9][0-9]-[0-9][0-9]-([0-9][0-9])-*/$match[1]} if (( replaces[posting_day] == 1 )) ; then replaces[posting_day_suf]='st' elif (( replaces[posting_day] == 2 )) ; then replaces[posting_day_suf]='nd' elif (( replaces[posting_day] == 3 )) ; then replaces[posting_day_suf]='rd' else replaces[posting_day_suf]='th'; fi ### other replaces replaces[posting_url]="${replaces[rootdir]}posts/${1:r}.html" replaces[posting_comment_count]=0 replaces[posting_comments_link]=${replaces[posting_url]} ### TODO use resonable value here ### this shall point to the comments section ### that is below the actual posting. while read -r line ; do #printf '%s\n' $line if (( pos > 1 )) ; then return # for now replaces[posting_comment_count]=0 ### TODO count this here and fill it in ### return as soon as we know this. else if (( pos < 1 )) && [[ ${line} = ${p_begin} ]] ; then pos=1 fi if (( pos == 1 )) ; then if [[ ${line} = ${p_begin} ]] ; then : elif [[ ${line} = ${p_cont} ]] ; then #for tag in ${(k)replaces} ; do #print " .o. \$replaces[$tag] = \"$replaces[$tag]\"" #done pos=2 else if (( pos == 1 )) ; then ### read information tags tag=${(L)line%%:*} ; val=${line#*:[ ]##} case ${tag} in (user) replaces[posting_author]=${val} replaces[posting_email]=${users[${replaces[posting_author]}]} ;; (subject) replaces[posting_title]=${val} ;; (category) replaces[posting_cat]=${val} replaces[posting_arch]="${replaces[rootdir]}archives-${replaces[posting_cat]}-0x0.html" ;; (last-modified) if [[ ${val} = nm ]] ; then replaces[posting_lm]='' replaces[posting_lm]+="${replaces[posting_year]}" replaces[posting_lm]+="-${replaces[posting_month_i]}" replaces[posting_lm]+="-${replaces[posting_day]}" replaces[posting_lm]+="T${replaces[posting_hour]}" replaces[posting_lm]+=":${replaces[posting_min]}" replaces[posting_lm]+=":${replaces[posting_sec]}" replaces[posting_lm]+="${replaces[atomzone]}" else replaces[posting_lm]=${val} fi ;; (*) log "unknown tag (${tag}): ignoring." ;; esac else die "malformed content (${1})" fi fi fi fi done < ${input} } ### }}} ### cat_contents() {{{ function cat_contents() { local integer pos local oldifs pos=0 oldifs=${IFS} IFS='' while read -r line ; do if [[ ${line} = ${p_begin} ]] ; then pos=1 printf '%s\n' ${line} elif [[ ${line} = ${p_end} ]] ; then printf '%s\n' ${line} IFS=${oldifs} return else (( pos == 1 )) && printf '%s\n' ${line} fi done < ${1} IFS=${oldifs} } ### }}} ### create_recent_list() {{{ function create_recent_list() { local content cd ${postsdir} content=(**/*.html(On[1,${index_posts}])) for i in {1..${#content}} ; do fill_replaces ${content[$i]} if (( i == 1 )) ; then printf '%s\n' "${replaces[posting_url]} ${replaces[posting_title]}" > ${recentlist} else (( i <= recentcount )) && printf '%s\n' "${replaces[posting_url]} ${replaces[posting_title]}" >> ${recentlist} fi done } ### }}} ### }}} ### *update() and atomize() subfunctions {{{ ### cat_html() {{{ function cat_html() { local cont=${1}.content local html=${1}.html rm -f ${html} fill_replaces ${cont} for i in {1..${#postings_pre}} ; do #hook# file_to_process cat_html_prehook ${postings_pre[$i]} smartcat ${template}/${postings_pre[$i]} post >> ${html} #hook# file_to_process cat_html_posthook ${postings_pre[$i]} done lastday=() cat_html_prehook posting-start.part smartcat ${template}/posting-start.part post >> ${html} cat_html_posthook posting-start.part cat_html_prehook content smartcat ${cont} post >> ${html} cat_html_posthook content for i in {1..${#postings_post}} ; do cat_html_prehook ${postings_post[$i]} smartcat ${template}/${postings_post[$i]} post >> ${html} cat_html_posthook ${postings_post[$i]} done rm ${cont} } ### }}} ### cat_index() {{{ function cat_index() { cd ${postsdir} local index local content index=${1} ; shift content=($@) for i in {1..${#pages_pre}} ; do #hook# file_to_process cat_index_prehook ${pages_pre[$i]} smartcat ${template}/${pages_pre[$i]} index >> ${index} #hook# file_to_process cat_index_posthook ${pages_pre[$i]} done for i in {1..${#content}} ; do fill_replaces ${content[$i]} cat_index_prehook posting-start.part smartcat ${template}/posting-start.part index >> ${index} cat_index_posthook posting-start.part cat_index_prehook catcont catcont ${content[$i]} >> ${index} cat_index_posthook catcont cat_index_prehook posting-end.part smartcat ${template}/posting-end.part index >> ${index} cat_index_posthook posting-end.part done for i in {1..${#pages_post}} ; do cat_index_prehook ${pages_post[$i]} smartcat ${template}/${pages_post[$i]} index >> ${index} cat_index_posthook ${pages_post[$i]} done } ### }}} ### cat_atom() {{{ function cat_atom() { local atom local content atom=${1} ; shift content=($@) fill_replaces ${content[1]} #hook# file_to_process cat_atom_prehook atom_header.part smartcat ${template}/atom_header.part index >> ${atom} #hook# file_to_process cat_atom_posthook atom_header.part for i in {1..${#content}} ; do fill_replaces ${content[$i]} cat_atom_prehook atom_prepost.part smartcat ${template}/atom_prepost.part index >> ${atom} cat_atom_posthook atom_prepost.part cat_atom_prehook atomcont atomcont ${content[$i]} >> ${atom} cat_atom_posthook atomcont cat_atom_prehook atom_postpost.part smartcat ${template}/atom_postpost.part index >> ${atom} cat_atom_posthook atom_postpost.part done cat_atom_prehook atom_footer.part smartcat ${template}/atom_footer.part index >> ${atom} cat_atom_posthook atom_footer.part } ### }}} ### insert_archline() {{{ function insert_archline() { local oldifs repl line local file=$1 fill_replaces ${file} oldifs=${IFS} while read -r line ; do for repl in ${(k)replaces} ; do line=${line//\{\$${repl}\$\}/${replaces[$repl]}} done printf '%s\n' ${line} done < ${template}/arch-line.part } ### }}} ### cat_arch() {{{ function cat_arch() { local index content curpage lastpage index=${blogroot}/${1} ; shift curpage=$1 ; shift lastpage=$1 ; shift content=($@) rm -f ${index} fill_replaces ${content[1]} for i in {1..${#pages_pre}} ; do #hook# file_to_process cat_arch_prehook ${pages_pre[$i]} smartcat ${template}/${pages_pre[$i]} archive ${replaces[posting_cat]} ${curpage} ${lastpage} >> ${index} #hook# file_to_process cat_arch_posthook ${pages_pre[$i]} done cat_arch_prehook arch-start.part smartcat ${template}/arch-start.part archive ${replaces[posting_cat]} ${curpage} ${lastpage} >> ${index} cat_arch_posthook arch-start.part for i in {1..${#content}} ; do cat_arch_prehook archline insert_archline ${contents[$i]} >> ${index} cat_arch_posthook archline done cat_arch_prehook arch-end.part smartcat ${template}/arch-end.part archive ${replaces[posting_cat]} ${curpage} ${lastpage} >> ${index} cat_arch_posthook arch-end.part for i in {1..${#pages_post}} ; do cat_arch_prehook ${pages_post[$i]} smartcat ${template}/${pages_post[$i]} archive ${replaces[posting_cat]} ${curpage} ${lastpage} >> ${index} cat_arch_posthook ${pages_post[$i]} done } ### }}} ### create_posts() {{{ function create_posts() { # TODO: enhance; make comment aware. cd ${postsdir} local newcontent newcontent=(**/*.content) (( ${#newcontent} <= 0 )) && return for i in {1..${#newcontent}} ; do log "adding: ${${newcontent[$i]}:r}" cat_html ${${newcontent[$i]}:r} done } ### }}} ### create_index() {{{ function create_index() { cd ${postsdir} local index local content local file local integer need2update need2update=0 index="${blogroot}/index.html" content=(**/*.html(On[1,${index_posts}])) if [[ -e ${index} ]] ; then for file in ${content} ; do if [[ ${index} -ot ${file} ]] ; then need2update=1 ; fi done else need2update=1 fi (( need2update == 0 )) && return [[ -e ${index} ]] && rm ${index} reset_replaces lastday=() cat_index ${index} ${content} } ### }}} ### create_atom() {{{ function create_atom() { cd ${postsdir} local atom local content atom="${blogroot}/atom.xml" content=(**/*.html(On[1,${index_posts}])) reset_replaces lastday=() cat_atom ${atom} ${content} } ### }}} ### create_cat_arch() {{{ function create_cat_arch() { local cat count_s count_e contents pagenum last_page cat=$1 count_s=1 count_e=${arch_posts} pagenum=0 [[ -z ${cat} ]] && log "create_cat_arch(): wrong usage! check your code!" && return cd ${postsdir} contents=( **/*-${cat}.html) last_page=$(( ( ${#contents} - 1) / arch_posts )) contents=( **/*-${cat}.html(On[${count_s},${count_e}]) ) while [[ ${#contents} -ne 0 ]] ; do cat_arch archives-${cat}-$(( [#16] pagenum )).html ${pagenum} ${last_page} ${contents} (( pagenum++ )) count_s=$(( count_s + arch_posts )) count_e=$(( count_e + arch_posts )) contents=( **/*-${cat}.html(On[${count_s},${count_e}]) ) done cd ${OLDPWD} } ### }}} ### create_arch() {{{ function create_arch() { local line cats cat cats=() while read -r line ; do cats+=${line} ; done < ${catlist} for cat in ${cats} ; do create_cat_arch ${cat} ; done } ### }}} ### extract_contents() {{{ function extract_contents() { local html cd ${postsdir} for html in **/*.html ; do cat_contents ${html} > ${html:r}.content #TODO #cat_comments ${html} done } ### }}} ### }}} ### commentmode {{{ ### check_from_against_bwlists {{{ function check_from_against_bwlists() { ### 0: white; 1: grey; 2: black; local infile from line from=${1} for infile in ${blist} ${wlist} ; do while read -r line ; do if [[ ${line} == ${from} ]] ; then [[ ${infile} == ${wlist} ]] && return 0 return 2 fi done < ${infile} done return 1 } ### }}} ### comment_get_nextnumber {{{ function comment_get_nextnumber() { local posting comment posting="${postsdir}/${1}.html" comment=( ${(s:.:)2} ) } ### }}} ### comment_get_link {{{ function comment_get_link() { local line from from=${1} while read line ; do if [[ ${line} == (#m)${from}\ (*)\ (*) ]] ; then printf '%s\n' ${match[2]} return 0 fi done < ${commentinfo} return 1 } ### }}} ### comment_get_author {{{ function comment_get_author() { local line from from=${1} while read line ; do if [[ ${line} == (#m)${from}\ (*)\ (*) ]] ; then printf '%s\n' ${match[1]} return 0 fi done < ${commentinfo} return 1 } ### }}} ### comment_store_info {{{ function comment_store_info() { local line from author link content i ow from=${1} ; author=${2} ; link=${3} content=${(f)"$(< ${commentinfo} )"} for i in {1..${#content}} ; do if [[ ${content[$i]} == ${from}* ]] ; then content[$i]="${from} ${author} ${link}" ow=1 break fi done printf '%s\n' ${content[1]} > ${commentinfo} for i in {2..${#content}} ; do printf '%s\n' ${content[$i]} >> ${commentinfo} done return 0 } ### }}} ### txt2xhtml {{{ function txt2xhtml() { local outfile line oifs outfile=${1} ; line=${2} printf '%s
    \n' "${2}" >> ${outfile} } ### }}} ### }}} ### email/plain/commandmode functions {{{ ### exists() {{{ function exists() { case $1 in (category) [[ ! -e ${catlist} ]] && return 1 while read -r line ; do [[ ${line} == $2 ]] && return 0 done < ${catlist} ;; (sticky) [[ ! -e ${stickylist} ]] && return 1 while read -r line ; do [[ ${line%% *} == $2 ]] && return 0 done < ${stickylist} ;; (posting) [[ -e ${postsdir}/${2} ]] && return 0 ;; (*) warn "exists(): unknown argument ($1)" ;; esac return 1 } ### }}} ### addcat() {{{ function addcat() { printf '%s\n' ${1} >> ${catlist} } ### }}} ### unstick() {{{ function unstick() { [[ -e ${stickylist}.tmp ]] && rm ${stickylist}.tmp while read line ; do [[ ${line} != ${1} ]] && printf '%s\n' ${line} >> ${stickylist}.tmp done < ${stickylist} rm -f ${stickylist}.tmp } ### }}} ### chdate() {{{ function chdate() { local cat old_dir new_dir old_file new_file old_dir=${${1:h}#/} old_file=${1:t} cat=${old_file##*-} new_dir=${${2:h}#/} new_file=${2:t} [[ ! -d ${new_dir} ]] && mkdir -p ${blogroot}/${new_dir} mv ${blogroot}/${old_dir}/${old_file} ${blogroot}/${new_dir}/${new_file}-${cat} } ### }}} ### remove_cat() {{{ function remove_cat() { [[ -e ${catlist}.tmp ]] && rm ${catlist}.tmp while read line ; do [[ ${line} != ${1} ]] && printf '%s\n' ${line} >> ${catlist}.tmp done < ${catlist} [[ -e ${catlist}.tmp ]] && mv ${catlist}.tmp ${catlist} } ### }}} ### editmsg() {{{ function editmsg() { log "editmsg(): ${postsdir}/${1}" ${patch} ${postsdir}/${1} <&0 > /dev/null 2>&1 if (( status == 1 )) ; then rm ${postsdir}/${1}.rej mv ${postsdir}/${1}.orig ${1} fi } ### }}} ### com_post() {{{ function com_post() { local cat f oldifs line cat=${2} ; f=${1}-${cat}.content oldifs=${IFS} IFS='' while read -r line ; do printf '%s\n' "${line}" >> ${f} done <&0 IFS=${oldifs} } ### }}} ### decrypt_mail() {{{ function decrypt_mail() { local cry=$1 local dec=$2 [[ -z ${dec} || -z ${cry} ]] && die "broken usage of decrypt_mail()! - check your code!" ${gpg} --no-tty --passphrase-fd 0 --quiet --decrypt ${cry} > ${dec} <<< ${gpg_passphrase} 2> /dev/null } ### }}} ### zblog_auth() {{{ function zblog_auth() { ### this function reads a file and checks for a ^ZBLOG-AUTH: user passwd line ### return 1 if authentication fails local file=$1 local oldifs line user passwd oldifs=${IFS} IFS='' while read -r line ; do if [[ ${line} = ZBLOG-AUTH:' '(#b)(*)' '(*) ]] ; then user=$match[1] passwd=$match[2] if [[ -z ${users[$user]} ]] ; then ### user does not exist IFS=${oldifs} return 1 fi if [[ ${passwords[$user]} = ${passwd} ]] ; then ### user exists, password is correct IFS=${oldifs} return 0 fi ### password is incorrect IFS=${oldifs} return 1 fi done < ${file} IFS=${oldifs} return 1 } ### }}} ### add_crypted_post() {{{ function add_crypted_post() { ### Notes: ### If the message contains only text, that text is evaluated. ### If the message is multipart: ### . only the first textpart is evaluated. ### . only only files with $mediafiles extentions are moved to $mediadir ### . THE REST WILL BE DELETETED! local dir=$1 local mtype cd ${dir} decrypt_mail crypted decrypted mtype=$(${com_file} decrypted) if [[ ${mtype} = *multipart* ]] ; then ${mime_unpack} ${mime_unpack_options} decrypted > /dev/null else mv decrypted part1 fi rm -f crypted decrypted zblog_auth part1 if (( status == 1 )) ; then ### by convention '0' means positive execution log "auth failed! aborting." cd ${OLDPWD} rm -Rf ${dir} return fi cat_contents part1 > ../${dir:t}.content fill_replaces ../${dir:t}.content mv ../${dir:t}.content ../${dir:t}-${replaces[posting_cat]}.content log "${0}: adding post (${replaces[posting_title]})" for file in *.${^mediafiles} ; do log "${0}: installing ${file} to ${mediadir}" mv ${file} ${mediadir} done cd ${OLDPWD} rm -Rf ${dir} } ### }}} ### add_plain_post() {{{ function plain_type() { local oldifs oldifs=${IFS} IFS='' while read -r line; do if [[ ${line} == Content-Type:* ]] ; then printf '%s\n' ${line} IFS=${oldifs} return 0 fi done < ${1} } function add_plain_post() { local dir file oldifs mtype dir=${1} cd ${dir} file=${dir:t} oldifs=${IFS} IFS='' while read -r line; do printf "%s\n" ${line}; done >> __content__ mtype=$(plain_type __content__) if [[ ${mtype} = *multipart* ]] ; then ${mime_unpack} ${mime_unpack_options} __content__ > /dev/null rm __content__ else mv __content__ part1 fi cat_contents part1 > ../${file}.content fill_replaces ../${file}.content mv ../${file}.content ../${file}-${replaces[posting_cat]}.content log "${0}: adding post (${replaces[posting_title]})" for file in *.${^mediafiles} ; do log "${0}: installing ${file} to ${mediadir}" mv ${file} ${mediadir} done cd ${OLDPWD} rm -Rf ${dir} } ### }}} ### mupload() {{{ media_marked_for_upload=() function mupload() { local media if [[ ! -d ${1} ]] ; then printf '%s(): no such directory: %s\n' ${0} ${1} return 1 fi for media in ${media_marked_for_upload} ; do if [[ -e ${1}/${media} ]] ; then #hook# mail_unpack_dir media_file mupload_pre_move ${1} ${media} mv ${1}/${media} else printf '%s(): cannot find attachment (%s). ignoring.\n' ${0} ${media} fi done media_marked_for_upload=() return 0 } function markmupload() { media_marked_for_upload+=${1} } ### }}} ### remove_posting() {{{ function remove_posting() { # away with it! rm ${postsdir}/${1} # and touch the most recent posting in ${postsdir} touch ${postsdir}/*/*(on[-1]) } ### }}} ### }}} ### }}} ### main functions {{{ ### banner() {{{ function banner() { local key aname printf '%s' "$name $version" if [[ -n ${codename} ]] && printf " ($codename)" printf "\n" for key in ${(onk)authors} ; do aname=${key##[0-9]##_} if [[ ${aname} = 'Frank Terbeck' ]] ; then printf '%s\n' " written by ${aname} <$authors[$key]>" else printf '%s\n' " ${aname} <$authors[$key]>" fi done } ### }}} ### check_installation() {{{ function check_installation() { local err #hook# pre_checks err=0; if [[ ! -e $rc ]] ; then warn "rc file ($rc) not found!"; err=1 ; fi if [[ ! -d $blogroot ]] ; then warn "blogroot ($blogroot) misconfigured."; err=1 ; fi if [[ ! -d $logdir ]] ; then warn "logdir ($logdir) misconfigured."; err=1 ; fi if [[ ! -d $postsdir ]] ; then warn "postsdir ($postsdir) misconfigured."; err=1 ; fi if [[ ! -d $mediadir ]] ; then warn "mediadir ($mediadir) misconfigured."; err=1 ; fi if [[ ! -d $comqueuedir ]] ; then warn "comqueuedir ($comqueuedir) misconfigured."; err=1 ; fi if [[ ! -d ${catlist%/*} ]] ; then warn "dir for ${catlist} does not exist."; err=1 ; fi if [[ ! -d ${stickylist%/*} ]] ; then warn "dir for ${stickylist} does not exist."; err=1 ; fi if [[ ! -d ${recentlist%/*} ]] ; then warn "dir for ${recentlist} does not exist."; err=1 ; fi if [[ ! -e ${blist} ]] ; then warn "blacklist ${blist} misconfigured."; err=1 ; fi if [[ ! -e ${wlist} ]] ; then warn "whitelist ${wlist} misconfigured."; err=1 ; fi if [[ -z ${commentinfo} ]] ; then warn "commentinfo ${commentinfo} misconfigured."; err=1 ; fi if [[ ! -d $template ]] ; then warn "template ($template) misconfigured."; err=1 ; fi if [[ ! -x $gpg ]] ; then warn "gnupg ($gpg) not found!"; err=1 ; fi if [[ ! -x $patch ]] ; then warn "patch ($patch) not found!"; err=1 ; fi if [[ ! -x $date ]] ; then warn "date ($date) not found!"; err=1 ; fi if [[ ! -x $mime_unpack ]] ; then warn "munpack ($mime_unpack) not found!"; err=1 ; fi if [[ ! -x $com_file ]] ; then warn "file ($com_file) not found!"; err=1 ; fi if (( err > 0 )) ; then die "encountered problems when checking installation!" ; fi #hook# post_checks } ### }}} ### commandmode() {{{ ### special checking functions {{{ function chdate_check() { # TODO # return 1 if arg1 does not exist as a posting # return 2 is arg2 is not a well-formed datestring # return 0 if everything is fine return 0 } function stick_check() { [[ -e ${1} ]] && return 1 exists sticky ${1} && return 2 return 0 } ### }}} typeset -A com2func com_usages com_errors com_data com_checks com2func=( #{{{ "addcat" "addcat" "chdate" "chdate" "com_post" "com_post" "edit" "editmsg" "mupload" "markmupload" "remove" "remove_posting" "removecat" "remove_cat" "stick" "stick" "unstick" "unstick" ) #}}} com_usages=( #{{{ "addcat" "addcat " "chdate" "(chdate|redate) " "com_post" "post " "edit" "(edit|diff|patch) " "mupload" "(mupload|mediaupload|mup) " "remove" "remove " "removecat" "removecat " "stick" "stick " "unstick" "unstick " ) #}}} com_errors=( #{{{ "addcat" "\${com2func[addcat]}(): category \${arglist[1]} already exists.;" "chdate" "\${com2func[chdate]}(): \${arglist[1]} does not exist.;"\ "\${com2func[chdate]}(): \${arglist[2]} is a malformed date string.;" "com_post" "\${com2func[com_post]}(): category (\${arglist[1]}) does not exit. *NOT* posting!;" "edit" "\${com2func[edit]}(): \${arglist[1]} does not exist.;" "mupload" ";" # mupload does not fail when it's called. "remove" "\${com2func[remove]}(): \${arglist[1]} does not exist.;" "removecat" "\${com2func[removecat]}(): will not try to remove non-existant category (\${arglist[1]}).;" "stick" "\${com2func[stick]}(): \${arglist[1]} does not exist.;"\ "\${com2func[stick]}(): \${arglist[1]} is already in stickylist.;" "unstick" "\${com2func[unstick]}(): \${arglist[1]} already exists.;" ) #}}} com_checks=( #{{{ "addcat" "exists category \${arglist[1]} && false || true" "chdate" "chdate_check \${arglist[1]} \${arglist[2]}" "com_post" "exists category \${arglist[2]}" "edit" "exists posting \${arglist[1]}" "mupload" "true" "remove" "exists posting \${arglist[1]}" "removecat" "exists category \${arglist[1]}" "stick" "stick_check \${arglist[1]}" "unstick" "exists sticky \${arglist[1]}" ) #}}} com_data=( #{{{ #command args_n:read_stdin "addcat" "1:0" "chdate" "2:0" "com_post" "2:1" "edit" "1:1" "mupload" "1:0" "remove" "1:0" "removecat" "1:0" "stick" "1:0" "unstick" "1:0" ) #}}} function commandmode_checkargs() { #{{{ # the returnvalue of this function is used by errmsg to determine what errormessage to spit out. local cn local -a arglist cn=${1} ; arglist=(${argv[2,-1]}) eval ${com_checks[$cn]} return ${?} #}}} } function usagemsg() { #{{{ printf "${com_usages[$1]}" #}}} } function errmsg() { #{{{ printf '%s abort!' "${(e)${(s.;.)${com_errors[$2]}}[$1]}" #}}} } function commandmode_call() { #{{{ local cn rstd errmsg usgmsg neededargs an local -a arglist if (( ARGC < 1 )) ; then die "commandmode_call(): usage: commandmode_call [args]" fi cn=${1} if [[ -z ${com2func[$cn]} ]] ; then warn "cannot find function for $cn command!" return 3 fi rstd=${${(s.:.)${com_data[$cn]}}[2]} ; neededargs=${${(s.:.)${com_data[$cn]}}[1]} arglist=(${argv[2,-1]}) ; an=${#arglist} if (( neededargs != an )) ; then log $(usagemsg ${cn} ${arglist}) return 1 elif ! commandmode_checkargs ${cn} ${(Q)arglist} ; then log $(errmsg ${pipestatus[1]} ${cn} ${(Q)arglist}) return 2 else #printf 'DEBUG: call: %s %s\n' ${com2func[$cn]} ${(Q)arglist} eval ${com2func[$cn]} ${arglist} (( ${rstd} > 0 )) && exec <&- return 0 fi #}}} } function commandmode() { #{{{ local oldifs line seen_hi comline #hook# pre_commandmode comline=() seen_hi=0 oldifs=${IFS} IFS='' while read -r line ; do if (( seen_hi == 1 )) ; then [[ ${line} = (#i)thanks[.!]# ]] && log "end of message found ($line); stopping processing" && IFS=${oldifs} && return comline=( ${(z)line} ) case ${comline[1]} in (addcat) #{{{ #hook# com_addcat_hook commandmode_call addcat ${comline[2,-1]} ;; #}}} (removecat) #{{{ #hook# com_removecat_hook commandmode_call removecat ${comline[2,-1]} ;; #}}} (stick) #{{{ #hook# com_stick_hook commandmode_call stick ${comline[2,-1]} ;; #}}} (unstick) #{{{ #hook# com_unstick_hook commandmode_call unstick ${comline[2,-1]} ;; #}}} (diff|edit|patch) #{{{ #hook# com_edit_hook commandmode_call edit ${comline[2,-1]} ;; #}}} (chdate|redate) #{{{ #hook# com_chdate_hook commandmode_call chdate ${comline[2,-1]} ;; #}}} (remove) #{{{ #hook# com_remove_hook commandmode_call remove ${comline[2,-1]} ;; #}}} (atomize) #{{{ TODO #hook# com_atomize_hook commandmode_call atomize ${comline[2,-1]} ;; #}}} (update) #{{{ TODO #hook# com_update_hook commandmode_call update ${comline[2,-1]} ;; #}}} (fupdate) #{{{ TODO #hook# com_fupdate_hook commandmode_call fupdate ${comline[2,-1]} ;; #}}} (post) #{{{ #hook# com_post_hook commandmode_call com_post ${1} ${comline[2,-1]} ;; #}}} (mupload|mediaupload|mup) #{{{ #hook# com_mupload_hook commandmode_call mupload ${comline[2,-1]} ;; #}}} (*) #{{{ #hook# com_unknown_hook log "unknown command (${comline[1]}); ignoring" ;; #}}} esac fi (( seen_hi == 0)) && [[ ${line} = (#i)hi[.!]# ]] && seen_hi=1 && log "start of message found ($line)" done <&0 IFS=${oldifs} #hook# post_commandmode #}}} } ### }}} ### commentmode() {{{ function commentmode() { ### How to implement comments in a static blog? ### + add an emailaddress that takes care of comments ### + force moderation of the incoming comments ### + provide a whitelist, that passes moderation without checks ### + provide a blacklist, that will be ignored by default ### ### When a comment comes in: ### + check against white/black list ### + blacklist -> ignore ### + whitelist -> go on with processing (next 'zblog update' should take care of it) ### + if the mail matches neither of the lists, put it into the moderation queue ### + moderation queue: ### + either wait for manual 'zblog approve' ### + or wait for 'approve' command in a command mail ### That means: ### + commands needed: ### + lsmodqueue [all|specific-comment] ### + approve [all|specific-comment] ### Oh, and if you by chance didn't realize how ugly this function is: IT'S UGLY! local oldifs line from subject cdate found_eoh found_eoc fromstatus i local posting comment comment_number outfile author link com_found temp integer com_found setopt xtrace (( com_found = -1 )) link='none' oldifs=${IFS} if [[ -n ${1} ]] ; then subject="${@}" fi cdate=$( date +'%a, %d %h %Y %H:%M:%S %z' ) IFS='' #hook# pre_commentmode while read -r line ; do ### a 'From:' Header is _needed_ no matter what; (we generate everything else in here). if [[ -n ${from} && -n ${subject} && ${found_eoh} -gt 0 && ${found_eoc} -gt 0 ]] ; then ### From: read, headers skipped and commands parsed; process payload. if [[ -z ${posting} ]] ; then posting="${subject%:*}" posting="${posting//.html/}" comment="${subject#*:}" if ! exists posting "${posting}.html" ; then warn "${0}: posting does not exist (${postsdir}/${posting}.html); abort." exec <&- post_commentmode return fi case ${fromstatus} in (0) outfile="${postsdir}/${posting}.html_${comment_number}" comment_number=$( comment_get_nextnumber ${posting} ${comment} ) ;; (1) i=0 posting=${posting/\//_} while [[ -e ${comqueuedir}/${posting}.html_followup-${comment}-${i} ]] ; do (( i++ )) done comment_number="${comment}.x" outfile="${comqueuedir}/${posting}.html_followup-${comment}-${i}" ;; (*) die "${0}: \${fromstatus} has unrecognized value (${fromstatus})." ;; esac rm -f ${outfile} touch ${outfile} printf '%s\n' ${c_begin} >> ${outfile} printf 'Number: %s\n' ${comment_number} >> ${outfile} printf 'Author: %s\n' ${author} >> ${outfile} printf 'Link: %s\n' ${link} >> ${outfile} printf 'Date: %s\n' ${cdate} >> ${outfile} printf '%s\n' ${c_cont} >> ${outfile} fi if [[ -n ${temp} ]] ; then txt2xhtml ${outfile} ${temp} temp='' fi txt2xhtml ${outfile} ${line} elif [[ -n ${from} && -n ${subject} && ${found_eoh} -gt 0 ]] ; then ### header skipped, check if there are commands, if so, parsed em. case ${line} in (name*) (( com_found <= 0 )) && com_found=1 author=${line#name[ $'\t']##} ;; (link*) (( com_found <= 0 )) && com_found=1 link=${line#link[ $'\t']##} ;; (store) (( com_found <= 0 )) && com_found=1 if [[ ${author} == ${com_anonymous} ]] ; then warn "${0}: sorry, not storing ${author} for (${from})" else comment_store_info ${from} ${author} ${link} fi ;; ('') (( com_found >= 0 )) && found_eoc=1 ;; (*) temp=${line} found_eoc=1 ;; esac elif [[ -n ${from} && -n ${subject} ]] ; then if [[ ${line} == '' ]] ; then found_eoh=1 elif [[ ${line} = Date:' '(#b)(*) ]] ; then cdate=$(${date} --date=$match[1] '%a, %d %h %Y %H:%M:%S %z') fi elif [[ -z ${from} || -z ${subject} ]] ; then if [[ ${line} == '' ]] ; then die "${0}: malformed input (Subject: ${subject}; From: ${from})." elif [[ ${line} = From:' '[^\<]#\<(#b)(*)\> ]] ; then from=$match[1] check_from_against_bwlists ${from} fromstatus=${status} if (( ${fromstatus} == 2 )) ; then log "${0}: blacklisted mail (${from}): bye! :-)" exec <&- post_commentmode return fi link=$( comment_get_link ${from} ) author=$( comment_get_author ${from} ) elif [[ ${line} = Subject:' '(#b)(*) ]] ; then subject=$match[1] fi else fi done <&0 #hook# post_commentmode } ### }}} ### approve() {{{ function approve() { #hook# pre_approve __niy "$0" #hook# post_approve } ### }}} ### lsmodqueue() {{{ function lsmodqueue() { #hook# pre_lsmodqueue __niy "$0" #hook# post_lsmodqueue } ### }}} ### plain() {{{ function plain() { local returnpath oldifs lines dir from subject ctype file zblog_tmp dpost dline dstart dstart=0 outstdout=1 zblog_tmp="${tmpdir}/zblog_tmp" cd ${postsdir} returnpath=${OLDPWD} #hook# pre_plain printf '\n -!- running in PLAIN mode!\n' printf ' -!- DO NOT USE THIS MODE IN REAL APPLICATIONS!\n' printf ' -!- This mode is supposed to be used _only_ for testing.\n\n' oldifs=${IFS} IFS='' subject='' dir='' rm -f ${zblog_tmp} while read -r line ; do printf '%s\n' $line >> ${zblog_tmp} [[ ${line} = Subject:' '(#b)(*) ]] && subject=$match[1] if [[ ${line} = Date:' '(#b)(*) ]] ; then dir=$(${date} --date=$match[1] '+%Y/%m-%d.%H-%M-%S') mkdir -p ${dir} fi if [[ -n ${dir} ]] ; then case ${subject} in ('') ;; (post*) while read -r inline ; do printf '%s\n' ${inline} ; done < ${zblog_tmp} > ${dir}/__content__ add_plain_post ${dir} <&0 ;; (comm*) commandmode ${dir} <&0 mupload ${dir} rm -R ${dir} exec <&- ;; (diff*|edit*|patch*) dpost=${subject/(patch|edit|diff)[^ ]# #/} if [[ ! -e ${blogroot}/${dpost} ]] ; then log "${0}(): received (Subject: ${subject}); however ${blogroot}/${dpost} does not exist. abort." break else while read -r dline ; do ### a unified diff starts with --- (( ${dstart} == 0 )) && [[ ${dline} == ---\ * ]] && printf '%s\n' ${dline} > ${zblog_tmp} && dstart=1 (( ${dstart} == 1 )) && printf '%s\n' ${dline} >> ${zblog_tmp} done <&0 editmsg ${blogroot}/${dpost} < ${zblog_tmp} rm -f ${zblog_tmp} fi ;; (*) die "unknown query ($subject) by $from"; esac fi done <&0 rm -f ${zblog_tmp} outstdout=0 cd ${returnpath} #hook# post_plain } ### }}} ### email() {{{ function email() { ### this function handles the decryption and all attachments of an incoming email on stdin. ### this will discard plaintext emails. use this with procmail #hook# pre_email local returnpath oldifs lines write cryptdir from subject ctype file dpost write=0 cd ${postsdir} returnpath=${OLDPWD} oldifs=${IFS} IFS='' while read -r line ; do if (( write > 0 )) ; then printf '%s\n' ${line} >> ${cryptdir}/crypted fi if [[ ${line} = Content-type:' '(#b)(*) ]] ; then ctype=$match[1] [[ ${ctype} != *encrypted* ]] && log "message not encrypted ($subject) by $from; discarding." && rm -rf ${cryptdir} && return fi [[ ${line} = Subject:' '(#b)(*) ]] && subject=$match[1] [[ ${line} = From:' '[^\<]#\<(#b)(*)\> ]] && from=$match[1] if [[ ${line} = Date:' '(#b)(*) ]] ; then cryptdir=$(${date} --date=$match[1] '+%Y/%m-%d.%H-%M-%S') mkdir -p ${cryptdir} fi (( write == 0 )) && [[ -n ${ctype} && -n ${subject} && -n ${cryptdir} && -n ${from} ]] && write=1 done <&0 #hook# email_postread IFS=${oldifs} case ${subject} in (post*) add_crypted_post ${cryptdir} ;; (comm*) decrypt_mail ${cryptdir}/crypted ${cryptdir}/decrypted zblog_auth ${cryptdir}/decrypted if (( status == 1 )) ; then ### by convention '0' means positive execution log "auth failed! aborting." cd ${returnpath} rm -Rf ${dir} return fi commandmode ${cryptdir} < ${cryptdir}/decrypted mupload ${cryptdir} rm -R ${cryptdir} ;; (diff*|edit*|patch*) dpost=${subject/(edit|patch|diff)[^ ]# #/} if [[ ! -e ${blogroot}/${dpost} ]] ; then log "${0}(): received (Subject: ${subject}); however ${blogroot}/${dpost} does not exist. abort." break else decrypt_mail ${cryptdir}/crypted ${cryptdir}/decrypted zblog_auth ${cryptdir}/decrypted if (( status == 1 )) ; then log "auth failed! aborting." cd ${returnpath} rm -Rf ${dir} return fi editmsg ${blogroot}/${dpost} exec <&- rm -R ${cryptdir} fi (*) die "unknown query ($subject) by $from"; esac cd ${returnpath} #hook# post_email } ### }}} ### update() {{{ function update() { ### this creates .html pages for postings, if there are .content files log "update(): running update" up_run='-!- update:' #hook# pre_update create_posts create_recent_list init_insert_recent ### this recreates the index page if needed create_index ### this recreates archive pages (only the ones that need to be updated) create_arch #hook# post_update up_run='' } ### }}} ### fupdate() {{{ function fupdate() { ### if there are .content pages for all posts, the whole blog will be recreated log "fupdate(): running forced update" fup_run='-!- fupdate:' #hook# pre_fupdate extract_contents update #hook# post_fupdate fup_run='' } ### }}} ### atomize() {{{ function atomize() { ### take last $index_posts posts and create an atom.xml of it #hook# pre_atomize [[ -e ${blogroot}/atom.xml ]] && rm -f ${blogroot}/atom.xml create_atom #hook# post_atomize } ### }}} ### exports() {{{ function exports() { #hook# pre_exports #hook# exports_hook #hook# post_exports } ### }}} ### usage_err() {{{ function usage_err() { #hook# pre_usage_err printf '%s\n' "${flames[$(( RANDOM % ${#flames} + 1))]}" #hook# post_usage_err exit 1 } ### }}} ### genlastmod() {{{ function genlastmod() { #hook# pre_genlastmod ### (YYYY)-(MM)-(DD)T(HH):(MM):(SS)($atomzone) printf '%s%s' $(${date} +%Y-%m-%dT%H:%M:%S) ${atomzone} #hook# post_genlastmod } ### }}} ### lsreplaces() {{{ function lsreplaces() { local key #hook# pre_lsreplaces reset_replaces if [[ ! -e ${zbloglib}/666/06-06.06-06-06.content ]] ; then printf 'Sorry I could not find my reference posting (%s).\nMaybe $zbloglib is misset in your rc file (%s)\n' \ "${zbloglib}/666/06-06.06-06-06.content" \ "${rc}" exit 1 fi fill_replaces ${zbloglib}/666/06-06.06-06-06.content replaces+=( arch-pager 'is_kind_of_special' ) replaces+=( smarttitle ', too.' ) for key in ${(onk)replaces} ; do printf '%s\n' "${key}" done #hook# post_lsreplaces } ### }}} ### cli_post() {{{ function cli_post() { #hook# pre_cli_post __niy "$0" #hook# post_cli_post } ### }}} ### cli_diff() {{{ function cli_diff() { #hook# pre_cli_diff __niy "$0" #hook# post_cli_diff } ### }}} ### cli_mupload() {{{ function cli_mupload() { #hook# pre_cli_mupload __niy "$0" #hook# post_cli_mupload } ### }}} ### cli_exec() {{{ function cli_exec() { #hook# pre_cli_exec __niy "$0" #hook# post_cli_exec } ### }}} function __niy() { printf 'Sorry, %s() is not yet implemented. Bug the author!\n' "$1" } ### }}} ### startup {{{ (( $# == 0 )) && usage_err check_installation cd ${blogroot} if [[ ${1} = 'email' ]] ; then email elif [[ ${1} = 'plain' ]] ; then plain elif [[ ${1} = 'commentmode' ]] ; then commentmode "${argv[2,-1]}" elif [[ ${1} = 'lsmodqueue' ]] ; then lsmodqueue elif [[ ${1} = 'approve' ]] ; then approve elif [[ ${1} = 'update' ]] ; then update elif [[ ${1} = 'fupdate' ]] ; then fupdate elif [[ ${1} = 'atomize' ]] ; then atomize elif [[ ${1} = 'exports' ]] ; then exports elif [[ ${1} = 'commands' ]] ; then commandmode elif [[ ${1} = 'genlastmod' ]] ; then genlastmod elif [[ ${1} = 'lsreplaces' ]] ; then lsreplaces elif [[ ${1} = 'post' ]] ; then cli_post elif [[ ${1} = 'diff' ]] ; then cli_diff elif [[ ${1} = 'mupload' ]] ; then cli_mupload elif [[ ${1} = 'exec' ]] ; then cli_exec elif [[ ${1} = 'version' ]] ; then banner else usage_err fi #hook# commandline-arguments post_zblog_main "$@" ### }}}