#! /bin/bash
############################################################
#  NanoBlogger 3.2.3 Copyright 2005 n1xt3r (Kevin R. Wood)   #
############################################################

# nanoblogger's version.
VERSION="3.2.3"

# nanoblogger's base install directory.
NB_BASE_DIR=`dirname $0`

# create a semi ISO 8601 formatted timestamp for archives
# used explicitly, please don't edit unless you know what you're doing.
NB_TimeStamp(){ date "+%Y-%m-%dT%H_%M_%S"; }

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

# set current path
CURR_PATH="$PWD"

# the temp directory
TEMP_DIR=/tmp

# default verbosity, 0 = silent
VERBOSE=1

# automatically set time zone using GNU specific, 'date +%z'
tzd_mm=`date +%z |cut -c4-5`
AUTO_TZD=`date +%z |sed 's/..$/\:'$tzd_mm'/'`

# directory to store archives of weblog
ARCHIVES_DIR="archives"

# directory to store cached data of weblog
CACHE_DIR="cache"

# directory to store parts of weblog
PARTS_DIR="parts"

# letter to prepend to entry's html id tag
x_id=e

# function to die with a message
die(){
cat <<-EOF
	$@
EOF
exit 1
}

nb_msg(){
if [ "$VERBOSE" != 0 ]; then
	cat <<-EOF
		$@
	EOF
fi
}

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

