OpenSolaris_b135/tools/scripts/wx.sh

#!/bin/ksh -p
#
# CDDL HEADER START
#
# The contents of this file are subject to the terms of the
# Common Development and Distribution License (the "License").
# You may not use this file except in compliance with the License.
#
# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
# or http://www.opensolaris.org/os/licensing.
# See the License for the specific language governing permissions
# and limitations under the License.
#
# When distributing Covered Code, include this CDDL HEADER in each
# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
# If applicable, add the following below this CDDL HEADER, with the
# fields enclosed by brackets "[]" replaced with your own identifying
# information: Portions Copyright [yyyy] [name of copyright owner]
#
# CDDL HEADER END
#
#

#
# Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
# Use is subject to license terms.
# 

# wx -- workspace extensions.  Jeff Bonwick, December 1992.

# The bugster cat/subcat = consolidation/os-net-tools

version() {
	if [[ $(whence $0) = "/opt/onbld/bin/wx" ]] && \
	    pkginfo SUNWonbld > /dev/null 2>&1; then
		pkginfo -l SUNWonbld | egrep "PKGINST:|VERSION:|PSTAMP:"
	else
		ls -l $(whence $0)
	fi    
}

ring_bell() {
	# Sound bell to stderr, no newline 
	print -u2 "\007\c"
}

fail() {
	ring_bell
	# output error message to stderr
	print -u2 "$@ Aborting $command."
	exit 1
}

ask() {
	typeset question=$1 default_answer=$2
	if [ -z "$default_answer" ]; then
		echo "$question \c"
	else
		echo "$question [$default_answer]: \c"
	fi
	read answer
	[ -z "$answer" ] && answer="$default_answer"
}

yesno() {
	typeset question="$1"
	answer=
	while [ -z "$answer" ]; do
		ask "$question" y/n
		case $answer in
			y|yes)	answer=yes;;
			n|no)	answer=no;;
			*)	answer=;;
		esac
	done
}

ok_to_proceed() {
	yesno "$*"
	if [[ "$answer" == no ]]; then
		echo "Exiting, no action performed"
		exit 1
	fi
}

escape_re() {
	# Escape the . so they are treated as literals in greps.
	echo "$1"|sed 's{\.{\\.{g'
}

remove_local_nt_entry() {
	# remove entries in local nt cache
	grep -v "^$(escape_re $1) " $wxdir/local_nametable > \
		$wxtmp/local_nametable
	[[ ! -f $wxtmp/local_nametable ]] && \
		fail "Error: cannot create $wxtmp/local_nametable"
	mv -f $wxtmp/local_nametable $wxdir/local_nametable ||
		fail "Error: cannot create $wxdir/local_nametable."
}

remove_renamed_entry() {
	# remove entries in renamed list
	# assuming arg is the new filename to remove
	grep -v "^$(escape_re $1) " $wxdir/renamed > \
		$wxtmp/renamed
	[[ ! -f $wxtmp/renamed ]] && fail "Error: cannot create $wxtmp/renamed"
	mv -f $wxtmp/renamed $wxdir/renamed ||
		fail "Error: mv -f $wxtmp/renamed $wxdir/renamed failed"
}

add_local_nt_entry() {
	# Add workspace nametable entry to local nt cache for better perf.
	[[ -r $wsdata/nametable ]] || return 0
	if ! grep -q "^$(escape_re $1) " $wxdir/local_nametable; then
		# add entries from workspace nt to local nt cache
		grep "^$(escape_re $1) " $wsdata/nametable >> \
			$wxdir/local_nametable
		[[ ! -f $wxdir/local_nametable ]] && \
			fail "Error: cannot create $wxdir/local_nametable"
	fi
	return 0
}

remove_active_entry() {
	# Remove entry from active list 
	# $1 is the filepath to remove

	nawk '
	$1 == target {
		# Get past filepath line
		while(NF > 0)
			getline;
		# Get past blank lines
		while(NF == 0)
			getline;
		# Get past comments
		while(NF > 0)
			getline;
		# Get past ending blank lines
		while(NF == 0) {
			if (getline) {
				continue;
			} else {
				next;
			}
		}
	}
	# print the other active list entries
	{ print $0; } ' target=$1 $wxdir/active >$wxtmp/tmp_active ||
		fail "Error: cannot create $wxtmp/tmp_active."
	if mv -f $wxtmp/tmp_active $wxdir/active; then
		echo "$1 removed from active list."
		remove_local_nt_entry $1
	else
		cat >&2 <<-EOF
An error occured trying to remove $1 from the active list.
The active list may be corrupt.  You should check it out and 
possibly run '$ME update' to fix.
		EOF
		fail 
	fi
}

rename_active_entry() {
	# renamed $1 to $2 filepath in active list
	sed "s|^$(escape_re $1)$|$2|" $wxdir/active >\
		$wxtmp/active || fail "Error: cannot create $wxtmp/active."
	mv -f $wxtmp/active $wxdir/active || \
		fail "Error: mv $wxtmp/active $wxdir/active failed."
}

is_active() {
	# Return 0 if filepath arg is found in active list.
	wx_active|grep -q "^$(escape_re $1)$"
}

