#!/bin/sh
#
# @(#)  /u/des/src/sortmail/sortmail  1.20  00/07/28  18:37:09
#
# sortmail: automate decomposemail & recomposemail
#
# See "USAGE" and "SUMMARY" below for more information.
#
# 10/96, D.Singer
#
#
# Copyright (c) 1998 by Daniel E. Singer.  All rights reserved.
# Permission is granted to reproduce and distribute this program
# with the following conditions:
#   1) This copyright notice and the author identification below
#      must be left intact in the program and in any copies.
#   2) Any modifications to the program must be clearly identified
#      in the source file.
# 
# Written by:
#   Daniel E. Singer
#   UNIX Systems Administrator
#   Department of Computer Science
#   Duke University, Durham, NC
#   Phone: 919/660-6500
#   Email: des@cs.duke.edu
#


#
# add a PATH if needed
#
#PATH='/usr/bin:/bin:/usr/sbin:/sbin:/usr/ucb:/usr/bsd:/usr/etc:/usr/gnu/bin:/usr/local/bin'
#export PATH

PATH=$PATH:/home/lasse/work
export PATH

PROG=`basename "$0"`

umask 077	# create files and dirs with restrictive perms,
		# since this involves email;

WORK_DIR="SM_WORK"	# where work will be done
BACKUP_DIR="SM_BACKUP"	# where backups will be stored

#
# SRC_DIR and SRC_PATH refer to the relative starting location of this script;
# usually we will be working from a subdirectory;  these are altered when
# using the -H (Here) flag;
#
SRC_DIR=".."
SRC_PATH="../"

#
# these scripts must be accessible for any of this to work;
# add paths if needed;
#
DECOMPOSE="decomposemail"
RECOMPOSE="recomposemail"

USAGE="
Usage:	$PROG [-chHmMyYrRvx] mbox...

	  -c	move current month mbox back to source dir mbox, implies -r
		(use with -m, -M, -y, or -Y);
	  -h	help, print help message and exit;
	  -H	here, do work in this dir instead of in \"./$WORK_DIR\" dir;
	  -m	sort to monthly mailboxes of type mbox.YYMM;
	  -M	sort to monthly mailboxes of type YYMM/mbox;
	  -y	sort to yearly mailboxes of type mbox.YYYY;
	  -Y	sort to yearly mailboxes of type YYYY/mbox;
		with -m, yields YYYY/mbox.MM;
		with -M, yields YYYY/MM/mbox;
	  -r	remove the original mailbox after sort/merging;
	  -R	recurse, redo any appended mailboxes (use with -m, -M, -y,
		or -Y);
	  -v	verbose, more messages;
	  -x	don't make any backup copies in \"./$BACKUP_DIR\" dir;
"

SUMMARY="
'sortmail' automates the scripts 'decomposemail' and 'recomposemail',
and provides some additional functionality.

Default behavior is to take each mbox (mailbox) argument, make a copy,
decompose it into individual message files, and then recompose the
messages, ordered by date in increasing order.

With the '-m' flag, the messages are re-assembled into separate files
per month, mbox.YYMM.  For example, \"mlist.9801\".

With the '-M' flag, the messages are re-assembled into separate files
per month in subdirectories, YYMM/mbox.  For example, \"9801/mlist\".

With the '-y' flag, the messages are re-assembled into separate files
per year, mbox.YYYY.  For example, \"mlist.1998\".

With the '-Y' flag, the messages are re-assembled into separate files
per year in subdirectories, YYYY/mbox.  For example, \"1998/mlist\".
If combined with '-m', the files have a month suffix, for example,
\"1998/mlist.01\".  If combined with '-M', the files have an additional
month sudirectory, for example, \"1998/01/mlist\".

With the '-c' flag (along with -m, -M, -y, or -Y), the mbox file for
the current month (if any) will be moved into the location of the
original mbox.

With the '-R' flag (along with -m, -M, -y, or -Y), when a month file
is combined with one that already exists, the resulting file will be
re-sorted.  If any such existing files are compressed (.Z or .gz),
they will be uncompressed and then recompressed after being modified.

With the '-H' flag, all work is done in the current directory instead
of in the \"./$WORK_DIR\" directory.  This reduces the usefulness of
some of the other options, and is a bit riskier regarding your original
mboxes.

By default, all work is done in a subdirectory \"./$WORK_DIR\", and
all files to be modified are backed up in a subdirectory \"./$BACKUP_DIR\".
These subdirectories are created if they don't already exist.