# loads global and user configurations
load_config(){
# set deprecated BASE_DIR for temporary compatibility
BASE_DIR="$NB_BASE_DIR"
# always load global configs
[ -f "$NB_BASE_DIR/nb.conf" ] && . "$NB_BASE_DIR/nb.conf"
# check for user's .nb.conf in their home directory
[ -f "$HOME/.nb.conf" ] && . "$HOME/.nb.conf"
# allow user specified weblog directories 
[ ! -z "$USR_BLOGDIR" ] && BLOG_DIR="$USR_BLOGDIR"
# auto-detect blog.conf from our CWD
[ -f "$PWD/blog.conf" ] && BLOG_DIR="$PWD"
# export BLOG_DIR for the benefit of other components
export BLOG_DIR
: ${BLOG_CONF:="$BLOG_DIR/blog.conf"}
# allow user specified weblog config files
[ -f "$USR_BLOGCONF" ] && BLOG_CONF="$USR_BLOGCONF"
# load weblog config file
[ -f "$BLOG_CONF" ] && . "$BLOG_CONF"
# set data directory
[ -d "$BLOG_DIR/data" ] && NB_DATA_DIR="$BLOG_DIR/data"
# allow user specified weblog data directories
[ ! -z "$USR_DATADIR" ] && NB_DATA_DIR="$USR_DATADIR"
# set template directory
: ${NB_TEMPLATE_DIR:=$BLOG_DIR/templates}
# allow user specified template directories
[ ! -z "$USR_TEMPLATE_DIR" ] && NB_TEMPLATE_DIR="$USR_TEMPLATE_DIR"
# where plugins are located and run by default
: ${PLUGINS_DIR:=$NB_BASE_DIR/plugins}
# default to $USER for author
: ${BLOG_AUTHOR:=$USER}
# allow user specified author names
[ ! -z "$USR_AUTHOR" ] && BLOG_AUTHOR="$USR_AUTHOR"
# default to lynx for browser
: ${BROWSER:=lynx}
# default to vi for editor
: ${EDITOR:=vi}
# default to txt for datatype suffix
: ${NB_DATATYPE:=txt}
# default to db for database suffix
: ${NB_DBTYPE:=db}
# default to html for page suffix
: ${NB_FILETYPE:=html}
# default to xml for feed suffix
: ${NB_SYND_FILETYPE:=xml}
# default to AUTO_TZD for iso dates
: ${BLOG_TZD:=$AUTO_TZD}
# default to max filter for query mode
: ${QUERY_MODE:=max}
# defaults for maximum entries to display on each page
: ${MAX_ENTRIES:=10}; : ${MAX_PAGE_ENTRIES:=$MAX_ENTRIES}
# defaults for index file name
: ${NB_INDEXFILE:=index.$NB_FILETYPE}
# default for previous and next page symbols, using html entities
: ${NB_NextPage:=[&#62;&#62;]}
: ${NB_PrevPage:=[&#60;&#60;]}
# default sort arguments (-u|nique is required)
: ${SORT_ARGS:=-ru}
}

# deconfigure, clear some auto-default variables
deconfig(){ BLOG_AUTHOR=; PLUGINS_DIR=; NB_DATATYPE=; NB_DBTYPE=; \
	NB_FILETYPE=; NB_SYND_FILETYPE=; BLOG_TZD=; QUERY_MODE=; MAX_ENTRIES=; \
	SORT_ARGS=; }

# filter custom date format for a new entry
filter_dateformat(){
FILTER_VAR="$1"
# use date's defaults, when no date format is specified
if [ ! -z "$FILTER_VAR" ]; then
	[ ! -z "$DATE_LOCALE" ] && LC_ALL="$DATE_LOCALE" date +"$FILTER_VAR"
	[ -z "$DATE_LOCALE" ] && date +"$FILTER_VAR"
else
	[ ! -z "$DATE_LOCALE" ] && LC_ALL="$DATE_LOCALE" date
	[ -z "$DATE_LOCALE" ] && date
fi
}

# change suffix of file
chg_suffix(){
filename="$1"
suffix="$2"
old_suffix=`echo $filename |cut -d"." -f2`
[ ! -z "$suffix" ] && NB_FILETYPE="$suffix"
echo "$filename" |sed -e '{$ s/\.'$old_suffix'$/\.'$NB_FILETYPE'/g; }'
}

nb_edit(){
# TODO: test with external editor (outside of script's process)
EDIT_FILE="$1"
$EDITOR "$EDIT_FILE"
#if [ ! -f "$EDIT_FILE" ] || [ -N "$EDIT_FILE" ]; then
if [ ! -f "$EDIT_FILE" ]; then
	echo "press [enter] to continue."
	read -p "$NB_PROMPT" enter_key
fi
[ ! -f "$EDIT_FILE" ] && die "failed to write '$EDIT_FILE'! goodbye."
}

# insure a sane configuration or die
check_config(){
load_config
# die without the base directory
[ ! -d "$NB_BASE_DIR" ] &&
	die "`basename $0`: base directory '$NB_BASE_DIR' doesn't exist! goodbye."
[ -z "$BLOG_DIR" ] && die "no weblog directory specified! goodbye."
[ ! -z "$USR_BLOGCONF" ] &&
	[ ! -f "$USR_BLOGCONF" ] && die "weblog config file '$USR_BLOGCONF' doesn't exist! goodbye."
[ ! -d "$BLOG_DIR" ] && die "weblog directory '$BLOG_DIR' doesn't exist! goodbye."
[ ! -d "$NB_DATA_DIR" ] && die "weblog's data directory '$NB_DATA_DIR' doesn't exist! goodbye."
[ ! -d "$BLOG_DIR/$CACHE_DIR" ] && die "weblog's cache directory '$CACHE_DIR' doesn't exist! goodbye."
[ ! -d "$NB_TEMPLATE_DIR" ] && die "weblog's templates directory '$NB_TEMPLATE_DIR' doesn't exist! goodbye."
}

# create list of entries based on a month or interval
query_db(){
db_query="$1"
db_catquery="$2"
db_setlimit="$3"
db_limit="$4"
db_offset="$5"
# sanitize db_limit and db_offset
db_limit=`echo "$db_limit" |sed -e '/[A-Z,a-z,\-]/d'`
db_offset=`echo "$db_offset" |sed -e '/[A-Z,a-z,\-]/d'`
: ${db_limit:=$MAX_ENTRIES}
: ${db_limit:=0}; : ${db_offset:=1}
current_date=`date "+%Y.%m"`
cd "$NB_DATA_DIR"
# get list of categories or accept a user specified list
if [ -z "$db_catquery" ] || [ "$db_catquery" = nocat ]; then
	db_catquery=
	db_categories=`for cat_db in cat_*.$NB_DBTYPE; do echo "$cat_db"; done`
else
	db_categories="$db_catquery"
fi
if [ "$db_categories" = "cat_*.$NB_DBTYPE" ]; then db_categories=; fi
# list amount of entries based on db_limit
filter_limit(){
	[ "$db_limit" = 0 ] && grep "." # regex hack for non-GNU versions
	[ ! -z "$db_limit" ] && sed -n "$db_offset,$db_limit"p
	db_setlimit=; db_limit=; db_offset=
	}
filter_query(){ grep "$db_query." |sort $SORT_ARGS; } # allow for empty $db_query
# list all entries
list_db(){
	DB_YYYY=`echo "$db_query" |cut -c1-4`
	DB_MM=`echo "$db_query" |cut -c6-7`
	DB_DD=`echo "$db_query" |cut -c9-10`
	: ${DB_YYYY:=[0-9][0-9][0-9][0-9]}
	: ${DB_MM:=[0-9][0-9]}
	: ${DB_DD:=[0-9][0-9]}
	DB_DATE="${DB_YYYY}*${DB_MM}"
	for entry in ${DB_DATE}*${DB_DD}*.$NB_DATATYPE; do
		[ -f "$entry" ] && echo "$entry"
	done
	}
# include categorized entries
cat_db(){
	[ -z "$db_catquery" ] && list_db
	for cat_db in $db_categories; do
		[ ! -z "$cat_db" ] &&
			grep "[\.]$NB_DATATYPE" "$cat_db"
	done
	}
query_data(){
	if [ "$db_setlimit" = limit ]; then
		DB_RESULTS=`cat_db |filter_query |filter_limit`
	else
		DB_RESULTS=`cat_db |filter_query`
	fi
}
if [ "$db_query" = all ]; then
	db_query=; query_data
elif [ "$db_query" = current ]; then
	db_query="$current_date"; query_data
elif [ "$db_query" = max ]; then
	db_setlimit=limit; db_query=; query_data
else
	query_data
fi
db_query=; cd "$CURR_PATH"
}

# convert category number to existing category database
cat_id(){
cat_query=`echo "$cat_num" |grep '[0-9]' |sed -e '/,/ s// /g; /[A-Z,a-z\)\.-]/d'`
query_db
if [ ! -z "$cat_query" ]; then
	for cat_id in $cat_query; do
		cat_valid=`echo "$db_categories" |grep cat_$cat_id.$NB_DBTYPE`
		echo "$cat_valid"
		[ -z "$cat_valid" ] &&
			echo "bad id(s)!"
	done
fi
}

# validate category's id number
check_catid(){
cat_list=`cat_id`
for cat_db in $cat_list; do
	[ ! -f "$NB_DATA_DIR/$cat_db" ] &&
		die "invalid category id(s): $cat_num"
done
[ ! -z "$cat_num" ] && [ -z "$cat_list" ] && die "must specify a valid category id!"
}

# special conversion for titles to link form
set_title2link(){ title2link_var="$1"
echo "$title2link_var" |sed -e "y/ABCDEFGHIJKLMNOPQRSTUVWXYZ/abcdefghijklmnopqrstuvwxyz/; s/[\`]//g; s/[\~]//g; s/[\!]//g; s/[\@]//g; s/[\#]//g; s/[\$]//g; s/[\%]//g; s/[\^]//g; s/ [\&] / and /g; s/[\&]//g; s/[\*]//g; s/[\(]//g; s/[\)]//g; s/[\+]//g; s/[\=]//g; s/\[//g; s/\]//g; s/[\{]//g; s/[\}]//g; s/[\|]//g; s/[\\]//g; s/[\;]//g; s/[\:]//g; s/[\']//g; s/[\"]//g; s/[\,]//g; s/[\<]//g; s/[\.]//g; s/[\>]//g; s/[\/]//g; s/[\?]//g; s/^[ ]//g; s/[ ]$//g; s/ /_/g"
}

# set base url based on parameters
set_baseurl(){
node_var="$1"
base_dir=`dirname "$2"`
# check if we want absolute links
if [ "$ABSOLUTE_LINKS" = 1 ]; then
	BASE_URL="$BLOG_URL/"
else
	BASE_URL="$node_var"
	if [ "$base_dir" != . ]; then
		blogdir_sedvar=`echo "$BLOG_DIR" |sed -e 's/\//\\\\\//g'`
		BASE_URL=`echo "$base_dir" |sed -e 's/'$blogdir_sedvar'//g; s/[^ \/]*./..\//g; s/^[\.][\.]\///g'`
	fi
	[ -z "$BASE_URL" ] && BASE_URL="./"
fi
# set link path to archives
ARCHIVES_PATH="${BASE_URL}$ARCHIVES_DIR/"
}

# set link/file for given category
set_catlink(){
catlink_var="$1"
# default
category_dir=`echo $catlink_var |cut -d"." -f 1`
category_file="$category_dir/$NB_INDEXFILE"
category_link="$category_dir/$NB_INDEX"

# experimental title-based links
#category_title=`sed 1q "$NB_DATA_DIR/$catlink_var"`
#category_dir=`set_title2link "$category_title"`
#category_file="$category_dir/$NB_INDEXFILE"
#category_link="$category_dir/$NB_INDEX"

# old way
#category_file=`chg_suffix "$catlink_var"`
#category_link="$category_file"
}

# set link/file for given month
set_monthlink(){
monthlink_var="$1"
# default
month_dir=`echo $monthlink_var |sed -e '/[-]/ s//\//g'`
month_file="$month_dir/$NB_INDEXFILE"
NB_ArchiveMonthLink="$month_dir/$NB_INDEX"

# old way
#month_file="$monthlink_var.$NB_FILETYPE"
#month_link="$month_file"
#NB_ArchiveMonthLink="$month_file"
}

# generate entry's anchor/id
set_entryid(){ entryid_var="$1"
echo "$x_id$entryid_var" |sed -e '/[\/]/ s//-/g'; }

# set link/file for given entry
set_entrylink(){
entrylink_var="$1"
link_type="$2"
if [ "$ENTRY_ARCHIVES" = 1 ] && [ "$link_type" != altlink ]; then
	# default
	entrylink_var=`echo $entrylink_var |sed -e '/[-]/ s//\//g'`
	entry_dir=`echo "$entrylink_var" |cut -d "." -f 1 |sed -e '/\T/ s//\/T/g'`
	permalink_file="$entry_dir/$NB_INDEXFILE"
	NB_EntryPermalink="$entry_dir/$NB_INDEX"

	# experimental title-based links
	#entrylink_var=`echo $entrylink_var |sed -e '/[-]/ s//\//g'`
	#entry_dir=`echo "$entrylink_var" |cut -d"." -f 1 |cut -c1-10`
	#entry_linkname=`set_title2link "$NB_EntryTitle"`
	#permalink_file="$entry_dir/$entry_linkname/$NB_INDEXFILE"
	#NB_EntryPermalink="$entry_dir/$entry_linkname/$NB_INDEX"

	# old way
	#permalink_file=`chg_suffix $entrylink_var`
	#NB_EntryPermalink="$permalink_file"

	month=`echo "$entrylink_var" |cut -c1-7`
	set_monthlink "$month"
else
	month=`echo "$entrylink_var" |cut -c1-7`
	set_monthlink "$month"
	entry_id=`set_entryid $entrylink_var`
	NB_EntryPermalink="$NB_ArchiveMonthLink#$entry_id"
fi
}

# 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, '$TEMPLATE_FILE' doesn't exist! goodbye."
fi
}

# read file's metadata
read_metadata(){
MTAG="$1"
META_FILE="$2"
META_DATA=`sed -e '/^'$MTAG'[\:]/!d; /^'$MTAG'[\:] */ s///' "$META_FILE"`
[ "$MTAG" = BODY ] &&
META_DATA=`sed -e '/^'$MTAG'[\:]/,/^[\-][\-][\-][\-][\-]/!d; //d; /^'$MTAG'[\:]/d' "$META_FILE"`
}


# write metadata to file
write_metadata(){
MTAG="$1"
META_DATA="$2"
META_FILE="$3"
if [ "$MTAG" = BODY ]; then
	if [ ! -z "$META_DATA" ]; then
		if [ -f "$META_FILE" ]; then
			META_OTHER=`sed -e '/^'$MTAG'[\:]/,/^[\-][\-][\-][\-][\-]/d; //d; /^'$MTAG'[\:]/d' "$META_FILE"`
			echo "$META_OTHER" > "$META_FILE"
		fi
		cat >> "$META_FILE" <<-EOF
			$MTAG:
			$META_DATA
			-----
		EOF
	fi
else
	if [ ! -z "$MTAG" ] && [ ! -z "$META_DATA" ]; then
		if [ -f "$META_FILE" ]; then
			META_OTHER=`sed -e '/^'$MTAG'[\:]/d' "$META_FILE"`
			echo "$META_OTHER" > "$META_FILE"
		fi
		echo "$MTAG: $META_DATA" >> "$META_FILE"
	fi
fi
}

# load plugins from $PLUGINS_DIR
load_plugins(){
PLUGIN_DIR="$1"
[ ! -z "$PLUGIN_DIR" ] && PLUGIN_DIR="/$1"
# loads plugins in alpha-numeric order (0-9, a-z)
[ -d "$PLUGINS_DIR${PLUGIN_DIR}" ] &&
	for nb_plugin in "$PLUGINS_DIR${PLUGIN_DIR}"/*.sh; do
		[ -f "$nb_plugin" ] && . "$nb_plugin"
	done
}

# write entry to file
write_entry(){
ENTRY_FILE="$1"
cat > "$ENTRY_FILE" <<-EOF
	TITLE: $NB_EntryTitle
	AUTHOR: $NB_EntryAuthor
	DATE: $NB_EntryDate
	DESC: $NB_EntryDescription
	-----
	BODY:
	$NB_EntryBody
	-----
EOF
}

# read an entry
read_entry(){
ENTRY_FILE="$1"
if [ -f "$ENTRY_FILE" ]; then
	NB_EntryID=`set_entryid $entry`
	read_metadata TITLE "$ENTRY_FILE"; NB_EntryTitle="$META_DATA"
	read_metadata AUTHOR "$ENTRY_FILE"; NB_EntryAuthor="$META_DATA"
	read_metadata DATE "$ENTRY_FILE"; NB_EntryDate="$META_DATA"
	read_metadata DESC "$ENTRY_FILE"; NB_EntryDescription="$META_DATA"
	load_plugins entry
	if [ "$ENTRY_FILE" -nt "$BLOG_DIR/$CACHE_DIR/$entry.entry" ]; then
		read_metadata BODY "$ENTRY_FILE"; NB_EntryBody="$META_DATA"
		load_plugins entry/mod
		write_metadata BODY "$NB_EntryBody" "$BLOG_DIR/$CACHE_DIR/$entry.entry"
		update_cache build entry "$entry"
	else
		read_metadata BODY "$BLOG_DIR/$CACHE_DIR/$entry.entry"; NB_EntryBody="$META_DATA"
	fi
fi
}

# create page from source and template files
make_page(){
MKPAGE_SRCFILE="$1"
MKPAGE_TMPLFILE="$2"
MKPAGE_OUTFILE="$3"
[ ! -z "$USR_TITLE" ] && MKPAGE_TITLE="$USR_TITLE"
[ ! -z "$MKPAGE_TITLE" ] && NB_EntryTitle="$MKPAGE_TITLE"
if [ ! -z "$USR_SRCFILE" ]; then
	MKPAGE_SRCFILE="$USR_SRCFILE"; MKPAGE_OUTFILE="$USR_FILE"; MKPAGE_TMPLFILE="$USR_TEMPLATE"
	[ -z "$USR_TEMPLATE" ] && MKPAGE_TMPLFILE="$NB_TEMPLATE_DIR/$MAKEPAGE_TEMPLATE"
fi
[ ! -f "$MKPAGE_SRCFILE" ] && die "source file, '$MKPAGE_SRCFILE' doesn't exist! goodbye."
[ -z "$MKPAGE_OUTFILE" ] && die "output file not specified."
[ ! -f "$MKPAGE_TMPLFILE" ] && die "template file, '$MKPAGE_TMPLFILE' doesn't exist! goodbye."
# make sure the output directory is present before writing to it
mkdir -p `dirname "$MKPAGE_OUTFILE"`
set_baseurl "" "$MKPAGE_OUTFILE"
# preserve content of the source, but let plugins modify it
: ${MKPAGE_CONTENT:=$(< "$MKPAGE_SRCFILE")}
NB_Entries="$MKPAGE_CONTENT"
load_plugins page
load_template "$MKPAGE_TMPLFILE"
echo "$TEMPLATE_DATA" > "$MKPAGE_OUTFILE"
nb_msg "$MKPAGE_OUTFILE"
# load makepage plugins, but with reusable functionality
for mkpage_plugin in "$PLUGINS_DIR"/makepage/*.sh; do
	[ -f "$mkpage_plugin" ] && . "$mkpage_plugin"
done
MKPAGE_CONTENT=; MKPAGE_TITLE=; NB_EntryTitle=
}

# create individual page for an entry
build_permalink(){
if [ "$ENTRY_ARCHIVES" = 1 ]; then
	if [ "$NB_DATA_DIR/$entry" -nt "$BLOG_DIR/$ARCHIVES_DIR/$permalink_file" ]; 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
fi
}

# build entry archives
build_entryarchives(){
ENTRY_ARCHIVES_LIST="$1"
entry_template="$2"
for entry in $ENTRY_ARCHIVES_LIST; do
	if [ -f "$NB_DATA_DIR/$entry" ]; then
		touch "$NB_DATA_DIR/$entry"
		[ -z "$PARTS_FILE" ] &&
			PARTS_FILE="$BLOG_DIR/$PARTS_DIR/$permalink_file"
		if [ "$entry_template" = "$PERMALINKENTRY_TEMPLATE" ]; then
			set_baseurl "" "$BLOG_DIR/$ARCHIVES_DIR/$permalink_file"
			read_entry "$NB_DATA_DIR/$entry"
			set_entrylink "$entry"
			load_template "$NB_TEMPLATE_DIR/$entry_template"
			[ ! -z "$TEMPLATE_DATA" ] &&
				echo "$TEMPLATE_DATA" >> "$PARTS_FILE"
			build_permalink
		else
			set_baseurl "$BASE_URL"
			read_entry "$NB_DATA_DIR/$entry"
			set_entrylink "$entry"
			load_template "$NB_TEMPLATE_DIR/$entry_template"
			[ ! -z "$TEMPLATE_DATA" ] &&
				echo "$TEMPLATE_DATA" >> "$PARTS_FILE"
		fi
	fi
done
}

# generate archive content
make_archive(){
query_type="$1"
db_catquery="$2"
archive_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"
build_entryarchives "$ARCHIVE_LIST" "$archive_template"
}

# divide larger archives into multiple pages
paginate(){
page_query="$1"
page_catquery="$2"
page_items="$3"
page_template="$4"
page_dir="$5"
page_file="$6"
page_fbasedir=`dirname "$page_file"`
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 "$end" "$begin"
		for page_entry in $UPDATE_LIST; do
			if [ -z "$Edit_EntryFile" ]; then
				build_pagelist
			else
				page_match=`echo "$DB_RESULTS" |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" "$ENTRY_TEMPLATE" \
			"$BLOG_DIR/$PARTS_DIR/${page_filedir}$arch_file" limit "$end" "$begin"
		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=`echo "$DB_RESULTS" |grep -c "[\.]$NB_DATATYPE"`
if [ "$total_items" -gt "$page_items" ] && [ "$page_items" != 0 ]; then
	nb_msg "paginating ${page_filedir}$page_file ..."
	get_pages(){ y=0; while [ "$y" -lt "$total_items" ]; do
		y=`expr $page_items + $y`; echo $y; done |grep -c "."; }
	total_pages=`get_pages`
	end=0; page_num=0
	while [ "$end" -lt "$total_items" ]; do
		begin=`expr "$end" + 1`; end=`expr "$page_items" + "$end"`
		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`
		> "$SCRATCH_FILE"
		echo '<br />' >> "$SCRATCH_FILE"
		[ "$prev_num" = 1 ] &&
			echo '<a href="'$arch_link'">'$NB_PrevPage'</a>' >> "$SCRATCH_FILE"
		[ "$prev_num" -gt 1 ] &&
			echo '<a href="'$prev_page'">'$NB_PrevPage'</a>' >> "$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 '['$i']' >> "$SCRATCH_FILE"
			else
				echo '<a href="'$page'">['$i']</a>' >> "$SCRATCH_FILE"
			fi
			i=`expr $i + 1`
		done
		! [ "$next_num" -gt "$total_pages" ] &&
				echo '<a href="'$next_page'">'$NB_NextPage'</a>' >> "$SCRATCH_FILE"
		NB_PageLinks=$(< "$SCRATCH_FILE")
		if [ ! -z "$UPDATE_LIST" ] && [ ! -z "$USR_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" "$ENTRY_TEMPLATE" "$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
page_filedir=
}

# create archive of current month
build_montharchive(){
query_db "$month"
if [ ! -z "$DB_RESULTS" ]; then
	set_monthlink "$month"
	set_baseurl "" "$BLOG_DIR/$ARCHIVES_DIR/$month_file"
	make_archive "$month" nocat "$ENTRY_TEMPLATE" "$BLOG_DIR/$PARTS_DIR/$month_file"
	NB_ArchiveTitle="$month"
	load_plugins archive/month
	make_page "$BLOG_DIR/$PARTS_DIR/$month_file" "$NB_TEMPLATE_DIR/$MONTH_TEMPLATE" \
	"$BLOG_DIR/$ARCHIVES_DIR/$month_file"
fi
}

# 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=`echo "$ARCHIVE_MASTER" |cut -c1-4 |sort $SORT_ARGS`
for yearn in $ARCHIVE_YEARS; do
	# execute instructions for each year
	[ ! -z "$DB_RESULTS" ] && [ ! -z "$looparchexec_years" ] &&
		$looparchexec_years
	for monthn in 12 11 10 09 08 07 06 05 04 03 02 01; do
		ARCHIVE_MONTHS=`echo "$ARCHIVE_MASTER" |grep $yearn'[-]'$monthn'[-]' |sed 1q`
		for entry_month in $ARCHIVE_MONTHS; do
			month="$yearn-$monthn"
			query_db "$month"
			# execute instructions for each month
			[ ! -z "$DB_RESULTS" ] && [ ! -z "$looparchexec_months" ] &&
				$looparchexec_months
		done
	done
done
}

# helps update relative categories
find_categories(){
UPDATE_CATLIST="$1"
	build_catlist(){
	if [ ! -z "$cat_var" ]; then
		[ -z "$CAT_LIST" ] && CAT_LIST="$cat_db"
		[ "$CAT_LIST" != "$OLD_CATLIST" ] && CAT_LIST="$OLD_CATLIST $cat_db"
		OLD_CATLIST="$CAT_LIST"
	fi
	}
# find related categories for a given set of entries
if [ "$USR_QUERY" != all ]; then
	for relative_entry in $UPDATE_CATLIST; do
		query_db "$USR_QUERY"
		for cat_db in $db_categories; do
			cat_var=`grep "$relative_entry" "$NB_DATA_DIR/$cat_db"`
			build_catlist
		done
	done
else
	query_db; CAT_LIST="$db_categories"
fi
[ -z "$CAT_LIST" ] && CAT_LIST="$db_catquery"
CAT_LIST=`for cat_id in $CAT_LIST; do echo "$cat_id"; done |sort -u`
}

# build category archives
build_catarchives(){
db_categories="$CAT_LIST"
if [ ! -z "$db_categories" ]; then
	for cat_arch in $db_categories; do
		if [ -f "$NB_DATA_DIR/$cat_arch" ]; then
			NB_ArchiveTitle=`sed 1q "$NB_DATA_DIR/$cat_arch"`
			set_catlink "$cat_arch"
			paginate all "$cat_arch" "$MAX_PAGE_ENTRIES" "$CATEGORY_TEMPLATE" \
				"$BLOG_DIR/$ARCHIVES_DIR/" "$category_file"
		fi
	done
fi
}

# update the cache
update_cache(){
cache_update="$1"
cache_def="$2"
CACHEUPDATE_LIST="$3"
> "$SCRATCH_FILE-$cache_def".cache_list
if [ "$cache_update" = build ]; then
	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
	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")
else
	[ ! -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=`echo "$CACHEUPDATE_LIST" |sort -u`
}

# generate the archives
build_archives(){
load_plugins archive
nb_msg "generating archives ..."
# build/update the category archives
build_catarchives
if [ "$USR_QUERY" = all ]; then
	# build the monthly archives
	query_db all
	loop_archive "$DB_RESULTS" months build_montharchive
	# build the entry archives
	[ "$ENTRY_ARCHIVES" = 1 ] &&
		build_entryarchives "$DB_RESULTS" "$PERMALINKENTRY_TEMPLATE"
else
	# update relateive month archives
	MOD_MONTHS=`echo "$UPDATE_LIST" |sort $SORT_ARGS`
	loop_archive "$MOD_MONTHS" months build_montharchive
	# update relative entry archives
	[ "$ENTRY_ARCHIVES" = 1 ] &&
		build_entryarchives "$UPDATE_LIST" "$PERMALINKENTRY_TEMPLATE"
fi
}

# build the weblog
build_weblog(){
# update archives based on a query match
if [ -f "$BLOG_DIR/.nb_new_weblog" ]; then
	USR_QUERY=all
	rm -f "$BLOG_DIR/.nb_new_weblog"
fi
if [ "$USR_QUERY" = all ]; then
	nb_msg "updating all files! this may take a while ..."
	# remove all generated files for a clean build
	rm -fr "$BLOG_DIR"/{"$ARCHIVES_DIR","$CACHE_DIR","$PARTS_DIR"}/*
fi
if [ -z "$USR_QUERY" ]; then
	USR_QUERY="current"
else
	db_query="$USR_QUERY"; query_db "$db_query"
	UPDATE_LIST="$DB_RESULTS"
fi
# query database and check for categories
db_catquery=`cat_id`; check_catid
[ -z "$CAT_LIST" ] &&
	find_categories "$UPDATE_LIST"
update_cache all entry
load_plugins
# update previously cached entries
update_cache rebuild entry "$CACHE_LIST"
# build the archives
nb_msg "generating $NB_FILETYPE pages ..."
[ "$USR_QUERY" != main ] && build_archives
# build main index
update_cache "$QUERY_MODE" entry
nb_msg "generating main index page(s) ..."
paginate "$QUERY_MODE" nocat "$MAX_ENTRIES" "$MAIN_TEMPLATE" "$BLOG_DIR/" "$NB_INDEXFILE"
}

# add a new entry
add_entry(){
New_EntryFile=`NB_TimeStamp`.$NB_DATATYPE
NB_EntryDate=`filter_dateformat "$DATE_FORMAT"`
write_entry "$NB_DATA_DIR/$New_EntryFile"
if [ ! -z "$cat_num" ]; then
	db_catquery=`cat_id`; check_catid
	for cat_db in $db_catquery ; do 
		echo "$New_EntryFile" >> "$NB_DATA_DIR/$cat_db"
	done
fi
UPDATE_LIST="$New_EntryFile"
build_weblog
}

# edit $BLOG_CONF
config_weblog(){
nb_edit "$BLOG_CONF"
# check if file's been modified since opened
[ ! -N "$BLOG_CONF" ] && die "no changes were made! goodbye."
deconfig; load_config
}

# edit entry or category by id number
edit_weblog(){
if [ ! -z "$cat_num" ] && [ "$edit_num" = cat ]; then
	cat_var=`echo "$cat_num" |sed -e '/,/d'`
	[ -z "$cat_var" ] && die "must specify one category at a time when editing a title!"
	db_catquery=`cat_id`; check_catid
	query_db "$db_query" "$db_catquery"
	if [ ! -z "$USR_TITLE" ]; then
		nb_msg "changing title to '$USR_TITLE' for category id: $cat_num ..."
		echo "$USR_TITLE" > "$NB_DATA_DIR/$db_catquery"
		[ ! -z "$DB_RESULTS" ] && echo "$DB_RESULTS" >> "$NB_DATA_DIR/$db_catquery"
		UPDATE_LIST="$DB_RESULTS"
		build_weblog; exit 0
	else
		die "no changes were made! goodbye."
	fi
fi
NUMVAR=`echo "$edit_num" |grep '[0-9]' |sed -e '/\,/ s// /g; /[A-Z,a-z\)\.\-]/d'`
[ -z "$NUMVAR" ] && die "must specify a valid entry id."
db_catquery=`cat_id`; check_catid
query_db "$db_query" "$db_catquery"; ENTRY_LIST="$DB_RESULTS"
for entry_id in $NUMVAR; do
	Edit_EntryFile=`echo "$ENTRY_LIST" |sed -n "$entry_id"p`
	[ ! -f "$NB_DATA_DIR/$Edit_EntryFile" ] && die "invalid entry id(s): $edit_num"
done
> "$SCRATCH_FILE"
for entry_id in $NUMVAR; do
	Edit_EntryFile=`echo "$ENTRY_LIST" |sed -n "$entry_id"p`
	nb_edit "$NB_DATA_DIR/$Edit_EntryFile"
	if [ -N "$NB_DATA_DIR/$Edit_EntryFile" ]; then
		echo "$Edit_EntryFile" >> "$SCRATCH_FILE"
	fi
done
UPDATE_LIST=$(< "$SCRATCH_FILE")
if [ ! -z "$UPDATE_LIST" ]; then
	nb_msg "processing modified weblog entries ..."
	build_weblog
else
	die "no changes were made! goodbye."
fi
}

# delete entry or category by id number
delete_weblog(){
db_catquery=`cat_id`; check_catid
if [ ! -z "$cat_num" ]; then
	cat_list="$db_catquery"
	cat_msg=", from category id(s): $cat_num"
fi
if [ ! -z "$cat_num" ] && [ "$delete_num" = cat ]; then
	nb_msg "deleting category id(s): $cat_num ..."
	query_db "$db_query" "$cat_list"; UPDATE_LIST="$DB_RESULTS"
	for cat_db in $cat_list; do
		rm -f "$NB_DATA_DIR/$cat_db" "$BLOG_DIR/$ARCHIVES_DIR"/`chg_suffix "$cat_db"`
	done
	cat_num=; [ ! -z "$UPDATE_LIST" ] && build_weblog; exit 0
fi
NUMVAR=`echo "$delete_num" |grep '[0-9]' |sed -e '/\,/ s// /g; /[A-Z,a-z\)\.\-]/d'`
[ -z "$NUMVAR" ] && die "must specify a valid entry id!"
if [ ! -z "$cat_list" ]; then
	CATNUMVAR=`echo "$cat_num" |grep '[0-9]' |sed -e '/\,/ s// /g; /[A-Z,a-z\)\.\-]/d'`
	[ -z "$CATNUMVAR" ] &&
		die "must specify a single category when deleting entries from a category!"
fi
query_db "$db_query" "$cat_list"; ENTRY_LIST="$DB_RESULTS"
for entry_id in $NUMVAR; do
	Delete_EntryFile=`echo "$ENTRY_LIST" |sed -n "$entry_id"p`
	[ ! -f "$NB_DATA_DIR/$Delete_EntryFile" ] && die "invalid entry id(s): $delete_num"
done
nb_msg "deleting weblog entry id(s): $delete_num$cat_msg ..."
UPDATE_LIST=`for entry_id in $NUMVAR; do echo "$DB_RESULTS" |sed -n "$entry_id"p; done`
find_categories "$UPDATE_LIST"
for entry_id in $NUMVAR; do
	Delete_EntryFile=`echo "$ENTRY_LIST" |sed -n "$entry_id"p`
	if [ -f "$NB_DATA_DIR/$Delete_EntryFile" ]; then
		if [ ! -z "$cat_list" ]; then
			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"
				fi
			done
		else
			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"
				fi
			done
			rm -f "$NB_DATA_DIR/$Delete_EntryFile"
			Delete_PermalinkFile=`chg_suffix "$Delete_EntryFile"`
			if [ -f "$BLOG_DIR/$ARCHIVES_DIR/$Delete_PermalinkFile" ]; then
				rm -f "$BLOG_DIR/$ARCHIVES_DIR/$Delete_PermalinkFile"
			fi
			rm -f "$BLOG_DIR/$CACHE_DIR/$Delete_EntryFile".*
		fi
	fi
done
build_weblog
}

# list entries and categories
list_weblog(){
query_db; db_query="$USR_QUERY"
db_catquery=`cat_id`; check_catid
if [ "$db_query" = cat ]; then
	[ -z "$db_categories" ] && die "no categories exist yet! goodbye."
	nb_msg "ID, Title"
	id=0
	cat_total=`echo "$db_categories" |grep -c "[\.]$NB_DBTYPE"`
	while [ "$id" != "$cat_total" ]; do
		id=`expr 1 + $id`
		if [ -f "$NB_DATA_DIR/cat_$id.$NB_DBTYPE" ]; then
			echo " $id, `sed 1q "$NB_DATA_DIR"/cat_$id.$NB_DBTYPE`"
		else
			cat_total=`expr 1 + $cat_total`
		fi
	done; exit 0
fi
# default to entries listed by limit
[ -z "$db_query" ] && db_query="$QUERY_MODE"
list_query="$db_query"
query_db "$db_query" "$db_catquery"
[ -z "$DB_RESULTS" ] && die "nothing matched your query: '$list_query'! goodbye."
nb_msg "ID, Title - [Category] - Date"
id=0
for entry in $DB_RESULTS; do
	for cat_db in $db_categories; do
		cat_var=`grep "$entry" "$NB_DATA_DIR/$cat_db"`
		if [ ! -z "$cat_var" ]; then
			cat_title=`sed 1q "$NB_DATA_DIR/$cat_db"`
			[ "$cat_title" != "$oldcat_title" ] && cat_title="$oldcat_title $cat_title"
			oldcat_title="$cat_title,"
		fi
	done
	cat_title=`echo $cat_title |sed -e '{$ s/\,[ ]$//g; }'`
	[ ! -z "$cat_title" ] && NB_Category="- [$cat_title] -" || NB_Category="-"
	read_metadata TITLE "$NB_DATA_DIR/$entry"; NB_EntryTitle="`echo "$META_DATA" |cut -c1-32`..."
	read_metadata DATE "$NB_DATA_DIR/$entry"; NB_EntryDate="$META_DATA"
	id=`expr 1 + $id`
	echo " $id, $NB_EntryTitle $NB_Category $NB_EntryDate"
	oldcat_title=; cat_title=; NB_Category=
done
}

# 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 "must specify a valid entry id!"
query_db
for entry_id in $NUMVAR; do
	Move_EntryFile=`echo "$DB_RESULTS" |sed -n "$entry_id"p`
	[ ! -f "$NB_DATA_DIR/$Move_EntryFile" ] && die "invalid entry id(s): $move_num"
done
db_catquery=`cat_id`; check_catid; [ -z "$cat_num" ] && die "must specify category before entry!"
nb_msg "moving weblog entry id(s): $move_num, to category id(s): $cat_num ..."
UPDATE_LIST=`for entry_id in $NUMVAR; do echo "$DB_RESULTS" |sed -n "$entry_id"p; done`
query_db "$db_query" "$db_catquery"
for entry_id in $UPDATE_LIST; do
	Move_EntryFile="$entry_id"
	if [ -f "$NB_DATA_DIR/$Move_EntryFile" ]; then
		if [ ! -z "$db_catquery" ]; then
			for cat_db in $db_categories; do
				cat_mod=`grep "$Move_EntryFile" "$NB_DATA_DIR/$cat_db"`
				if [ -z "$cat_mod" ]; then
					echo "$Move_EntryFile" >> "$NB_DATA_DIR/$cat_db"
				fi
			done
		fi
	fi
done
find_categories "$UPDATE_LIST"
build_weblog
}

preview_weblog(){
[ -z "$BLOG_PREVIEW_CMD" ] && die "BLOG_PREVIEW_CMD is not set! goodbye."
nb_msg "previewing weblog ..."
$BLOG_PREVIEW_CMD
}

preview_ask(){
echo "would you like to preview your weblog now? [y/N]"
read -p "$NB_PROMPT" choice
case $choice in
	[Yy])
		preview_weblog;;
	[Nn]|"")
	;;
esac
}

publish_weblog(){
[ -z "$BLOG_PUBLISH_CMD" ] && die "BLOG_PUBLISH_CMD is not set! goodbye."
nb_msg "publishing weblog ..."
$BLOG_PUBLISH_CMD
}

publish_ask(){
echo "would you like to publish the weblog now? [y/N]"
read -p "$NB_PROMPT" choice
case $choice in
	[Yy])
		publish_weblog;;
	[Nn]|"")
		;;
esac
}

# create a new entry, category or weblog directory
add_weblog(){
load_config
[ -z "$BLOG_DIR" ] && die "no weblog directory specified! goodbye."
# automatically create new weblog directory.
if [ ! -d "$BLOG_DIR" ]; then
	nb_msg "creating weblog directory '$BLOG_DIR' ..."
	mkdir -p "$BLOG_DIR"
	[ ! -d "$BLOG_DIR" ] &&
		die "failed to create weblog directory!"
	> "$BLOG_DIR/.nb_new_weblog"
	nb_msg "copying default weblog files ..."
	# copy default files and directories
	cp "$NB_BASE_DIR"/default/*.* "$BLOG_DIR"
	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 "would you like to configure the new weblog now? [Y/n]"
	read -p "$NB_PROMPT" choice
	case $choice in
	[Yy]|"")
		nb_msg "configuring new weblog ..."
		nb_edit "$BLOG_DIR"/blog.conf
		exit 0;;
	[Nn])
		die "weblog not configured! use '`basename $0` -b $BLOG_DIR --configure -u all' to configure later.";;
	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 "creating new category id: $id ..."
				if [ ! -z "$USR_TITLE" ]; then
					cat_title=$USR_TITLE; USR_TITLE=
				else
					echo "enter the new category's title [Untitled]"
					read -p "$NB_PROMPT" cat_title
					[ -z "$cat_title" ] && cat_title=Untitled
				fi
				echo "$cat_title" > "$NB_DATA_DIR"/cat_$id.$NB_DBTYPE
				cat_num="$id"; db_catquery=`cat_id`; check_catid; cat_total="$id"
			else
				cat_total=`expr 1 + $cat_total`
			fi
		done
		nb_msg "category database created for '$cat_title'."; exit 0
	else
		db_catquery=`cat_id`; check_catid
	fi
fi
[ ! -z "$db_catquery" ] && cat_msg=", using category id(s): $cat_num"
nb_msg "creating new entry in '$BLOG_DIR'$cat_msg ..."
# read user specified attributes for entry
[ ! -z "$USR_TITLE" ] && NB_EntryTitle="$USR_TITLE"; USR_TITLE=
[ ! -z "$USR_AUTHOR" ] && NB_EntryAuthor="$USR_AUTHOR"
if [ -z "$NB_EntryAuthor" ]; then
	echo "enter author's name [$BLOG_AUTHOR]"
	read -p "$NB_PROMPT" NB_EntryAuthor
fi
[ -z "$NB_EntryAuthor" ] && NB_EntryAuthor="$BLOG_AUTHOR"
if [ -z "$NB_EntryTitle" ]; then
	echo "enter a title"
	read -p "$NB_PROMPT" NB_EntryTitle
fi
if [ -z "$USR_TEXT" ]; then
	echo "editing body of entry ..."
	# entry's new file for editing
	NB_EditFile="$TEMP_DIR/nb_entry$$.$NB_DATATYPE"
	nb_edit "$NB_EditFile"
	# load content of edited file into entry variable NB_EntryBody
	if [ -f "$NB_EditFile" ]; then
		NB_EntryBody=$(< "$NB_EditFile")
	fi
else
	NB_EntryBody="$USR_TEXT"
fi
[ ! -z "$USR_DESC" ] && NB_EntryDescription="$USR_DESC"
# prompt for descritption
if [ -z "$NB_EntryDescription" ] && [ "$desc_var" != 1 ]; then
	echo "enter a short descriptive comment"
	read -p "$NB_PROMPT" NB_EntryDescription
fi
# generate date format for entry's content
NB_EntryDate=$(filter_dateformat "$DATE_FORMAT")
# reload configuration with new weblog directory
check_config
add_entry
[ ! -z "$BLOG_PREVIEW_CMD" ] && preview_ask
[ ! -z "$BLOG_PUBLISH_CMD" ] && publish_ask
}

show_help(){
cat <<-EOF
	NanoBlogger - Console weblog engine.
	Version $VERSION, by Kevin Wood <un1xt3r@fastmail.fm>

	Usage:
	 `basename $0` [-b blog_dir] [options]

	Options:
	 -a, --add				create new entry, category, or weblog
	 					(directory).
	 -b, --blogdir <directory>		specify weblog directory.
	 -B, --body <text>			set body of entry (for '--add').
	 -c, --category	<ID,cat>		specify category (for '--add', '--delete',
	 					'--edit', '--list' and '--update').
	 --configure				configure weblog (for '--update').
	 --datadir <directory>			specify weblog's data directory.
	 -d, --delete <ID,cat>			delete an entry or category.
	 -D, --desc <text>			set description of entry (for '--add').
	 -e, --edit <ID,cat>			edit an entry or category.
	 -f, --blogconf <file>			specify an alternate configuration file.
	 -h, --help				show this help message.
	 -l, --list <all,cat,current,DATE,max>	list entries or categories
	 					(defaults to max).
	 --makepage <source> <output>		specify source and output file to create
	 					web page.
	 --manual				view the manual.
	 -m, --move <ID>			move an entry to a specified category (for
	 					'--category').
	 -n, --author <text>			set author of entry (for '--add').
	 -p, --preview				run command to preview weblog.
	 -P, --publish				run command to publish weblog.
	 --template <file>			specify file to load as template (for
	 					'--makepage').
	 --templatedir <directory>		specify weblog's template directory.
	 -t, --title <text>			set title of entry/category/page
	 					(for '--add', '--category', and
	 					'--makepage').
	 -u, --update <all,current,DATE,main> 	force update of weblog (defaults to
	 					current).
	 -v, --verbose <1=on/0=off>		toggle level of verbosity.
	 -V, --version				display version information.

	 -c,-d,-e,-m accepts multiple ID numbers seperated by commas (e.g. 1,2,3).
	 
	 --list and --update accepts regular expressions to match entries by date.
	 date = YYYY-MM-DD, YYYY-MM, ^YYYY

	Examples:
	
	 specify the weblog directory to create or add new entry
	 	nb -b ~/public_html/weblog -a

	 create new category and title it "News"
	 	nb -b ~/public_html/weblog -t "News" -c new -a

	 add new entry to category 1
	 	nb -b ~/public_html/weblog -c 1 -a

	 remove entry 2 from category 1
	 	nb -b ~/public_html/weblog -c 1 -d 2

	 set author, title, and body for new entry
	 	nb -b ~/public_html/weblog -n myname -t 'My Title' -B 'My message!' -a

	More info:
	 URL: http://nanoblogger.sourceforge.net
EOF
}

argument=$@
[ $# -lt 1 ] && show_help
check_arg(){
if [ -z "$argument" ]; then
	echo "$bad_argument option requires an argument!"
	echo "Try '`basename $0` --help' for more information."
	exit 1
fi
}
sanity_check(){
invalid_opt=`echo "$argument" |grep '^[--]$*'`
[ ! -z "$invalid_opt" ] && argument=
}
while [ $# -gt 0 ]; do
	[ $# -gt 1 ] && argument=$2
	bad_argument=$1
	case "$1" in
		-a|--add)		add_weblog;;
		-b|--blogdir)		check_arg; USR_BLOGDIR="$2"; shift;;
		-B|--body)		check_arg; USR_TEXT="$2"; shift;;
		-c|--category)		check_arg; cat_num="$2"; shift;;
		--configure)		check_config; config_weblog;;
		--datadir)		check_arg; USR_DATADIR="$2"; shift;;
		-d|--delete)		check_arg; delete_num="$2"; shift
					check_config; delete_weblog;;
		-D|--desc)		desc_var=1; USR_DESC="$2"; shift;;
		-e|--edit)		check_arg; edit_num="$2"; shift
					check_config; edit_weblog;;
		-f|--blogconf)		USR_BLOGCONF="$2"; shift;;
		-h|--help)		show_help; exit 0;;
		-l|--list)		USR_QUERY="$2"; shift
					check_config; list_weblog;;
		-n|--author)		check_arg; USR_AUTHOR="$2"; shift;;
		--makepage)		check_arg; USR_SRCFILE="$2"; USR_FILE="$3"; shift 2
					check_config; make_page;;
		--manual)		check_config; $BROWSER "$NB_BASE_DIR/docs/nanoblogger.html";;
		-m|--move)		check_arg; move_num="$2"; shift
					check_config; move_entry;;
		-p|--preview)		check_config; preview_weblog;;
		-p|--preview)		preview_weblog;;
		-P|--publish)		check_config; publish_weblog;;
		--template)		check_arg; USR_TEMPLATE="$2"; shift;;
		--templatedir)		check_arg; USR_TEMPLATE_DIR="$2"; shift;;
		-t|--title)		check_arg; USR_TITLE="$2"; shift;;
		-u|--update)		USR_QUERY="$2"; shift
					check_config; build_weblog;;
		-v|--verbose)		check_arg; VERBOSE="$2"; shift;;
		-V|--version)		echo "NanoBlogger $VERSION"; exit 0;;
		--)			shift; break;;
		*)
					sanity_check
					echo "invalid option: $@"
					echo "Try '`basename $0` --help' for more information."
					exit 1
					;;
	esac
	shift
done

exit 0

#
# End of script
#