#
# ask user if s/he wants to remove an entry from the active list.
# Will not remove entries that differ from parent or are new.
#
ask_remove_active_entry() {
	# if an arg is passed in then this is assumed to be the filepath
	# otherwise we assume the variables were set properly by the caller
	if [[ $# -eq 1 ]]; then
		dir=$(dirname $1)
		file=$(basename $1)
		filepath=$1

		if [[ ! -f $file ]]; then
			echo "Cannot find $file"
			return 1
		fi
	fi

	if is_active $filepath; then
		if wx_pnt_filepath $filepath; then
			if ! cmp -s $file $parentfilepath; then
				cat <<-EOF

The file $filepath
differs from parent file:
$parentfilepath
and will remain in the active list.
				EOF
				return
			fi
		else
			# New file, leave in active list.
			cat <<-EOF
$filepath
is new and will remain in active list.  Use:
'$ME uncreate $filepath'
to remove from active list.
			EOF
			return
		fi
		# Remove entry from active list because it is the same as 
		# the parent.
		echo "There is no difference between $filepath and the parent"
		echo "$parentfilepath"
		yesno "Okay to remove $filepath from active list?"
		if [[ "$answer" == 'yes' ]]; then
			remove_active_entry $filepath
		fi
	fi
}

refresh_pnt_nt_cache() {
	# Refresh the parent nametable cache if necessary.
	# Note, this is a cache for the parent nametable entries that
	# have the same hash as the local active and renamed files.

	# no parent so nothing to update.
	[[ -z $parent ]] && return 0

	if [[ ! -r $parent/Codemgr_wsdata/nametable ]]; then
		fail "Error: cannot read $parent/Codemgr_wsdata/nametable"
	fi

	if [[ ! -f $wxtmp/parent_nametable ||
	    $parent/Codemgr_wsdata/nametable -nt $wxtmp/parent_nametable ||
	    $wsdata/parent -nt $wxtmp/parent_nametable ||
	    $wxdir/local_nametable -nt $wxtmp/parent_nametable ]]; then
		cut -f2- -d' ' $wxdir/local_nametable > $wxtmp/hash_list

		if [[ ! -f $wxtmp/hash_list ]]; then
			fail "Error: cannot create $wxtmp/hash_list."
		fi

		if [[ -s $wxtmp/hash_list ]]; then
			# Use hash list to get only the parent files
			# we're interested in.
			fgrep -f $wxtmp/hash_list \
			    $parent/Codemgr_wsdata/nametable \
				 > $wxtmp/parent_nametable 
			[[ ! -f $wxtmp/parent_nametable ]] && \
				fail "Error: cannot create $wxtmp/parent_nametable"
		else

			# There aren't any files to search for so just
			# update the timestamp.

			touch $wxtmp/parent_nametable
		fi
	fi
}

# add an active file to the new list
add_new() {
	typeset efp=$(escape_re $1)
	# update new file list
	if [[ ! -f $wxdir/new ]]; then
		touch $wxdir/new || fail "Error: cannot create $wxdir/new."
	fi
	if is_active $1 && ! grep -q "^$efp$" $wxdir/new; then
		echo "$1" >> $wxdir/new || fail "Error: cannot update $wxdir/new."
	fi
}

# remove a file from the new list
remove_new() {
	# remove entries in new list
	typeset efp=$(escape_re $1)
	if [[ -f $wxdir/new ]] && grep -q "^$efp$" $wxdir/new; then
		grep -v "^$efp$" $wxdir/new > $wxtmp/new
		[[ ! -f $wxtmp/new ]] && fail "Error: cannot create $wxtmp/new"
		mv -f $wxtmp/new $wxdir/new || fail "Error: cannot create $wxdir/new." 
	fi
}

update_active() {
	# Try to add an entry to the active list
	typeset efp=$(escape_re $1)

	if ! is_active $1; then
		if [[ -n "$comment_file" ]]; then
			# Use sed to remove any empty lines from comment file.
			(echo $1; echo; sed '/^[ 	]*$/d' $comment_file;\
				echo) >>$wxdir/active ||
				fail "Could not update active list."
		else
			(echo $1; echo; wx_show_comment; echo) \
			    >> $wxdir/active ||
				fail "Could not update active list."
			echo "Remember to edit the comment in the active list "\
				 "(use '$ME ea')."
		fi
		add_local_nt_entry $1
	fi  # End if not in active list
}

sort_active() {
	typeset origfp=$filepath

	# Note must use filepath for wx_show_comment
	wx_active | sort | while read filepath; do
		(print "$filepath"; print; wx_show_comment; print)
	done > $wxtmp/active_sort || \
		fail "Error: cannot create $wxtmp/active_sort"
	mv -f $wxtmp/active_sort $wxdir/active || \
		fail "Error: cannot create $wxdir/active"

	filepath=$origfp
}

sort_renamed() {
	sort $wxdir/renamed > $wxtmp/renamed_sort || \
		fail "Error: cannot create $wxtmp/renamed_sort"
	mv -f $wxtmp/renamed_sort $wxdir/renamed || \
		fail "Error: cannot create $wxdir/active"
}

update_active_comment() {
	# replace comment in active list entry with contents of $comment_file
	nawk '
	# find active list entry to modify
	$1 == filepath {
		# print filepath line
		while(NF > 0){
			print $1;
			getline;
		}
		#print 1 blank (delimit)
		print "";
		# Get past blank lines
		while(NF == 0){
			getline;
		}
		# Get past active entry comments
		# append to or replace comment
		if (comment_mode == "append"){
			while(NF > 0) {
				# output existing comments
				print $0;
				getline;
			}
		} else {
			# get past existing comments
			while(NF > 0) getline;
		}

		# output new comments
		while (getline < comments){
			# do not print blank lines
			if (NF > 0)
				print $0
		}
		close comments
		# print delimiting blank line
		printf "\n"

		# Get past ending blank lines in active entry
		NF=0
		while(NF == 0) {
			if (getline) {
				continue;
			} else {
				next;
			}
		}
	}
	# print the other active list entries
	{ print $0; } ' filepath=$1 comment_mode=$comment_mode \
		comments=$comment_file $wxdir/active >$wxtmp/tmp_active && \
		mv $wxtmp/tmp_active $wxdir/active 
	if [[ $? -eq 0 ]]; then
		echo "$1 comment(s) updated in active list."
	else
		cat <<-EOF

An error occured trying to update comments for $1 in the active list.
The active list ($wxdir/active) may be corrupt.  You should check it out
and possilbly run '$ME ea' to fix.

		EOF
		fail
	fi
}

lookup_parent() {
	# Find a local file's parent filepath.
	# Returns 1 if not found (local file is new)
	# Sets env var. parentfilepath and parenthash if found
	# Requires a file arg.
	# Updates local and parent nt caches.

	typeset efp parententry localfile hash1 hash2 hash3 hash4 \
		local_nt pnt_nt

	parentfile=
	parenthash=

	if [[ -z $parent ]]; then
		cat >&2 <<-EOF

Warning: there is no parent for the current workspace so local file:
$1
is assumed to be new.
		EOF
		return 1
	fi
	if [[ ! -f $wsdata/nametable ]]; then
		# Nothing has been brought over so assuming new file.
		cat >&2 <<-EOF

Warning: the $wsdata/nametable
doesn't exist so
$1
is assumed to be new.
		EOF
		return 1
	fi

	if [[ ! -r $parent/Codemgr_wsdata/nametable ]]; then
		fail "Error: cannot read $parent/Codemgr_wsdata/nametable."
	fi
	if [[ ! -f $wxtmp/parent_nametable ]] || $need_pnt_refresh; then
		refresh_pnt_nt_cache
		need_pnt_refresh=false
	fi
	if [[ ! -f $wxdir/local_nametable ]]; then
		touch $wxdir/local_nametable ||
			fail "Error: cannot create $wxdir/local_nametable."
	fi

	efp=$(escape_re $1)

	for local_nt in $wxdir/local_nametable $wsdata/nametable; do

		# May be multiple entries in nametable, see if one
		# matches parent.

		grep "^$efp " $local_nt |\
		while read localfile hash1 hash2 hash3 hash4; do
			for pnt_nt in $wxtmp/parent_nametable \
			    $parent/Codemgr_wsdata/nametable; do
				# get current parent nt entry
				parententry=$(grep \
				    " $hash1 $hash2 $hash3 $hash4$" $pnt_nt)
				if [[ -n "$parententry" ]]; then
					# found parent entry
					parentfile=$(echo "$parententry" |\
						cut -f1 -d' ')
					parenthash="$(echo "$parententry" |\
						cut -f2- -d' ')"
					if [[ "$local_nt" == \
						"$wsdata/nametable" ]]; then

						# Update the local nt
						# hash cache if parent
						# found and local
						# workspace nt used.

						add_local_nt_entry $localfile
					fi
					if [[ $pnt_nt == \
					    $parent/Codemgr_wsdata/nametable ]]
					then

						# Update the parent nt
						# hash cache if actual
						# parent nt used.

						echo $parententry >>\
						    $wxtmp/parent_nametable
					fi

					# break out of all the loops if
					# parent found

					break 3
				fi
			done # for pnt_nt
		done # while read active file
	done # for local_nt

	if [[ -z "$parentfile" ]]; then
		# parent filepath not found.
		return 1
	else
		# parent filepath found.
		return 0
	fi
}

#
# Detect if a file was locally renamed
#
renamed() {
	# Return 0 if renamed, 1 if not locally renamed
	# parentfile and parenthash set as side effect
	# Must be used by commands that set filepath (like wx_eval)

	if [[ ! -f $wxdir/renamed ]]; then
		update_renamed_dir
	fi

	# new is the new filename in the current ws, old is the previous
	# filename that should exist in parent ws.

	if grep -q "^$(escape_re $filepath) " $wxdir/renamed; then
		if lookup_parent $filepath; then
			return 0
		else

			# New files aren't in $wxdir/renamed so no
			# parent is a problem

			fail "Error: renamed $filepath but no matching parent"\
				"file in $parent"
		fi
	else
		# not locally renamed
		return 1
	fi
}

wx_pnt_filepath() {
	# return 0 if parent file found. Side effect: sets parentfilepath
	# and parentsdot.  parentfile and parenthash are set via lookup_parent.

	# if an arg is passed in then this is assumed to be the filepath
	# otherwise we assume the variables were set properly by the caller
	if [[ $# -eq 1 ]]; then
		dir=$(dirname $1)
		file=$(basename $1)
		filepath=$1
	fi

	# Find the parent filename
	if lookup_parent $filepath; then
		parentfilepath=$parent/$parentfile
		parentsdot=$parent/$(dirname $parentfile)/SCCS/s.$(basename \
		    $parentfile)
		if [[ -f $parentfilepath && -s $parentsdot ]]; then
			# found
			return 0
		else
			fail "Error: located parent filepath $parentfilepath"\
				"but file does not exist or SCCS file is empty."
		fi
	fi

	# wasn't found
	return 1
}

get_pb_output() {
	# Get output of putback -n (useful for parsing to find diffs between
	# workspaces).  Creates $wxtmp/putback.err and $wxtmp/putback.out
	typeset -i rc=0
	typeset origdir=$(pwd)
	# clean up if interrupted.
	trap "rm $wxtmp/putback.out;exit 1" HUP INT QUIT TERM

	cat <<-EOF
Doing a '$PUTBACK -n $*' to find diffs between workspaces.
Please be patient as this can take several minutes.
	EOF

        cd $workspace

	$PUTBACK -n $* 2>$wxtmp/putback.err >$wxtmp/putback.out
	rc=$?
	# Note a blocked putback returns a 2 but is not a problem.
	if [[ $rc -ne 0 && $rc -ne 2 ]]; then
		rm $wxtmp/putback.out
		fail "Error, $PUTBACK -n $* failed. See $wxtmp/putback.err"\
			"for details."
	fi
	trap - HUP INT QUIT TERM
        cd $origdir
	return 0
}

wx_usage() {
	version

	cat << EOF

See <http://www.opensolaris.org/os/community/on/wx/> for usage tips.

Usage:  $ME command [-D] [args]

        -D turn on debugging for any command (output to stderr)

===================== Initialization and Update Commands ====================
        $ME init [-f(t|q|n) [-s]] [src-root-dir]     
                        initialize workspace for $ME usage
                        -f(t|q|n): non-interactive mode of update.  Use this
                                   to keep init from asking questions.
                            -ft: thorough update (update both active and 
                                 renamed lists with all diffs between parent
                                 and current workspace).
                            -fq: quick update (update active list with files
                                 currently checked out in current workspace).
                            -fn: no update (just create empty active and 
                                 renamed lists if they don't exist already).
                            -s:  keep active list sorted by default.  Must
                                 follow a -f(t|q|n) flag.
                        src-root-dir: optional path relative to top of 
                                      workspace where wx will search for files.
                                      Use "." to set src-root to top of
                                      workspace.  Default is usr.
        $ME update [-q|-r] [-s]
                        Update the active and renamed file lists by
                        appending names of all files that have been
                        checked out, changed, created or renamed as
                        compared to the parent workspace.  This is the
                        most accurate way of updating but it is slow.
                        All files in the workspace must be under SCCS
                        control in order for update to find them.  Note,
                        this operation can be sped up in some cases by
                        setting the PUTBACK env. variable to use 
                        "cm_env -g -o putback". (See
                        http://webhome.holland.sun.com/casper/ for more
                        info about the turbo def.dir.flp tool).

                        -q: quick update (only updates active list with
                            files currently checked out in workspace).  This
                            is faster but will not find renames or files that
                            have been checked-in/delget'ed.
                        -r: only update the renamed list.  Does not update
                            the active list.
                        -s: sort the active list.

======================== Information Commands ===========================
        $ME list [-r|-p|-w] list active files (the ones you are working on)
                        -r: list only renamed active files.
                        -p: output list of both active and renamed files 
                            suitable for input to putback. 
                        -w: output list of both active and renamed files
                            suitable for input to webrev (see $ME webrev 
                            subcommand below).
        $ME active          alias for list 
        $ME pblist          alias for list -p (see above).

        $ME renamed [-a|-d|-p]
                         list locally renamed files. The output format is:
                         "new_name previous_name". Note, deleted files are
                         a special case of rename. 
                        -a: list only renamed active files (same as list -r)
                        -d: list only deleted files
                        -p: show "new_name parent_name" (Note, parent_name 
                            may not be the same as previous_name)
        $ME new [-t]    List new active files (files that exist in child only)
                        Note, should be run before reedit (see reedit below).
                        -t: thorough, does not use new cache (slower but more
                            accurate if new cache isn't current).
        $ME out         find all checked-out files in workspace
        $ME info        [file ...] show all info about active files

        $ME diffs [file ...]
                        show sccs diffs for files (current vs previous
                        local version).  Will show diffs for all active
                        files if no files given on command line.  Will
                        use WXDIFFCMD environment variable if set.  Hint,
                        try: export WXDIFFCMD="diff -bwU5"

        $ME tdiffs [file ...]
                        Similar to diffs but new files are also displayed.
                        New files are those listed by '$ME new'.

        $ME pdiffs [file ...]   show diffs against parent files
                        Will show diffs between local file and it's
                        parent for all active files if no files given on
                        command line.  Will use WXDIFFCMD environment
                        variable if set. 

        $ME tpdiffs [file ...]   show diffs against parent files
                        Similar to pdiffs but new files are also displayed.
                        A file is considered new if it does not exist in
                        the parent.

        $ME prt [-y]    show sccs history for all active files

        $ME comments    display check-in comments for active files
        $ME bugs [-u]   display all bugids in check-in comments
        $ME arcs [-u]   display all ARC cases in check-in comments
        $ME pbcom [-v] [-u] [-N]  display summarized comments suitable for putback
                        Default is to display only bugs and arc cases. Will
                        display warnings about non-bug comments to stderr.
                        -v: display all comments verbatim including non-bug/arc 
                        -u: prevent sorting, order determined by active list
                        -N: don't check bug synopsis against bug database

======================== File Manipulation Commands ======================
        $ME edit [-s] [file ...]        
                        check out either file(s) on command line or
                        all active files if no file args.
                        (Updates the active list.)
                        -s: silent, less sccs diagnostic output.  This is 
                            true for the other commands that accept the 
                            -s flag.
        $ME checkout    Alias for edit command.
        $ME co          Alias for edit command.

        $ME unedit [-s][-f] [file ...] 
                        Returns file(s) to state prior to edit/checkout
                        Note, files will be unlocked and any changes made
                        when file was last checked out will be lost.
                        Unedit all active files if no files listed on
                        command line.  Removes active list entry if there
                        are no diffs between local and parent file.
                        (Updates active list)
                        -f: force unedit, non-interactive. Will backup
                            if wx files newer than last backup.
        $ME uncheckout  Alias for unedit command.
        $ME unco        Alias for unedit command.

        $ME delget [-(c|C) comment_file][-s][-f] [file ...] 
                        Check in all active files or files on command
                        line. Check in comments will be those in active
                        file.  See '$ME comments' for more info.
                        -c comment_file: use comment(s) in specified comment
                                         file when checking in file(s). Note,
                                         each comment should be on new line,
                                         blank lines not allowed.  Existing
                                         comments in active list will be 
                                         replaced by contents of comment_file.
                        -C comment_file: Similar to -c but comments are 
                                         appended to current active list 
                                         comments.
                        -f: force checkin, no checks, non-interactive.
                            Use this if your sure the files okay to checkin
                            otherwise this command will check for
                            keyword problems. Will backup if wx files
                            newer than last backup.

                        NOTE: use redelget to reset new file's version to 1.1.

        $ME checkin     Alias for delget command.
        $ME ci          Alias for delget command.

        $ME create [-(c|C) comment_file] [-f] [-o] file [file ...]
                        Creates one or more files in the workspace.
                        (Updates active list)
                        -(c|C) comment_file: see delget
                        -f: force create regardless of warnings,
                            (non-interactive).
                        -o: also check out file for further editing.

        $ME uncreate [-f] [file ...]
                        Undoes the create of a new file.  The file's
                        active list entry, its SCCS history and the
                        entry in the workspace nametable will be removed
                        but the file will stay in the workspace.
                        Will uncreate all new files in active list if
                        no file argument is specified.
                        -f: force uncreate, non-interactive. Will backup
                            if wx files newer than last backup.

        $ME get [-k][-r #][-p] [file ...]
                        Get a copy of all active files or files on command
                        line.  By default this is a read only version of
                        the file.
                        -r #: get specified version #
                        -p: output to stdout
                        -k: don't expand the sccs ID string
        $ME extract     Alias for get command.

        $ME reedit [-m] [-s] [file ...]
                        Collapse the sccs delta (file history) such that
                        all changes made to the file in the current
                        workspace are now in one delta.  If no files are
                        given on command line then all the active files
                        are processed.  The files are left in a checked
                        out state so you can make further changes if a
                        resolve to make all your changes look like a
                        single delta.  This eliminates the uninteresting
                        leaf deltas that arise from resolving conflicts,
                        so your putbacks do not contain a bunch of noise
                        about every bringover/resolve you did in the
                        interim.  Accepts the same compression flags as
                        $ME backup.  If [file ...] given, wx only
                        reedits files passed on command line.  This adds
                        files to active list if not already there.
                        
                        NOTE: reedit is appropriate for leaf workspaces
                        ONLY -- applying reedit to an interior-node
                        workspace would delete all childrens comments
                        and confuse Teamware tools in general.
                        
                        NOTE: if files are listed as new that are not
                        then DO NOT use the reedit as it will destroy
                        the file history.

                        NOTE: if a file is new reedit will leave the
                        file checked out so in order to keep the delta
                        version at 1.1 redelget must be used for
                        checkin.

                        -m: only reedit files that have more that one
                            delta as compared to parent file.  New files
                            will be recreated with comment found in
                            active list.
                        -s: silent checkin

        $ME recheckout  Alias for reedit command.
        $ME reco        Alias for reedit command.

        $ME redelget [-m][-s] [file ...]
                        Similar to reedit but the file is checked in
                        when the command is done. This is the command to
                        use to collapse new files to their initial
                        1.1 delta (will assign comment in active list).
        $ME recheckin   Alias for redelget command.
        $ME reci        Alias for redelget command.

        $ME delete [-f] [file ...]
                        Delete one or more files from the workspace.
                        Will delete all files in active list if no file
                        args.  Note, for files brought over from parent,
                        this command actually moves the file under the
                        deleted_files/ subdir so it can be recovered.
                        For new files this command can remove the file
                        and file history completely.
                        (Updates active list if file is in there.)
                        -f : force delete regardless of warnings 
                             (non-interactive)

                             Warning, this will completely remove new
                             files from the workspace.  Will backup
                             if wx files newer than last backup.
        $ME rm          Alias for delete command.

        $ME mv file newfile     
                        Rename file to newfile
                        (Updates active list with new file name)
        $ME mv file newdir      
        $ME mv dir newdir       
                        Renames dir or file to newdir.  If newdir exists
                        then dir will be subdir under newdir.  Note,
                        this renames all files in dir and can take a
                        while if there are a lot of files affected by
                        the rename.  (Updates active list)

        $ME reset [-f] [file ...]
                        Resets file contents and history to that of
                        parent file.  If the file was renamed locally it
                        will be renamed to that of the parent.  It does not
                        work on new files (see uncreate or delete).

                        NOTE: use with care.  If something goes wrong,
                        do a wx restore from the last backup and copy
                        wx/tmp/nametable.orig to Codemgr_wsdata/nametable.

======================== Teamware Commands ======================
        $ME putback [-v][-f][-N][Teamware putback flags, see below][file ...]
                        Use Teamware putback command to putback either
                        file(s) specified or *all* active and renamed
                        files if no file(s) specified.  Will use pbcom
                        output as the putback comments. 
                        -f: force non-interactive mode (will not prompt)
                        -v: pass comments verbatim to putback(see pbcom)
                        -N: don't check bug synopsis against bug database
                        Accepts -n, -p pws, -q putback flags ('man putback' 
                        for more info).
        $ME pb          alias for putback.

        $ME resolve [Teamware resolve args]
                        resolve bringover conflicts and reedit merged 
                        files.  See 'man resolve' for args.

======================== ON Rules checking Commands ======================
        $ME cstyle      run cstyle over all active .c and .h files
                        skips files in wx/cstyle.NOT
        $ME jstyle      run jstyle over all active .java files
                        skips files in wx/jstyle.NOT
        $ME hdrchk      run 'hdrchk -a' over all active .h files
                        skips files in wx/hdrchk.NOT
        $ME makestyle   run makestyle over all active Makefiles
        $ME keywords    run keywords over all active files
                        skips files in wx/keywords.NOT
        $ME copyright   make sure there is a correct copyright message
                        that contains the current year
                        skips files in wx/copyright.NOT
        $ME cddlchk     make sure there is a current CDDL block in
                        active files
                        skips files in wx/cddlchk.NOT
        $ME rmdelchk    make sure sccs rmdel was not run on active files
                        (This causes Teamware problems.)
        $ME deltachk    make sure only 1 sccs delta in active files
                        (more than 1 breaks gate putback rules unless > 1
                        bug in putback.)
        $ME comchk      make sure comments are okay
        $ME rtichk      make sure RTI is approved for bugs/rfe's listed
                        in active list comments.  Will skip rtichk if
                        wx/rtichk.NOT exists.
        $ME outchk      warn if there are files checked out that aren't in
                        active list.
        $ME nits [file ...]
                        nits checking.  Run cstyle, jstyle, hdrchk, copyright,
                        cddlchk, and keywords over files to which they are
                        applicable (makestyle is not currently run
                        because it seems to be quite broken -- more
                        noise than data).  This is a subset of pbchk checks
                        suitable for checking files during development.
                        Use pbchk before doing the final putback.
                        Will run checks on all active files if no file args.
                        Will skip checks for files listed in wx/nits.NOT.
        $ME pbchk [file ...]
                        putback check.  Run cstyle, jstyle, hdrchk, copyright,
                        cddlchk, keywords, rmdelchk, deltachk, comchk, rtichk
                        and outchk over files to which they are
                        applicable (makestyle is not currently run
                        because it seems to be quite broken -- more
                        noise than data).  Good command to run before
                        doing a putback.  
                        Will run checks on all active files if no file args.
                        Will skip checks for files listed in wx/pbchk.NOT.

======================== Code Review Commands ======================
        $ME webrev [webrev-args]
                        generate webrev for active and renamed/deleted files.
                        Note, uses comments in the active list.  This is the
                        preferred way of reviewing code.  Arguments to webrev
                        may also be specified.
                        Will skip files listed in wx/webrev.NOT

        $ME codereview [-N] [codereview options]
                        generate environmentally friendly codereview diffs
                        for all active files.  -N indicates that delta
                        comments should not be included.

        $ME fullreview [-N] [codereview options]
                        generate full codereview diffs for all active files.
                        -N indicates that delta comments should not be
                        included.

======================== Backup and Restore Commands ======================
        $ME backup [-i|-n|-z|-b|-t]
                        make backup copies of all active and renamed files.
                        -i: info about backups (backup dir and contents)
                        -n: no compression 
                        -z: use gzip, faster than bzip2 but less compression.
                        -b: use bzip2, slower but better compression.
                        -t: backup if wx files are newer than last backup.
                        Defaults to the compression of the previous backup.
        $ME bu          Alias for backup command.

        $ME restore [-f] [backup_dir]   
                        restore a backup in a workspace (restores both
                        active files and performs file renames).  A path
                        to the directory containing the backup to
                        restore from can be optionally specified.
                        -f: non-interactive.  Will restore from last backup.

======================== Misc Commands ============================
        $ME apply <cmd> apply cmd to all active files; for example,
                        "$ME apply cat" cats every file
        $ME eval <cmd>  like apply, but more general.  In fact,
                        "$ME apply cmd" is implemented internally as
                        "$ME eval 'cmd \$file'".  When using eval,
                        you can refer to \$dir, \$file, \$filepath,
                        \$parent, and \$workspace.  For example:
                        $ME eval 'echo \$dir; sccs prt \$file | more'
                        will show the sccs history for each active file,
                        preceded by its directory.
        $ME grep        search all active files for pattern; equivalent to
                        "$ME eval 'echo \$filepath; grep pattern \$file'"
        $ME egrep       see $ME grep
        $ME sed         see $ME grep
        $ME nawk        see $ME grep

        $ME dir         echo the $ME directory path (\$workspace/$ME)
        $ME e <file>    edit the named $ME control file, e.g. "$ME e active".
                        The editor is \$EDITOR if set, else vi.

        $ME ea          shorthand for "$ME e active" (edit active list).  
                        Note, the format for each entry in the active
                        list is:

                        filepath
                        <empty line> # no spaces allowed
                        one or more comment lines # no blank lines between.
                        <empty line> # no spaces allowed, ends the entry.

                        In general, it is best to only edit the active
                        list to update comments.  Use the other $ME
                        commands like edit or create to update the
                        active list when possible.

        $ME ws <file>   cat the named workspace control file, i.e.
                        \$workspace/Codemgr_wsdata/file
        $ME args        shorthand for "$ME ws args"
        $ME access      shorthand for "$ME ws access_control"

        $ME help        print this usage message
        $ME version     print current version of this program
EOF
	exit 1
}

list_putback() {
	(wx_active; list_renamed -n)|nawk '!seen[$0]++'
}

list_new() {
	# list new files (not found in parent ws)
	typeset new

	if [[ $1 == '-t' ]]; then
		wx_active|
		while read new; do
			# thorough, skip new cache
			if ! lookup_parent $new; then
				# no parent, new file
				echo "$new"
			fi
		done
	else
		while read new; do
			# use new cache
			if ! lookup_parent $new; then
				# no parent, new file
				echo "$new"
			fi
		done < $wxdir/new
	fi
}

list_renamed() {
	typeset active current old

	if [[ $# -eq 0 ]]; then
		cat $wxdir/renamed
	elif [[ $1 == '-d' ]]; then
		# Only list only deleted file renames
		grep '^deleted_files/' $wxdir/renamed
	elif [[ $1 == '-n' ]]; then
		# Only list only current filenames
		cut -f1 -d' ' $wxdir/renamed
	elif [[ $1 == '-p' ]]; then
		# list parent for current instead of local previous name
		while read current old; do
			if lookup_parent $current; then
				print "$current $parentfile"
			else
				ring_bell
				print -u2 "Warning: cannot find parent file"\
					"for $current"
			fi
		done < $wxdir/renamed
	elif [[ $1 == '-a' ]]; then
		# Just list active renamed files
		for active in $(wx_active); do
			grep "^$(escape_re $active) " $wxdir/renamed
		done
	elif [[ $1 == '-r' ]]; then
		wx_active > $wxtmp/alist || fail "Error: cannot create $wxtmp/alist"
		# Just list non-active renamed files (just current name)
		while read current old; do
			grep -q "^$(escape_re $current)$" $wxtmp/alist || 
				echo $current
		done < $wxdir/renamed
		rm -f $wxtmp/alist
	else
		fail "Invalid flag $i. Run 'wx help' for info."
	fi
}

list_webrev() {
	# Output a list of active and renamed files suitable for webrev
	for filepath in $( (wx_active; list_renamed -n)|nawk '!seen[$0]++'); do
		if renamed; then
			echo "$filepath $parentfile"
		else
			echo "$filepath"
		fi
	done
}

wx_webrev() {
	typeset origdir=$PWD

	cd $workspace
	# skip files listed in .NOT files
	cat -s $wxdir/$command.NOT >$wxtmp/NOT
	
	( # do the loops in a subshell so there is only one file open
	for filepath in $(wx_active); do
		if grep -q "^$(escape_re $filepath)$" $wxtmp/NOT; then
			print -u2 "$filepath (skipping)"
			continue
		fi
		if renamed; then
			echo "$filepath $parentfile"
		else
			echo "$filepath"
		fi
		echo "\n$(wx_show_comment)\n"
	done 

	# Do non-active renamed files
	for filepath in $(list_renamed -r); do
		if grep -q "^$(escape_re $filepath)$" $wxtmp/NOT; then
			print -u2 "renamed $filepath (skipping)"
			continue
		fi
		lookup_parent $filepath ||
			fail "Error: cannot find parent for $filepath."
		if [[ $filepath != $parentfile ]]; then
			echo "$filepath $parentfile"
			# output empty line, comment, empty line
			echo "\nRenamed only (webrev comment generated by "\
			    "$ME, not an sccs comment)\n"
		else
			echo "$filepath"
			echo "\nWarning, file in renamed list but has same "\
			    "name as parent (not an sccs comment)\n"
		fi
	done
	# End of subshell
	) > $wxtmp/webrev.list
	
	# Note that the file list must come last.
	${WXWEBREV:-webrev} -w "$@" $wxtmp/webrev.list

	cd $origdir
}

#
# list all active files
#
wx_active() {
	# do not print hash by default
	typeset rc hash=0
	
	case $1 in
		# list only renamed active files
		-r) list_renamed -a; return 0;;
		# list suitable for putback (both active and renamed)
		-p) list_putback; return 0;;
		# list suitable for webrev (both active and renamed)
		-w) list_webrev; return 0;;
		-*) fail "Invalid flag $1. See 'wx help' for more info.";;
	esac

        if [[ ! -r $wxdir/active ]]; then
                fail "Error: cannot read $wxdir/active"
        fi

	nawk '
	{
	    # skip blank lines
	    while (NF == 0) { 
		    if (getline == 0)
			    next;
	    }
	    if (NF > 0){
		    # print the active filepath
		    print $1;
		    getline;
	    }
	    # skip blank lines
	    while (NF == 0) { 
		    if (getline == 0){
			    # Error, corrupt active list (missing comment)
			    exit 1
		    }
	    }
	    # skip comment
	    while (NF > 0) {
		    if (getline == 0){
			    # Error, blank line after comment missing
			    exit 2
		    }
	    }
	}' $wxdir/active

	rc=$?
	if [[ $rc -eq 1 ]];then
		fail "Corrupt active list (missing comment)."
	elif [[ $rc -eq 2 ]];then
		fail "Corrupt active list (blank line needed after comment)."
	fi
}

#
# show the comment for $filepath
#
wx_show_comment() {
	if [[ -f $wxdir/active ]]; then
		nawk '
		{
			filename=$1
			getline
			while (getline) {
				if (length == 0)
					next
				if (filename == target) {
					if ($0 == "NO_COMMENT") {
							found = 0
							exit 1
					}
					found = 1
					print
				}
			}
		}
		END {
			if (found == 0)
				print "NO_COMMENT"
			exit 1 - found
		}' target=$filepath $wxdir/active
		return $?
	else
		echo "NO_COMMENT"
	fi
}

bugdb_lookup_monaco() {
	typeset buglist=$1
	typeset monaco=/net/monaco.sfbay/export/bin/monaco 

	if [[ ! -x $monaco ]]; then
		return 1
	fi

	$monaco -text <<-EOF
	set What =
	substr(cr.cr_number, 1, 7), synopsis

	set Which =
	cr.cr_number in ($buglist)

	set FinalClauses =
	order by cr.cr_number

	do query/CR
	EOF
}

bugdb_compare() {
	nawk -v bugdb=$1 -v active=$2 -f - <<-"EOF"
	BEGIN {
		file = bugdb
		while (getline < file) {
			synbugdb[$1] = substr($0, length($1) + 1)
		}
		
		file = active
		while (getline < file) {
			synactive = substr($0, length($1) + 1)
			# If the synopsis doesn't match the bug database
			# and the synopsis isn't what's in the database
			# with " (something)" appended, spew a warning.
			if (!($1 in synbugdb)) {
				print "Bug " $1 " is not in the database"
			} else if (synbugdb[$1] != synactive &&
				!(index(synactive, synbugdb[$1]) == 1 &&
				  substr(synactive, length(synbugdb[$1])) ~ \
					/ \([^)]*\)$/)) {
				print "Synopsis of " $1 " is wrong:"
				print "  should be:" synbugdb[$1]
				print "         is:" synactive
			}
		}
	}
	EOF
}

#
# Summarize all comments listing, in order:
#	 - free-form comments
#	 - ARC case comments
#	 - Bug comments
#
# -a will suppress ARC case comments
# -b will suppress bug comments
# -n will do pbchk processing (give all warnings, no error exit)
# -o will suppress free-form (other) comments
# -p will do pbcom checking and exit if it finds problems unless -v is given
# -u will prevent sorting of the ARC case comments and bug comments
# -v will output all comments verbatim (no sorting)
# -N will prevent bug lookup, which may take a while.
#
# Note, be careful when modifying this function as sometimes the output
# should go to stdout, as in the case of being called via pbchk and
# sometimes the output should go to stderr.  Think about this when
# making changes in here.
#

function wx_summary {
	typeset i comment arc arcerr bug bugnospc bugid buglist synopsis \
		show_arcs=true \
		show_bugs=true \
		show_others=true \
		verbatim=false \
		pbchk=false \
		pbcom=false \
		cmd=sort \
		nolookup=false

	while getopts :abnopuvN i; do
		  case $i in
		  a)	show_arcs=false;;
		  b)	show_bugs=false;;
		  n)	pbchk=true;;
		  o)	show_others=false;;
		  p)	pbcom=true;;
		  v)	verbatim=true;;
		  u)	cmd=cat;;
		  N)	nolookup=true;;
		  *)	fail "Invalid flag -$OPTARG. See 'wx help' for more"\
		  		"info.";;
		  esac
	done

	# Store all the comments in a tmp file
	for filepath in $file_list; do
		wx_show_comment
	done | nawk '!seen[$0]++' > $wxtmp/comments

	if grep -q "^NO_COMMENT$" $wxtmp/comments; then
		print -u2 "Warning, found NO_COMMENT. Run '$ME ea' to edit the "\
			  "comments."
		if $pbcom; then
			# Don't want to output anything to stdout for a pbcom
			# if there is an error.
			return 1
		fi
	fi
	if $pbcom && $verbatim; then
		# All comments output verbatim.  Note, is hacky because
		# of the wx_summary interface.  pbcom calls wx_summary
		# with -po assuming other comments shouldn't be output.
		show_others=true
	fi

	# Note, hard tab in the arc regex.  This only recognizes FWARC,
	# LSARC and PSARC.  Also, regex must be compat with both egrep
	# and nawk.
	arc='(FW|LS|PS)ARC[\/ 	][12][0-9][0-9][0-9]\/[0-9][0-9][0-9][^0-9]'
	# bug ID must followed by single space
	bug='[0-9][0-9][0-9][0-9][0-9][0-9][0-9] '
	bugnospc='[0-9][0-9][0-9][0-9][0-9][0-9][0-9][^ ]'

	if $show_arcs; then
		# Note must use /usr/bin/sort.
		if ! egrep "^$arc" $wxtmp/comments |
			 sed 's#\([A-Z][A-Z]*ARC\)[/ 	]#\1 #' | sort |
			 /usr/bin/sort -cu -k 1,2 2>$wxtmp/error >/dev/null
		then
			arcerr=$(nawk '{match($0, '"/$arc/"'); \
				print substr($0, RSTART, RLENGTH);}' \
				$wxtmp/error)

			if $pbchk; then
				# if pbchk print to stdout
				print "Inconsistent ARC summaries for: $arcerr"
			else
				# else print to stderr
				print -u2 \
				"Inconsistent ARC summaries for: $arcerr"
			fi
			if $pbcom && ! $verbatim; then

				# Don't want to output anything to
				# stdout for a pbcom if there is an
				# error.

				return 1
			fi
		fi
	fi

	if $show_bugs; then
		# Treating bug comments with "(nit comment)" at the end as the
		# same as the original bug hence the sed and nawk below
		# Note hard tabs in sed arg.
		# Must use /usr/bin/sort.
		if ! grep "^$bug" $wxtmp/comments |sort|
			 sed 's/[ 	][ 	]*([^)]*) *$//' |
			 nawk '!seen[$0]++'|
			 /usr/bin/sort -cnu 2>$wxtmp/error >/dev/null; then
			if $pbchk; then
				print -n "Inconsistent bug summaries for: "
				sed 's#^[^0-9]*\('"${bug}"'\).*#\1#' \
				    $wxtmp/error
			else
				print -nu2 "Inconsistent bug summaries for: "
				sed 's#^[^0-9]*\('"${bug}"'\).*#\1#' \
				    $wxtmp/error >&2
			fi
			if $pbcom && ! $verbatim; then
				# Don't want to output anything to
				# stdout for a pbcom if there is an
				# error.
				return 1
			fi
		fi

		# Compare bug synopsis in active comments with the bug database.
		# If we've been passed -N, then we just never set buglist, thus
		# skipping the lookup.
		if ! $nolookup; then
			grep "^$bug" $wxtmp/comments | sort > $wxtmp/buglist.active
			cat $wxtmp/buglist.active | while read bugid synopsis; do
				buglist=$buglist,$bugid
			done
			buglist=${buglist#,}
		else
			if $pbchk; then
				print "Not performing bug synopsis check (be careful)"
			else
				print -u2 "Not performing bug synopsis check (be careful)"
			fi
		fi
		if [[ -n $buglist ]]; then
			bugdb_lookup_monaco $buglist > $wxtmp/buglist.bugdb
			if [[ -s $wxtmp/buglist.bugdb && $? -eq 0 ]]; then
				bugdb_compare $wxtmp/buglist.bugdb \
					$wxtmp/buglist.active > $wxtmp/error
				if [[ -s $wxtmp/error ]]; then
					if $pbchk; then
						cat $wxtmp/error
					else
						cat $wxtmp/error >&2
					fi
					if $pbcom && ! $verbatim; then
						# Don't want to output anything
						# to stdout for a pbcom if there
						# is an error.
						return 1
					fi
				fi
			else
				if $pbchk; then
					print "Could not perform bug synopsis check"
				else
					print -u2 "Could not perform bug synopsis check"
					if $pbcom; then
						print -u2 "Use -N to skip bug synopsis check (be careful)"
					fi
				fi
				if $pbcom && ! $verbatim; then
					# Don't want to output anything
					# to stdout for a pbcom if there
					# is an error.
					return 1
				fi
			fi
		fi

		if egrep "^$bugnospc" $wxtmp/comments >$wxtmp/error; then
			# must have single space following bug ID
			if $pbchk; then
				print "\nThese bugs are missing single space following the bug ID"
				print "(output prefaced by active file line number ':'):"
				while read comment
				do
					grep -n "^$comment" $wxdir/active
				done < $wxtmp/error
			else
				print -u2 "\nThese bugs are missing single space following the bug ID"
				print -u2 "(output prefaced by active file line number ':'):"
				while read comment
				do
					grep -n "^$comment" $wxdir/active >&2
				done < $wxtmp/error
			fi
			if $pbcom && ! $verbatim; then
				# Don't want to output anything to
				# stdout for a pbcom if there is an
				# error.
				return 1
			fi
		fi
	fi

	# Create warning for pbchk or pbcom.
	if $pbchk || ($pbcom && ! $verbatim) &&
		egrep -v "^($bug|$bugnospc|$arc|NO_COMMENT$)" $wxtmp/comments\
				> $wxtmp/other_comments; then
		cat <<-EOF
Warning, the following comments are found in your active list
that are neither bug or arc cases: 

		EOF
		cat $wxtmp/other_comments
		print -- "\n---- End of active list comment warnings ----"
	fi > $wxtmp/comment_warning

	if [[ -s $wxtmp/comment_warning ]]; then
		if $pbchk; then
			# output to stdout, don't exist in case there are more
			# warnings.
			cat $wxtmp/comment_warning
		elif $pbcom && ! $verbatim ; then
			# Don't want to output anything to stdout for a pbcom
			# if there is an error.
			cat $wxtmp/comment_warning >&2
			return 1
		fi
	fi

	if $pbchk; then
		cd $workspace
		# See if sccs delta comment is the same as active list comment
		for filepath in $file_list; do
			# extract most recent delta comment
			sccs prs -d ':C:' $filepath | sort > $wxtmp/com1 ||
				fail "sccs prs -d ':C:' $filepath failed."

			# Add blank line to active comments so following cmp will
			# work properly.
			(wx_show_comment; print) | sort > $wxtmp/com2

			if ! cmp -s $wxtmp/com1 $wxtmp/com2; then
				print "\n$filepath"
				print "Warning: current sccs comment does not"\
					"match active list comment"
				print "(< sccs comment, > active list comment):"
				diff $wxtmp/com1 $wxtmp/com2 
			fi
		done 
		# Just output warnings for pbchk
		return 0
	fi

	if $show_others; then
		  if ! $verbatim; then
			  # The non-verbatim check should have produced the
			  # other_comments file.
			  cat $wxtmp/other_comments| $cmd
		  else
			  # just output comments verbatim then return
			  cat $wxtmp/comments
			  return 0
		  fi
	fi

	if $show_arcs; then
		  egrep "^$arc" $wxtmp/comments | $cmd
	fi

	if $show_bugs; then
		  egrep "^$bug" $wxtmp/comments | $cmd
	fi
	return 0
}