Due to copies made during the sorting process, you should have enough
additional space in your current partition and/or quota to hold three
full copies of each original mbox.

Much of this can be accomplished using 'decomposemail' and 'recomposemail'
directly, but many more manual steps are required.
"

# for use with 'eval'
CMD='echo "$PROG: $cmd" >&2; eval "$cmd"'

CURRENT=0	# current flag (-c)
DEBUG=0		# debug flag (-d)
HERE=0		# here flag (-H)
MFLAG=0		# month flag (-m), merge to mbox.yymm
MMFLAG=0	# Month flag (-M), merge to yymm/mbox
PASS_MFLAG=	# month flag to pass to other scripts
YFLAG=0		# year flag (-y), merge to mbox.yyyy
YYFLAG=0	# Year flag (-Y), merge to yyyy/mbox
PASS_YFLAG=	# year flag to pass to other scripts
RECURSE=0	# recurse flag (-R)
REMOVE=0	# remove flag (-r)
VERBOSE=0	# verbose flag (-v)
PASS_VFLAG=	# verbose flag to pass to other scripts
BACKUP=1	# backup flag (! -x)

# these are some possible combination of the date sort flags,
# and are compared against SMODE
no_SORT="0000"	# no date sorting at all
 m_SORT="0001"	# -m
 M_SORT="0011"	# -M
 y_SORT="0100"	# -y
 Y_SORT="1100"	# -Y
Ym_SORT="1101"	# -Y and -m
YM_SORT="1111"	# -Y and -M
  SMODE="$no_SORT" # sort mode, a combination of the date sort flags

USE_DATES=0	# will be using dates (any of -[mMyY])
USE_DIRS=0	# will be using subdirs (any of -[MY])

#
# platform specific settings
#
AWK=awk
SYS="`uname -sr`"      # OS type
case "$SYS" in
  "SunOS "*)
        AWK=nawk
  esac

#
# function to append items to a newline-separated list;
# list goes to stdout;
# Usage:  NEW_LIST=`append_list "$LIST" "$ITEM"`
#
append_list() {
	for _AL_ARG do
		[ "$_AL_ARG" ] && echo "$_AL_ARG"
	  done
	return
}

#
# function to move a file to a unique name with numeric extension;
# eg, file => file.1;
# argument should be the path to a file
# Usage:  rotate_file "$FILE"
#
rotate_file() {
	_RF_FILE="$1"
	if [ -f "$_RF_FILE" ]; then
		_RF_EXT=1
		while [ -f "$_RF_FILE.$_RF_EXT" ]; do
			_RF_EXT=`expr "$_RF_EXT" '+' '1'`
		  done
		cmd="mv \"$_RF_FILE\" \"$_RF_FILE.$_RF_EXT\""
		eval "$CMD" ||
		  return 1
	  fi
	return 0
}

#
# process command line options
#

syntax_error() {
	echo "$PROG: option syntax error." >&2
	echo "$USAGE" >&2
	exit 1
}

unknown_opt() {
	echo "$PROG: unknown option \"$OPT\"." >&2
	echo "$USAGE" >&2
	exit 1
}

#arg_syntax_check() {
#	[ "$1" -lt 2 ] && syntax_error
#}

while [ "$#" -gt 0 ]; do
	OPT="$1"
	case "$OPT" in
	  # options without arguments
	  -c)	# replace original mbox with current month
		CURRENT=1
		REMOVE=1
		;;
	  -d)	# debug, many commands are not executed
		echo "$PROG: debug mode." >&2
		CMD='echo "$PROG: $cmd" >&2'
		DEBUG=1
		;;
	  -h)	# help
		echo "
