#! /bin/bash
############################################################
#  NanoBlogger 3.3 Copyright 2006 n1xt3r (Kevin R. Wood)   #
############################################################
# Last modified: 2007-01-13T16:12:31-05:00

# nanoblogger's version.
VERSION="3.3"

# nanoblogger's base install directory.
NB_BASE_DIR=`dirname $0`
# documentation
NB_DOC_DIR="$NB_BASE_DIR/docs"
# nanoblogger's conf file
NB_CFG_DIR="$NB_BASE_DIR"

### hardcoded paths ###

# nanoblogger's language definitions directory.
NB_LANG_DIR="${NB_BASE_DIR}/lang"
# nanoblogger's module directory.
NB_LIB_DIR="${NB_BASE_DIR}/lib"
# the world writable temp directory
NB_TEMP_DIR="/tmp"

### end hardcoded paths ###

# prompt to use when asking something.
NB_PROMPT=": "

# default verbosity, 0 = silent
VERBOSE=1

# cleanup for special temp files
SCRATCH_FILE="$NB_TEMP_DIR/nb_scratch$$"
NB_TEMP_FILES="$NB_TEMP_DIR/nb_entry$$.* $SCRATCH_FILE*"
[ ! -z "$NB_TEMP_FILES" ] && trap "rm -fr $NB_TEMP_FILES; exit" 0 1 2 3 15

# load the modules
. ${NB_LIB_DIR}/error.sh
. ${NB_LIB_DIR}/tools.sh
. ${NB_LIB_DIR}/config.sh
. ${NB_LIB_DIR}/query.sh

# insure a sane configuration or die
check_config(){
load_config
# die without the base directory
[ ! -d "$NB_BASE_DIR" ] &&
	die "`basename $0`: '$NB_BASE_DIR' - $checkconf_nobase."
[ -z "$BLOG_DIR" ] && die "$checkconf_noblog"
[ ! -z "$USR_BLOGCONF" ] &&
	[ ! -f "$USR_BLOGCONF" ] && die "'$USR_BLOGCONF' - $checkconf_nousrconf"
[ ! -d "$BLOG_DIR" ] && die "'$BLOG_DIR' - $checkconf_noblogdir"
[ ! -d "$NB_DATA_DIR" ] && die "'$NB_DATA_DIR' - $checkconf_nodata"
[ ! -d "$BLOG_DIR/$CACHE_DIR" ] && die "'$CACHE_DIR' - $checkconf_nocache"
[ ! -d "$BLOG_DIR/$PARTS_DIR" ] && die "'$PARTS_DIR' - $checkconf_nocache"
[ ! -d "$NB_TEMPLATE_DIR" ] && die "'$NB_TEMPLATE_DIR' - $checkconf_notemplates"
# die without write permissions
[ ! -w "$NB_TEMP_DIR" ] &&
	die "'$NB_TEMP_DIR' - $nowritedir"
[ ! -w "$BLOG_DIR" ] &&
	die "'$BLOG_DIR' - $nowritedir"
}