update_renamed_file() {
	# Try to add a entry to the renamed list.  Note, this stores the new
	# name and previous name, not the  parent name as this is more useful in
	# detecting cyclic renames.

	# ofp: old filepath, nfp: new filepath
	typeset ofp=$1 nfp=$2

	# remove old and new entries from renamed
	egrep -v "^($(escape_re $ofp)|$(escape_re $nfp)) " $wxdir/renamed \
		>$wxtmp/renamed
	[[ ! -f $wxtmp/renamed ]] && fail "Error: cannot create $wxtmp/renamed"
	mv -f $wxtmp/renamed $wxdir/renamed || \
		fail "Error: cannot create $wxdir/renamed."

	# remove old entries from local nt cache
	remove_local_nt_entry $ofp

	# Do not update renamed list if the filepath is the same as
	# the parent or the file is new.
	if lookup_parent $nfp; then
		if [[ $parentfile != $nfp ]]; then
			print "$nfp $ofp" >> $wxdir/renamed || \
				fail "Error: cannot append $wxdir/renamed."
			
			[[ $ACTSORT == sort ]] && do_renamed_sort=true
		fi
	fi
	return 0
}

update_renamed_dir() {
	typeset append pb_files new orig
	typeset -i rc

	if [[ $# -eq 0 ]]; then
		# No args so we need to create the renamed list from
		# the source root.
		append=false
		if [ -r $wxdir/srcroot_dir ]; then
			pb_files=$(cat $wxdir/srcroot_dir)
		else
			pb_files=$DEFAULT_SRCDIR
		fi
	else
		# Assuming one or more filepaths as args
		append=true
		pb_files="$*"
	fi
	echo "Updating $ME renamed list... this may take several minutes."

	# Get output of putback -n to detect renames elsewhere in
	# this script. 

	get_pb_output $pb_files

	nawk '
		/^rename from:/{orig_file=$3}
		$1 == "to:" {print $2 " " orig_file}' \
		$wxtmp/putback.out  >$wxtmp/pb_renames || \
			fail "Error, creation of $wxtmp/pb_renames failed."

	cp $wxdir/renamed $wxdir/renamed.old || 
		fail "Error: cannot create $wxdir/renamed.old."

	if $append; then
		nawk '!seen[$0]++' $wxtmp/pb_renames $wxdir/renamed \
			> $wxtmp/renamed || \
			fail "Error: cannot create $wxtmp/renamed."
		mv -f $wxtmp/renamed $wxdir/renamed || \
			fail "Error: cannot create $wxdir/renamed."
	else
		mv -f $wxtmp/pb_renames $wxdir/renamed ||
			fail "Error: cannot create $wxdir/renamed."
	fi

	[[ $ACTSORT == sort ]] && do_renamed_sort=true
}

# returns 0 if a pattern in patternfile matches input path arg
pathcheck() {
	typeset pattern path=$1 patternfile=$2

	while read pattern; do
		if [[ $path == $pattern ]]; then
			return 0 # pattern matched path
		fi
	done < $patternfile
	return 1 # file path not matched by pattern
}

#
# Evaluate a command for all listed files.  This is the basic building
# block for most wx functionality.
#

wx_eval() {
	typeset -i changedir=1

	if [[ $# -gt 1 && "$1" == "-r" ]]; then
		changedir=0
		shift
	fi

	pre_eval=$*
	# skip files listed in .NOT files
	cat -s $wxdir/$command.NOT >$wxtmp/NOT
	for filepath in $file_list; do
		if pathcheck $filepath $wxtmp/NOT; then
			print "$filepath (skipping)"
		else
			cd $workspace
			dir=`dirname $filepath`
			file=`basename $filepath`
			[[ $changedir -eq 1 ]] && cd $dir
			eval $pre_eval
		fi
	done
}

#
# Initialize a workspace for wx.
#

wx_init() {
	typeset srcroot_dir force=false

	# check that srcroot is relative to top of workspace
	if cd $workspace/$1; then
		# normalize the srcroot dir for test below
		srcroot_dir=$(/bin/pwd)
		srcroot_dir="${srcroot_dir#$workspace/}"
		if [[ $srcroot_dir == $workspace ]]; then
			# Special case need to set srcroot_dir to
			# a relative path but we're at the top of the
			# workspace.
			srcroot_dir="."
		fi
	else
		fail "Source root '$1' does not exist in workspace"\
			"($workspace)."
	fi

	if [ -d $wxtmp ]; then
                if [[ $2 != -f[nqt] ]]; then
                        echo "This workspace has already been initialized."
                        ok_to_proceed 'Do you really want to re-initialize?'
                fi
	else
		mkdir -p $wxtmp
	fi

	#
	# Make sure to save $srcroot_dir as a path relative to the workspace
	# root; an absolute path would break if the workspace name changed.
	#
	rm -f $wxdir/srcroot_dir
	echo $srcroot_dir >$wxdir/srcroot_dir

	backup_dir=$HOME/$ME.backup/$workspace_basename
	[[ -d $backup_dir ]] || mkdir -p $backup_dir ||
		fail "mkdir -p $backup_dir failed."
	cd $backup_dir
	rm -f $wxdir/backup_dir
	pwd >$wxdir/backup_dir || fail "Creation of  $wxdir/backup_dir failed." 

	touch $wxdir/renamed
	touch $wxdir/active
	touch $wxdir/new
	touch $wxdir/local_nametable 

	if [[ -z "$2" ]]; then
		# Interactive mode
		cat << EOF
    Pick one of the following update methods:

    1) Thorough: Detect any differences between the current workspace
       and its parent and update the active, new and renamed lists.  Use
       this in workspaces where files have been renamed or deleted or
       there are files that are different from the parent that are not
       checked out.  Note, files must be under SCCS control in order for
       this method to compare them to the parent workspace.

    2) Quick: Only update the active list with files that are currently
       checked out.  Will not update the renamed list.

    3) None: Use this on workspaces where there are no changes between
       the workspace and its parent.  This is very quick but will not
       update the active, new or renamed lists.

EOF
		read answer?"Which update method? [1|2|3]: "
		case $answer in
			1) wx_update;;
			2) wx_update -q;;
			3) ;;
			*) fail "Bad answer: ${answer}. Rerun init command"\
				"again";;
		esac
		yesno "Keep active list sorted by default?"
		if [[ "$answer" == 'yes' ]]; then
			print "true" > $wxdir/sort_active
		else
			print "false" > $wxdir/sort_active
		fi
	else
		# non-interactive mode
		case $2 in
			# forced thorough update
			-ft) wx_update; force=true;;
			# forced quick update
			-fq) wx_update -q; force=true;;
			# forced no update
			-fn) force=true;;
			# invalid arg
			-*) fail "$ME $command: unrecognized argument";;
		esac
		if [[ $3 == -s ]]; then
			print "true" > $wxdir/sort_active
		else
			print "false" > $wxdir/sort_active
		fi
	fi
	print

	if [ -s $wxdir/active ]; then
		basedir=$workspace
		file_list=`wx_active`
		if $force; then
			# only backup if necessary
			print "Will backup wx and active files if necessary"
			wx_backup -t
		else
			print "Making backup copies of all wx and active files"
			wx_backup
		fi
	else
		echo "Active list empty, not doing backup."
		echo
	fi

	echo "$ME initialization complete"
}

#
# Find all checked out files
#

wx_checked_out() {
	typeset origdir=$(pwd)
	cd $workspace
	x=$(ls -t $wsdata/nametable $wxdir/sccs_dirs 2>/dev/null)
	if [[ -z $x || "`basename $x`" == nametable ]]; then
		if [ -f $wxdir/srcroot_dir ]; then
			srcroot_dir=`cat $wxdir/srcroot_dir`
		else
			srcroot_dir=$DEFAULT_SRCDIR
		fi
		print -u2 "Workspace nametable changed: sccs_dirs out of date"
		print -u2 "Updating $wxdir/sccs_dirs...this may take a few minutes.\n"
		rm -f $wxdir/sccs_dirs
		find $srcroot_dir -name SCCS -print | sort >$wxdir/sccs_dirs
	fi
	cd $workspace
	rm -f $wxtmp/checked_out
	# Note if srcroot_dir = . this must be removed from the front of the
	# filepaths.
	echo $(sed -e 's,$,/p.*,' $wxdir/sccs_dirs) | \
		tr \\040 \\012 | \
		grep -v '*' | \
		sed -e 's,SCCS/p.,,' |sed -e 's,^\./,,' >$wxtmp/checked_out
	cd $origdir
}