<< `$AWK <&- 'BEGIN { print toupper(\"'\"$PROG\"'\"); }'` >>
$SUMMARY$USAGE"
		exit 0
		;;
	  -H)	# do the work in this dir, not "./$WORK_DIR"
		HERE=1
		SRC_DIR="."
		SRC_PATH=
		;;
	  -m)	# sort to mbox.YYMM
		MFLAG=1
		PASS_MFLAG=" -m"
		;;
	  -M)	# sort to YYMM/mbox
		MMFLAG=1
		PASS_MFLAG=" -M"
		;;
	  -y)	# sort to mbox.YYYY
		YFLAG=1
		PASS_YFLAG=" -y"
		;;
	  -Y)	# sort to YYYY/mbox
		YYFLAG=1
		PASS_YFLAG=" -Y"
		;;
	  -r)	# remove the original
		REMOVE=1
		;;
	  -R)	# recurse, resort appended files
		RECURSE=1
		;;
	  -v)	# verbose
		VERBOSE=1
		PASS_VFLAG=" -v"
		;;
	  -x)	# don't make backup copy
		BACKUP=0
		;;
	#  # options with arguments
	#  -c)
	#	CFLAG=1
	#	arg_syntax_check "$#"
	#	CARG="$2"
	#	shift
	#	;;
	  --)	# no more flags
		shift
		break
		;;

	  # unknown option
	  -?)
		syntax_error
		;;
	  # compound option
	  -??*)
		# break up a compound option
		NEW_OPTS=`$AWK 'BEGIN {
			OPT_STR = "'"$OPT"'";
			LEN = length(OPT_STR);
			NEW_OPTS = "";
			STATUS = 0;
			for (POS=2; POS+0 <= LEN; ++POS) {
				OPT = substr(OPT_STR,POS,1);
				if (OPT !~ /[a-zA-Z0-9_]/)
					STATUS = 1;
				NEW_OPTS = NEW_OPTS " -" OPT;
			}
			print NEW_OPTS;
			exit STATUS;
		  }' <&-` || {
			syntax_error
		  }
		shift
		if [ "$#" -gt 0 ]; then
			set -- $NEW_OPTS "$@"
		  else
			set -- $NEW_OPTS
		  fi
		continue
		;;
	  # end of options, just command arguments left
	  *)
		break
	  esac
	shift
  done

# some options don't mix
case "$MFLAG$MMFLAG" in
  11)
	echo "$PROG: use only one of -m and -M." >&2
	echo "$USAGE" >&2
	exit 1
	;;
  01)
	# this will make some tests below easier
	MFLAG=1
  esac
case "$YFLAG$YYFLAG" in
  11)
	echo "$PROG: use only one of -y and -Y." >&2
	echo "$USAGE" >&2
	exit 1
	;;
  01)
	# this will make some tests below easier
	YFLAG=1
  esac

[ "$MFLAG" = 1 -o "$YFLAG" = 1 ] && USE_DATES=1
[ "$MMFLAG" = 1 -o "$YYFLAG" = 1 ] && USE_DIRS=1

case "$RECURSE$USE_DATES" in
  10)
	echo "$PROG: -R only works with -m, -M, -y, or -Y." >&2
	echo "$USAGE" >&2
	exit 1
  esac
case "$CURRENT$USE_DATES" in
  10)
	echo "$PROG: -c only works with -m, -M, -y, or -Y." >&2
	echo "$USAGE" >&2
	exit 1
  esac
case "$RECURSE$HERE" in
  11)
	echo "$PROG: -R and -H do not mix." >&2
	echo "$USAGE" >&2
	exit 1
  esac
#
# check for other bad date option combos, and set SMODE (sort mode)
#
SMODE="$YYFLAG$YFLAG$MMFLAG$MFLAG"
case "$SMODE" in
  "$no_SORT") ;;
  "$y_SORT") ;;
  "$Y_SORT") ;;
  "$m_SORT") ;;
  "$M_SORT") ;;
  "$Ym_SORT") ;;
  "$YM_SORT") ;;
  *)
	echo "$PROG: cannot mix -y with -m or -M." >&2
	echo "$USAGE" >&2
	exit 1
  esac

#
# must have at least one mailbox arg
#
if [ "$#" = 0 ]; then
	echo "$PROG: need to specify one or more mailboxes." >&2
	echo "$USAGE" >&2
	exit 1
  fi

#
# check that all mailboxes are accessible before starting;
# also, see if any match files that are zipped;
#
ERROR=0
NEW_MB_LIST=
IS_NEW_LIST=0
for MB do
	if [ ! -f "$MB" ]; then
		case "$MB" in
		  *'.Z'|*'.gz')
			;;
		  *)
			if [ -f "$MB.Z" ]; then
				MB="$MB.Z"
				IS_NEW_LIST=1
			  elif [ -f "$MB.gz" ]; then
				MB="$MB.gz"
				IS_NEW_LIST=1
			  fi
		  esac
	  fi

	NEW_MB_LIST=`append_list "$NEW_MB_LIST" "$MB"`

	if [ ! -f "$MB" ]; then
		echo "$PROG: no such file \"$MB\"." >&2
		ERROR=1
	  elif [ ! -r "$MB" ]; then
		echo "$PROG: cannot read \"$MB\"." >&2
		ERROR=1
	  fi
  done