# load specified plugins (defaults to all) from $PLUGINS_DIR
load_plugins(){
PLUGIN_DIR="$1"
PLUGINS_LIST=( ${@:1} )
	add_plugin_script(){
	plugin_file="$1"
	if [ -f "$plugin_file" ]; then
		plugin_basefile="${PLUGIN_DIR}/${plugin_file##*\/}"
		plugin_basefile="${plugin_basefile##\/}"
		plugin_rmdir="${PLUGIN_DIR}/"
		plugin_rmfile="${plugin_rmdir##\/}${plugin_basefile##*\/}"
		# remove plugins while we add them to eliminate duplications
		plugin_scripts=(${plugin_scripts[@]//$plugin_rmfile/} "$plugin_basefile" )
	fi
	}
# determine the plugin direcory
[ ! -z "$PLUGIN_DIR" ] && PLUGIN_DIR="/$1"
# cleanup any trailing "/" in $USR_PLUGINSDIR
USR_PLUGINSDIR=${USR_PLUGINSDIR%%\/}
# determine the plugins directory list
if [ -d "${USR_PLUGINSDIR}${PLUGIN_DIR}" ] && [ -d "${PLUGINS_DIR}${PLUGIN_DIR}" ]; then
	if [ "${PLUGINS_DIR}" != "${USR_PLUGINSDIR}" ]; then
		PLUGINSDIR_LIST="${USR_PLUGINSDIR}${PLUGIN_DIR} ${PLUGINS_DIR}${PLUGIN_DIR}"
	else
		PLUGINSDIR_LIST="${PLUGINS_DIR}${PLUGIN_DIR}"
	fi
elif [ -d "${USR_PLUGINSDIR}${PLUGIN_DIR}" ]; then
	PLUGINSDIR_LIST="${USR_PLUGINSDIR}${PLUGIN_DIR}"
elif [ -d "${PLUGINS_DIR}${PLUGIN_DIR}" ]; then
	PLUGINSDIR_LIST="${PLUGINS_DIR}${PLUGIN_DIR}"
fi
# initialise the array of plugins
plugin_scripts=()
for plugin_path in $PLUGINSDIR_LIST; do
	# break out of loop on bogus plugin path with last error value
	[ ! -d "$plugin_path" ] && return
	# were plugin names specified?
	PLUGINLIST_DEFINED=`echo "${PLUGINS_LIST[*]}" |grep -c '.[ ].'`
	if [ "$PLUGINLIST_DEFINED" != 0 ] ; then
		# yes, so we add them in order specified (removing any commas)
		for plugin in `echo ${PLUGINS_LIST[@]} |sed -e '/[\,]/ s// /g; /[ ][ ]/ s// /g'`; do
			nb_plugin="$plugin_path/${plugin}.sh"
			add_plugin_script "$nb_plugin"
		done
	else
		# no specified plugins, so get them all
		for nb_plugin in "$plugin_path"/*.sh ; do
			add_plugin_script "$nb_plugin"
		done
	fi
done
# load user and main plugins in alpha-numeric order (0-9, A-z)
for nb_plugin in ${plugin_scripts[@]}; do
	#nb_msg "loading ..$nb_plugin ..."
	# allow user plugins to override main plugins
	if [ -f "${PLUGINS_DIR}/$nb_plugin" ] && [ -f "${USR_PLUGINSDIR}/$nb_plugin" ]; then
		. "${USR_PLUGINSDIR}/$nb_plugin"
	elif [ -f "${USR_PLUGINSDIR}/$nb_plugin" ]; then
		. "${USR_PLUGINSDIR}/$nb_plugin"
	elif [ -f "${PLUGINS_DIR}/$nb_plugin" ]; then
		. "${PLUGINS_DIR}/$nb_plugin"
	fi
done
}

# filter content through a template
load_template(){
TEMPLATE_FILE="$1"
if [ -f "$TEMPLATE_FILE" ]; then
	# prefix lines with an X
	TEMPLATE=`sed -e '/^/ s//X: /' < "$TEMPLATE_FILE"`
	# remove X's and source variables into a temp file
	cat > "$SCRATCH_FILE" <<-EOF
		sed -e '/^X:[ ]/ s///' <<TMPL
			$TEMPLATE
		TMPL
	EOF
	TEMPLATE_DATA=`. "$SCRATCH_FILE"`
else
	die "'$TEMPLATE_FILE' - $loadtemplate_nofile"
fi
}

# create entry archive
make_entryarchive(){
if [ "$ENTRY_ARCHIVES" = 1 ]; then
	mkdir -p `dirname "$BLOG_DIR/$PARTS_DIR/$permalink_file"`
	echo "$TEMPLATE_DATA" > "$BLOG_DIR/$PARTS_DIR/$permalink_file"
	make_page "$BLOG_DIR/$PARTS_DIR/$permalink_file" "$NB_TEMPLATE_DIR/$PERMALINK_TEMPLATE" \
	"$BLOG_DIR/$ARCHIVES_DIR/$permalink_file"
fi
}

# build entry archives
build_entryarchives(){
ENTRYARCHIVES_LIST=($1)
ENTRYARCHIVES_TEMPLATE="$2"
ENTRYARCHIVES_DATATYPE="$3"
for entry in ${ENTRYARCHIVES_LIST[@]}; do
	if [ -f "$NB_DATA_DIR/$entry" ]; then
		[ -z "$PARTS_FILE" ] &&
			PARTS_FILE="$BLOG_DIR/$PARTS_DIR/$permalink_file"
		if [ "$ENTRYARCHIVES_TEMPLATE" = "$PERMALINKENTRY_TEMPLATE" ]; then
			set_baseurl "" "$BLOG_DIR/$ARCHIVES_DIR/$permalink_file"
			load_entry "$NB_DATA_DIR/$entry" "$ENTRYARCHIVES_DATATYPE" entry
			set_entrylink "$entry"
			year=`echo "$month" |cut -c1-4`
			month=`echo "$month" |cut -c6-7`
			day=`echo "$entry" |cut -c9-10`
			findba_entries "$entry" "${MASTER_DB_RESULTS[*]}"
			set_entrynavlinks prev "$before_entry"
			set_entrynavlinks next "$after_entry"
			load_template "$NB_TEMPLATE_DIR/$ENTRYARCHIVES_TEMPLATE"
			[ ! -z "$TEMPLATE_DATA" ] &&
				echo "$TEMPLATE_DATA" >> "$PARTS_FILE"
			make_entryarchive
		else
			set_baseurl "$BASE_URL"
			load_entry "$NB_DATA_DIR/$entry" "$ENTRYARCHIVES_DATATYPE"
			set_entrylink "$entry"
			load_template "$NB_TEMPLATE_DIR/$ENTRYARCHIVES_TEMPLATE"
			[ ! -z "$TEMPLATE_DATA" ] &&
				echo "$TEMPLATE_DATA" >> "$PARTS_FILE"
		fi
	fi
done
}

# generate archive content
make_archive(){
query_type="$1"
db_catquery="$2"
MKARCH_ENTRY_TEMPLATE="$3"
PARTS_FILE="$4"
db_setlimit="$5"
db_limit="$6"
db_offset="$7"
query_db "$query_type" "$db_catquery" "$db_setlimit" "$db_limit" "$db_offset"
ARCHIVE_LIST=(${DB_RESULTS[@]})
mkdir -p `dirname "$PARTS_FILE"`
> "$PARTS_FILE"
# fallback to default entry template
[ ! -f "$NB_TEMPLATE_DIR/$MKARCH_ENTRY_TEMPLATE" ] &&
	MKARCH_ENTRY_TEMPLATE="$ENTRY_TEMPLATE"
build_entryarchives "${ARCHIVE_LIST[*]}" "$MKARCH_ENTRY_TEMPLATE" "$ARCH_DATATYPE"
db_setlimit=; db_limit=; db_offset=
}

# divide larger archives into multiple pages
paginate(){
page_query="$1"
page_catquery="$2"
page_items="$3"
page_template="$4"
page_entrytemplate="$5"
page_dir="$6"
page_file="$7"
page_fbasedir=`dirname "$page_file"`
[ -z "$ARCH_DATATYPE" ] &&
	ARCH_DATATYPE=ALL
ENTRY_DATATYPE="$ARCH_DATATYPE"
if [ "$page_fbasedir" != . ]; then
	page_filedir="$page_fbasedir/"
	page_file=`basename "$page_file"`
fi
	update_pages(){
		build_pagelist(){
		if [ ! -z "$page_num" ]; then
			[ -z "$PAGE_LIST" ] && PAGE_LIST="page$page_num"
			[ "$PAGE_LIST" != "$OLD_PAGELIST" ] && PAGE_LIST="$OLD_PAGELIST page$page_num"
			OLD_PAGELIST="$PAGE_LIST"
		fi
		}
		query_db "$page_query" "$page_catquery" limit "$page_limit" "$page_offset"
		PAGEMOD_VAR="$New_EntryFile$Delete_EntryFile$Move_EntryFile"
		for page_entry in ${UPDATE_LIST[@]}; do
			if [ "$NB_QUERY" = all ] || [ ! -z "$PAGEMOD_VAR" ] && [ "$page_catquery" = nocat ]; then
				build_pagelist
			else
				page_match=`for db_item in ${DB_RESULTS[@]}; do echo $db_item; done |grep "$page_entry"`
				[ ! -z "$page_match" ] && build_pagelist
			fi
		done
		PAGE_LIST=`for page_n in $PAGE_LIST; do echo "$page_n"; done |sort -u`
	}
	page_bynumber(){
	set_baseurl "" "${page_dir}${page_filedir}$page_file"
	make_archive "$page_query" "$page_catquery" "$page_entrytemplate" \
		"$BLOG_DIR/$PARTS_DIR/${page_filedir}$arch_file" limit "$page_limit" "$page_offset"
	make_page "$BLOG_DIR/$PARTS_DIR/${page_filedir}$arch_file" \
		"$NB_TEMPLATE_DIR/$page_template" "${page_dir}${page_filedir}$arch_file"
	}
query_db "$page_query" "$page_catquery"
total_items=${#DB_RESULTS[*]}
if [ "$total_items" -gt "$page_items" ] && [ "$page_items" != 0 ]; then
	get_pages(){ y=0; while [ "$y" -lt "$total_items" ]; do
		y=`expr $page_items + $y`; echo $y; done |grep -c "."; }
	total_pages=`get_pages`; page_totals=0
	# cleanup numbered page(s)
	if [ "$NB_QUERY" = all ]; then
		rm -f "${page_dir}${page_filedir}${page_file%%\.$NB_FILETYPE}"*[0-9]."$NB_FILETYPE"
		nb_msg "$paginate_action $total_pages ($page_items/$total_items) ..."
	else
		nb_msg "$paginate_action ($page_items/$total_items) ..."
	fi
	page_limit=0; page_offset=1; page_num=0
	while [ "$page_totals" -lt "$total_items" ]; do
		page_totals=`expr "$page_items" + "$page_totals"`
		page_offset=`expr "$page_offset" + "$page_limit"`
		# if page items overflow, use difference to set new limit
		if [ "$page_totals" -ge "$total_items" ]; then
			page_diff=`expr "$page_totals" - "$total_items"`
			page_limit=`expr "$page_limit" - "$page_diff"`
		else
			page_limit="$page_items"
		fi
		page_num=`expr "$page_num" + 1`
		prev_num=`expr "$page_num" - 1`
		next_num=`expr "$page_num" + 1`
		arch_file="$page_file"
		arch_link="./$NB_INDEX"
		arch_name=`echo "$page_file" |cut -d"." -f 1`
		prev_page=`chg_suffix "$arch_name-page$prev_num".no`
		next_page=`chg_suffix "$arch_name-page$next_num".no`
		[ "$page_num" -gt 1 ] &&
			arch_file=`chg_suffix "$arch_name-page$page_num".no`
		echo '<table border="0" cellspacing="1" cellpadding="3" summary="page-menu">' > "$SCRATCH_FILE"
		echo '<tr>' >> "$SCRATCH_FILE"
		[ "$prev_num" = 1 ] &&
			echo '<td><a href="'$arch_link'">'$NB_PrevPage'</a></td>' >> "$SCRATCH_FILE"
		[ "$prev_num" -gt 1 ] &&
			echo '<td><a href="'$prev_page'">'$NB_PrevPage'</a></td>' >> "$SCRATCH_FILE"
		i=1
		while [ $i -le $total_pages ]; do
			[ "$i" = 1 ] && page="$arch_link" ||
				page=`chg_suffix "$arch_name-page$i".no`
			if [ "$i" = "$page_num" ]; then
				echo '<td>'$i'</td>' >> "$SCRATCH_FILE"
			else
				echo '<td><a href="'$page'">'$i'</a></td>' >> "$SCRATCH_FILE"
			fi
			i=`expr $i + 1`
		done
		! [ "$next_num" -gt "$total_pages" ] &&
				echo '<td><a href="'$next_page'">'$NB_NextPage'</a></td>' >> "$SCRATCH_FILE"
		echo '</tr>' >> "$SCRATCH_FILE"; echo '</table>' >> "$SCRATCH_FILE"
		NB_PageLinks=$(< "$SCRATCH_FILE")
		if [ ! -z "${UPDATE_LIST[*]}" ] && [ ! -z "$NB_QUERY" ]; then
			update_pages
			for page_mod in ${PAGE_LIST[@]}; do
				[ "page$page_num" = "$page_mod" ] && page_bynumber
			done
		else
			page_bynumber
		fi
		NB_PageLinks=
	done
else
	set_baseurl "" "${page_dir}${page_filedir}$page_file"
	make_archive "$page_query" "$db_catquery" "$page_entrytemplate" "$BLOG_DIR/$PARTS_DIR/${page_filedir}$page_file"
	make_page "$BLOG_DIR/$PARTS_DIR/${page_filedir}$page_file" "$NB_TEMPLATE_DIR/$page_template" \
		"${page_dir}${page_filedir}$page_file"
fi
ARCH_DATATYPE=; page_filedir=
}

# build yearly archives (plugins only)
build_yeararchive(){
query_db "$yearn"
[ ! -z "${DB_RESULTS[*]}" ] &&
	load_plugins archive/year
}

# build daily archives
build_dayarchive(){
CACHE_TYPE=day
query_db "$day"
if [ ! -z "${DB_RESULTS[*]}" ]; then
	set_daylink "$day"
	set_daynavlinks "$day"
	set_baseurl "" "$BLOG_DIR/$ARCHIVES_DIR/$day_file"
	make_archive "$day" nocat "$DAYENTRY_TEMPLATE" "$BLOG_DIR/$PARTS_DIR/$day_file"
	NB_ArchiveTitle="$day_dir"
	dayn=`echo "$day" |cut -c9-10`
	load_plugins archive/day
	make_page "$BLOG_DIR/$PARTS_DIR/$day_file" "$NB_TEMPLATE_DIR/$DAY_TEMPLATE" \
	"$BLOG_DIR/$ARCHIVES_DIR/$day_file"
fi
}

# build monthly archives
build_montharchive(){
CACHE_TYPE=month
query_db "$month"
if [ ! -z "${DB_RESULTS[*]}" ]; then
	LOOPMONTH_LIST=(${DB_RESULTS[*]})
	[ "$NB_QUERY" != all ] &&
		LOOPMONTH_LIST=(`for update_entry in ${UPDATE_LIST[@]}; do \
			for month_entry in ${DB_RESULTS[@]}; do echo $month_entry; done \
				|grep "$update_entry"; done`)
	set_monthlink "$month"
	set_monthnavlinks "$month"
	set_baseurl "" "$BLOG_DIR/$ARCHIVES_DIR/$month_file"
	make_archive "$month" nocat "$MONTHENTRY_TEMPLATE" "$BLOG_DIR/$PARTS_DIR/$month_file"
	NB_ArchiveTitle="$month"
	load_plugins archive/month
	paginate "$month" nocat "$MAX_MONTHPAGE_ENTRIES" "$MONTH_TEMPLATE" \
		"$MONTHENTRY_TEMPLATE" "$BLOG_DIR/$ARCHIVES_DIR/" "$month_file"
	loop_day "${LOOPMONTH_LIST[*]}" build_dayarchive
fi
}

# build category archives
build_catarchives(){
CACHE_TYPE=cat
if [ ! -z "${CAT_LIST[*]}" ]; then
	for cat_arch in ${CAT_LIST[@]}; do
		if [ -f "$NB_DATA_DIR/$cat_arch" ]; then
			NB_ArchiveTitle=`sed 1q "$NB_DATA_DIR/$cat_arch"`
			set_catlink "$cat_arch"
			[ ! -z "$CATARCH_DATATYPE" ] &&
				ARCH_DATATYPE="$CATARCH_DATATYPE"
			load_plugins archive/category
			paginate all "$cat_arch" "$MAX_CATPAGE_ENTRIES" "$CATEGORY_TEMPLATE" \
				"$CATENTRY_TEMPLATE" "$BLOG_DIR/$ARCHIVES_DIR/" "$category_file"
		fi
	done
fi
}

# update the cache
update_cache(){
cache_update="$1"
cache_def="$2"
CACHEUPDATE_LIST=($3)
if [ "$cache_update" = build ]; then
	[ -z "$cache_def" ] &&
		cache_def=entry_metadata
	for cache_item in ${CACHEUPDATE_LIST[@]}; do
		echo "$cache_item" >> "$SCRATCH_FILE".$cache_def-cache_list
	done
	CACHEUPDATE_LIST=($(< "$SCRATCH_FILE".$cache_def-cache_list))
elif [ "$cache_update" = rebuild ]; then
	> "$SCRATCH_FILE".$cache_def-cache_list
	[ -z "$cache_def" ] &&
		cache_def=entry_metadata
	for cache_item in ${CACHEUPDATE_LIST[@]}; do
		echo "$cache_item" >> "$SCRATCH_FILE".$cache_def-cache_list
		rm -f "$BLOG_DIR/$CACHE_DIR/$cache_item".$cache_def
	done
	CACHEUPDATE_LIST=($(< "$SCRATCH_FILE".$cache_def-cache_list))
elif [ "$cache_update" = expired ]; then
	[ -z "$cache_def" ] && cache_def="*"
	# always cache more recent entries
	[ "$CHRON_ORDER" != 1 ] && db_order="-ru"
	[ -z "$CACHEUPDATE_LIST" ] &&
		query_db "$QUERY_MODE" "$db_catquery" limit "$MAX_CACHE_ENTRIES" "" "$db_order"
	for cache_item in "$BLOG_DIR/$CACHE_DIR"/*.$cache_def; do
		cache_item=${cache_item##*\/}
		cache_regex=`echo "$cache_item" |sed -e '/[\.]'$cache_def'[^. ]*$/ s///g'`
		cache_match=`for db_item in ${DB_RESULTS[@]}; do echo $db_item; done |grep -c "$cache_regex"`
		[ "$cache_match" != 1 ] &&
			rm -f "$BLOG_DIR/$CACHE_DIR/$cache_item"
	done
else
	[ -z "$cache_def" ] &&
		cache_def="*"
	[ ! -z "$cache_update" ] && query_db "$cache_update" "$db_catquery"
	for cache_item in ${DB_RESULTS[@]}; do
		rm -f "$BLOG_DIR/$CACHE_DIR/$cache_item".$cache_def
	done
fi
CACHE_LIST=(`for cache_item in ${CACHEUPDATE_LIST[@]}; do echo $cache_item; done |sort -u`)
}

loop_day(){
loopday_list=($1)
loopday_exec="$2"
LOOP_MASTER=(${loopday_list[*]})
ARCHIVE_DAYS=(`for db_item in ${LOOP_MASTER[@]}; do echo $db_item |cut -c1-10; done |sort $SORT_ARGS`)
for dayn in ${ARCHIVE_DAYS[@]}; do
	day="$dayn"
	query_db "$day"
	[ ! -z "${DB_RESULTS[*]}" ] && [ ! -z "$loopday_exec" ] &&
		$loopday_exec
done
}

# loops through archives, and executes instructions by years or months
loop_archive(){
looparch_list=($1)
looparch_type="$2"
looparch_exec="$3"
# set instructions to execute based on $looparch_type
if [ "$looparch_type" = years ]; then
	looparchexec_years="$looparch_exec"
elif [ "$looparch_type" = months ]; then
	looparchexec_months="$looparch_exec"
fi
ARCHIVE_MASTER=(${looparch_list[*]})
ARCHIVE_YEARS=(`for db_item in ${ARCHIVE_MASTER[@]}; do echo $db_item |cut -c1-4; done |sort $SORT_ARGS`)
for yearn in ${ARCHIVE_YEARS[@]}; do
	# execute instructions for each year
	if [ "$looparch_type" = years ] && [ ! -z "$looparchexec_years" ]; then
		$looparchexec_years
	else
		for monthn in 12 11 10 09 08 07 06 05 04 03 02 01; do
			ARCHIVE_MONTHS=(`for db_item in ${ARCHIVE_MASTER[@]}; do echo $db_item; done |grep $yearn'[-]'$monthn'[-]' |sed 1q`)
			for entry_month in ${ARCHIVE_MONTHS[@]}; do
				year="$yearn"
				month="$yearn-$monthn"
				query_db "$month"
				# execute instructions for each month
				[ ! -z "${DB_RESULTS[*]}" ] &&
					[ ! -z "$looparchexec_months" ] &&
						$looparchexec_months
			done
		done
	fi
done
}

# generate the archives
build_archives(){
load_plugins archive
nb_msg "$buildarchives_action"
# build/update the category archives
build_catarchives
if [ "$NB_QUERY" = all ]; then
	# build the monthly archives
	LOOP_LIST=(${UPDATE_LIST[*]})
	loop_archive "${LOOP_LIST[*]}" years build_yeararchive
	loop_archive "${LOOP_LIST[*]}" months build_montharchive
	# build the entry archives
	[ "$ENTRY_ARCHIVES" = 1 ] &&
		build_entryarchives "${UPDATE_LIST[*]}" "$PERMALINKENTRY_TEMPLATE" ALL
else
	# update relateive month archives
	MOD_MONTHS=`for moditem in ${UPDATE_LIST[@]}; do echo $moditem; done |sort $SORT_ARGS`
	loop_archive "$MOD_MONTHS" years build_yeararchive
	loop_archive "$MOD_MONTHS" months build_montharchive
	# update relative entry archives
	[ "$ENTRY_ARCHIVES" = 1 ] &&
		build_entryarchives "${UPDATE_LIST[*]}" "$PERMALINKENTRY_TEMPLATE" ALL
fi
}

# build the weblog
build_weblog(){
# add welcome message with cat id 1 (NanoBlogger Help) to new weblogs
if [ -f "$BLOG_DIR/.nb_newblogdir" ]; then
	# force full update
	NB_QUERY=all
	import_file "$NB_BASE_DIR/welcome-to-nb.txt"
	old_catnum="$cat_num"; cat_num=1
	add_entry; cat_num="$old_catnum"
	rm -f "$BLOG_DIR/.nb_newblogdir"
fi
# for clean build remove all generated files
if [ "$NB_QUERY" = all ] && [ -z "$cat_num" ]; then
	nb_msg "$buildweblog_all"
	# confirm removal of any previously generated files
	[ "$BLOG_INTERACTIVE" = 1 ] && confirm_action
	# use "--update-cache all" to clear cached data
	for update_dir in "$ARCHIVES_DIR" "$PARTS_DIR"; do
		[ ! -z "$update_dir" ] && [ -d "$BLOG_DIR/$update_dir" ] &&
			rm -fr "$BLOG_DIR"/"$update_dir"/*
	done
fi
# update master.db
if [ "$NB_QUERY" != main ]; then
	nb_msg "$querydb_init"
	query_db master
fi
# update special query results for months and years
query_db years
query_db months
query_db days
# set default query type for updates
[ -z "$NB_QUERY" ] &&
	NB_QUERY=max
# set default list for updates
if [ -z "${UPDATE_LIST[*]}" ]; then
	query_db "$NB_QUERY" "$db_catquery"
	UPDATE_LIST=(${DB_RESULTS[*]})
fi
# update categories found in update list
[ -z "$CAT_LIST" ] && [ ! -z "${UPDATE_LIST[*]}" ] &&
	find_categories "${UPDATE_LIST[*]}"
# update categories specified when 'all' is queried (ignores update list)
[ "$NB_QUERY" = all ] && [ ! -z "$cat_num" ] &&
	find_categories
# update previous entry's nav. links for new entries
if [ ! -z "$New_EntryFile" ]; then
	findba_entries "$New_EntryFile" "${MASTER_DB_RESULTS[*]}"
	set_entrynavlinks prev "$before_entry"
	set_entrynavlinks next "$after_entry"
	[ ! -z "$prev_entry" ] &&
		echo "$prev_entry" >> "$SCRATCH_FILE".newentry_list
	[ ! -z "$next_entry" ] &&
		echo "$next_entry" >> "$SCRATCH_FILE".newentry_list
	UPDATE_LIST=($(< "$SCRATCH_FILE".newentry_list))
fi
# setup cache for plugins
CACHE_TYPE=plugin
# always load main plugins
load_plugins
nb_msg "$buildweblog_files"
if [ "$NB_QUERY" = all ] && [ ! -z "$cat_num" ]; then
	# only build/update specified categories
	build_catarchives
elif [ "$NB_QUERY" != main ]; then
	# build/update archives based on the query results
	build_archives
fi
# build main weblog index
nb_msg "$buildweblog_main"
CACHE_TYPE=main
paginate "$QUERY_MODE" nocat "$MAX_MAINPAGE_ENTRIES" "$MAIN_TEMPLATE" \
	"$ENTRY_TEMPLATE" "$BLOG_DIR/" "$NB_INDEXFILE"
# remove expired cache data to save disk space
if [ "$NB_QUERY" != main ]; then
	if [ "$BLOG_CACHEMNG" != 0 ]; then
		nb_msg "$buildweblog_cache"
		update_cache expired
	fi
fi
# always load post plugins
load_plugins post
}

# add new entry
add_entry(){
# acquire and validate specified categories
if [ ! -z "$cat_num" ]; then
	db_catquery=`cat_id "$cat_num"`; check_catid "$cat_num"
	nb_msg "$addentry_catinfo $cat_num ..."
fi
nb_msg "$addentry_action"
# generate formatted date string for entry's metadata
meta_timestamp; NB_EntryDate="$NB_MetaDate"
# load user specified timestamp
if [ -f "$NB_EditFile" ]; then
	read_metadata TIMESTAMP "$NB_EditFile"
	New_TimeStamp="$METADATA"
fi
New_EntryFile="$NB_MetaTimeStamp.$NB_DATATYPE"
write_entry "$NB_DATA_DIR/$New_EntryFile"
# use specified timestamp as entry's new date
chg_entrydate "$New_EntryFile" "$New_TimeStamp"
[ ! -z "$New_EntryDateFile" ] &&
	New_EntryFile="$New_EntryDateFile"
# add to specified categories
if [ ! -z "$cat_num" ]; then
	for cat_db in $db_catquery; do 
		echo "$New_EntryFile" >> "$NB_DATA_DIR/$cat_db"
	done
fi
echo "$New_EntryFile" >> "$SCRATCH_FILE".newentry_list
UPDATE_LIST=($(< "$SCRATCH_FILE".newentry_list))
find_categories "${UPDATE_LIST[*]}"
nb_msg "$querydb_update"; query_db update
}

# edit entry or category by id number
edit_weblog(){
# edits categories
if [ ! -z "$cat_num" ] && [ "$edit_num" = cat ]; then
	cat_var=`echo "$cat_num" |sed -e '/,/d'`
	[ -z "$cat_var" ] && die "$editweblog_onecat"
	db_catquery=`cat_id "$cat_num"`; check_catid "$cat_num"
	nb_msg "$editweblog_catinfo $cat_num"
	query_db "$db_query" "$db_catquery"
	if [ ! -z "$USR_TITLE" ]; then
		set_catlink "$db_catquery"
		[ ! -z "$category_dir" ] && [ -d "$BLOG_DIR/$ARCHIVES_DIR/$category_dir" ] &&
			rm -fr "$BLOG_DIR/$ARCHIVES_DIR/$category_dir"
		nb_msg "$editweblog_title '$USR_TITLE' ..."
		echo "$USR_TITLE" > "$NB_DATA_DIR/$db_catquery"
		for db_item in ${DB_RESULTS[@]}; do
			echo $db_item >> "$NB_DATA_DIR/$db_catquery"
		done
		UPDATE_LIST=(${DB_RESULTS[*]})
		find_categories "${UPDATE_LIST[*]}"
		nb_msg "$querydb_update"; query_db update
		build_weblog; exit 0
	else
		die "$editweblog_nomod"
	fi
fi
# edits weblog entries
NUMVAR=`echo "$edit_num" |grep '[0-9]' |sed -e '/[\,]/ s// /g; /[A-Z,a-z\)\.\-]/d'`
[ -z "$NUMVAR" ] && die "$novalid_entryid"
db_catquery=`cat_id "$cat_num"`; check_catid "$cat_num"
# find entry id(s) from specified query
[ ! -z "$NB_QUERY" ] && db_query="$NB_QUERY"
query_db "$db_query" "$db_catquery"; ENTRY_LIST=(${DB_RESULTS[*]})
for entry_id in $NUMVAR; do
	# adjust offset by 1 for arrays (1 = 0)
	((entry_id--))
	Edit_EntryFile=${ENTRY_LIST[$entry_id]}
	[ ! -f "$NB_DATA_DIR/$Edit_EntryFile" ] && die "$invalid_entryid $edit_num"
done
> "$SCRATCH_FILE"
# for chg_entrydate
> "$SCRATCH_FILE.mod-catdbs"
[ ! -z "$cat_num" ] && nb_msg "$editweblog_catinfo $cat_num"
for entry_id in $NUMVAR; do
	((entry_id--)) # adjust offset by 1 for arrays (1 = 0)
	Edit_EntryFile=${ENTRY_LIST[$entry_id]}
	# write any user metadata
	write_tag "$USR_METATAG" "$USR_TAGTEXT" "$NB_DATA_DIR/$Edit_EntryFile"
	nb_edit -p "$NB_DATA_DIR/$Edit_EntryFile"
	# validate metafile
	check_metatags "TITLE: AUTHOR: DATE: BODY: $METADATA_CLOSETAG" \
		"$NB_DATA_DIR/$Edit_EntryFile"
	# load user specified timestamp
	read_metadata TIMESTAMP "$NB_DATA_DIR/$Edit_EntryFile"
	New_TimeStamp="$METADATA"
	# use specified timestamp as entry's new date
	chg_entrydate "$Edit_EntryFile" "$New_TimeStamp"
	[ -f "$NB_DATA_DIR/$New_EntryDateFile" ] &&
		Edit_EntryFile="$New_EntryDateFile"
	# add timestamp modified entries to update list
	[ ! -z "$Old_EntryFile" ] &&
		echo "$Old_EntryFile" >> "$SCRATCH_FILE"
	# use cached version for 'newer than' comparison
	nt_cachefile=`echo "$BLOG_DIR/$CACHE_DIR/$Edit_EntryFile".*`
	nt_cachefile=${nt_cachefile//[ ]*}
	if [ "$NB_DATA_DIR/$Edit_EntryFile" -nt "$nt_cachefile" ]; then
		# update previous and next entries of modified entry
		nb_msg "$querydb_update"; query_db update
		query_db master
		findba_entries "$Edit_EntryFile" "${MASTER_DB_RESULTS[*]}"
		set_entrynavlinks prev "$before_entry"
		set_entrynavlinks next "$after_entry"
		[ ! -z "$prev_entry" ] &&
			echo "$prev_entry" >> "$SCRATCH_FILE"
		[ ! -z "$next_entry" ] &&
			echo "$next_entry" >> "$SCRATCH_FILE"
		echo "$Edit_EntryFile" >> "$SCRATCH_FILE"
		# remove old cache data for clean build
		rm -f "$BLOG_DIR/$CACHE_DIR/$Edit_EntryFile".*
		# remove old entry archive directory
		set_entrylink "$Edit_EntryFile"
		Delete_EntryArchiveDir="$BLOG_DIR/$ARCHIVES_DIR/$entry_dir/$entry_linkname"
		[ ! -z "$entry_linkname" ] && [ -d "$Delete_EntryArchiveDir" ] &&
			rm -fr "$Delete_EntryArchiveDir"
	fi
done
UPDATE_LIST=($(< "$SCRATCH_FILE"))
if [ ! -z "${UPDATE_LIST[*]}" ]; then
	nb_msg "$editweblog_action"
	find_categories "${UPDATE_LIST[*]}"
	CAT_MODLIST=(`sort -u "$SCRATCH_FILE.mod-catdbs"`)
	# update all categories effected by entry modifications
	[ ! -z "${CAT_MODLIST[*]}" ] &&
		CAT_LIST=(`for modcatdb in ${CAT_LIST[@]} ${CAT_MODLIST[*]}; do echo $modcatdb; done |sort -u`)
	nb_msg "$querydb_update"; query_db update
	build_weblog
else
	die "$editweblog_noaction"
fi
}

# delete entry or category by id number
delete_weblog(){
# deletes categories
db_catquery=`cat_id "$cat_num"`; check_catid "$cat_num"
# find entry id(s) from specified query
[ ! -z "$NB_QUERY" ] && db_query="$NB_QUERY"
cat_list="$db_catquery"
if [ ! -z "$cat_num" ] && [ "$delete_num" = cat ]; then
	nb_msg "$deleteweblog_delcat $cat_num ..."
	# confirm delete in interactive mode
	[ "$BLOG_INTERACTIVE" = 1 ] && confirm_action
	query_db "$db_query" "${cat_list[*]}"
	for cat_db in ${cat_list[@]}; do
		Delete_CatDBFile="$cat_db"
		[ -f "$NB_DATA_DIR/$cat_db" ] && set_catlink "$cat_db"
		[ ! -z "$category_dir" ] && [ -d "$BLOG_DIR/$ARCHIVES_DIR/$category_dir" ] &&
			rm -fr "$BLOG_DIR/$ARCHIVES_DIR/$category_dir"
		rm -f "$NB_DATA_DIR/$cat_db" "$BLOG_DIR/$ARCHIVES_DIR"/`chg_suffix "$cat_db"`
	done; cat_num=
	UPDATE_LIST=(${DB_RESULTS[*]})
	nb_msg "$querydb_update"; query_db update
	build_weblog; exit 0
fi
# deletes entries
NUMVAR=`echo "$delete_num" |grep '[0-9]' |sed -e '/[\,]/ s// /g; /[A-Z,a-z\)\.\-]/d'`
[ -z "$NUMVAR" ] && die "$novalid_entryid"
if [ ! -z "$cat_list" ]; then
	CATNUMVAR=`echo "$cat_num" |grep '[0-9]' |sed -e '/[\,]/ s// /g; /[A-Z,a-z\)\.\-]/d'`
	[ -z "$CATNUMVAR" ] &&
		die "$deleteweblog_onecat"
fi
[ ! -z "$NB_QUERY" ] && db_query="$NB_QUERY"
query_db "$db_query" "$cat_list"; ENTRY_LIST=(${DB_RESULTS[*]})
for entry_id in $NUMVAR; do
	# adjust offset by 1 for arrays (1 = 0)
	old_entryid="$entry_id"
	((entry_id--))
	Delete_EntryFile=${ENTRY_LIST[$entry_id]}
	[ ! -f "$NB_DATA_DIR/$Delete_EntryFile" ] && die "$invalid_entryid $delete_num"
	read_metadata TITLE "$NB_DATA_DIR/$Delete_EntryFile"; Delete_EntryTitle=`echo "$METADATA" |cut -c1-25`..
	delete_titles="$delete_titles $old_entryid=$Delete_EntryTitle,"
done
delete_titles=`echo "$delete_titles" |sed -e '/^[ ]/ s///; /[\,]$/ s///'`
[ ! -z "$cat_num" ] && nb_msg "$deleteweblog_catinfo $cat_num"
nb_msg "$deleteweblog_delentry $delete_num ($delete_titles) ..."
# confirm delete in interactive mode
[ "$BLOG_INTERACTIVE" = 1 ] && confirm_action
# initialize master.db
if [ ! -z "$NB_QUERY" ]; then
	db_query="$NB_QUERY"
else
	db_query=master
fi
query_db "$db_query"
> "$SCRATCH_FILE"
> "$SCRATCH_FILE.x-catdbs"; > "$SCRATCH_FILE.x-entries"; > "$SCRATCH_FILE.x-update"
for entry_id in $NUMVAR; do
	# adjust offset by 1 for arrays (1 = 0)
	((entry_id--))
	Delete_EntryFile=${ENTRY_LIST[$entry_id]}
	if [ -f "$NB_DATA_DIR/$Delete_EntryFile" ]; then
		echo "$Delete_EntryFile" >> "$SCRATCH_FILE"
		if [ ! -z "${cat_list[*]}" ]; then
			# removes entry from categories
			for cat_db in ${cat_list[@]}; do
				cat_mod=`grep "$Delete_EntryFile" "$NB_DATA_DIR/$cat_db"`
				if [ ! -z "$cat_mod" ] && [ ! -z "$Delete_EntryFile" ]; then
					sed -e '/'$Delete_EntryFile'/d' "$NB_DATA_DIR/$cat_db" \
					> "$NB_DATA_DIR/$cat_db".tmp
					mv "$NB_DATA_DIR/$cat_db".tmp "$NB_DATA_DIR/$cat_db"
					echo "$cat_db" >> "$SCRATCH_FILE.x-catdbs"
				fi
			done
		else
			# permanently deletes entry from categories
			for cat_db in ${db_categories[@]}; do
				cat_mod=`grep "$Delete_EntryFile" "$NB_DATA_DIR/$cat_db"`
				if [ ! -z "$cat_mod" ] && [ ! -z "$Delete_EntryFile" ]; then
					sed -e '/'$Delete_EntryFile'/d' "$NB_DATA_DIR/$cat_db" \
					> "$NB_DATA_DIR/$cat_db".tmp
					mv "$NB_DATA_DIR/$cat_db".tmp "$NB_DATA_DIR/$cat_db"
					echo "$cat_db" >> "$SCRATCH_FILE.x-catdbs"
				fi
			done
			set_entrylink "$Delete_EntryFile"
			Delete_PermalinkFile="$BLOG_DIR/$ARCHIVES_DIR/$permalink_file"
			Delete_EntryArchiveDir="$BLOG_DIR/$ARCHIVES_DIR/$entry_dir/$entry_linkname"
			# delete permalink file
			[ -f "$Delete_PermalinkFile" ] && rm -fr "$Delete_PermalinkFile"
			# delete permalink directory
			[ ! -z "$entry_linkname" ] && [ -d "$Delete_EntryArchiveDir" ] &&
				rm -fr "$Delete_EntryArchiveDir"
			# delete old cache data
			rm -f "$BLOG_DIR/$CACHE_DIR/$Delete_EntryFile".*
			# update previous and next entries of deleted entry
			findba_entries "$Delete_EntryFile" "${MASTER_DB_RESULTS[*]}"
			set_entrynavlinks prev "$before_entry"
			set_entrynavlinks next "$after_entry"
			# permanently delete entry
			rm -f "$NB_DATA_DIR/$Delete_EntryFile"
			echo "$Delete_EntryFile" >> "$SCRATCH_FILE.x-entries"
			# hide prev and next entries from category updates
			[ ! -z "$prev_entry" ] &&
				echo "$prev_entry" >> "$SCRATCH_FILE.x-update"
			[ ! -z "$next_entry" ] &&
				echo "$next_entry" >> "$SCRATCH_FILE.x-update"
		fi
	fi
done
UPDATE_LIST=($(< "$SCRATCH_FILE"))
[ ! -z "$UPDATE_LIST" ] &&
	find_categories "${UPDATE_LIST[*]}"
CAT_XLIST=(`sort -u "$SCRATCH_FILE.x-catdbs"`)
# update all categories with removed entries
[ ! -z "$CAT_XLIST" ] &&
	CAT_LIST=(`for xcatdb in ${CAT_LIST[@]} ${CAT_XLIST[@]}; do echo $xcatdb; done |sort -u`)
# prune all removed entries from update list
for xentry in $(< "$SCRATCH_FILE.x-entries"); do
	sed -e '/'$xentry'/d' "$SCRATCH_FILE".x-update > "$SCRATCH_FILE".x-update.new
	mv "$SCRATCH_FILE".x-update.new "$SCRATCH_FILE".x-update
done
UPDATE_LIST=(`sort -u "$SCRATCH_FILE.x-update"`)
nb_msg "$querydb_update"; query_db update
build_weblog
}

# move entries into other categories
move_entry(){
NUMVAR=`echo "$move_num" |grep '[0-9]' |sed -e '/[\,]/ s// /g; /[A-Z,a-z\)\.\-]/d'`
[ -z "$NUMVAR" ] && die "$novalid_entryid"
query_db
for entry_id in $NUMVAR; do
	# adjust offset by 1 for arrays (1 = 0)
	((entry_id--))
	Move_EntryFile=${DB_RESULTS[$entry_id]}
	[ ! -f "$NB_DATA_DIR/$Move_EntryFile" ] && die "$invalid_entryid $move_num"
done
db_catquery=`cat_id "$cat_num"`; check_catid "$cat_num"; [ -z "$cat_num" ] && die "$moveentry_catfirst"
nb_msg "$moveentry_catinfo $cat_num"
nb_msg "$moveentry_moventry $move_num ..."
for entry_id in $NUMVAR; do
	# adjust offset by 1 for arrays (1 = 0)
	((entry_id--))
	Move_EntryFile=${DB_RESULTS[$entry_id]}
	if [ -f "$NB_DATA_DIR/$Move_EntryFile" ]; then
		if [ ! -z "$db_catquery" ]; then
			for cat_db in ${cat_list[@]}; do
				cat_mod=`grep "$Move_EntryFile" "$NB_DATA_DIR/$cat_db"`
				[ -z "$cat_mod" ] &&
					echo "$Move_EntryFile" >> "$NB_DATA_DIR/$cat_db"
			done
		fi
	fi
done
UPDATE_LIST=(`for entry_id in $NUMVAR; do ((entry_id--)); echo ${DB_RESULTS[$entry_id]}; done`)
[ ! -z "${UPDATE_LIST[*]}" ] &&
	find_categories "${UPDATE_LIST[*]}"
nb_msg "$querydb_update"; query_db update
build_weblog
}

# list entries and categories
list_weblog(){
query_db cat; db_query="$NB_QUERY"
db_catquery=`cat_id "$cat_num"`; check_catid "$cat_num"
# lists categories
if [ "$db_query" = cat ]; then
	[ -z "${db_categories[*]}" ] && die "$listweblog_nocat"
	nb_msg "$header_ID, $header_Title"
	id=0
	cat_total=${#db_categories[*]}
	while [ "$id" -le "$cat_total" ]; do
		[ -f "$NB_DATA_DIR/cat_$id.$NB_DBTYPE" ] &&
			echo " $id, `sed 1q "$NB_DATA_DIR"/cat_$id.$NB_DBTYPE`"
		id=`expr 1 + $id`
	done; exit 0
fi
explode_list(){
EXPLODE_RESULTS=($1)
list_entryid=0
id=0
total_items=${#EXPLODE_RESULTS[*]}
while [ "$id" -lt "$total_items" ]; do
	list_entryid=`expr 1 + $list_entryid`
	entry=${EXPLODE_RESULTS[$id]%%>[0-9]*}
	entry_cat=${EXPLODE_RESULTS[$id]##*\>}
	[ "$entry_cat" = "$entry" ] && entry_cat=
	read_metadata TITLE "$NB_DATA_DIR/$entry"; NB_EntryTitle="$METADATA"
	[ ! -z "$entry_cat" ] && entry_catids="- [$entry_cat]"
	entry_date=${entry%%.$NB_DATATYPE}; entry_date=${entry_date//\_/:}
	NB_EntryDate=${entry_date//[A-Z]/ }
	echo " $list_entryid, $NB_EntryTitle - ($NB_EntryDate) $entry_catids"
	id=`expr 1 + $id`
	entry_cat=; entry_catids=; entry=
done
}
# lists entries
[ -z "$db_query" ] && db_query="$QUERY_MODE"
list_query="$db_query"
# raw_db necessary for including category info
raw_db "$db_query" "$db_catquery"
[ -z "${DB_RESULTS[*]}" ] && die "'$list_query' - $listweblog_nomatch"
nb_msg "$header_ID, $header_Title - ($header_Date) - [$header_Category]"
explode_list "${DB_RESULTS[*]}"
}

# create a new entry, category or weblog directory
add_weblog(){
load_config
[ -z "$BLOG_DIR" ] && die "$addweblog_noweblog"
# automatically create new weblog directory ...
if [ ! -d "$BLOG_DIR" ]; then
	nb_msg "$addweblog_newblog '$BLOG_DIR' ..."
	mkdir -p "$BLOG_DIR"
	[ ! -d "$BLOG_DIR" ] &&
		die "$addweblog_nocreate"
	> "$BLOG_DIR/.nb_newblogdir"
	nb_msg "$addweblog_copyaction"
	# copy default files and directories
	for weblog_dir in "$NB_BASE_DIR"/default/*; do
		cp -R "$weblog_dir" "$BLOG_DIR"
	done
	# create some critical empty directories
	for weblog_emptydir in "$ARCHIVES_DIR" "$CACHE_DIR" "$PARTS_DIR"; do
		[ ! -d "$BLOG_DIR/$weblog_emptydir" ] && mkdir "$BLOG_DIR/$weblog_emptydir"
	done
	# ... but prompt for configuration.
	echo "$addweblog_askconf [Y/n]"
	read -p "$NB_PROMPT" choice
	case $choice in
	[Yy]|"")
		nb_msg "$addweblog_confaction"
		nb_edit "$BLOG_DIR"/blog.conf
		check_config; build_weblog
		exit 0;;
	[Nn])
		check_config; build_weblog
		nb_msg "$addweblog_noconf"
		exit 0;;
	esac
fi
check_config
# create a new category 
if [ ! -z "$cat_num" ]; then
	if [ "$cat_num" = new ]; then
		query_db; id=0
		cat_total=`echo "$db_categories" |grep -c "[\.]$NB_DBTYPE"`
		while [ "$id" != "$cat_total" ] || [ "$cat_total" = "0" ]; do
			id=`expr 1 + $id`
			if [ ! -f "$NB_DATA_DIR/cat_$id.$NB_DBTYPE" ]; then
				nb_msg "$addweblog_newcat id: $id ..."
				if [ ! -z "$USR_TITLE" ]; then
					cat_title=$USR_TITLE; USR_TITLE=
				else
					echo "$addweblog_titlecat [$addweblog_untitled]"
					read -p "$NB_PROMPT" cat_title
					[ -z "$cat_title" ] && cat_title="$notitle"
				fi
				echo "$cat_title" > "$NB_DATA_DIR"/cat_$id.$NB_DBTYPE
				cat_num="$id"; db_catquery=`cat_id "$cat_num"`; check_catid "$cat_num"
				cat_total="$id"
			else
				cat_total=`expr 1 + $cat_total`
			fi
		done
		nb_msg "$addweblog_madecat '$cat_title'."; exit 0
	else
		# validate categories
		db_catquery=`cat_id "$cat_num"`; check_catid "$cat_num"
	fi
fi
# add new entry from metafile
if [ ! -z "$USR_ADDFILE" ]; then
	import_file "$USR_ADDFILE"
fi
# read user specified attributes for entry (interactively)
[ ! -z "$USR_TITLE" ] && NB_EntryTitle="$USR_TITLE"; USR_TITLE=
[ ! -z "$USR_AUTHOR" ] && NB_EntryAuthor="$USR_AUTHOR"
if [ -z "$NB_EntryAuthor" ] && [ "$BLOG_INTERACTIVE" = 1 ]; then
	echo "$addweblog_author [$BLOG_AUTHOR]"
	read -p "$NB_PROMPT" NB_EntryAuthor
fi
[ -z "$NB_EntryAuthor" ] && NB_EntryAuthor="$BLOG_AUTHOR"
if [ -z "$NB_EntryTitle" ] && [ "$BLOG_INTERACTIVE" = 1 ]; then
	echo "$addweblog_title"
	read -p "$NB_PROMPT" NB_EntryTitle
fi
[ -z "$NB_EntryFormat" ] && NB_EntryFormat="$ENTRY_FORMAT"
[ ! -z "$USR_TEXT" ] && NB_EntryBody="$USR_TEXT"; USR_TEXT=
if [ -z "$NB_EntryBody" ]; then
	nb_msg "$addweblog_editnew"
	# new entry temp file for editing
	NB_EditFile="$BLOG_DIR/nb_edit-newentry-`nb_timestamp`.$NB_DATATYPE"
	# write any user metadata
	write_tag "$USR_METATAG" "$USR_TAGTEXT" "$NB_EditFile"
	# write current metadata to temp file and edit
	write_entry "$NB_EditFile"; nb_edit -p "$NB_EditFile"
	# validate metafile
	check_metatags "TITLE: AUTHOR: DATE: BODY: $METADATA_CLOSETAG" \
		"$NB_EditFile"
	# load all new entry metadata from temp file
	if [ -f "$NB_EditFile" ]; then
		load_metadata ALL "$NB_EditFile"
	fi
fi
[ ! -z "$USR_DESC" ] && NB_EntryDescription="$USR_DESC"
# prompt for description
if [ -z "$NB_EntryDescription" ] && [ "$DESC_PROMPT" != 1 ] &&
	[ "$BLOG_INTERACTIVE" = 1 ]; then
		echo "$addweblog_desc"
		read -p "$NB_PROMPT" NB_EntryDescription
fi
# set a default entry format
[ -z "$NB_EntryFormat" ] && NB_EntryFormat="$ENTRY_FORMAT"
# reload configuration and add new entry
check_config
add_entry
build_weblog
# remove editing session file upon success
[ $? = 0 ] && [ -f "$NB_EditFile" ] && rm -fr "$NB_EditFile"
# prompt for some post-tasks
[ ! -z "$BLOG_PREVIEW_CMD" ] && preview_weblog
[ ! -z "$BLOG_PUBLISH_CMD" ] && publish_weblog
}

show_help(){
NB_ShowHelp=$(< "${NB_LANG_DIR}/$NB_LANG"/help.txt)
BASENAME=`basename $0`
# transform keywords to variables and delete all hash-marked lines for display
sed -e "/[\$]VERSION/ s//${VERSION}/g; /[\$]BASENAME/ s//${BASENAME}/g; /^[\#]/d" <<-EOF
	$NB_ShowHelp
EOF
}

# load global config
load_globals
# load all language definitions
for lang_def in ${NB_LANG_DIR}/${NB_LANG}/*.lang; do
	. "$lang_def"
done

[ $# -lt 1 ] && show_help
check_arg(){
if [ -z "$1" ]; then
	echo "$bad_argument $checkarg_badarg"
	echo "$checkarg_help"
	exit 1
fi
}
sanity_check(){
invalid_opt=`echo "$@" |grep '^[--]$*'`
[ ! -z "$invalid_opt" ] && argument=
}
while [ $# -gt 0 ]; do
	bad_argument=$1
	case "$1" in
		-a|--add)		ADD_WEBLOG=1;;
		-b|--blogdir)		check_arg "$2"; USR_BLOGDIR="$2"; shift;;
		# -B and --body options deprecated in favor of -T and --text
		-B|--body)		check_arg "$2"; USR_TEXT="$2"; shift;;
		-c|--category)		check_arg "$2"; cat_num="$2"; shift;;
		--cfgfile)		check_arg "$2"; USR_BLOGCONF="$2"; shift;;
		--configure)		CONFIG_WEBLOG=1;;
		--datadir)		check_arg "$2"; USR_DATADIR="$2"; shift;;
		-d|--delete)		check_arg "$2"; delete_num="$2"; shift; DELETE_WEBLOG=1;;
		-D|--desc)		DESC_PROMPT=1; USR_DESC="$2"; shift;;
		-e|--edit)		check_arg "$2"; edit_num="$2"; shift; EDIT_WEBLOG=1;;
		-E|--draft) 		check_arg "$2"; USR_DRAFTFILE="$2"; shift; DRAFT_WEBLOG=1;;
		-f|--file) 		check_arg "$2"; USR_ADDFILE="$2"; shift;;
		-h|--help)		show_help; exit 0;;
		-i|--interactive) 	check_arg "$2"; USR_INTERACTIVE="$2"; shift;;
		-l|--list)		USR_QUERY="$2"; shift; LIST_WEBLOG=1;;
		-n|--author)		check_arg "$2"; USR_AUTHOR="$2"; shift;;
		--manual)		load_config; nb_browser "$NB_DOC_DIR/nanoblogger.html"
					exit 0;;
		-m|--move)		check_arg "$2"; move_num="$2"; shift; MOVE_WEBLOG=1;;
		-M|--makepage)		check_arg "$2"; USR_MKPSRCFILE="$2"; USR_MKPOUTFILE="$3"; shift 2
					MAKEPAGE_WEBLOG=1;;
		--makefile) 		check_arg "$2"; USR_MKFFILE="$2"; shift; MAKEFILE_WEBLOG=1;;
		--plugindir) 		check_arg "$2"; USR_PLUGINSDIR="$2"; shift;;
		-p|--preview)		PREVIEW_WEBLOG=1;;
		-P|--publish)		PUBLISH_WEBLOG=1;;
		-q|--query) 		NB_QUERY="$2"; shift; QUERY_WEBLOG=1;;
		--template)		check_arg "$2"; USR_TEMPLATE="$2"; shift;;
		--template-dir)		check_arg "$2"; USR_TEMPLATE_DIR="$2"; shift;;
		-t|--title)		check_arg "$2"; USR_TITLE="$2"; shift;;
		--tag) 			check_arg "$2"; USR_METATAG="$2"; shift;;
		--tag-text) 		check_arg "$2"; USR_TAGTEXT="$2"; shift;;
		-T|--text) 		check_arg "$2"; USR_TEXT="$2"; shift;;
		-u|--update)		USR_QUERY="$2"; shift; UPDATE_WEBLOG=1;;
		-U|--update-cache) 	USR_CACHEQUERY="$2"; shift; UPDATECACHE_WEBLOG=1;;
		-v|--verbose)		check_arg "$2"; VERBOSE="$2"; shift;;
		-V|--version)		echo "NanoBlogger $VERSION"; exit 0;;
		--)			shift; break;;
		*)
					sanity_check
					echo "$main_badopts $bad_argument"; echo "$main_panic"
					exit 1
					;;
	esac
	shift
done


# post-processing for all command line args
: ${NB_QUERY:=$USR_QUERY}
if [ "$CONFIG_WEBLOG" = 1 ]; then
	check_config; config_weblog
fi
if [ "$ADD_WEBLOG" = 1 ]; then
	add_weblog
fi
if [ "$DELETE_WEBLOG" = 1 ]; then
	check_config; delete_weblog
fi
if [ "$DRAFT_WEBLOG" = 1 ]; then
	check_config; nb_draft
fi
if [ "$EDIT_WEBLOG" = 1 ]; then
	check_config; edit_weblog
fi
if [ "$LIST_WEBLOG" = 1 ]; then
	check_config; list_weblog
fi
if [ "$MAKEPAGE_WEBLOG" = 1 ]; then
	check_config; weblog_page "$USR_MKPSRCFILE" "$USR_TEMPLATE" \
		"$USR_MKPOUTFILE"
fi
if [ "$MAKEFILE_WEBLOG" = 1 ]; then
	check_config
	# don't overwrite existing files
	[ -f "$USR_MKFFILE" ] &&
		die "'$USR_MKFFILE' - $samefilename"
	make_file "$USR_MKFFILE" "$USR_TEMPLATE"
fi
if [ "$MOVE_WEBLOG" = 1 ]; then
	check_config; move_entry
fi
if [ "$UPDATECACHE_WEBLOG" = 1 ]; then
	check_config; db_catquery=`cat_id "$cat_num"`; check_catid "$cat_num"
	[ -z "$USR_CACHEQUERY" ] && USR_CACHEQUERY=expired
	update_cache "$USR_CACHEQUERY" "*"
fi
if [ "$UPDATE_WEBLOG" = 1 ]; then
	check_config; db_catquery=`cat_id "$cat_num"`; check_catid "$cat_num"
	build_weblog
fi
if [ "$PREVIEW_WEBLOG" = 1 ]; then
	check_config; preview_weblog
fi
if [ "$PUBLISH_WEBLOG" = 1 ]; then
	check_config; publish_weblog
fi

exit 0