deal_ws_renames() {
	# See if any active/renamed files were renamed externally
	# (perhaps by bringover?) and try to rename the active entry
	# filepath.
	typeset fp newfp renamed localfile hash1 hash2 hash3 hash4 \
		notrenamed_list origdir=$(pwd)
	cd $workspace
	list_putback |\
	while read fp; do
		if [[ ! -f $fp ]]; then
			# file not found, suspect rename.
			# using renamed for error checking.
			renamed=false
			# search cached local nt to find old hash info
			grep "^$(escape_re $fp) " $wxdir/local_nametable |\
			while read localfile hash1 hash2 hash3 hash4; do
				# find new filepath
				newfp=$(fgrep " $hash1 $hash2 $hash3 $hash4"\
					$wsdata/nametable|cut -f1 -d' ')

				[[ -z $newfp ]] && continue

				if [[ $newfp != $fp ]]; then
					update_renamed_file $fp $newfp 
					rename_active_entry $fp $newfp
					echo "\nRenamed active file"\
						"$fp to $newfp"
					renamed=true
					break
				fi
			done
			if ! $renamed; then
				if [[ -z $notrenamed_list ]]; then
					notrenamed_list="$fp"
				else
					notrenamed_list="$notrenamed_list\n$fp"
				fi
			fi
		fi
	done
	if [[ -n $notrenamed_list ]]; then
		ring_bell
		cat <<-EOF
Warning, active file(s):
   $(echo $notrenamed_list)
not found and cannot be renamed.  Use "$ME ea" to edit the active list to
remove these entries if they do not exist in this workspace.
		EOF
	fi
	cd $origdir
}

#
# Old style update the active file list (by appending all checked out files).
# This is what the original wx update did.
#
wx_update_quick() {

	# Sort active if requested.
	[[ "$1" == "-s" ]] && ACTSORT=sort

	wx_checked_out
	cd $wxdir
	rm -f tmp/files.old tmp/files.new active.new
	wx_active >tmp/files.old || fail "Error: cannot create $wxtmp/files.old"
	# sed has a hard tab, used to delete lines containing only whitespace
	sed '/^[	 ]*$/d' tmp/files.old tmp/checked_out|
		nawk '!seen[$0]++' | $ACTSORT > tmp/files.new ||
			fail "Error: cannot create $wxtmp/files.new"
	cp -f new new.old || fail "Error: cannot create new.old."
	while read filepath ; do
		add_local_nt_entry $filepath
		(echo "$filepath"; echo; wx_show_comment; echo) 
	done < tmp/files.new > active.new || 
		fail "Error: cannot create $wxdir/active.new"

	mv -f active active.old
	mv -f active.new active

	echo
	echo "New active file list:"
	echo
	cat tmp/files.new
	echo
	echo "Diffs from previous active file list:"
	echo
	${WXDIFFCMD:-diff} tmp/files.old tmp/files.new
	echo "End active diffs =========================="

	# Do new file processing after active list is updated.

	# Note, the parent nt read check below is hackish because we are
	# assuming that lookup_parent needs to read the parent nt and if it
	# can't then we want to just issue the warning.
	if [[ -n $parent && ! -r $parent/Codemgr_wsdata/nametable ]]; then
		echo "\nWarning: cannot read parent nametable, new file list"\
			"not output." >&2
	else
		while read filepath; do
			# lookup_parent populates local nt cache
			if lookup_parent $filepath; then
				remove_new $filepath
			else
				add_new $filepath
			fi
		done < tmp/files.new

		echo
		echo "New new-file list:"
		echo
		cat new
		echo
		echo "Diffs from previous new-file list:" 
		echo
		${WXDIFFCMD:-diff} new.old new
		echo "End new diffs =========================="
	fi
}

#
# Update various lists (active, renamed, new)
#

wx_update() {
	typeset i efp sortarg do_quick=false
	typeset -i rc found
	# default, update all lists
	typeset update_active=true update_renamed=true update_new=true

	while getopts :qrs i; do
		  case $i in
		  q)	do_quick=true;;
		  r)	update_active=false
			update_new=false;;
		  s)	ACTSORT=sort; sortarg="-s";;
		  *)	fail "Invalid flag -$OPTARG. See 'wx help' for more"\
		  		"info.";;
		  esac
	done

	deal_ws_renames

	if $do_quick; then
		# Do old school wx update (only for checked out files)
		# This is faster but not as thorough.
		wx_update_quick $sortarg
		return
	fi

	if [[ -z $parent ]]; then
		fail "Error: cannot do thorough update, no parent.  Use 'update -q'"\
			"instead."
	fi
	if [[ ! -r $parent/Codemgr_wsdata/nametable ]]; then
		fail "Error: cannot read $parent/Codemgr_wsdata/nametable"
	fi

	# create tmp/checked_out file which putback -n may not list.
	# Do this before putback so it doesn't unnecessarily update the
	# sccs dirs cache.
	wx_checked_out 

	# Get output of putback -n to detect renames and active files.
	if [ -f $wxdir/srcroot_dir ]; then
		get_pb_output $(cat $wxdir/srcroot_dir)
	else
		get_pb_output $DEFAULT_SRCDIR
	fi

	cd $wxdir

	if $update_renamed; then
		if [[ -f renamed ]]; then
			mv renamed renamed.old ||
			    fail "Error: cannot create $wxdir/renamed.old."
		else
			touch renamed.old ||
			    fail "Error: cannot create $wxdir/renamed.old."
		fi

		nawk '  /^rename from:/{orig_file=$3}
			$1 == "to:" {print $2 " " orig_file}' \
			tmp/putback.out |$ACTSORT > tmp/renamed ||\
				fail "Error, creation of $wxdir/tmp/renamed failed."
			mv -f tmp/renamed renamed || \
				fail "Error: cannot create $wxdir/renamed"

		for filepath in $(cut -f1 -d' ' renamed); do
			add_local_nt_entry $filepath
		done

		echo "New renamed file list:"
		echo
		cat renamed
		echo
		echo "Diffs from previous renamed file list:"
		echo
		${WXDIFFCMD:-diff} renamed.old renamed
		echo "End renamed diffs =========================="
	fi

	if $update_active; then
		# Create active list from putback output.
		nawk '/^(update|create): / {print $2};
		     /^The following files are currently checked out/ \
			{p=1; continue};
		     /^"/ {continue};
		     NF == 0 {p=0; continue};
		     {if (p==1)
		     	print $1}' tmp/putback.out |
		     sort -u > tmp/active_nawk_out ||
		     fail "Error: cannot create $wxtmp/active_nawk_out"

		# list files in conflict also
		nawk '/^(conflict): / {print $2}' tmp/putback.out |
		    sort -u > tmp/conflict_nawk_out ||
		    fail "Error: cannot create $wxtmp/conflict_nawk_out"

		# Need to read $wsdata/nametable if there are conflicts
		if [[ -s tmp/conflict_nawk_out && ! -r $wsdata/nametable ]]
		then
			fail "Error: cannot read $wsdata/nametable."
		fi

		# clear the tmp active file
		print -n > tmp/active.new || fail "Error: cannot create tmp/active.new."

		# store current active list
		wx_active > tmp/files.old ||
			fail "Error: cannot create $wxtmp/files.old"

		# go through all the possible active files (keeping the existing
		# ones as well). Note hard tab in sed arg.
		for filepath in $(sed '/^[	 ]*$/d' tmp/files.old \
			tmp/checked_out tmp/conflict_nawk_out \
			tmp/active_nawk_out | nawk '!seen[$0]++' | $ACTSORT); do

			efp=$(escape_re $filepath)

			if grep -q "^$efp$" tmp/conflict_nawk_out; then

				# conflict files have a parent but the
				# putback output only shows the parent's
				# filename, need to find local name in
				# case of rename

				grep "^$efp " $parent/Codemgr_wsdata/nametable|\
					read localfile hash1 hash2 hash3 hash4
				local_file="$(\
				    fgrep " $hash1 $hash2 $hash3 $hash4" \
					$wsdata/nametable | cut -f1 -d' ')"

				# continue if empty string
				[[ -z "$local_file" ]] && continue

				if ! grep -q "^$local_file" tmp/active.new; then
					filepath=$local_file
				else
					continue
				fi
			fi
			add_local_nt_entry $filepath
			(echo $filepath; echo; wx_show_comment; echo)\
			    >> tmp/active.new
		done

		rm -f tmp/active_nawk_out

		mv -f active active.old
		mv -f tmp/active.new active
		wx_active > tmp/files.new

		echo
		echo "New active file list:"
		echo
		cat tmp/files.new
		echo
		echo "Diffs from previous active file list:" 
		echo
		${WXDIFFCMD:-diff} tmp/files.old tmp/files.new
		echo "End active diffs =========================="
		rm -f tmp/files.old tmp/files.new

	fi

	# The new list is for caching names of new files to speed up commands
	# that list the new files.
	if $update_new; then
		if [ -f new ]; then
			cp -f new new.old || fail "Error: cannot create file new.old."
		elif [ ! -f new.old ]; then
			touch new.old || fail "Error: cannot create file new.old."
		fi
		# Create new list from putback output.
		nawk '/^create: / {print $2};' tmp/putback.out |
			sort -u > tmp/new || fail "Error: cannot create $wxtmp/new."
		mv -f tmp/new new
		echo
		echo "New new-file list:"
		echo
		cat new
		echo
		echo "Diffs from previous new-file list:" 
		echo
		${WXDIFFCMD:-diff} new.old new
		echo "End new diffs =========================="
	fi
}

wx_edit() {
	# Must be called via wx_eval
	if [ -f SCCS/p.$file ]; then
		echo "$filepath already checked out"
		update_active $filepath
	elif [ -f $file ]; then
		echo $filepath
		if sccs edit $silent $file; then
			update_active $filepath
		else
			fail "sccs edit $filepath failed."
		fi
	else
		ring_bell
		echo "Warning. file $filepath not found."
	fi

	[[ $ACTSORT == sort ]] && do_active_sort=true
}

wx_unedit() {
	# Must be called via wx_eval
	typeset -i force=0
	typeset arg msg

	# set positional args to contents of global $args
	set -- $args

	while getopts :f arg; do
		case $arg in
			 f) force=1;;
			 *) fail "Invalid flag -$OPTARG. See 'wx help' for"\
			 	"more info.";;
		esac
	done

	msg="differs from parent file, will remain in active list."

	if [[ ! -f SCCS/p.$file ]]; then
		echo "$filepath not checked out"
	else
		if [[ $backup_done -eq 0 ]]; then
			if [[ $force -eq 0 ]]; then
				yesno "Do you want to backup files first?"
				if [[ "$answer" == "yes" ]]; then
					wx_backup || fail "Backup failed."
				fi
			else
				# only backup if necessary
				print "Will backup wx and active files if necessary"
				wx_backup -t || fail "Backup failed."
			fi
			backup_done=1;

			# cd to the dir where the file is in case
			# wx_backup took us somewhere else.

			cd ${workspace}/$dir
		fi

		echo $filepath
		if sccs unedit $silent $file; then
			if [[ $force -eq 1 ]]; then
				if is_active $filepath; then
					if wx_pnt_filepath $filepath; then
						if cmp -s $file $parentfilepath; then
							remove_active_entry $filepath
						else
							print "$filepath $msg"
						fi
					fi
				fi
			else
				ask_remove_active_entry
			fi
		fi
	fi
}

wx_create() {
	# Must be called via wx_eval
	typeset -i checkout=0 force=0
	typeset arg

	while getopts :fo arg; do
		case $arg in
			 o) checkout=1;;
			 f) force=1;;
			 *) fail "Invalid flag -$OPTARG. See 'wx help' for"\
			 	"more info.";;
		esac
	done

	if [ ! -f $file ]; then
		ring_bell
		echo "Error! $filepath is not a file."
		return 1
	elif [ -f $workspace/deleted_files/$filepath ]; then
		ring_bell
		cat >&2 <<-EOF
Error: a deleted version of $filepath exists.
You must undelete the file and edit that version.
Run:
'cd $workspace'
'$ME mv deleted_files/$filepath $filepath'
'$ME edit $filepath'
		EOF
		return 1
	elif [[ -n $parent && -f $parent/$filepath ]]; then
		ring_bell
		cat >&2 <<-EOF
Error! $filepath exists in the parent workspace $parent.
Choose a different name.
		EOF
		return 1
	elif [[ -n $parent && -f $parent/deleted_files/$filepath ]]; then
		ring_bell
		cat >&2 <<-EOF
Error! a deleted version of $filepath exists in the parent workspace
You must undelete the file and edit that version.
Run:
'cd $workspace'
'bringover deleted_files/$filepath'
'$ME mv deleted_files/$filepath $filepath'
'$ME edit $filepath'
		EOF
		return 1
	elif [ -f SCCS/s.$file ]; then
			echo "$filepath already created, active list not"\
				"updated." >&2
	else
		# XXX it would be nice if keyword would work on new files
		if ! egrep "$SCCSKEYWORD" $file >/dev/null; then
			ring_bell
			cat >&2 <<-EOF

Warning!!!
$filepath 
is missing SCCS keywords.  See
/net/wizard.eng/export/misc/general_docs/keyword_info.txt
for more info.  Note, pay attention to the tabs.
			EOF
			if [[ $force -ne 1 ]]; then
				yesno "Continue with create?" 
				if [[ "$answer" != 'yes' ]]; then
					echo "Aborting create $filepath"
					return 1
				fi
			fi
		fi

		if ! copyrightchk $file; then
			# Sound bell
			ring_bell
			cat >&2 <<-EOF

Warning!!!
$filepath 
has copyright problems.  See
/net/wizard.eng/export/misc/general_docs/golden_rules.txt
for more info.
			EOF
			if [[ $force -ne 1 ]]; then
				yesno "Continue with create?" 
				if [[ "$answer" != 'yes' ]]; then
					echo "Aborting create $filepath"
					return 1
				fi
			fi
		fi

		if ! cddlchk -a $file; then
			# Sound bell
			ring_bell
			cat >&2 <<-EOF

Warning!!!
$filepath 
has CDDL block problems.  See
http://www.opensolaris.org/os/community/onnv/devref_toc/devref_7/#7_2_3_nonformatting_considerations
for more info.
			EOF
			if [[ $force -ne 1 ]]; then
				yesno "Continue with create?" 
				if [[ "$answer" != 'yes' ]]; then
					echo "Aborting create $filepath"
					return 1
				fi
			fi
		fi

		if [[ ! -d SCCS ]]; then
			mkdir SCCS || fail "Error: cannot create SCCS dir."
		fi

		if [[ -n "$comment_file" ]]; then
			sccs create $silent -y"$(\
			    sed '/^[ 	]*$/d' $comment_file)" $file ||
				    fail "sccs create $filepath failed."
		else
			sccs create $silent $file ||
				fail "sccs create $filepath failed."
		fi
		rm -f ,$file
		update_active $filepath
		add_new $filepath
		if [[ $checkout -eq 1 ]]; then
			sccs edit $silent $file ||
				fail "sccs edit $filepath failed."
		fi
		[[ $ACTSORT == sort ]] && do_active_sort=true
	fi
}

wx_uncreate() {
	# Must be called via wx_eval
	# undoes a 'wx create'

	typeset efp
	typeset -i force=0

	case $1 in
		-f) force=1;;
		-*) fail "$ME $command: unrecognized argument";;
	esac

	if [[ $backup_done -eq 0 ]]; then
		if [[ $force -eq 0 ]]; then
			yesno "Do you want to backup files first?"
			if [[ "$answer" == 'yes' ]]; then
				wx_backup || fail "Backup failed."
			fi
		else
			# only backup if necessary
			print "Will backup wx and active files if necessary"
			wx_backup -t || fail "Backup failed."
		fi
		backup_done=1;
		cd $workspace/$dir
	fi

	if [[ ! -f $file ]]; then
		echo "$filepath not found, skipping"
		return 1
	fi

	efp=$(escape_re $filepath)

	if ! wx_pnt_filepath; then
		# This is a new file so let's uncreate it.
		answer='no'
		if [[ $force -ne 1 ]]; then
			cat <<-EOF

$filepath appears to be a new file.  
Note, $command will remove its SCCS info from your
workspace and entry in the active list but will leave
the file in your workspace.  

			EOF
			# yesno sets answer
			yesno "Continue $command $filepath?"
		else
			# forced to yes
			answer='yes'
		fi

		if [[ "$answer" == 'yes' ]]; then
			if [[ ! -f SCCS/p.$file ]]; then
				sccs edit $file ||
				    fail "sccs edit $filepath failed."
			fi
			rm -f SCCS/s.$file SCCS/p.$file
			# For later cleanup on exit
			if grep -q "^$efp " $wsdata/nametable 2>/dev/null; then
				NEED_WS_CLEAN='y'
			fi
		
			if is_active $filepath; then
				remove_active_entry $filepath
			fi
			remove_new $filepath
		else
			echo "skipping $filepath"
		fi
	else
		echo "Not new, skipping $filepath"
	fi # if ! wx_pnt_filepath
}

wx_reset() {
	# Must be called via wx_eval
	# resets a local file to the parent version

	typeset efp
	typeset -i force=0

	case $1 in
		-f) force=1;;
		-*) fail "$ME $command: unrecognized argument";;
	esac

	if [[ $backup_done -eq 0 ]]; then
		if [[ $force -eq 0 ]]; then
			yesno "Do you want to backup files first?"
			if [[ "$answer" == 'yes' ]]; then
				wx_backup || fail "Backup failed."
			fi
		else
			# only backup if necessary
			print "Will backup wx and active files if necessary"
			wx_backup -t || fail "Backup failed."
		fi
		backup_done=1;
	fi

	if [[ ! -f $file ]]; then
		print "$filepath not found, skipping"
		return 1
	fi

	efp=$(escape_re $filepath)

	if wx_pnt_filepath; then
		if [[ $force -ne 1 ]]; then
			answer='no' # safe default
			cat <<-EOF

Regarding: $filepath
$command will reset the file contents and sccs history to that of the parent:
$parentfilepath
and remove the entry from the active and renamed lists.

			EOF
			if [[ $filepath != $parentfile ]]; then
				print "Note: local file will be reset to parent filepath."
			fi
			# yesno sets answer
			yesno "Continue $command $filepath?"
		else
			# forced to yes
			answer='yes'
		fi

		if [[ "$answer" == 'yes' ]]; then
			if is_active $filepath; then
				remove_active_entry $filepath
			fi
			if renamed; then
				remove_renamed_entry $filepath
			fi
			rm -f $file SCCS/[ps].$file
			grep -v "^$efp " $wsdata/nametable > $wxtmp/nametable.new || \
				fail "Error: cannot create $wxtmp/nametable.new ."
			mv -f $wxtmp/nametable.new $wsdata/nametable || \
				fail "Error: mv -f $wxtmp/nametable.new $wsdata/nametable failed."

			# add to bringover list for more efficient bringover
			bofilelist="$bofilelist $parentfile"
		else
			print -u2 "Skipping $filepath"
		fi
	else
		cat >&2 <<-EOF

Warning: skipping $filepath 
as it appears to be new. Use 'uncreate' to remove this new file from the
workspace.
		EOF
	fi # if ! wx_pnt_filepath
}


cyclic_rename() {
	# Detect the cyclic rename that causes Teamware problems.
	# See 'man workspace' for more info
	typeset new_filepath=$1 old_filepath=$2\
		found_new=false found_old=false
	typeset prev_new prev_old

	while read prev_new prev_old; do
		if [[ "deleted_files/$new_filepath" == $prev_new &&
			  $old_filepath != $prev_new ]]; then

			# Cyclic rename
			return 0
		fi

		if [[ $new_filepath == $prev_old && $prev_new != $old_filepath ]]
		then
			# The new file was the old file of a previous rename
			found_new=true
			if $found_old; then
				# Cyclic rename
				return 0
			fi
		elif [[ $old_filepath == $prev_new && 
		    $new_filepath != $prev_old ]]; then

			# The old filepath was the new filepath of a
			# previous rename and this rename is not an undo
			# (new filepath is diff from previous old
			# filepath)

			found_old=true
			if $found_new; then
				# Cyclic rename
				return 0
			fi
		fi
	done < $wxdir/renamed

	# Not a cyclic rename
	return 1
}