#
# if the mailbox list has been modified, set the new list to the
# positional parameter list
#
if [ "$ERROR" = 1 ]; then
	exit 1
  elif [ "$IS_NEW_LIST" = 1 ]; then
	set -f
	_IFS="$IFS"
	IFS='
'
	set -- $NEW_MB_LIST
	IFS="$_IFS"
	set +f
  fi

echo "$PROG: Begin..." >&2

#
# make backup dir?
#
if [ "$BACKUP" = 1 ]; then
	if [ ! -d "$BACKUP_DIR" ]; then
		cmd="mkdir \"$BACKUP_DIR\""
		eval "$CMD" || {
			echo "$PROG: cannot create dir \"$BACKUP_DIR\"." >&2
			exit 1
		}
	  fi
  fi

#
# make and/or 'cd' WORKDIR?
#
if [ "$HERE" = 0 ]; then
	if [ ! -d "$WORK_DIR" ]; then
		cmd="mkdir \"$WORK_DIR\""
		eval "$CMD" || {
			echo "$PROG: cannot create dir \"$WORK_DIR\"." >&2
			exit 1
		}
	  fi
	cmd="cd \"$WORK_DIR\""
	eval "$CMD" || {
		echo "$PROG: cannot change to dir \"$WORK_DIR\"." >&2
		exit 1
	}
  fi
echo "$PROG: working directory is \"`pwd`\"..." >&2

if [ "$CURRENT" = 1 ]; then
	#
	# set current date vars C_Y4, C_Y2, C_M2,
	# for use with CURRENT option
	#
	eval `date '+C_Y4="%Y" C_Y2="%y" C_M2="%m"'`
  fi


##
## each command line mailbox
##
for MB do
#
	#
	# MB_DIR and MB_PATH are the relative or absolute path to the
	# original mailbox;
	# MB will become the basename of the mbox;
	#
	MB_DIR=
	MB_PATH=
	#
	# resolve some mbox path issues
	#
	MB_BASE=`basename "$MB"`
	if [ "./$MB_BASE" = "$MB" ]; then
		MB="$MB_BASE"
	  fi
	case "$MB" in
	  /*)
		# absolute path
		MB_DIR=`dirname "$MB"`
		MB_PATH="$MB_DIR/"
		MB=`basename "$MB"`
		;;
	  */*)
		# relative path
		MB_DIR="$SRC_PATH"`dirname "$MB"`
		MB_PATH="$MB_DIR/"
		MB=`basename "$MB"`
		;;
	  *)
		# initial directory
		MB_DIR="$SRC_DIR"
		MB_PATH="$SRC_PATH"
	  esac

	echo "
$PROG: sorting mailbox \"$MB_PATH$MB\" ..." >&2

	# presumably this has already been checked;
	# if file is not found, there's definitely a problem...
	if [ ! -f "$MB_PATH$MB" ]; then
		echo "$PROG: no such file \"$MB_PATH$MB\"." >&2
		exit 1
	  fi

	#
	# make a backup copy?
	#
	if [ "$BACKUP" = 1 ]; then
		echo "
$PROG: backing up mailbox \"$SRC_DIR/$MB\" ..." >&2
		# if there's already a copy, move it, don't overwrite
		rotate_file "$SRC_PATH$BACKUP_DIR/$MB" || {
			echo "$PROG: cannot rotate file \"$SRC_PATH$BACKUP_DIR/$MB\"." >&2
			exit 1
		}

		cmd="cp \"$MB_PATH$MB\" \"$SRC_PATH$BACKUP_DIR\""
		eval "$CMD" || {
			echo "$PROG: cannot copy \"$MB_PATH$MB\" to \"$SRC_PATH$BACKUP_DIR\"." >&2
			exit 1
		}
	  fi

	#
	# get a copy of the source file;
	# if REMOVE, then just move it here;
	#
	if [ "$MB_DIR" != "." ]; then
		if [ "$REMOVE" = 1 ]; then
			echo "
$PROG: moving mailbox \"$MB_PATH$MB\" to working directory ..." >&2
			cmd="mv \"$MB_PATH$MB\" \"./$MB\""
			_STR=move
		  else
			echo "