wx_delete() {
	# Must be called via wx_eval
	typeset efp
	typeset -i force=0

	case $1 in
		-f) force=1;;
		-*) fail "$ME $command: unrecognized argument";;
	esac

	if [[ $backup_done -eq 0 ]]; then
		if [[ $force -eq 0 ]]; then
			yesno "Do you want to backup files first?"
			if [[ "$answer" == 'yes' ]]; then
				wx_backup || fail "Backup failed."
			fi
		else
			# only backup if necessary
			print "Will backup wx and active files if necessary"
			wx_backup -t || fail "Backup failed."
		fi
		backup_done=1;
		cd $workspace/$dir
	fi

	if [[ ! -f $file ]]; then
		fail "$filepath isn't a file."
	fi

	# this is used a couple times so save escape_re value
	efp=$(escape_re $filepath)

	if wx_pnt_filepath; then
		# Not a new file (has a parent)
		if is_active $filepath; then
			ring_bell
			cat >&2 <<-EOF

Warning! $filepath 
is in active list. You should run
"$ME reedit $filepath" 
"$ME unedit $filepath" 
which should remove it from the active list then run 
"$ME $command $filepath".
Note, if you have made changes to this file that you want to keep, back
it up first.

$filepath not deleted.

			EOF
			return 1
		fi
		# See if this is already in the renamed list
		if grep -q "^deleted_files/$efp " $wxdir/renamed; then
			ring_bell
			if [[ -f $workspace/deleted_files/$filepath ]]; then
				cat >&2  <<-EOF
Warning: $filepath
has already been deleted.
Check for deleted_files/$filepath
in $wxdir/renamed .
				EOF
			else
				cat >&2 <<-EOF
Warning! the $ME renamed list appears to be corrupt.
				EOF
				fail "Please run '$ME update -r' and try this"\
					"command again."
			fi
		fi
		if workspace filerm $file; then
			# we know filerm renames files under deleted_files/
			update_renamed_file $filepath deleted_files/$filepath

			print "$filepath deleted"
			print
			print "To recover $filepath do:"
			print "'cd $workspace'"
			print "'$ME mv deleted_files/$filepath $filepath'"
		else
			print "There was an error while trying to delete $filepath"
		fi
	else
		# This is a new file so let's remove it.
		if is_active $filepath; then
			ring_bell
			cat >&2 <<-EOF

Warning: $filepath 
is in active list. You should run
"$ME uncreate $filepath" 
which should remove it from the active list
then run "$ME $command $filepath". 

$filepath not deleted.

			EOF
			return 1
		fi

		answer='no'
		if [[ $force -ne 1 ]]; then
			cat <<-EOF

Warning: $filepath 
appears to be a new file.

Do you want to completely remove the file and SCCS info from 
your workspace?  (If you answer no, the file will just be 
removed from the active list if it's in there.  If you answer 
yes, the file and associated SCCS history files will removed 
from the active list and also removed entirely from the 
workspace.)

			EOF
			# yesno sets answer
			yesno "Completely remove $filepath?"
		else
			# forced to yes
			answer='yes'
		fi

		if [[ "$answer" == 'yes' ]]; then
			rm -f $file SCCS/s.$file 
			[[ -f SCCS/p.$file ]] && rm -f SCCS/p.$file
			echo "$filepath removed from workspace."
			# For later cleanup, optional
			if grep -q "^$efp " $wsdata/nametable 2>/dev/null; then
				NEED_WS_CLEAN='y'
			fi
		fi
		remove_new $filepath
	fi
}

wx_mv() {
	# create some local variables to avoid side effects
	typeset efp old_filepath new_filepath

	cd $workspace

	old_filepath=$1
	new_filepath=$2

	if [[ -d $old_filepath && ( -d $new_filepath || ! -f $new_filepath ) ]]
	then

		if [[ $(basename $old_filepath) == "SCCS" ]]; then
			return
		fi

		echo "Doing a $command between two directories can take a "\
		    "while, please be patient."
		# deal with directory to directory move
		if [[ -d $new_filepath ]]; then
			base="$(basename $old_filepath)/"
		else
			base=
		fi

		sccsmv $old_filepath $new_filepath ||
		    fail "sccsmv $old_filepath $new_filepath failed."

		# remove previous renamed entry
		remove_renamed_entry $old_filepath
		
		update_renamed_dir $new_filepath/$base

		if grep -q "^$efp/" $wsdata/nametable 2>/dev/null; then
			# Old entries in workspace nametable so set this
			# to clean up on exit
			NEED_WS_CLEAN='y'
		fi

		# rename path of active entry
		sed "s|^$efp/|$new_filepath/$base|" $wxdir/active \
			   > $wxtmp/active || fail "Error: cannot create $wxtmp/active."
		mv $wxtmp/active $wxdir/active ||
			fail "Error: cannot create $wxdir/active."
		sed "s|^$efp/|$new_filepath/$base|" $wxdir/new \
			   > $wxtmp/new || fail "Error: cannot create $wxtmp/new."
		mv $wxtmp/new $wxdir/new || fail "Error: cannot create $wxdir/new."
		
	elif [[ -f $old_filepath && -d $new_filepath ]]; then
		wx_mv_file $old_filepath $new_filepath/$(basename $old_filepath)
	elif [[ -f $old_filepath && ! -f $new_filepath ]]; then
		wx_mv_file $old_filepath $new_filepath
	elif [[ ! -f $old_filepath ]]; then
		fail "Error! $old_filepath not found."
	elif [[ -f $new_filepath ]]; then
		fail "Error! $new_filepath exists."
	fi
}

wx_mv_file() {
	# store literal filepath in local var. to avoid side effects
	typeset efp old_filepath new_filepath

	cd $workspace

	old_filepath=$1
	new_filepath=$2

	if [[ ! -f $old_filepath ]]; then
		fail "Error! $old_filepath does not exist."

	elif [[ -f $new_filepath ]]; then
		fail "Error! $new_filepath already exists."

	else
		if cyclic_rename $new_filepath $old_filepath; then
			fail "Cyclic renamed detected. See 'man workspace'"\
				"for more info."
		fi

		if workspace filemv $old_filepath $new_filepath; then
			update_renamed_file $old_filepath $new_filepath 
			efp=$(escape_re $old_filepath)
			if is_active $old_filepath; then
				# In active list so update list with new
				# file name
				rename_active_entry $old_filepath $new_filepath
			fi
			if grep -q "^$efp$" $wxdir/new; then
				remove_new $old_filepath
				add_new $new_filepath
			fi
			if grep -q "^$efp " $wsdata/nametable 2>/dev/null; then
				NEED_WS_CLEAN=y
			fi
		else
			echo "There was an error renaming $old_filepath to "\
			    "$new_filepath"
		fi
	fi
}

sccs_rmdel_done() {
	# Note there are literal tabs in the []'s below so be careful when
	# editing.

	# file not in SCCS so return false (1)
	[[ ! -f SCCS/s.$file ]] && return 1

	if wx_pnt_filepath; then
		sccs prt -a $parentfilepath > $wxtmp/parenthist
		sccs prt -a $file > $wxtmp/filehist
		diff $wxtmp/parenthist $wxtmp/filehist |
			grep '^> R [0-9]\.[0-9]'|
			egrep -v ' (Codemgr|Fake)[ 	]' |
			sed 's/^> //' > $wxtmp/newrmdels
		[[ ! -f $wxtmp/newrmdels ]] && fail "Error: cannot create $wxtmp/newrmdels"
		rm -f $wxtmp/parenthist $wxtmp/filehist
	else
		# New file, no parent
		sccs prt -a $file |
		    egrep -v \
			'^R [0-9]+(\.[0-9]+)+[ 	].* (Codemgr|Fake)[ 	]' |
		    egrep '^R [0-9]+(\.[0-9]+)+[ 	]' > $wxtmp/newrmdels
		[[ ! -f $wxtmp/newrmdels ]] && fail "Error: cannot create $wxtmp/newrmdels"
	fi

	if [[ -s $wxtmp/newrmdels ]]; then
		cat $wxtmp/newrmdels
		rm -f $wxtmp/newrmdels
		# an rmdel was done so return true
		return 0
	else
		rm -f $wxtmp/newrmdels
		# no rmdel was done so return false
		return 1
	fi
}

rmdelchk() {
	# returns 0 (success) if an rmdel was done.
	# Should be called via wx_eval().
	if sccs_rmdel_done ; then
		ring_bell
		cat <<-EOF

Warning, it looks like 'sccs rmdel' was run on $filepath
This will cause a problem for Teamware.  Please fix this.
Note, this can be fixed by doing '$ME reedit $filepath'
		EOF
		return 0
	else
		return 1
	fi
}

wx_delget() {
	typeset -i force=0
	typeset arg comment_found=false

	while getopts :f arg; do
		case $arg in
			 f) force=1;;
			 *) fail "Invalid flag -$OPTARG. See 'wx help' for"\
			 	"more info.";;
		esac
	done

	if [[ (! -f SCCS/s.$file && ! -f SCCS/p.$file) || 
		(-f SCCS/s.$file &&  -f SCCS/p.$file) ]]; then

		# Check for keywords unless force is set or file is in
		# keywords.NOT
		if [[ $force -ne 1 ]] && [ -f SCCS/p.$file ] && 
			! grep -q "^$(escape_re $filepath)$" \
				$wxdir/keywords.NOT 2>/dev/null &&
			! keywords -p $file; then

			ring_bell
			cat <<-EOF

The keywords check has detected a problem with $filepath
If this check should be skipped for this file, put the filepath in
${wxdir}/keywords.NOT.
See /net/wizard.eng/export/misc/general_docs/keyword_info.txt
for more info about keywords.  Note, pay attention to the tabs.

			EOF
			yesno "Continue with $command for $filepath?" 
			if [[ "$answer" != 'yes' ]]; then
				echo "Aborting $command $filepath\n"
				return
			fi
		fi

		[[ -f $wxtmp/comment ]] && rm $wxtmp/comment
		if [[ -n "$comment_file" ]]; then
			# note hard tab in sed r.e.
			sed '/^[ 	]*$/d' $comment_file > $wxtmp/comment &&
			    comment_found=true
		else
			wx_show_comment >$wxtmp/comment && comment_found=true
		fi
		if $comment_found; then
			echo $filepath
			cat $wxtmp/comment
			if [[ -f SCCS/s.$file ]]; then
				# file history so check in file
				sccs delget $silent -y"`cat $wxtmp/comment`" \
					$file ||
					fail "sccs delget failed $filepath."
			else
				# no file history so create file
				sccs create $silent -y"`cat $wxtmp/comment`" \
					$file ||
					fail "sccs create $filepath failed."
				rm -f ,$file
			fi
			[[ -n "$comment_file" ]] && 
			    update_active_comment $filepath
		else
			ring_bell
			print "\nError: no comments (NO_COMMENT) registered for $filepath"
			if [[ $force -ne 1 ]] ; then
				yesno "Invoke ${EDITOR:-vi} to edit"\
					"$wxdir/active"'?'
				if [ "$answer" == 'yes' ]; then
					${EDITOR:-vi} $wxdir/active
					wx_delget
				else
					fail "Edit $wxdir/active and try again."
				fi
			else
				fail "Edit $wxdir/active and try again."
			fi
		fi
	elif [[ -f SCCS/s.$file && ! -f SCCS/p.$file ]]; then
		echo "$filepath already checked in"
	elif [[ ! -f SCCS/s.$file && -f SCCS/p.$file ]]; then
		fail "Error, $filepath is missing SCCS/s.$file ."
	fi
}

wx_get() {
	if [[ -f SCCS/s.$file ]]; then
		sccs get $args -s $file || fail "sccs get $file failed."
	else
		ring_bell
		echo "$filepath not in SCCS"
	fi
}

wx_info() {
	if [ -f SCCS/p.$file ]; then
		if [[ -w $file ]]; then
			echo "$filepath (checked out)"
		else
			ring_bell
			echo "$filepath (Warning, inconsistent state."
			echo "   SCCS/p.$file exists but $file is readonly.)"
		fi
	elif [[ ! -f $file ]]; then
		ring_bell
		echo "$filepath (Warning, not found in $workspace)"
	elif [[ ! -f SCCS/s.$file ]]; then
		ring_bell
		echo "$filepath (Warning, not in SCCS)"
	else
		echo "$filepath (checked in)"
	fi
	echo "Check-in comment:"
	wx_show_comment
	if [ -f SCCS/s.$file ]; then
		echo "Most recent delta: \c"
		sccs prt -y $file
	fi
	echo "File name status:"
	if renamed; then
		# old is set by renamed
		echo "Locally renamed, parent file = $parentfile"
	elif lookup_parent $filepath; then
		# parentfile is set by lookup_parent
		if [[ "$filepath" != "$parentfile" ]]; then
			echo "In parent ws, file renamed to: $parentfile"
		else
			echo "Same as parent."
		fi
	else
		echo "New file (does not exist in parent ws)."
	fi
	echo
}

get_multi_deltas() {
	# Get list of files with more that one delta when putback.
	# set global multi_delta_list.
	if ! deltachk >/dev/null 2>&1; then
		multi_delta_list="$multi_delta_list $filepath"
	fi
}

wx_reedit() {
	typeset -i numkids=`workspace children | wc -l`
	typeset i newfiles only_multideltas=false

	case $1 in
		-m)	only_multideltas=true;;
		-*)	fail "Invalid flag $1. See 'wx help' for more"\
		  		"info.";;
	esac

	if [[ ! -f $wsdata/nametable ]]; then
		echo "$wsdata/nametable not found, all files assumed new."
		ok_to_proceed "Okay to continue with $command?"
	elif [[ ! -r $wsdata/nametable ]]; then
		fail "Error: cannot read $wsdata/nametable."
	fi

	if $only_multideltas; then
		# get_multi_deltas sets multi_delta_list
		wx_eval get_multi_deltas
		# set file_list for wx_eval wx_reedit_file below
		file_list=$multi_delta_list
	fi

	cd $workspace

	for i in $file_list; do
		if [[ ! -f $i ]]; then
			fail "$i does not exist."
		fi
		if ! lookup_parent $i; then
			if [[ -z $newfiles ]]; then
				newfiles="$i"
			else
				newfiles="$newfiles\n$i"
			fi
		fi
	done
	
	if [[ -n $newfiles ]]; then
		# If there are some new files, give user a warning
		cat <<-EOF | ${PAGER:-more}

$ME thinks following files are new (not in parent workspace) and will
reset their file histories to version 1.1 (exit if this list isn't correct):
$(echo $newfiles)

		EOF
		ok_to_proceed "Okay to continue with $command?"
		if ! $CHECKIN; then
			cat <<-EOF

Hint, use '$ME redelget' to collapse/reset new file histories to version
1.1 since '$ME $command' will check out the file and '$ME delget' always
increments the file version doing the check in.

			EOF
		fi
	fi

	if [ $numkids -gt 0 ]; then
		echo "WARNING: This workspace has the following children:"
		echo
		workspace children
		echo
		echo "The reedit command will coalesce all children's deltas"
		echo "into one, losing all delta comments in the process."
		ok_to_proceed 'Are you sure you want to proceed?'
	fi
	echo
	yesno "Do you want to backup files first?"
	if [[ "$answer" == 'yes' ]]; then
		wx_backup || fail "Backup failed."
	fi

	echo "$command beginning..."
	echo
	wx_eval wx_reedit_file
	echo
	echo "$command complete"
	echo
	[[ $ACTSORT == sort ]] && do_active_sort=true
}

wx_reedit_file() {
	# Must be called via wx_eval
	typeset comment_found=false

	if [[ ! -f $file ]]; then
		echo "$file does not exist.  Can not reedit $file"
		return
	fi

	echo $filepath
	# Is there a parent file?
	if wx_pnt_filepath; then
		rm -f $wxtmp/s.$file
		cp -p $parentsdot $wxtmp/s.$file ||
			fail "Error: cannot cp $parentsdot $wxtmp/s.$file ."

		# get the latest parent delta and comment and filter out
		# certain fields removing trailing spaces

		p_delta="$(sccs prt -y $parentsdot|expand -1|grep 'SCCS'|\
				   cut -f'4,5,6,9-' -d' '|sed 's/  *$//')" 

		if [[ -z "$p_delta" ]]; then
			ring_bell
			echo "Warning ${command}: skipping $filepath,"
			echo "cannot get parent delta info"
			echo
			return 1
		fi

		# create a list of local deltas in the same format
		# also removing trailing spaces
		sccs prt $file|expand -1|
			nawk '
			/^D [0-9]+(\.[0-9]+)+ +[0-9][0-9]\/[0-9][0-9]/ {
				if (delta != "") {
					# print previous delta info
					print delta comment;
				}
				delta=sprintf("%s %s %s %s ",$3, $4, $5, $8);
				comment = "";
			}
			! /^D [0-9]+(\.[0-9]+)+ +[0-9][0-9]\/[0-9][0-9]/ {
				# Add comment lines to comment variable
				if (comment == "") {
					if (NF > 0) {
						comment = $0;
					} else {
						# empty lines require a space
						# in comment.
						comment = " ";
					}
				} else {
					if (NF > 0) {
						comment = comment " " $0;
					} else {
						comment = comment " ";
					}
				}
			}
			END {
				if (delta != "") {
					# print last delta info
					print delta comment;
				}
			}' | sed 's/  *$//' > $wxtmp/l_deltas ||
			    	fail "Error: cannot create $wxtmp/l_deltas."

		# If the latest parent delta doesn't appear in the local file
		# then a bringover is required.  Use fgrep because comment may
		# have RE chars in it.
		if ! fgrep "$p_delta" $wxtmp/l_deltas >/dev/null; then
			ring_bell
			echo "\nWarning ${command}: skipping $filepath because:"
			echo "parent's version of $filepath"
			echo "is newer than child's -- bringover required."
			echo
			return 1
		fi

		if [ ! -f SCCS/p.$file ]; then
			if sccs edit $silent $file; then
				update_active $filepath
			else
				fail "sccs edit $file failed."
			fi
		fi

		# make copy of local file and copy parent's SCCS s. file over
		# local.
		mv -f $file ${file}.wx_reedit ||
			fail "mv -f $file ${file}.wx_reedit failed."
		rm -f SCCS/s.$file SCCS/p.$file
		cp $wxtmp/s.$file SCCS/s.$file ||
			fail "cp $wxtmp/s.$file SCCS/s.$file failed."

		if sccs edit $silent $file; then
			update_active $filepath
		else
			fail "sccs edit $file failed."
		fi

		mv -f ${file}.wx_reedit $file ||
			fail "mv -f ${file}.wx_reedit $file failed."

		if $CHECKIN; then
			wx_delget
		fi
		touch $file
	else
		# reediting a new file.
		if [[ -f SCCS/s.$file ]]; then
			if [[ ! -f SCCS/p.$file ]]; then
				# File needs to be checked out
				sccs edit $silent $file ||
				    fail "sccs edit $file failed."
			fi
			# clean up SCCS since we are going to create again.
			rm -f SCCS/s.$file
		fi
		# clean up SCCS since we are going to create again.
		[[ -f SCCS/p.$file ]] && rm -f SCCS/p.$file

		[[ -f $wxtmp/comment ]] && rm $wxtmp/comment
		wx_show_comment >$wxtmp/comment && comment_found=true
		if $comment_found; then
			echo $filepath
			cat $wxtmp/comment
			rm -f SCCS/s.$file SCCS/p.$file
			sccs create $silent -y"`cat $wxtmp/comment`" $file ||
			    fail "sccs create $filepath failed."
			rm -f ,$file
			[[ -n "$comment_file" ]] && 
			    update_active_comment $filepath
		else
			ring_bell
			echo "\nError, no comments registered for $filepath"
			if [[ $force -ne 1 ]] ; then
				yesno "Invoke ${EDITOR:-vi} to edit"\
					"$wxdir/active"'?'
				if [[ "$answer" == 'yes' ]]; then
					${EDITOR:-vi} $wxdir/active
					wx_reedit_file
				else
					fail "Edit $wxdir/active and try again."
				fi
			else
				fail "Edit $wxdir/active and try again."
			fi
		fi

		if $CHECKIN; then
			# No need to check out file.
			return
		fi

		if sccs edit $silent $file; then
			update_active $filepath
			add_new $filepath
		else
			fail "sccs edit $file failed."
		fi
	fi
}

#
# Warn if there are sccs delta issues
#
deltachk() {
	# must be run via wx_eval
	typeset -i numdeltas
	typeset newfile checkedout=false

	if wx_pnt_filepath; then
		# find number of deltas by subtracting the number in the parent
		# from the local file (note the literal Control-A in the grep
		# R.E.s below).
		(( numdeltas = $(grep '^d D' SCCS/s.$file|wc -l) - \
				    $(grep '^d D' $parentsdot|wc -l) ))
		newfile=false
	else
		# checking a new file (note the literal Control-A in the grep
		# R.E.)
		numdeltas=$(grep '^d D' SCCS/s.$file|wc -l)
		newfile=true
	fi

	if [[ -z $numdeltas ]]; then
		cat <<-EOF

Warning: the local file:
$filepath
does not appear to have a sccs delta history file or the sccs delta
history file is corrupt.  If the local file is new try using:
"cd $dir"
"$ME create $file"

If the file is not new (exists in parent):
"cd $dir"
Save a copy of the local file
Remove the SCCS/[ps].$file history files
"bringover $filepath"
"$ME edit $file"
Then carefuly merge the saved copy of local file with the
file brought over from parent.  Hint: twmerge is a good merge 
tool.
		EOF
		return 1
	fi

	[[ -f SCCS/p.$file ]] && checkedout=true

	# Note the use of hard tabs in the messages
	case $numdeltas in
		0)	if $checkedout; then
                                # file is checked out so assume there
                                # will be 1 delta when checked in.
				return 0 
			else
				if [[ -n $parentfilepath ]];  then
					if cmp -s $file $parentfilepath; then
						cat <<-EOF

Warning: the local file:
$filepath
and the parent file:
$parentfilepath
content are identical.  There are no new deltas in the local file.
If this file is no longer required in the active list use:
"cd $dir"
"$ME reset $file"
to remove file from the wx state files (active list, etc...)
						EOF
					else
						cat <<-EOF

Warning: the local file:
$filepath
and the parent file:
$parentfilepath
have the same number of deltas but contents differ.  A bringover may be
required before putback.
						EOF
					fi
				else
					cat <<-EOF

Warning: the local file:
$filepath
is new but doesn't appear to contain any deltas.  The SCCS delta history file
may need to be recreated.  If so: 
"cd $dir"
"rm SCCS/s.$file"
"$ME create $file"
					EOF
				fi
				return 1 
			fi ;;

		1)	if $checkedout; then
				cat <<-EOF

Regarding $filepath
Warning! There may be more than 1 delta when you check this file in
(currently checked out).  Run '$ME redelget' on this file to collapse
the deltas and check in with 1 delta unless putting back > 1 bug.
				EOF
				return 1 
			else
				return 0 
			fi ;;

		-*) # a negative number means the parent has more deltas
			
			cat <<-EOF

Regarding $filepath
Warning! The parent file has more deltas than the local file.
You should bringover the local file to fix this.
			EOF
			;;

		*)	if $newfile && $checkedout; then
				cat <<-EOF

Regarding $filepath
Warning! There may be more than 1 delta when you check this file in
(currently checked out).  Run '$ME redelget' on this file to collapse
the deltas and check in with 1 delta unless putting back > 1 bug.
				EOF
			else
				cat <<-EOF

Regarding $filepath
Warning! There is more than 1 delta.  Run:
'cd $dir; $ME redelget $file' 
to collapse the deltas on this file and check in with 1 delta unless
putting back > 1 bug.
				EOF
			fi
			return 1;;
	esac
}

wx_cstyle() {
	case $file in
		*.[ch])	;;
		*)	return;;
	esac
	((CSTYLE_INDEX = CSTYLE_INDEX + 1))
	(cd $workspace;
	 cstyle ${CSTYLE_FLAGS} $args $filepath >\
	    $wxtmp/wx.cstyle.$CSTYLE_INDEX) &
}

wx_jstyle() {
	case $file in
		*.java)	;;
		*)	return;;
	esac
	((JSTYLE_INDEX = JSTYLE_INDEX + 1))
	(cd $workspace;
	 jstyle ${JSTYLE_FLAGS} $args $filepath >\
	    $wxtmp/wx.jstyle.$JSTYLE_INDEX) &
}

wx_find_compression_progs() {
	gzip=/usr/bin/gzip
	if [[ ! -x $gzip && -n "$GZIPBIN" ]]; then
		gzip=$GZIPBIN
	fi

	bzip2=/usr/bin/bzip2
	if [[ ! -x $bzip2 && -n "$BZIP2BIN" ]]; then
		bzip2=$BZIP2BIN
	fi
}

wx_get_backup_dir() {
	typeset backup_dir_file
	# if backup_dir hasn't been set already...
	if [[ -z "$backup_dir" ]]; then
		# use the backup dir specifier in the wx/
		backup_dir_file=$wxdir/backup_dir
		if [[ ! ( -f $backup_dir_file && -r $backup_dir_file && 
			-s $backup_dir_file ) ]]; then
			fail "$backup_dir_file: missing, empty, or not readable"
		fi
		backup_dir=`cat $backup_dir_file`
	fi
	if [[ ! ( -d $backup_dir && -x $backup_dir && -r $backup_dir ) ]]; then
		fail "$backup_dir: missing, not a directory, or bad permissions"
	fi
}

#
# This code requires that the files (n.sdot, n.pdot and n.clear) for a given
# backup have the same extension (.tar, .tar.gz, or .tar.bz2).  It also
# disallows the existance of two incarnations of the same file (i.e.
# n.clear.tar and n.clear.tar.gz)
#
# It's up to the user to straighten things out if the above conditions are
# violated.  The only time that is a problem is if they are trying to
# restore a version which violates the above rules.
#
#  Takes one argument, the version number.
#
#  Returns:
#	(return code)	0 if exists and consistent,
#			1 if not found,
#			2 if inconsistent
#	b_clear, b_sdot, b_pdot	On success, the full path to the clear, sdot
#	and pdot files comp, ext	The compression program for and
#	extension of said files
#
wx_check_backup() {
	typeset _new _b_new _renamed _b_renamed _active _b_active \
		_local_nt _b_local_nt found bad

	_version=$1
	clear=$_version.clear.tar
	sdot=$_version.sdot.tar
	pdot=$_version.pdot.tar
	not=$_version.not.tar
	_renamed=$_version.renamed
	_b_renamed=$backup_dir/$_renamed
	_new=$_version.new
	_b_new=$backup_dir/$_new
	_active=$_version.active
	_b_active=$backup_dir/$_active
	_local_nt=$_version.local_nametable
	_b_local_nt=$backup_dir/$_local_nt
	_sort=$_version.sort_active
	_b_sort=$backup_dir/$_sort
	found=false
	bad=false
	#
	# these arrays must be in sync with:
	# 1. the immediately following _count variable
	# 2. wx_find_last_backup's egrep expression
	# 3. wx_backup's "$args" handling.
	# 4. wx_find_compression_progs's programs
	#
	set -A _comps	""  "$gzip" "$bzip2"
	set -A _extns	""  ".gz"   ".bz2"
	_count=3

	idx=0
	while [[ $idx -lt $_count ]] ; do
		_ext=${_extns[$idx]}
		_comp=${_comps[$idx]}
		_clear=$clear$_ext
		_sdot=$sdot$_ext
		_pdot=$pdot$_ext
		_b_clear=$backup_dir/$_clear
		_b_sdot=$backup_dir/$_sdot
		_b_pdot=$backup_dir/$_pdot

		if [[ -f $_b_clear || -f $_b_sdot ]]; then
			if $found; then
				echo "$backup_dir: both $_version.*.tar$ext "\
					 "and $_version.*.tar$_ext exist"
				bad=true
			else
				ext=$_ext
				comp=$_comp
				found=true
			fi
		fi

		if [[ -f $_b_clear && ! -f $_b_sdot ]]; then
			echo "$backup_dir: $_clear exists; $_sdot does not"
			bad=true
		elif [[ ! -f $_b_clear && -f $_b_sdot ]]; then
			echo "$backup_dir: $_sdot exists; $_clear does not"
			bad=true
		elif [[ ! -f $_b_sdot && -f $_b_pdot ]]; then
			echo "$backup_dir: $_pdot exists; $_sdot does not"
			bad=true
		fi
		idx=`expr $idx + 1`
	done

	if [[ ! -f $_b_renamed && -f $_b_active ]]; then
		# Can determine compression only
		return 1
	fi
	
	if [[ -f $_b_renamed && -f $_b_active && -f $_b_new &&
		-f $_b_local_nt ]]; then
		found=true
	else
		bad=true
	fi

	$bad && return 2
	$found || return 1

	b_renamed=$_b_renamed
	b_new=$_b_new
	b_active=$_b_active
	b_local_nt=$_b_local_nt

	if [[ -f $backup_dir/$clear$ext && -f $backup_dir/$sdot$ext ]]; then
		b_clear=$backup_dir/$clear$ext
		b_sdot=$backup_dir/$sdot$ext
	else
		b_clear=
		b_sdot=
	fi

	# It's not an error if this doesn't exist.
	if [[ -f $backup_dir/$pdot$ext ]]; then
		b_pdot=$backup_dir/$pdot$ext
	else
		b_pdot=
	fi
	# It's not an error if this doesn't exist.
	if [[ -f $backup_dir/$not ]]; then
		b_not_files=$backup_dir/$not
	else
		b_not_files=
	fi
	# It's not an error if this doesn't exist.
	if [[ -f $_b_sort ]]; then
		b_sort=$_b_sort
	else
		b_sort=
	fi

	return 0
}

#
# finds the number of the last backup.
#
# Returned in $result, which is -1 if no backups are found
#
wx_find_last_backup() {
	#
	# The list of extensions in the egrep expression must be in sync
	# with wx_check_backup's arrays
	#
	result=`ls -1 $backup_dir | egrep \
	    '^[0-9][0-9]*\.((pdot|sdot|clear)\.tar($|\.gz$|\.bz2$)|active|renamed|new|local_nametable$)'| \
	    sed 's/^\([0-9][0-9]*\)\..*$/\1/'| sort -rn | head -1`

	[[ -n "$result" ]] # fail if result is empty
}

#
# wx_do_backup
# Returns 0 on successful backup, 1 if nothing to backup, 2 any other
# error.
#

wx_do_backup() {
	_type=$1	# type of files (for user)
	_out=$2		# file to write to
	_comp="$3"	# compression program, or empty for no compression
	_evalarg=$4	# arg to wx_eval to get the correct file list
	typeset backupfiles=$(wx_eval "$_evalarg")

	echo
	echo "Saving $_type files to $_out"
	echo

	if [[ -z $backupfiles ]]; then
		echo "Note, nothing to backup."
		return 1
	fi

	if [[ -n "$_comp" ]]; then
		( tar cvf - $backupfiles 2>$BACKUP_ERRORS || \
		    rm -f $_out ) | $_comp -9 -c > $_out || rm -f $_out
	else
		tar cvf $_out $backupfiles 2>$BACKUP_ERRORS || 
		    rm -f $_out
	fi

	[[ -f "$_out" ]] || return 2	# $_out is removed on any error

	return 0
}

wx_do_restore() {
	_type=$1		# type of files (for user)
	_in=$2		  # file to read from
	_comp="$3"	  # uncompressing program
	echo
	echo "Restoring $_type files from $_in"
	echo

	if [[ -n "$_comp" ]]; then
		#
		# if decompression fails, echo a bad value to make tar fail
		#
		($_comp -dc < $_in || echo "fail") | tar xvpf - || return 1
	else
		tar xvpf $_in || return 1
	fi
	return 0
}

#
# do renames in a workspace from a backup set
#

wx_do_renames() {
	typeset _in=$1 # file to read from

	if [[ ! -f $wsdata/nametable ]]; then
		echo "$wsdata/nametable not found, not doing renames."
		return 0
	fi

	echo
	echo "Restoring renamed files from $_in"
	echo

	# Note this assumes we're staring in $workspace

	while read new hash1 hash2 hash3 hash4; do
		# get current local file name
		current=$(grep " $hash1 $hash2 $hash3 $hash4$" \
				$wsdata/nametable | cut -f1 -d' ')

		if [[ -z $current ]]; then
			# nothing to rename
			continue
		fi

		if [[ "$new" == "$current" ]]; then
			# rename not needed
			continue
		fi

		if [[ ! -f $new ]]; then
			if [[ ! -d $(dirname $new) ]]; then
				mkdir -p $(dirname $new) ||
					fail "Error: cannot create dir $(dirname $new)"
			fi
			echo "Renaming current workspace file $current to $new"
			workspace filemv $current $new
		else
			if [[ -f $current ]]; then
				ring_bell
				cat >&2 <<-EOF

Warning: $current 
and $new 
files both exist in current workspace with the
same hash.  The restored renamed list should be recreated by running:
'$ME update -r'
Skipping rename of $current 
to $new
				EOF
				
			fi
		fi 
	done < $_in

	return 0 
}