$PROG: copying mailbox \"$MB_PATH$MB\" to working directory ..." >&2
			cmd="cp \"$MB_PATH$MB\" \"./$MB\""
			_STR=copy
		  fi
		eval "$CMD" || {
			echo "$PROG: cannot $_STR \"$MB_PATH$MB\" to \"./$MB\"." >&2
			exit 1
		}
	  fi

	#
	# do the decomposition, producing a file for each message in the mbox;
	# if it's a zipped file, DECOMPOSE can deal with it as is;
	#
	echo "" >&2
	cmd="$DECOMPOSE$PASS_VFLAG \"$MB\""
	eval "$CMD" || {
		echo "$PROG: problem disassembling \"$MB\"." >&2
		exit 1
	}

	#
	# get rid of ./copy, or
	# get rid of ./orig if REMOVE;
	#
	if [ "$MB_DIR" != '.' ]; then
		echo "
$PROG: removing working copy of mailbox \"$MB\" ..." >&2
		cmd="rm \"$MB\""
		eval "$CMD" || {
			echo "$PROG: cannot remove \"$MB\"." >&2
			exit 1
		}
	  elif [ "$REMOVE" = 1 -o "$USE_DATES" = 0 ]; then
		echo "
$PROG: removing original copy of mailbox \"$MB\" ..." >&2
		cmd="rm \"$MB\""
		eval "$CMD" || {
			echo "$PROG: cannot remove \"$MB\"." >&2
			exit 1
		}
	  fi

	#
	# do the recomposition, based on options;
	# if the mbox was a zipped file, RECOMPOSE needs the
	# non-zipped filename;
	#
	echo "" >&2
	case "$MB" in
	  *'.Z'|*'.gz')
		# get rid of the suffix
		_MB=`$AWK <&- 'BEGIN {
			F = "'"$MB"'";
			sub(/((\.Z)|(\.gz))$/,"",F);
			print F;
		}'`
		;;
	  *)
		_MB="$MB"
	  esac
	cmd="$RECOMPOSE$PASS_MFLAG$PASS_YFLAG$PASS_VFLAG \"$_MB\""
	eval "$CMD" || {
		echo "$PROG: problem reassembling \"$_MB\"." >&2
		exit 1
	}

	#
	# if none of the date flags used, only do this part;
	# the logic here is a little convoluted -- if you can think
	# of anything that makes more sense, let me know;
	#
	if [ "$USE_DATES" = 0 ]; then
		#
		# if REMOVE or
		# if we're working in the directory of the original mbox,
		# and if the original file was zipped, then re-zip it;
		#
		if [ "$REMOVE" = 1 -o "$MB_DIR" = '.' ]; then
			case "$MB" in
			  *'.Z')
				cmd="compress \"$_MB\""
				eval "$CMD" || {
					echo "$PROG: cannot compress \"$_MB\"." >&2
					exit 1
				}
				;;
			  *'.gz')
				cmd="gzip \"$_MB\""
				eval "$CMD" || {
					echo "$PROG: cannot gzip \"$_MB\"." >&2
					exit 1
				}
			  esac
		  else
			MB="$_MB"  # don't want the suffix (if there is one)
		  fi
		if [ "$REMOVE" = 1 -a "$MB_DIR" != '.' ]; then
			cmd="mv \"$MB\" \"$MB_DIR\""
			eval "$CMD" || {
				echo "$PROG: cannot move \"$MB\" to \"$MB_DIR\"." >&2
				exit 1
			}
		  fi

		continue
	  fi

	#
	# from this point on, we're concerned with moving the various
	# merged mailboxes to where they need to go, ie, any of:
	#   SRC_DIR/mbox.YYMM
	#   SRC_DIR/YYMM/mbox
	#   SRC_DIR/mbox.YYYY
	#   SRC_DIR/YYYY/mbox
	#   SRC_DIR/YYYY/mbox.MM
	#   SRC_DIR/YYYY/MM/mbox
	#   SRC_DIR/mbox (CURRENT)
	#

	REDO_LIST=	# mboxes to redo (ie, after appending, sort/merge again)
	COMPRESS_LIST=	# mboxes to re-compress
	GZIP_LIST=	# mboxes to re-gzip
	MB="$_MB"	# don't want any zip suffix any more (if there was one)

	#
	# only want to do this (move files and recurse) if not HERE
	#
	if [ "$SRC_DIR" != '.' ]; then
#	#
		#
		# get list of [path/]files that were generated;
		# make them into the positional parameters;
		# unfortunately, we do not get this info from RECOMPOSEMAIL;
		#
		DFILES=
		case "$SMODE" in
		  $m_SORT|$y_SORT)
			DFILES=`ls -d "$MB".[0-9][0-9][0-9][0-9]`
			;;
		  $M_SORT|$Y_SORT)
			DFILES=`ls -d [0-9][0-9][0-9][0-9]/"$MB"`
			;;
		  $Ym_SORT)
			DFILES=`ls -d [0-9][0-9][0-9][0-9]/"$MB".[0-1][0-9]`
			;;
		  $YM_SORT)
			DFILES=`ls -d [0-9][0-9][0-9][0-9]/[0-1][0-9]/"$MB"`
		  esac
		set -f
		_IFS="$IFS"
		IFS='
'
		set -- X $DFILES
		shift
		IFS="$_IFS"
		set +f

		for DFILE do
#	#	#
			echo "" >&2

			#
			# decompose the [path/]mbox name, based on sort mode;
			# this info will be needed for moves and such;
			#

			DDIR= SUFX=	# dir and/or suffix
			B_SUFX=		# for backup filenames

			case "$SMODE" in
			  "$y_SORT") # mbox.yyyy
				eval `$AWK <&- 'BEGIN {
					DF = "'"$DFILE"'";
					N = split(DF,A,"\.");
					print "SUFX=\"" A[N] "\"";
					print "B_SUFX=\"" A[N] "\"";
				}'`
				;;
			  "$Y_SORT") # yyyy/mbox
				eval `$AWK <&- 'BEGIN {
					DF = "'"$DFILE"'";
					N = split(DF,A,"/");
					print "DDIR=\"" A[1] "\"";
					print "B_SUFX=\"" A[1] "\"";
				}'`
				;;
			  "$m_SORT") # mbox.yymm
				eval `$AWK <&- 'BEGIN {
					DF = "'"$DFILE"'";
					N = split(DF,A,"\.");
					print "SUFX=\"" A[N] "\"";
					print "B_SUFX=\"" A[N] "\"";
				}'`
				;;
			  "$M_SORT") # yymm/mbox
				eval `$AWK <&- 'BEGIN {
					DF = "'"$DFILE"'";
					N = split(DF,A,"/");
					print "DDIR=\"" A[1] "\"";
					print "B_SUFX=\"" A[1] "\"";
				}'`
				;;
			  "$Ym_SORT") # yyyy/mbox.mm
				eval `$AWK <&- 'BEGIN {
					DF = "'"$DFILE"'";
					N = split(DF,A,"/");
					N2 = split(A[2],A2,"\.");
					print "DDIR=\"" A[1] "\"";
					print "SUFX=\"" A2[N2] "\"";
					print "B_SUFX=\"" substr(A[1],3) A2[N2] "\"";
				}'`
				;;
			  "$YM_SORT") # yyyy/mm/mbox
				DDIR="$_MF_Y4/$_MF_M2"
				eval `$AWK <&- 'BEGIN {
					DF = "'"$DFILE"'";
					N = split(DF,A,"/");
					print "DDIR=\"" A[1] "/" A[2] "\"";
					print "B_SUFX=\"" substr(A[1],3) A[2] "\"";
				}'`
			  esac

			#
			# will the existing file need to be unzipped?
			#
			if [ -f "$SRC_PATH$DFILE" ]; then
#	#	#	#
				[ "$RECURSE" = 1 ] &&
				  REDO_LIST=`append_list "$REDO_LIST" "$DFILE"`
			  elif [ -f "$SRC_PATH$DFILE.Z" ]; then
				cmd="uncompress \"$SRC_PATH$DFILE\""
				if eval "$CMD"
				  then
					COMPRESS_LIST=`append_list "$COMPRESS_LIST" "$DFILE"`
					REDO_LIST=`append_list "$REDO_LIST" "$DFILE"`
				  else
					echo "$PROG: cannot uncompress \"$SRC_PATH$DFILE\"." >&2
					exit 1
				  fi
			  elif [ -f "$SRC_PATH$DFILE.gz" ]; then
				cmd="gunzip \"$SRC_PATH$DFILE\""
				if eval "$CMD"
				  then
					GZIP_LIST=`append_list "$GZIP_LIST" "$DFILE"`
					REDO_LIST=`append_list "$REDO_LIST" "$DFILE"`
				  else
					echo "$PROG: cannot gunzip \"$SRC_PATH$DFILE\"." >&2
					exit 1
				  fi
			  fi

			#
			# move or append to the source location
			#
			if [ -f "$SRC_PATH$DFILE" ]; then