wx_backup() {
	typeset orig_file_list ws_file back_file
	typeset newer=false
	typeset origdir=$PWD

	case $1 in
		-i)	wx_get_backup_dir
			echo "Backup dir is $backup_dir"
			ls -ltr $backup_dir
			echo "Backup dir is $backup_dir"
			cd $origdir
			return ;;
		-t) 	newer=true
                        # backup if wx files are newer than last backup.
                        # Implies use of default compression and no
                        # interaction.  Doing shift so case further down
                        # won't see -t.
			shift;; 
	esac
	# save state in case wx_backup called from another command.
	orig_file_list=$file_list

	# we always backup the active files.
	file_list=$(wx_active)

	if [[ ! -s $wxdir/renamed && -z $file_list ]]; then
		echo "There isn't anything to backup."
                file_list=$orig_file_list
		return 0
	fi

	# must be in workspace to do backup
	cd $workspace || fail "Error: cannot cd $workspace"

	if $newer; then
		# get latest wx state files and active files but skip
		# wx/tmp and wx/*.old files.
		ws_file=$(ls -1t $wxdir/!(tmp|*.old) $file_list|head -1)
		# get latest backup.
		wx_get_backup_dir
		back_file=$(ls -1t $backup_dir/*|head -1)
		if [[ ( -z "$back_file" && -n "$ws_file" ) || \
			(( -n "$back_file" && -n "$ws_file" ) && \
			$ws_file -nt $back_file ) ]]
		then
			: # continue with backup
		else
			print "Skipping backup, last backup newer than wx"\
				"files."
                        file_list=$orig_file_list
			cd $origdir
			return 0
		fi
	fi

	wx_find_compression_progs
	wx_get_backup_dir

	if [[ ! -w $backup_dir ]]; then
		fail "$backup_dir: not writable"
	fi

	if wx_find_last_backup; then
		prev_backup=$result
		version=`expr $result + 1`
	else
		prev_backup=
		version=0
	fi

	#
	# This must be in sync with wx_check_backup's arrays
	#
	case $1 in
		-n) ext=;	comp=;;
		-z) ext=.gz;	comp=$gzip;;
		-b) ext=.bz2; comp=$bzip2;;
		"-") shift;; # treat this as use default compression
		"") ;; # treat this as use default compression
		-??*) fail "$ME $command: only accepts a single argument";;
		*)  fail "$ME $command: unrecognized argument";;
	esac

	if [[ -z "$1" ]]; then
		#
		# default to the compression of the previous backup
		#
		if [[ -z "$prev_backup" ]]; then
			ext=
			comp=
		else 
			wx_check_backup $prev_backup
			# A return of 1 is okay
			if [ $? -gt 1 ]; then
				echo "$backup_dir/$prev_backup.*: "\
					"cannot determine previous "\
					"compression."
				if $newer; then
                                        # Assume we want backup.
					answer="yes"
				else
					yesno "Proceed with no "\
						"compression?"
				fi
				if [[ $answer == "no" ]]; then
					echo "No backup done."
                                        file_list=$orig_file_list
					cd $origdir
					return
				fi
				ext=
				comp=
			fi
		fi
	fi

	if [[ -n "$comp" && ! -x "$comp" ]]; then
		echo "${comp}: missing.  defaulting to no compression"
		ext=
		comp=
	fi

	b_clear=$backup_dir/$version.clear.tar$ext
	b_sdot=$backup_dir/$version.sdot.tar$ext
	b_pdot=$backup_dir/$version.pdot.tar$ext
	b_local_nt=$backup_dir/$version.local_nametable
	b_active=$backup_dir/$version.active
	b_renamed=$backup_dir/$version.renamed
	b_new=$backup_dir/$version.new
	b_not_files=$backup_dir/$version.not.tar
	b_sort=$backup_dir/$version.sort_active

	#
	# If anything goes wrong, clean up after ourselves
	#
	trap "/usr/bin/rm -f $b_clear $b_sdot $b_pdot $b_active $b_renamed $b_new $b_local_nt $b_not_files $b_sort; exit 1" 0 1 2 3 15

	fail_msg='failed.  Cleaning up. '

	#
	# It is not a hard error for the SCCS/s.file to be missing.  We just
	# let the user know what's going on.
	#
	sdot_cmd='
_sdot="SCCS/s.$file";
_file="$dir/$_sdot";
if [[ -f $_sdot ]]; then
	echo "$_file";
else
	echo "$_file: not found" >&2;
fi
'
	pdot_cmd='
_pdot="SCCS/p.$file";
_sdot="SCCS/s.$file";
_file="$dir/$_pdot";
if [[ -f $_pdot ]]; then
	echo "$_file";
elif [[ ! -f $_sdot ]]; then
	echo "$_file: not checked in" >&2;
elif [[ -w $file ]]; then
	echo "$_file: not found but $file is writable!" >&2;
fi
'
	# Do this first in case there are no active files
	echo
	echo "Saving renamed file list to $b_renamed"
	echo
	cp $wxdir/renamed $b_renamed || fail "$b_renamed: $fail_msg"

	if [[ -f $wxdir/local_nametable ]]; then
		echo
		echo "Saving local_nametable to $b_local_nt"
		echo
		cp $wxdir/local_nametable $b_local_nt || \
			fail "$b_local_nt: $fail_msg"
	fi

	if [[ -f $wxdir/sort_active ]]; then
		print
		print "Saving sort_active to $b_active"
		print
		cp $wxdir/sort_active $b_sort || fail "$b_sort: $fail_msg"
	fi

	if ls wx/*.NOT >/dev/null 2>&1; then
		echo
		echo "Saving .NOT files to $b_not_files"
		echo
		tar -cf $b_not_files wx/*.NOT || fail "$b_not_files: $fail_msg"
	fi

	# Are there any active files to backup?
	if [[ -n $file_list ]]; then
		wx_do_backup 'clear' $b_clear "$comp" 'echo $filepath' ||
			fail "$b_clear: $fail_msg"

		wx_do_backup 'sdot' $b_sdot "$comp" "$sdot_cmd" ||
			fail "$b_sdot: $fail_msg"

		echo
		echo "Saving new list to $b_new"
		echo
		cp $wxdir/new $b_new || fail "$b_new: $fail_msg"

		# It's not fatal if the backup error for pdot files is
		# 'no files to backup'.  This is because it's possible
		# that the active files aren't checked out so there
		# won't be any pdot files.
		wx_do_backup 'pdot (if any)' $b_pdot "$comp" "$pdot_cmd" 
		if [[ $? -gt 1 ]]; then
			fail "$b_pdot: $fail_msg $(cat $BACKUP_ERRORS)"
		fi
	fi
	
	echo
	echo "Saving active file list to $b_active"
	echo
	cp $wxdir/active $b_active || fail "$b_active: $fail_msg"

	trap - 0 1 2 3 15

	rm -f $BACKUP_ERRORS

	# restore file_list state.
	file_list=$orig_file_list

	cd $origdir
	return 0
}

wx_restore() {
	typeset force=0

	case $1 in
		-f) force=1;;
		-*) fail "Invalid flag $1. See 'wx help' for more  info.";;
	esac

	if [[ $force -eq 0 ]]; then
		cat <<-EOF

Warning, the restore command will overwrite several files including the
active and renamed lists.  This could be a problem if you have made
changes to your workspace and $ME related files following the last
backup.  It may be a good idea to run:

$ME update

after the restore so that the active and renamed lists are updated with
the new changes in the workspace.

Also, restore may perform workspace renames in this workspace if it
finds that the existing file has a pathname that differs from that in
the backup being restored.

		EOF
		ok_to_proceed "Do you really want to do the restore?"
	fi

	wx_find_compression_progs
	wx_get_backup_dir

	if wx_find_last_backup; then
		version=$result
	else
		fail "$backup_dir: no backups found"
	fi

	if [[ $force -eq 0 ]]; then
		ask 'Version to restore from' $version
		version=$answer
	fi

	#
	# wx_check_backup sets $comp, $b_clear, and $b_sdot when successful
	#
	if wx_check_backup $version; then
		:
	else
		if [[ $? -eq 2 ]]; then
			fail "$backup_dir/$version.*: unable to restore"\
				"inconsistent version"
		else
			fail "$backup_dir: Unable to find version $version"
		fi
	fi

	b_active=$backup_dir/$version.active

	if [[ -n "$comp" && ! -x "$comp" ]]; then
		fail "${comp}: missing -- cannot decompress $b_clear"
	fi

	# must be in workspace to do restore
	cd $workspace || fail "Error: cannot cd $workspace" 

	[[ -f $b_renamed ]] || fail "$b_renamed: missing"
	[[ -f $b_new     ]] || fail "$b_new: missing"
	[[ -f $b_active  ]] || fail "$b_active: missing"

	[[ -r $b_renamed ]] || fail "$b_renamed: not readable"
	[[ -r $b_new     ]] || fail "$b_new: not readable"
	[[ -r $b_active  ]] || fail "$b_active: not readable"

	if [[ -f $b_clear ]]; then
		[[ -r $b_clear ]] || fail "$b_clear: not readable"
	fi
	if [[ -f $b_sdot ]]; then
		[[ -r $b_sdot ]] || fail "$b_sdot: not readable"
	fi
	if [[ -f $b_pdot ]]; then
		[[ -r $b_pdot ]] || fail "$b_pdot: not readable"
	fi
	if [[ -f $b_local_nt ]]; then
		[[ -r $b_local_nt ]] || fail "$b_local_nt: not readable"
	fi
	if [[ -f $b_not_files ]]; then
		[[ -r $b_not_files ]] || fail "$b_not_files: not readable"
	fi
	if [[ -f $b_sort ]]; then
		[[ -r $b_sort ]] || fail "$b_sort: not readable"
	fi

	#
	# If something goes wrong, we need to make sure they notice, so
	# we make the message quite visible, and echo a BELL.
	#
	fail_msg='Extraction failed.

	*DANGER* *DANGER* workspace could be corrupted *DANGER* *DANGER*'

	cp $b_renamed $wxdir/renamed || fail "$wxdir/renamed: $fail_msg"
	cp $b_new $wxdir/new || fail "$wxdir/new: $fail_msg"
	cp $b_active $wxdir/active || fail "$wxdir/active: $fail_msg"
	cp $b_local_nt $wxdir/local_nametable ||
		fail "$wxdir/local_nametable: $fail_msg"
	if [[ -n $b_sort ]]; then
		cp $b_sort $wxdir/sort_active || \
			fail "$wxdir/sort_active: $fail_msg"
	fi

	# Need to move active files that are renamed in current ws back to
	# their name in the active list to avoid two copies of the file
	# occuring when the clear files are restored below.
	wx_do_renames $wxdir/local_nametable ||
		fail "$wxdir/local_nametable: $fail_msg"

	if [[ -n $b_not_files ]]; then
		tar -xf $b_not_files || fail "$wx/*.NOT: $fail_msg"
	fi
	# It's not an error if there is no clear backup.
	if [[ -f $b_clear ]]; then
		wx_do_restore "clear" $b_clear "$comp" ||
		    fail "$b_clear: $fail_msg"
	fi
	# It's not an error if there is no sdot backup.
	if [[ -f $b_sdot ]]; then
		wx_do_restore "sdot" $b_sdot "$comp" ||
			fail "$b_sdot: $fail_msg"
	fi
	# It's not an error if there is no pdot backup.
	if [[ -f $b_pdot ]]; then
		wx_do_restore "pdot" $b_pdot "$comp" ||
			fail "$b_pdot: $fail_msg"
	fi

	# Do some integrity checking
	for filepath in $(wx_active); do
		if cd ${workspace}/$(dirname $filepath); then
			file=$(basename $filepath)

			# If file is not writable then assume the
			# SCCS/p.file is bogus.  This can happen if a
			# file is checked out and a wx restore is done
			# and the restored file was not checked out when
			# it was backed up.

			if [[ ! -w $file && -f SCCS/p.$file ]]; then
				ring_bell
				cat <<-EOF

Warning! $filepath is in inconsistent state.
$filepath is not writable and SCCS/p.$file exists.
Removing SCCS/p.$file 
To edit the file run '$ME edit $filepath'
				EOF
				rm -f SCCS/p.$file
			elif [[ -w $file && ! -f SCCS/p.$file ]]; then
				ring_bell
				cat <<-EOF

Warning! $filepath is in inconsistent state.
$filepath is writable 
but there is no SCCS/p.$file

				EOF
				yesno "Should this file be checked out?"
				if [[ "$answer" == 'yes' ]]; then
					if mv $file $wxtmp; then
						if sccs edit $file; then
							update_active $filepath
						fi
						mv -f $wxtmp/$file $file
					fi
				else
					ask_remove_active_entry 
					echo "Setting $filepath read only"
					chmod ugo-w $file
				fi
			fi
		else
			ring_bell
			echo "\nWarning! Could not check sccs state of "\
			    "$filepath"
		fi
	done
}

wx_fullreview() {
	if wx_pnt_filepath; then
		:
	else
		parentfilepath=/dev/null
	fi
	if $show_comments && wx_show_comment > $wxdir/comment; then
		comm=-y"`cat $wxdir/comment`"
		codereview "$comm" $args $parentfilepath $workspace/$filepath
	else
		codereview $args $parentfilepath $workspace/$filepath
	fi
}

#
# Check on RTI status for bug ID's found in active list comments.
#

wx_rtichk() {
	# gate contains the gate dir, not full path
	typeset gate=${parent##*/}
	typeset -i rc=0
	typeset nolookup opt

	if [[ -f $wxdir/rtichk.NOT ]]; then
		print "\nSkipping RTI check:"
		return
	else
		print "\nDoing RTI check:"
	fi

	while getopts :N opt; do
		case $opt in
			N) nolookup='-N' ;;
			*) fail "Invalid flag -$OPTARG." ;;
		esac
	done

	# Note, rtichk needs a gate arg to correctly determine status.
	if [[ -z $gate ]]; then
		cat >&2 <<-EOF
Warning: cannot find a parent gate, skipping RTI checking.
		EOF
	fi

	# Use wx_summary to output bug ID's in active list comments,
	# redirecting warnings about non-bug ID's to file for later use.
	set -A bugs $(wx_summary -ao $nolookup 2>$wxtmp/bugwarnings|cut -f1 -d' ')
	rtichk -g $gate ${bugs[@]}
	rc=$?

	if [[ -s $wxtmp/bugwarnings ]]; then
		cat <<-EOF

There are issues with the bug ID format in the 
$wxdir/active file.
Please fix the following and run rtichk again:

		EOF
		cat $wxtmp/bugwarnings
		((rc = 1))
	fi
	if [[ ${#bugs} -eq 0 ]]; then
		print "\nWarning: no bug ID's in active list."
	fi
	return $rc
}

#
# Do a Teamware putback of active and renamed files.
#
wx_putback() {
	# Use pbargs array to store Teamware putback args.
	# verbatim is for -v verbatim flag which doesn't get passed to
	# putback.
	set -A pbargs
	typeset i verbatim pbfiles narg=false force=false
	typeset nolookup=false

	if $FILES_PASSED; then
		# use the user specified files
		pbfiles=$file_list
	else
		# use the pblist (active and renamed)
		pbfiles=$(wx_active -p)
	fi

	while getopts :fnp:qvN i; do
		case $i in
			# Force the putback (no user interaction)
			f)	force=true;;

			n)	narg=true
				pbargs[${#pbargs[@]}]="-$i" ;;

			q)	pbargs[${#pbargs[@]}]="-$i" ;;

			p)	pbargs[${#pbargs[@]}]="-$i" 
				pbargs[${#pbargs[@]}]="$OPTARG" 
				# setting parent for user prompt below
				parent="$OPTARG" ;; 

				# -v doesn't get passed to putback.
			v)	verbatim='-v';;

			N)	nolookup='-N';;

		  	*)	fail "Invalid flag -$OPTARG. See 'wx help'"\
					"for more info.";;
		esac
	done

	if ! $narg; then
		# real putback

		# get pb comments, will be used later.
		if ! wx_summary -p $verbatim $nolookup >$wxtmp/pb_comments; then
			# Fail if comments have problems.
			fail "\nError, improper comments found. Use -v"\
				"to bypass this check."
		fi

		if ! $force; then
			# not force so give more warning.
			( # using subshell to capture stdout to file.
			cat <<-EOF
Remember to run '$ME pbchk' before doing a final putback (esp. if
doing a putback to an official ON gate).  Make sure your workspace 
parent ($parent) is correct.  
It's a good idea to check your code diffs before putback ('$ME pdiffs').

Also, run '$ME $command -n' and check for conflicts before doing the
final putback.
			EOF

			if [[ -z "$(wx_summary -bo 2>/dev/null)" ]]; then
				cat <<-EOF

Don't forget the ARC ID info in your active list comments if there is an
ARC case associated with your putback.
				EOF
			fi

			echo "\nThe putback comment will be:"
			cat $wxtmp/pb_comments
			print "========== End of putback comments ======="

			# Output warning if RTI isn't approved.
			wx_rtichk $nolookup
			print "========== End of RTI check output ======="

			cat <<-EOF

The following files will be used for putback:
$pbfiles

			EOF
			) | ${PAGER:-more}

			ok_to_proceed "Do you really want to"\
				"'$PUTBACK ${pbargs[@]}' to $parent?"
		fi
	fi

	# Do the putback, passing in active list comments if required.
	# putback both active and renamed/deleted files.
	cd $workspace
	if $narg; then
		# Don't use putback comment if -n is given (trial putback)
		$PUTBACK "${pbargs[@]}" $pbfiles
	else
		# feed active list comments into real putback
		wx_summary $verbatim $nolookup |$PUTBACK "${pbargs[@]}" $pbfiles
	fi
	return
}

outchk() {

	# List files that are checked out but not in active list.
	typeset outfile do_header=true

	wx_checked_out >/dev/null
	# if $wxtmp/checked_out is 0 bytes then return
	[[ -s $wxtmp/checked_out ]] || return

	sort $wxtmp/checked_out > $wxtmp/co_sort
	wx_active | sort > $wxtmp/active_sort
	for outfile in $(comm -12 $wxtmp/active_sort $wxtmp/co_sort); do
		if $do_header; then
			echo "\nWarning, the following active list files are"\
			    "checked out:"
			do_header=false
		fi
		echo "$outfile"
	done
	do_header=true
	for outfile in $(comm -13 $wxtmp/active_sort $wxtmp/co_sort); do
		if $do_header; then
			cat <<-EOF

Warning, the following files are checked out but not in active list
(Run "$ME update -q" to add them to the active list): 
			EOF
			do_header=false
		fi
		echo "$outfile"
	done
	rm -f $wxtmp/co_sort $wxtmp/active_sort
}

#
# run Teamware resolve and do reedit only on merged files.
#
wx_resolve() {
	typeset merged_file

	# clear the file_list, will be set below
	file_list=

	grep -v '^VERSION ' $wsdata/conflicts > $wxtmp/conflicts
	[[ ! -f $wxtmp/conflicts ]] && fail "Error: cannot create $wxtmp/conflicts"

	# run Teamware resolve
	resolve $* || fail "Teamware resolve failed."

	# resolve will remove files from $wsdata/conflicts when
	# successfully merged.

	# set file_list to files that were merged.
	for merged_file in $(cat $wxtmp/conflicts); do
		if ! grep -q '^'"$(escape_re $merged_file)"'$' \
		    $wsdata/conflicts; then
			# set file_list for wx_eval later.
			file_list="$file_list $merged_file"
		fi
	done

	if [[ -n $file_list ]]; then
		ok_to_proceed "Re-edit merged files to collapse merge deltas?"
		echo "Re-editing merged files"
		echo
		wx_eval wx_reedit_file
		echo
		echo "Re-edit complete"
		echo
	else
		echo "No merged files to re-edit."
	fi
}

#########################################################################
#
# Main
#

#
# Do some initial sanity checking and set up.
#

# Set the lang to standard English so wx doesn't get confused. 
export LC_ALL=C

# Turn on debugging output early
if [[ "$*" == *' -D'*( *) ]]; then
	typeset -ft $(typeset +f)
	set -x
fi

ME=$(basename $0)
export ME

if [[ -d /usr/xpg4/bin ]]; then
	# Want to use xpg4 versions of fgrep and grep
	PATH=/usr/xpg4/bin:/usr/bin:/usr/sbin:/usr/ccs/bin:$PATH
else
	fail "Error: directory /usr/xpg4/bin not found."
fi

unset CDPATH	# if set "cd" will print the new directory on stdout
		# which screws up wx_eval.

DEFAULT_SRCDIR=usr

if [[ $# -eq 0 || "$1" == help ]]; then
	# output usage now to avoid unnecessary checking below.
	wx_usage
fi

if [[ "$1" == version ]]; then
	# output version now to avoid unnecessary checking below.
	version
	exit 0
fi

#
# Check to make sure we're not being run from within a Mercurial repo
#
if hg root >/dev/null 2>&1; then
	fail "Error: wx does not support Mercurial repositories.\n"\
"Please see http://opensolaris.org/os/community/tools/hg"
fi

whence workspace >/dev/null || fail "Error: cannot find workspace command in \$PATH."

# Note, PUTBACK can be set to "cm_env -g -o putback" to use Casper Dik's
# turbo-dir.flp scripts to speed up thorough updates.
PUTBACK=${PUTBACK:='putback'}
BRINGOVER=${BRINGOVER:='bringover'}

dot=$PWD

if [[ -n "$CODEMGR_WS" ]]; then
	# ws was used.
	# normalize the workspace name.
	workspace=$(cd $CODEMGR_WS && workspace name)

	# If the current dir is in a workspace check that it is the same
	# as CODEMGR_WS.
	if [[ -n "$(workspace name)" ]]; then
		if [[ "$(/bin/pwd)/" != "$workspace/"* ]]; then
			cat <<-EOF

Warning, $ME will use $ME files in workspace $workspace (the current
directory is not in this workspace).
			EOF
			ok_to_proceed "Okay to proceed?"
		fi
	fi
else
	# If current dir is in a workspace then use output of workspace
	# name as current ws.
	workspace=$(workspace name)
	if [[ -n "$workspace" ]]; then
		CODEMGR_WS=$workspace
		export CODEMGR_WS
	fi
fi

if [[ -z "$workspace" ]]; then
	fail "No active workspace, run \"ws <workspace>\" or"\
		"\"cd <workspace>\"."
fi

workspace_basename=`basename $workspace`
wxdir=${WXDIR:-$workspace/wx}
wxtmp=$wxdir/tmp
wsdata=$workspace/Codemgr_wsdata
node=`uname -n`

if [ -f $wsdata/parent ]; then
	parent=`tail -1 $wsdata/parent`
else
	parent=
fi
if [[ $parent == *:* ]]; then
	parentdir=${parent#*:}
	parentnode=${parent%%:*}
	if [[ $parentnode == $node ]]; then
		parent=$parentdir
	else
		parent=/net/$parentnode$parentdir
	fi
fi

# Store backup state
backup_done=0

# store state if new files are deleted
NEED_WS_CLEAN='n'

# XXX doing this because keywords doesn't work on new files
# Note doing the echo so the tabs are apparent,
# % is escaped to prevent keyword expansion by sccs commands on this file.
SCCSKEYWORD=$(echo "ident\t+\"(\%Z\%\%M\%\t+\%I\%|\%W\%)\t+\%E\% SMI\"")

# file that contains comments for use in create and checkin
comment_file=
# mode for updating comments in active list
comment_mode="replace"

CSTYLE_FLAGS=${CSTYLE_FLAGS:='-P -p -c'}
JSTYLE_FLAGS=${JSTYLE_FLAGS:='-p'}

BACKUP_ERRORS=/tmp/${ME}_berrors_$(/usr/xpg4/bin/id -un)_$$

# global for reedit command, don't checkin by default
CHECKIN=false

# Indicates that the parent nametable cache needs refreshing
need_pnt_refresh=true

# Indicate whether file args were specified
FILES_PASSED=false

# Used to store files that have more than one delta compared to parent
multi_delta_list=

# Used to bringover any files just before exit of wx
bofilelist=

# should codereviews include delta comments?
show_comments=true

# Determines if active list should be sorted by default
# If sort_active contains true then we sort the active list on updates.
if [[ -r $wxdir/sort_active && "$(cat $wxdir/sort_active)" == "true" ]]; then
	ACTSORT=sort
else
	ACTSORT=cat
fi

# These are set depending on what needs sorting
do_renamed_sort=false
do_active_sort=false

# Places to search for approved RTIs
RTIDIRS="/net/wizard.eng/export/consolidation/rtiroute/newrtis
	/net/wizard.eng/export/consolidation/rtiroute/oldrtis
	/net/onstc.eng/export/stc/Rtitool/consolidation/rtiroute/newrtis
	/net/onstc.eng/export/stc/Rtitool/consolidation/rtiroute/oldrtis"

# Places to search for approved Patch RTIs
PRTIDIRS="/net/wizard.eng/export/consolidation/rtiroute/newprtis
	/net/wizard.eng/export/consolidation/rtiroute/oldprtis"

export workspace parent wxdir file dir filepath backup_done DEFAULT_SRCDIR

#
# main section
#

# Get wx command
command=$1
comlist=$command
shift
# throw away -D flag after command assigned as this flag was processed earlier
[[ "$1" == '-D' ]] && shift

case $command in
	apply|eval)	subcommand=$1; shift;;
	grep|egrep|sed|nawk)	pattern=$1; shift;;
	nits)	comlist="cstyle jstyle hdrchk copyright cddlchk keywords"; 
		echo "Note, nits is a subset of pbchk checks.";;
	pbchk)	comlist="cstyle jstyle hdrchk copyright cddlchk keywords"
		comlist="$comlist rmdelchk deltachk comchk rtichk outchk";;
esac

orig_args="$@"
silent=
args=
file_list=
typeset tmp_file_list tmp_args

#
# Some subcommands pass through all arguments.
#
case $command in
	webrev)	args="$orig_args"; shift $#;;
esac

# Parse args
while [ $# -gt 0 ]; do
	case $1 in
		-c|-C) 
			if [[ $command == @(delget|checkin|ci|create) ]]; then
				# set global comment_file
				[[ "$1" == "-C" ]] && comment_mode="append"
				comment_file=$2; 
				if [[ $comment_file != '/'* ]]; then
					comment_file="$(pwd)/$comment_file"
				fi
				if [[ -z "$comment_file" ]]; then
					fail "Missing comment file."\
						"Run 'wx help' for info."
				fi
				[[ ! -r "$comment_file" ]] &&
				    fail "Can not read comment file"\
				    	"$comment_file."
				echo "Using comment file $comment_file"\
					"for comments."
				# shift past the comment_file arg
				shift
			elif [[ $1 == '-C' && $command == 'diffs' || 
				$command == 'tdiffs' &&
				-z $WXDIFFCMD ]]; then
				if [[ $2 != +([0-9]) ]]; then
					# provide default context value for
					# compat with old wx
					args="$args -C5"
				else
					args="$args -C$2"
					# shift past context lines arg
					shift 
				fi
			else
				args="$args $1"
			fi;;
		-p) 	if [[ $command == @(putback|pb) ]]; then
				if workspace access $2 >/dev/null; then
					# 2nd arg is a workspace
					args="$args $1 $2"
				else
					fail "$2 not a workspace."\
						"Run 'wx help' for info."
				fi
				# shift past the parent ws arg
				shift
			else
				# for other commands -p doesn't have a arg
				args="$args $1"
			fi;;
		-r) 	if [[ $command == @(get|extract) ]]; then
				# 2nd arg is the version #
				args="$args $1 $2"
				# shift past 2nd arg
				shift
			else
				# for other commands -r doesn't have a arg
				args="$args $1"
			fi;;
		-s) 	if [[ "$command" == @(update|init) ]]; then
                                args="$args $1"
                        else
                                silent=-s
                        fi;;
		-N)	if [[ "$command" == @(codereview|fullreview) ]]; then
				show_comments=false
			else
				args="$args $1"
			fi ;;
		-*) 	args="$args $1";;
		*)  	if [[ -z "$file_list" ]]; then
				file_list="$1"
			else
				file_list="$file_list $1"
			fi;;
	esac
	shift