#	#	#	#
				# backup any file already there
				if [ "$BACKUP" = 1 ]; then
				    # if there's already a copy, move it, don't overwrite
				    rotate_file "$SRC_PATH$BACKUP_DIR/$MB.$B_SUFX" || {
					echo "$PROG: cannot rotate file \"$SRC_PATH$BACKUP_DIR/$MB.$B_SUFX\"." >&2
					exit 1
				    }
				    cmd="cp \"$SRC_PATH$DFILE\" \"$SRC_PATH$BACKUP_DIR/$MB.$B_SUFX\""
				    eval "$CMD" || {
					echo "$PROG: cannot copy \"$SRC_PATH$DFILE\" to \"$SRC_PATH$BACKUP_DIR/$MB.$B_SUFX\"." >&2
					exit 1
				    }
				  fi &&
				#
				# append to the existing file
				#
				{
				  cmd="cat \"$DFILE\" >> \"$SRC_PATH$DFILE\""
				  eval "$CMD" || {
					echo "$PROG: cannot append \"$DFILE\" to \"$SRC_PATH$DFILE\"." >&2
					exit 1
				  }
				} &&
				{
				  cmd="rm \"$DFILE\""
				  eval "$CMD" || {
					echo "$PROG: cannot remove \"$DFILE\"." >&2
					exit 1
				  }
				}
			#
			# if no original
			#
			  else
#	#	#	#
				#
				# need to create the directory?
				#
				if [ "$USE_DIRS" = 1 -a ! -d "$SRC_PATH$DDIR" ]; then
#	#	#	#	#
					cmd="mkdir -p \"$SRC_PATH$DDIR\""
					eval "$CMD" || {
					  echo "$PROG: cannot mkdir \"$SRC_PATH$DDIR\"." >&2
					  exit 1
					}
				  fi
#	#	#	#	#
				#
				# new file moves to new dir
				#
				cmd="mv \"$DFILE\" \"$SRC_PATH$DFILE\""
				eval "$CMD" || {
					echo "$PROG: cannot move \"$DFILE\" to \"$SRC_PATH$DFILE\"." >&2
					exit 1
				}
			  fi
#	#	#	#
		  done
#	#	#

		#
		# a little message from RECURSE...
		#
		if [ "$RECURSE" = 1 ]; then
#	#	#
			if [ "$REDO_LIST" = '' ]; then
#	#	#	#
				echo "
$PROG: nothing to recurse..." >&2
			  else

				_STR=`echo $REDO_LIST`
				echo "
$PROG: recursing $_STR ..." >&2
			  fi
		  fi

		if [ \( "$RECURSE" = 1 -a -n "$REDO_LIST" \) -o -n "$COMPRESS_LIST" -o -n "$GZIP_LIST" ]; then
#	#	#

			# if RECURSE, redo any mailboxes that have been
			# appended to; also recompress any that were
			# decompressed;
			set -f
			_IFS="$IFS"
			IFS='
'
			set -- X $REDO_LIST
			shift
			IFS="$_IFS"
			set +f

			for RFILE do
#	#	#	#
				if [ "$RECURSE" = 1 ]; then