done

if [[ "$command" == "init" ]]; then
	if [ -z "$file_list" ]; then
		file_list=$DEFAULT_SRCDIR
	fi
	wx_init $file_list $args
	exit
fi

if [ ! -d $wxdir/tmp ]; then
	echo "Workspace does not appear to be initialized for $ME."
	echo "The initialization process will create a few files under"
	echo "$wxdir but will not otherwise affect your workspace."
	ok_to_proceed 'OK to proceed?'

	ask "Where is the root of the source code in this workspace?" \
		$DEFAULT_SRCDIR
	# wx_init modifies file_list so save current value
	tmp_file_list=$file_list
	file_list=
	# save off args and set to null to avoid side effects
	tmp_args=$args
	args=
	wx_init $answer
	# restore original file list and cd to original dir in case there's
	# a command to execute.
	file_list=$tmp_file_list
	args=$tmp_args
	cd $dot
fi

if [[ ! -f $wxdir/local_nametable ]]; then
	touch $wxdir/local_nametable 
fi

# Doing this for backward compat since old wx doesn't have a renamed list
if [[ ! -f $wxdir/renamed ]]; then
	# if 'wx update' or 'wx update -r' is the command then skip
	# renamed list creation since it will happen anyway.
	if [[ "$command" != "update" ]] || [[ "$args" == *'-q'* ]]; then
		ring_bell
		cat <<-EOF

$ME needs to create a renamed file list.  If you are sure that no files
were renamed or deleted in the current workspace then answer no to the
following question.

		EOF
		yesno "Okay to search for renamed files (can be slow)?"
		if [[ "$answer" == "yes" ]]
		then
			wx_update -r
		else
			touch $wxdir/renamed
		fi
	fi
fi

# Doing this for backward compat since old wx doesn't have a new list
if [[ ! -f $wxdir/new ]]; then
	ring_bell
	cat <<-EOF

$ME needs to create a new-file list (cache names of newly created
files).  Please be patient.
	EOF
	# Avoid a putback -n which is slow, just use lookup_parent()
	touch $wxdir/new || fail "Error: cannot create $wxdir/new list"
	wx_active |
		while read filepath; do
			if ! lookup_parent $filepath; then
				add_new $filepath
			fi
		done
	echo "\nNew files:"
	cat $wxdir/new
	echo
fi

if [[ "$command" == @(restore|backup|bu) ]]; then
	# If the backup dir was specified as a file arg...
	if [ -n "$file_list" ]; then
		backup_dir=$(echo "$file_list"|cut -f1 -d' ')
	fi
	# unset file_list since this file arg has been processed here.
	unset file_list
elif [[ "$command" == "ea" ]]; then
	# Do this command before wx_active is run because the active list
	# may be corrupt.
	cd $wxdir
	exec ${EDITOR-vi} active
elif [[ "$command" == @(unedit|uncheckout|unco) ]]; then
	if [[ -z "$file_list" && $args != *-f ]]; then
		echo "$ME will $command all active files which may remove"\
		    "them from the active list." 
		ok_to_proceed 'Do you REALLY want to do this?'
	fi
	cp $wxdir/active $wxdir/active.old
elif [[ "$command" == @(bugs|arcs) ]]; then
	# -v verbatim is not valid for these commands
	if [[ "$args" == *'-v'* ]]; then
		fail "Invalid flag -v. Run 'wx help' for info."
	fi
elif [[ "$command" == "create" ]]; then
	if [ -z "$file_list" ]; then
		fail "$command missing file arg(s). Run 'wx help' for info."
	fi

	cp $wxdir/active $wxdir/active.old ||
		fail "Error could not backup $wxdir/active"
elif [[ "$command" == @(delget|checkin|ci) && -n "$comment_file" ]]; then
	cp $wxdir/active $wxdir/active.old ||
		fail "Error could not backup $wxdir/active"
elif [[ "$command" == @(mv) ]]; then
	if [[ $(echo "$file_list"|wc -w) -ne 2 ]]; then
		fail "$command requires two args. Run 'wx help' for info."
	fi

	cp $wxdir/active $wxdir/active.old ||
		fail "Error could not backup $wxdir/active"
	cp $wxdir/renamed $wxdir/renamed.old ||
		fail "Error could not backup $wxdir/renamed"
elif [[ "$command" == @(delete|rm) ]]; then

	if [ -z "$file_list" ]; then
		echo "$ME will try to delete all active files which may "\
		    "remove them from the active list."
		ok_to_proceed 'Do you REALLY want to do this?'
	fi

	cp $wxdir/active $wxdir/active.old ||
		fail "Error: could not backup $wxdir/active"
	cp $wxdir/renamed $wxdir/renamed.old ||
		fail "Error: could not backup $wxdir/renamed"
elif [[ "$command" == reset ]]; then
	cp $wsdata/nametable $wxtmp/nametable.orig || \
		fail "Error: cp $wsdata/nametable $wxtmp/nametable.orig failed."
fi

if [ -z "$file_list" ]; then
	basedir=$workspace
	file_list=$(wx_active) || fail
else
	base_file_list=$file_list
	file_list=
	for basefile in $base_file_list; do
		# normalize the filepaths
		if [[ -d $basefile ]]; then
			basedir=$(cd $basefile && /bin/pwd)
			abspath=$basedir
		else
			basedir=$(cd $(dirname $basefile) && /bin/pwd)
			abspath=$basedir/$(basename $basefile)
		fi
		if [[ ! -d $basedir ]]; then
			fail "Error: Path to $basefile does not exist."
                elif [[ $(cd $basedir; workspace name) != $workspace ]]; then
			fail "Error: $basefile isn't in current workspace: $workspace."

		fi
		filepath=${abspath##$workspace/}
		if [[ -z "$file_list" ]]; then
			file_list="$filepath"
		else
			file_list="$file_list $filepath"
		fi
	done
	FILES_PASSED=true
fi

if [[ "$command" == @(nits|pbchk) ]]; then
	tmp_list=
	# skip nits/pbchk checks for files listed in $command.NOT
	if [[ -f $wxdir/${command}.NOT ]]; then
		for _a_file in $file_list; do
			if grep -q "^$(escape_re $_a_file)$" \
				$wxdir/${command}.NOT
			then
				echo "skipping $command checks for "\
				    "$_a_file (skipping)"
			else
				tmp_list="$tmp_list $_a_file"
			fi
		done
		file_list=${tmp_list# }
	fi
	[[ -z $file_list ]] && exit 0
fi

# This is where the commands are executed.
for command in $comlist; do
cd $dot
case $command in
	list|active) wx_active $args ;;
	pblist)	wx_active -p;;
	renamed)	list_renamed $args ;;
	new)	list_new $args;;
	update)	wx_update $args;;
	out)	wx_checked_out; cat $wxtmp/checked_out;;
	diffs)	wx_eval -r 'print -- "\n------- $filepath -------\n";
			sccs get -s -p -k $filepath |
			${WXDIFFCMD:-diff} $args - $filepath';;
	tdiffs)	## As diffs but also shows new files.
		if [[ -r $wxdir/new ]]; then	
		    ## Read names of new files into space separated list.
		    while read new_file
		    do
			new_files="${new_files}${new_file} "
		    done < $wxdir/new
		else
		    new_files=""
		fi
		## For new files a comparison is made with /dev/null thus
		## all lines will appear to have been added.
		wx_eval -r 'print -- "\n------- $filepath -------\n";
		    if [[ ${new_files} == *"${filepath} "* ]]; then
			${WXDIFFCMD:-diff} $args /dev/null $filepath;
		    else
			sccs get -s -p -k $filepath |
			${WXDIFFCMD:-diff} $args - $filepath;
		    fi';;
	pdiffs|tpdiffs) 
		## Parent Diffs - Compare with parent file.  For
		## 'tpdiffs' when the parent file does not exist the
		## child file is assumed new and compared to
		## /dev/null; thus all lines will appear to have been
		## added.
		wx_eval '
		print -- "\n------- $filepath -------\n";
		if wx_pnt_filepath; then
			echo "Index: $filepath";
			${WXDIFFCMD:-diff} $args $parentfilepath
			    $workspace/$filepath;
		    elif [[ $command == 'tpdiffs' ]]; then
			${WXDIFFCMD:-diff} $args /dev/null
			    $workspace/$filepath;
		    else
			print "New file (does not exist in parent).";
		    fi';;
	pvi)	wx_eval '
		echo $filepath;
		if wx_pnt_filepath; then
			${EDITOR-vi} $args $parentfilepath;
		else
			echo "New file (does not exist in parent)";
		fi';;
	edit|checkout|co)	wx_eval wx_edit;;
	unedit|uncheckout|unco)	wx_eval wx_unedit;;
	create)		wx_eval wx_create $args;;
	uncreate)	wx_eval wx_uncreate $args;;
	delete|rm)	wx_eval wx_delete $args;;
	mv)		wx_mv $file_list;;
	delget|checkin|ci)	wx_eval wx_delget $args;;
	get|extract)	wx_eval wx_get;;
	reset)		wx_eval wx_reset $args;;
	putback|pb)	wx_putback $args;;
	resolve)	wx_resolve $args;;
	prt)		wx_eval 'sccs prt $args $file';;
	comments)	wx_eval 'echo $filepath; echo; wx_show_comment; echo';;
	bugs)   wx_summary -ao $args;;
	arcs)   wx_summary -bo $args;;
	pbcom)  wx_summary -po $args;;
	info)	wx_eval wx_info;;
	reedit|recheckout|reco)  wx_reedit $args;;
	redelget|recheckin|reci) CHECKIN=true; wx_reedit $args;;
	cstyle) echo "\nDoing cstyle check:"
		rm -f $wxtmp/wx.cstyle.*;
		export CSTYLE_INDEX=0;
		wx_eval wx_cstyle;
		wait;
		sort -k 1,1 -k 2,2n $wxtmp/wx.cstyle.* 2> /dev/null
		;;
	jstyle) echo "\nDoing jstyle check:"
		rm -f $wxtmp/wx.jstyle.*;
		export JSTYLE_INDEX=0;
		wx_eval wx_jstyle;
		wait;
		sort -k 1,1 -k 2,2n $wxtmp/wx.jstyle.* 2> /dev/null
		;;
	hdrchk)	echo "\nDoing header format check:";
		cd $workspace; 
		hdrchk_files=;
		for filepath in $file_list ; do
			if [[ "$filepath" == *.h ]]; then
				if [[ -s $wxdir/${command}.NOT ]] &&
					grep -q "^$(escape_re $filepath)$" \
						$wxdir/${command}.NOT
				then
					echo "$filepath (skipping)"
				else
					hdrchk_files="$hdrchk_files $filepath"
				fi
			fi
		done
		hdrchk -a $args $hdrchk_files ;;
	makestyle) echo "\nDoing makestyle check:";
		cd $workspace; mlist=$(wx_active | grep '[Mm]akefile');
		[[ -n "$mlist" ]] && makestyle $args $mlist;;

	keywords) 
		echo "\nDoing keywords check:";
		cd $workspace;
		keyword_files=;
		for filepath in $file_list ; do
			if [[ -s $wxdir/${command}.NOT ]] &&
				grep -q "^$(escape_re $filepath)$" \
					$wxdir/${command}.NOT
			then
				echo "$filepath (skipping)"
			else
				keyword_files="$keyword_files $filepath"
			fi
		done
		keywords -p $args $keyword_files;;

	rmdelchk) echo "\nDoing sccs rmdel check:"; wx_eval rmdelchk;;

	rtichk) wx_rtichk;;

	deltachk)  echo "\nDoing multi delta check:"; wx_eval deltachk;;
	copyright) echo "\nDoing copyright check:"; 
		cd $workspace;
		copyright_files=;
		for filepath in $file_list; do
			if [[ -s $wxdir/${command}.NOT ]] &&
				grep -q "^$(escape_re $filepath)$" \
					$wxdir/${command}.NOT
			then
				echo "$filepath (skipping)"
			else
				copyright_files="$copyright_files $filepath"
			fi
		done
		copyrightchk $copyright_files;;

	cddlchk)
		echo "\nDoing CDDL block check:";
		cd $workspace;
		cddlnot="";
		if [[ -s $wxdir/${command}.NOT ]]; then
			cddlnot="-x $wxdir/${command}.NOT"
		fi

		#
		# Split the file list into new files and existing files.
		# New files must have a CDDL block whereas existing files don't
		# necessarily have to have a block, but if they do it must be
		# valid.  Both sets of files are subject to cddlchk.NOT
		# exception processing.
		# 
		old=""
		new=""
		for filepath in $file_list; do
			if wx_pnt_filepath $filepath; then
				old="$old $filepath"
			else
				new="$new $filepath"
			fi
		done
		[[ ! -z $new ]] && cddlchk $cddlnot $args -a $new
		[[ ! -z $old ]] && cddlchk $cddlnot $args $old
		;;
	comchk)	 echo "\nDoing comments check:"; wx_summary -n 2>&1;;
	outchk)	 echo "\nDoing out check:"; outchk;;
	backup|bu) wx_backup $args;;
	restore) wx_restore $args;;
	apply)	wx_eval "$subcommand \$file";;
	eval)	wx_eval "$subcommand";;
	grep|egrep)
		   wx_eval '
		   if egrep -s '\'$pattern\'' $file;
		   then
			   echo $filepath;
			   $command $args '\'$pattern\'' $file;
		   fi';;
	nawk|sed)
		wx_eval 'echo $filepath; $command $args '\'$pattern\'' $file';;
	codereview) args="-e $args"; wx_eval wx_fullreview;;
	fullreview) wx_eval wx_fullreview;;
	webrev) wx_webrev $args;;
	dir)	echo $wxdir;;
	e)	cd $wxdir; exec ${EDITOR-vi} $orig_args;;
	ws)	cd $wsdata; cat $orig_args;;
	args)	cat $wsdata/args;;
	access)	cat $wsdata/access_control;;
	*)	ring_bell;
		echo "Command not found. Run 'wx help' for command list.";
		exit 1;;
esac

done

if [[ $NEED_WS_CLEAN == 'y' ]]; then
	# clean up the nametable
	print -u2 "Running workspace updatenames to clean up nametable, may"\
	    "take a while."
	workspace updatenames >&2
fi

if [[ -n $bofilelist ]]; then
	$BRINGOVER $bofilelist
fi

# save sorting for last for some speed up.
if [[ $ACTSORT == sort ]]; then
	if $do_renamed_sort; then
		sort_renamed
	fi
	if $do_active_sort; then
		sort_active
	fi
fi