#	#	#	#	#
					echo "" >&2
					echo "$PROG: recursing \"$RFILE\"..." >&2

					#
					# REDO_LIST consists of
					# [path/]mbox[.sufx]
					# entries;
					#
					# decompose the entries,
					# based on sort mode
					#
					RDIR= RSUF=

					case "$SMODE" in
					  "$y_SORT") # mbox.yyyy
						eval `$AWK <&- 'BEGIN {
							DF = "'"$DFILE"'";
							N = split(DF,A,"\.");
							print "RSUF=\"" A[N] "\"";
						}'`
						;;
					  "$Y_SORT") # yyyy/mbox
						eval `$AWK <&- 'BEGIN {
							DF = "'"$DFILE"'";
							N = split(DF,A,"/");
							print "RDIR=\"" A[1] "\"";
						}'`
						;;
					  "$m_SORT") # mbox.yymm
						eval `$AWK <&- 'BEGIN {
							DF = "'"$DFILE"'";
							N = split(DF,A,"\.");
							print "RSUF=\"" A[N] "\"";
						}'`
						;;
					  "$M_SORT") # yymm/mbox
						eval `$AWK <&- 'BEGIN {
							DF = "'"$DFILE"'";
							N = split(DF,A,"/");
							print "RDIR=\"" A[1] "\"";
						}'`
						;;
					  "$Ym_SORT") # yyyy/mbox.mm
						eval `$AWK <&- 'BEGIN {
							DF = "'"$DFILE"'";
							N = split(DF,A,"/");
							N2 = split(A[2],A2,"\.");
							print "RDIR=\"" A[1] "\"";
							print "RSUF=\"" A2[N2] "\"";
						}'`
						;;
					  "$YM_SORT") # yyyy/mm/mbox
						DDIR="$_MF_Y4/$_MF_M2"
						eval `$AWK <&- 'BEGIN {
							DF = "'"$DFILE"'";
							N = split(DF,A,"/");
							print "RDIR=\"" A[1] "/" A[2] "\"";
						}'`
					  esac

					# move the file to recurse here
					cmd="mv \"$SRC_PATH$RFILE\" \"./$MB\""
					eval "$CMD" || {
						echo "$PROG: cannot move \"$SRC_PATH$RFILE\" to \"./$MB\"." >&2
						exit 1
					}

					# sort/merge it, no backup;
					# no additional recursing should be necessary;
					# should all be for the same month or year;

					# decompose the mbox
					echo "" >&2
					cmd="$DECOMPOSE$PASS_VFLAG \"$MB\""
					eval "$CMD" || {
						echo "$PROG: problem disassembling \"$MB\"." >&2
						exit 1
					}

					# remove the mbox
					cmd="rm \"$MB\""
					eval "$CMD" || {
						echo "$PROG: cannot remove \"$MB\"." >&2
						exit 1
					}

					# recompose the mbox;
					# probably dont really need the date
					# flags...
					echo "" >&2
					cmd="$RECOMPOSE$PASS_MFLAG$PASS_YFLAG$PASS_VFLAG \"$MB\""
					eval "$CMD" || {
						echo "$PROG: problem reassembling \"$MB\"." >&2
						exit 1
					}

					# move the sorted mbox to its final location 
					echo "" >&2
					cmd="mv \"$RFILE\" \"$SRC_DIR/$RFILE\""
					eval "$CMD" || {
						echo "$PROG: cannot move \"$RFILE\" to \"$SRC_DIR/$RFILE\"." >&2
						exit 1
					}
				  fi
#	#	#	#	#

				#
				# if the orig file
				# was compressed or gzipped, then
				# restore it thusly
				#
				case "
$COMPRESS_LIST
" in
#	#	#	#	#
				  *"
$RFILE
"*)
					cmd="compress \"$SRC_DIR/$RFILE\""
					eval "$CMD" || {
						echo "$PROG: cannot compress \"$SRC_DIR/$RFILE\"." >&2
						exit 1
					}
					;;
				  *)
					case "
$GZIP_LIST
" in
					  *"
$RFILE
"*)
						cmd="gzip \"$SRC_DIR/$RFILE\""
						eval "$CMD" || {
							echo "$PROG: cannot gzip \"$SRC_DIR/$RFILE\"." >&2
							exit 1
						}
						;;
					  *)
					  esac
				  esac
#	#	#	#	#
			  done
#	#	#	#
		  fi
#	#	#
	  fi
#	#

	#
	# if CURRENT, move current month mbox back to SRC_DIR
	#
	if [ "$CURRENT" = 1 ]; then
#	#
 		DDIR= DDIR_=
 		SUFX= _SUFX=
 
 		case "$SMODE" in
 		  "$y_SORT")
 			SUFX="$C_Y4"
 			;;
 		  "$Y_SORT")
 			DDIR="$C_Y4"
 			;;
 		  "$m_SORT")
 			SUFX="$C_Y2$C_M2"
 			;;
 		  "$M_SORT")
 			DDIR="$C_Y2$C_M2"
 			;;
 		  "$Ym_SORT")
 			DDIR="$C_Y4"
 			SUFX="$C_M2"
 			;;
 		  "$YM_SORT")
 			DDIR="$C_Y4/$C_M2"
 		  esac

		DDIR_="${DDIR:+$DDIR/}"
		_SUFX="${SUFX:+.$SUFX}"

		CFILE="$DDIR_$MB$_SUFX"

		if [ -f "$SRC_PATH$CFILE" ]; then
#	#	#
			cmd="mv \"$SRC_PATH$CFILE\" \"$MB_PATH$MB\""
			eval "$CMD" || {
				echo "$PROG: cannot move \"$SRC_PATH$CFILE\" to \"$MB_PATH$MB\"." >&2
				exit 1
			}

		#
		# if CURRENT and orig mbox is now non-existent,
		# make an empty one
		#
		  elif [ ! -f "$MB_PATH$MB" ]; then
			echo "" >&2
			cmd="> \"$MB_PATH$MB\""
			eval "$CMD" || {
				echo "$PROG: cannot create file \"$MB_PATH$MB\"." >&2
				exit 1
			}
		  fi
#	#	#
	  fi
#	#
  done
#
 
echo "
$PROG: done.
" >&2

exit
