xref: /onnv-gate/usr/src/tools/scripts/wx.sh (revision 11407:a911350b5377)
1#!/bin/ksh -p
2#
3# CDDL HEADER START
4#
5# The contents of this file are subject to the terms of the
6# Common Development and Distribution License (the "License").
7# You may not use this file except in compliance with the License.
8#
9# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10# or http://www.opensolaris.org/os/licensing.
11# See the License for the specific language governing permissions
12# and limitations under the License.
13#
14# When distributing Covered Code, include this CDDL HEADER in each
15# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16# If applicable, add the following below this CDDL HEADER, with the
17# fields enclosed by brackets "[]" replaced with your own identifying
18# information: Portions Copyright [yyyy] [name of copyright owner]
19#
20# CDDL HEADER END
21#
22#
23
24#
25# Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
26# Use is subject to license terms.
27#
28
29# wx -- workspace extensions.  Jeff Bonwick, December 1992.
30
31# The bugster cat/subcat = consolidation/os-net-tools
32
33version() {
34	if [[ $(whence $0) = "/opt/onbld/bin/wx" ]] && \
35	    pkginfo SUNWonbld > /dev/null 2>&1; then
36		pkginfo -l SUNWonbld | egrep "PKGINST:|VERSION:|PSTAMP:"
37	else
38		ls -l $(whence $0)
39	fi
40}
41
42ring_bell() {
43	# Sound bell to stderr, no newline
44	print -u2 "\007\c"
45}
46
47fail() {
48	ring_bell
49	# output error message to stderr
50	print -u2 "$@ Aborting $command."
51	exit 1
52}
53
54ask() {
55	typeset question=$1 default_answer=$2
56	if [ -z "$default_answer" ]; then
57		echo "$question \c"
58	else
59		echo "$question [$default_answer]: \c"
60	fi
61	read answer
62	[ -z "$answer" ] && answer="$default_answer"
63}
64
65yesno() {
66	typeset question="$1"
67	answer=
68	while [ -z "$answer" ]; do
69		ask "$question" y/n
70		case $answer in
71			y|yes)	answer=yes;;
72			n|no)	answer=no;;
73			*)	answer=;;
74		esac
75	done
76}
77
78ok_to_proceed() {
79	yesno "$*"
80	if [[ "$answer" == no ]]; then
81		echo "Exiting, no action performed"
82		exit 1
83	fi
84}
85
86escape_re() {
87	# Escape the . so they are treated as literals in greps.
88	echo "$1"|sed 's{\.{\\.{g'
89}
90
91remove_local_nt_entry() {
92	# remove entries in local nt cache
93	grep -v "^$(escape_re $1) " $wxdir/local_nametable > \
94		$wxtmp/local_nametable
95	[[ ! -f $wxtmp/local_nametable ]] && \
96		fail "Error: cannot create $wxtmp/local_nametable"
97	mv -f $wxtmp/local_nametable $wxdir/local_nametable ||
98		fail "Error: cannot create $wxdir/local_nametable."
99}
100
101remove_renamed_entry() {
102	# remove entries in renamed list
103	# assuming arg is the new filename to remove
104	grep -v "^$(escape_re $1) " $wxdir/renamed > \
105		$wxtmp/renamed
106	[[ ! -f $wxtmp/renamed ]] && fail "Error: cannot create $wxtmp/renamed"
107	mv -f $wxtmp/renamed $wxdir/renamed ||
108		fail "Error: mv -f $wxtmp/renamed $wxdir/renamed failed"
109}
110
111add_local_nt_entry() {
112	# Add workspace nametable entry to local nt cache for better perf.
113	[[ -r $wsdata/nametable ]] || return 0
114	if ! grep -q "^$(escape_re $1) " $wxdir/local_nametable; then
115		# add entries from workspace nt to local nt cache
116		grep "^$(escape_re $1) " $wsdata/nametable >> \
117			$wxdir/local_nametable
118		[[ ! -f $wxdir/local_nametable ]] && \
119			fail "Error: cannot create $wxdir/local_nametable"
120	fi
121	return 0
122}
123
124remove_active_entry() {
125	# Remove entry from active list
126	# $1 is the filepath to remove
127
128	nawk '
129	$1 == target {
130		# Get past filepath line
131		while(NF > 0)
132			getline;
133		# Get past blank lines
134		while(NF == 0)
135			getline;
136		# Get past comments
137		while(NF > 0)
138			getline;
139		# Get past ending blank lines
140		while(NF == 0) {
141			if (getline) {
142				continue;
143			} else {
144				next;
145			}
146		}
147	}
148	# print the other active list entries
149	{ print $0; } ' target=$1 $wxdir/active >$wxtmp/tmp_active ||
150		fail "Error: cannot create $wxtmp/tmp_active."
151	if mv -f $wxtmp/tmp_active $wxdir/active; then
152		echo "$1 removed from active list."
153		remove_local_nt_entry $1
154	else
155		cat >&2 <<-EOF
156An error occured trying to remove $1 from the active list.
157The active list may be corrupt.  You should check it out and 
158possibly run '$ME update' to fix.
159		EOF
160		fail
161	fi
162}
163
164rename_active_entry() {
165	# renamed $1 to $2 filepath in active list
166	sed "s|^$(escape_re $1)$|$2|" $wxdir/active >\
167		$wxtmp/active || fail "Error: cannot create $wxtmp/active."
168	mv -f $wxtmp/active $wxdir/active || \
169		fail "Error: mv $wxtmp/active $wxdir/active failed."
170}
171
172is_active() {
173	# Return 0 if filepath arg is found in active list.
174	wx_active|grep -q "^$(escape_re $1)$"
175}
176
177#
178# ask user if s/he wants to remove an entry from the active list.
179# Will not remove entries that differ from parent or are new.
180#
181ask_remove_active_entry() {
182	# if an arg is passed in then this is assumed to be the filepath
183	# otherwise we assume the variables were set properly by the caller
184	if [[ $# -eq 1 ]]; then
185		dir=$(dirname $1)
186		file=$(basename $1)
187		filepath=$1
188
189		if [[ ! -f $file ]]; then
190			echo "Cannot find $file"
191			return 1
192		fi
193	fi
194
195	if is_active $filepath; then
196		if wx_pnt_filepath $filepath; then
197			if ! cmp -s $file $parentfilepath; then
198				cat <<-EOF
199
200The file $filepath
201differs from parent file:
202$parentfilepath
203and will remain in the active list.
204				EOF
205				return
206			fi
207		else
208			# New file, leave in active list.
209			cat <<-EOF
210$filepath
211is new and will remain in active list.  Use:
212'$ME uncreate $filepath'
213to remove from active list.
214			EOF
215			return
216		fi
217		# Remove entry from active list because it is the same as
218		# the parent.
219		echo "There is no difference between $filepath and the parent"
220		echo "$parentfilepath"
221		yesno "Okay to remove $filepath from active list?"
222		if [[ "$answer" == 'yes' ]]; then
223			remove_active_entry $filepath
224		fi
225	fi
226}
227
228refresh_pnt_nt_cache() {
229	# Refresh the parent nametable cache if necessary.
230	# Note, this is a cache for the parent nametable entries that
231	# have the same hash as the local active and renamed files.
232
233	# no parent so nothing to update.
234	[[ -z $parent ]] && return 0
235
236	if [[ ! -r $parent/Codemgr_wsdata/nametable ]]; then
237		fail "Error: cannot read $parent/Codemgr_wsdata/nametable"
238	fi
239
240	if [[ ! -f $wxtmp/parent_nametable ||
241	    $parent/Codemgr_wsdata/nametable -nt $wxtmp/parent_nametable ||
242	    $wsdata/parent -nt $wxtmp/parent_nametable ||
243	    $wxdir/local_nametable -nt $wxtmp/parent_nametable ]]; then
244		cut -f2- -d' ' $wxdir/local_nametable > $wxtmp/hash_list
245
246		if [[ ! -f $wxtmp/hash_list ]]; then
247			fail "Error: cannot create $wxtmp/hash_list."
248		fi
249
250		if [[ -s $wxtmp/hash_list ]]; then
251			# Use hash list to get only the parent files
252			# we're interested in.
253			fgrep -f $wxtmp/hash_list \
254			    $parent/Codemgr_wsdata/nametable \
255				 > $wxtmp/parent_nametable
256			[[ ! -f $wxtmp/parent_nametable ]] && \
257				fail "Error: cannot create $wxtmp/parent_nametable"
258		else
259
260			# There aren't any files to search for so just
261			# update the timestamp.
262
263			touch $wxtmp/parent_nametable
264		fi
265	fi
266}
267
268# add an active file to the new list
269add_new() {
270	typeset efp=$(escape_re $1)
271	# update new file list
272	if [[ ! -f $wxdir/new ]]; then
273		touch $wxdir/new || fail "Error: cannot create $wxdir/new."
274	fi
275	if is_active $1 && ! grep -q "^$efp$" $wxdir/new; then
276		echo "$1" >> $wxdir/new || fail "Error: cannot update $wxdir/new."
277	fi
278}
279
280# remove a file from the new list
281remove_new() {
282	# remove entries in new list
283	typeset efp=$(escape_re $1)
284	if [[ -f $wxdir/new ]] && grep -q "^$efp$" $wxdir/new; then
285		grep -v "^$efp$" $wxdir/new > $wxtmp/new
286		[[ ! -f $wxtmp/new ]] && fail "Error: cannot create $wxtmp/new"
287		mv -f $wxtmp/new $wxdir/new || fail "Error: cannot create $wxdir/new."
288	fi
289}
290
291update_active() {
292	# Try to add an entry to the active list
293	typeset efp=$(escape_re $1)
294
295	if ! is_active $1; then
296		if [[ -n "$comment_file" ]]; then
297			# Use sed to remove any empty lines from comment file.
298			(echo $1; echo; sed '/^[ 	]*$/d' $comment_file;\
299				echo) >>$wxdir/active ||
300				fail "Could not update active list."
301		else
302			(echo $1; echo; wx_show_comment; echo) \
303			    >> $wxdir/active ||
304				fail "Could not update active list."
305			echo "Remember to edit the comment in the active list "\
306				 "(use '$ME ea')."
307		fi
308		add_local_nt_entry $1
309	fi  # End if not in active list
310}
311
312sort_active() {
313	typeset origfp=$filepath
314
315	# Note must use filepath for wx_show_comment
316	wx_active | sort | while read filepath; do
317		(print "$filepath"; print; wx_show_comment; print)
318	done > $wxtmp/active_sort || \
319		fail "Error: cannot create $wxtmp/active_sort"
320	mv -f $wxtmp/active_sort $wxdir/active || \
321		fail "Error: cannot create $wxdir/active"
322
323	filepath=$origfp
324}
325
326sort_renamed() {
327	sort $wxdir/renamed > $wxtmp/renamed_sort || \
328		fail "Error: cannot create $wxtmp/renamed_sort"
329	mv -f $wxtmp/renamed_sort $wxdir/renamed || \
330		fail "Error: cannot create $wxdir/active"
331}
332
333update_active_comment() {
334	# replace comment in active list entry with contents of $comment_file
335	nawk '
336	# find active list entry to modify
337	$1 == filepath {
338		# print filepath line
339		while(NF > 0){
340			print $1;
341			getline;
342		}
343		#print 1 blank (delimit)
344		print "";
345		# Get past blank lines
346		while(NF == 0){
347			getline;
348		}
349		# Get past active entry comments
350		# append to or replace comment
351		if (comment_mode == "append"){
352			while(NF > 0) {
353				# output existing comments
354				print $0;
355				getline;
356			}
357		} else {
358			# get past existing comments
359			while(NF > 0) getline;
360		}
361
362		# output new comments
363		while (getline < comments){
364			# do not print blank lines
365			if (NF > 0)
366				print $0
367		}
368		close comments
369		# print delimiting blank line
370		printf "\n"
371
372		# Get past ending blank lines in active entry
373		NF=0
374		while(NF == 0) {
375			if (getline) {
376				continue;
377			} else {
378				next;
379			}
380		}
381	}
382	# print the other active list entries
383	{ print $0; } ' filepath=$1 comment_mode=$comment_mode \
384		comments=$comment_file $wxdir/active >$wxtmp/tmp_active && \
385		mv $wxtmp/tmp_active $wxdir/active
386	if [[ $? -eq 0 ]]; then
387		echo "$1 comment(s) updated in active list."
388	else
389		cat <<-EOF
390
391An error occured trying to update comments for $1 in the active list.
392The active list ($wxdir/active) may be corrupt.  You should check it out
393and possilbly run '$ME ea' to fix.
394
395		EOF
396		fail
397	fi
398}
399
400lookup_parent() {
401	# Find a local file's parent filepath.
402	# Returns 1 if not found (local file is new)
403	# Sets env var. parentfilepath and parenthash if found
404	# Requires a file arg.
405	# Updates local and parent nt caches.
406
407	typeset efp parententry localfile hash1 hash2 hash3 hash4 \
408		local_nt pnt_nt
409
410	parentfile=
411	parenthash=
412
413	if [[ -z $parent ]]; then
414		cat >&2 <<-EOF
415
416Warning: there is no parent for the current workspace so local file:
417$1
418is assumed to be new.
419		EOF
420		return 1
421	fi
422	if [[ ! -f $wsdata/nametable ]]; then
423		# Nothing has been brought over so assuming new file.
424		cat >&2 <<-EOF
425
426Warning: the $wsdata/nametable
427doesn't exist so
428$1
429is assumed to be new.
430		EOF
431		return 1
432	fi
433
434	if [[ ! -r $parent/Codemgr_wsdata/nametable ]]; then
435		fail "Error: cannot read $parent/Codemgr_wsdata/nametable."
436	fi
437	if [[ ! -f $wxtmp/parent_nametable ]] || $need_pnt_refresh; then
438		refresh_pnt_nt_cache
439		need_pnt_refresh=false
440	fi
441	if [[ ! -f $wxdir/local_nametable ]]; then
442		touch $wxdir/local_nametable ||
443			fail "Error: cannot create $wxdir/local_nametable."
444	fi
445
446	efp=$(escape_re $1)
447
448	for local_nt in $wxdir/local_nametable $wsdata/nametable; do
449
450		# May be multiple entries in nametable, see if one
451		# matches parent.
452
453		grep "^$efp " $local_nt |\
454		while read localfile hash1 hash2 hash3 hash4; do
455			for pnt_nt in $wxtmp/parent_nametable \
456			    $parent/Codemgr_wsdata/nametable; do
457				# get current parent nt entry
458				parententry=$(grep \
459				    " $hash1 $hash2 $hash3 $hash4$" $pnt_nt)
460				if [[ -n "$parententry" ]]; then
461					# found parent entry
462					parentfile=$(echo "$parententry" |\
463						cut -f1 -d' ')
464					parenthash="$(echo "$parententry" |\
465						cut -f2- -d' ')"
466					if [[ "$local_nt" == \
467						"$wsdata/nametable" ]]; then
468
469						# Update the local nt
470						# hash cache if parent
471						# found and local
472						# workspace nt used.
473
474						add_local_nt_entry $localfile
475					fi
476					if [[ $pnt_nt == \
477					    $parent/Codemgr_wsdata/nametable ]]
478					then
479
480						# Update the parent nt
481						# hash cache if actual
482						# parent nt used.
483
484						echo $parententry >>\
485						    $wxtmp/parent_nametable
486					fi
487
488					# break out of all the loops if
489					# parent found
490
491					break 3
492				fi
493			done # for pnt_nt
494		done # while read active file
495	done # for local_nt
496
497	if [[ -z "$parentfile" ]]; then
498		# parent filepath not found.
499		return 1
500	else
501		# parent filepath found.
502		return 0
503	fi
504}
505
506#
507# Detect if a file was locally renamed
508#
509renamed() {
510	# Return 0 if renamed, 1 if not locally renamed
511	# parentfile and parenthash set as side effect
512	# Must be used by commands that set filepath (like wx_eval)
513
514	if [[ ! -f $wxdir/renamed ]]; then
515		update_renamed_dir
516	fi
517
518	# new is the new filename in the current ws, old is the previous
519	# filename that should exist in parent ws.
520
521	if grep -q "^$(escape_re $filepath) " $wxdir/renamed; then
522		if lookup_parent $filepath; then
523			return 0
524		else
525
526			# New files aren't in $wxdir/renamed so no
527			# parent is a problem
528
529			fail "Error: renamed $filepath but no matching parent"\
530				"file in $parent"
531		fi
532	else
533		# not locally renamed
534		return 1
535	fi
536}
537
538wx_pnt_filepath() {
539	# return 0 if parent file found. Side effect: sets parentfilepath
540	# and parentsdot.  parentfile and parenthash are set via lookup_parent.
541
542	# if an arg is passed in then this is assumed to be the filepath
543	# otherwise we assume the variables were set properly by the caller
544	if [[ $# -eq 1 ]]; then
545		dir=$(dirname $1)
546		file=$(basename $1)
547		filepath=$1
548	fi
549
550	# Find the parent filename
551	if lookup_parent $filepath; then
552		parentfilepath=$parent/$parentfile
553		parentsdot=$parent/$(dirname $parentfile)/SCCS/s.$(basename \
554		    $parentfile)
555		if [[ -f $parentfilepath && -s $parentsdot ]]; then
556			# found
557			return 0
558		else
559			fail "Error: located parent filepath $parentfilepath"\
560				"but file does not exist or SCCS file is empty."
561		fi
562	fi
563
564	# wasn't found
565	return 1
566}
567
568get_pb_output() {
569	# Get output of putback -n (useful for parsing to find diffs between
570	# workspaces).  Creates $wxtmp/putback.err and $wxtmp/putback.out
571	typeset -i rc=0
572	typeset origdir=$(pwd)
573	# clean up if interrupted.
574	trap "rm $wxtmp/putback.out;exit 1" HUP INT QUIT TERM
575
576	cat <<-EOF
577Doing a '$PUTBACK -n $*' to find diffs between workspaces.
578Please be patient as this can take several minutes.
579	EOF
580
581        cd $workspace
582
583	$PUTBACK -n $* 2>$wxtmp/putback.err >$wxtmp/putback.out
584	rc=$?
585	# Note a blocked putback returns a 2 but is not a problem.
586	if [[ $rc -ne 0 && $rc -ne 2 ]]; then
587		rm $wxtmp/putback.out
588		fail "Error, $PUTBACK -n $* failed. See $wxtmp/putback.err"\
589			"for details."
590	fi
591	trap - HUP INT QUIT TERM
592        cd $origdir
593	return 0
594}
595
596wx_usage() {
597	version
598
599	cat << EOF
600
601See <http://www.opensolaris.org/os/community/on/wx/> for usage tips.
602
603Usage:  $ME command [-D] [args]
604
605        -D turn on debugging for any command (output to stderr)
606
607===================== Initialization and Update Commands ====================
608        $ME init [-f(t|q|n) [-s]] [src-root-dir]     
609                        initialize workspace for $ME usage
610                        -f(t|q|n): non-interactive mode of update.  Use this
611                                   to keep init from asking questions.
612                            -ft: thorough update (update both active and 
613                                 renamed lists with all diffs between parent
614                                 and current workspace).
615                            -fq: quick update (update active list with files
616                                 currently checked out in current workspace).
617                            -fn: no update (just create empty active and 
618                                 renamed lists if they don't exist already).
619                            -s:  keep active list sorted by default.  Must
620                                 follow a -f(t|q|n) flag.
621                        src-root-dir: optional path relative to top of 
622                                      workspace where wx will search for files.
623                                      Use "." to set src-root to top of
624                                      workspace.  Default is usr.
625        $ME update [-q|-r] [-s]
626                        Update the active and renamed file lists by
627                        appending names of all files that have been
628                        checked out, changed, created or renamed as
629                        compared to the parent workspace.  This is the
630                        most accurate way of updating but it is slow.
631                        All files in the workspace must be under SCCS
632                        control in order for update to find them.  Note,
633                        this operation can be sped up in some cases by
634                        setting the PUTBACK env. variable to use 
635                        "cm_env -g -o putback". (See
636                        http://webhome.holland.sun.com/casper/ for more
637                        info about the turbo def.dir.flp tool).
638
639                        -q: quick update (only updates active list with
640                            files currently checked out in workspace).  This
641                            is faster but will not find renames or files that
642                            have been checked-in/delget'ed.
643                        -r: only update the renamed list.  Does not update
644                            the active list.
645                        -s: sort the active list.
646
647======================== Information Commands ===========================
648        $ME list [-r|-p|-w] list active files (the ones you are working on)
649                        -r: list only renamed active files.
650                        -p: output list of both active and renamed files 
651                            suitable for input to putback. 
652                        -w: output list of both active and renamed files
653                            suitable for input to webrev (see $ME webrev 
654                            subcommand below).
655        $ME active          alias for list 
656        $ME pblist          alias for list -p (see above).
657
658        $ME renamed [-a|-d|-p]
659                         list locally renamed files. The output format is:
660                         "new_name previous_name". Note, deleted files are
661                         a special case of rename. 
662                        -a: list only renamed active files (same as list -r)
663                        -d: list only deleted files
664                        -p: show "new_name parent_name" (Note, parent_name 
665                            may not be the same as previous_name)
666        $ME new [-t]    List new active files (files that exist in child only)
667                        Note, should be run before reedit (see reedit below).
668                        -t: thorough, does not use new cache (slower but more
669                            accurate if new cache isn't current).
670        $ME out         find all checked-out files in workspace
671        $ME info        [file ...] show all info about active files
672
673        $ME diffs [file ...]
674                        show sccs diffs for files (current vs previous
675                        local version).  Will show diffs for all active
676                        files if no files given on command line.  Will
677                        use WXDIFFCMD environment variable if set.  Hint,
678                        try: export WXDIFFCMD="diff -bwU5"
679
680        $ME tdiffs [file ...]
681                        Similar to diffs but new files are also displayed.
682                        New files are those listed by '$ME new'.
683
684        $ME pdiffs [file ...]   show diffs against parent files
685                        Will show diffs between local file and it's
686                        parent for all active files if no files given on
687                        command line.  Will use WXDIFFCMD environment
688                        variable if set. 
689
690        $ME tpdiffs [file ...]   show diffs against parent files
691                        Similar to pdiffs but new files are also displayed.
692                        A file is considered new if it does not exist in
693                        the parent.
694
695        $ME prt [-y]    show sccs history for all active files
696
697        $ME comments    display check-in comments for active files
698        $ME bugs [-u]   display all bugids in check-in comments
699        $ME arcs [-u]   display all ARC cases in check-in comments
700        $ME pbcom [-v] [-u] [-N]  display summarized comments suitable for putback
701                        Default is to display only bugs and arc cases. Will
702                        display warnings about non-bug comments to stderr.
703                        -v: display all comments verbatim including non-bug/arc 
704                        -u: prevent sorting, order determined by active list
705                        -N: don't check bug synopsis against bug database
706
707======================== File Manipulation Commands ======================
708        $ME edit [-s] [file ...]        
709                        check out either file(s) on command line or
710                        all active files if no file args.
711                        (Updates the active list.)
712                        -s: silent, less sccs diagnostic output.  This is 
713                            true for the other commands that accept the 
714                            -s flag.
715        $ME checkout    Alias for edit command.
716        $ME co          Alias for edit command.
717
718        $ME unedit [-s][-f] [file ...] 
719                        Returns file(s) to state prior to edit/checkout
720                        Note, files will be unlocked and any changes made
721                        when file was last checked out will be lost.
722                        Unedit all active files if no files listed on
723                        command line.  Removes active list entry if there
724                        are no diffs between local and parent file.
725                        (Updates active list)
726                        -f: force unedit, non-interactive. Will backup
727                            if wx files newer than last backup.
728        $ME uncheckout  Alias for unedit command.
729        $ME unco        Alias for unedit command.
730
731        $ME delget [-(c|C) comment_file][-s][-f] [file ...] 
732                        Check in all active files or files on command
733                        line. Check in comments will be those in active
734                        file.  See '$ME comments' for more info.
735                        -c comment_file: use comment(s) in specified comment
736                                         file when checking in file(s). Note,
737                                         each comment should be on new line,
738                                         blank lines not allowed.  Existing
739                                         comments in active list will be 
740                                         replaced by contents of comment_file.
741                        -C comment_file: Similar to -c but comments are 
742                                         appended to current active list 
743                                         comments.
744                        -f: force checkin, no checks, non-interactive.
745                            Use this if your sure the files okay to checkin
746                            otherwise this command will check for
747                            keyword problems. Will backup if wx files
748                            newer than last backup.
749
750                        NOTE: use redelget to reset new file's version to 1.1.
751
752        $ME checkin     Alias for delget command.
753        $ME ci          Alias for delget command.
754
755        $ME create [-(c|C) comment_file] [-f] [-o] file [file ...]
756                        Creates one or more files in the workspace.
757                        (Updates active list)
758                        -(c|C) comment_file: see delget
759                        -f: force create regardless of warnings,
760                            (non-interactive).
761                        -o: also check out file for further editing.
762
763        $ME uncreate [-f] [file ...]
764                        Undoes the create of a new file.  The file's
765                        active list entry, its SCCS history and the
766                        entry in the workspace nametable will be removed
767                        but the file will stay in the workspace.
768                        Will uncreate all new files in active list if
769                        no file argument is specified.
770                        -f: force uncreate, non-interactive. Will backup
771                            if wx files newer than last backup.
772
773        $ME get [-k][-r #][-p] [file ...]
774                        Get a copy of all active files or files on command
775                        line.  By default this is a read only version of
776                        the file.
777                        -r #: get specified version #
778                        -p: output to stdout
779                        -k: don't expand the sccs ID string
780        $ME extract     Alias for get command.
781
782        $ME reedit [-m] [-s] [file ...]
783                        Collapse the sccs delta (file history) such that
784                        all changes made to the file in the current
785                        workspace are now in one delta.  If no files are
786                        given on command line then all the active files
787                        are processed.  The files are left in a checked
788                        out state so you can make further changes if a
789                        resolve to make all your changes look like a
790                        single delta.  This eliminates the uninteresting
791                        leaf deltas that arise from resolving conflicts,
792                        so your putbacks do not contain a bunch of noise
793                        about every bringover/resolve you did in the
794                        interim.  Accepts the same compression flags as
795                        $ME backup.  If [file ...] given, wx only
796                        reedits files passed on command line.  This adds
797                        files to active list if not already there.
798                        
799                        NOTE: reedit is appropriate for leaf workspaces
800                        ONLY -- applying reedit to an interior-node
801                        workspace would delete all childrens comments
802                        and confuse Teamware tools in general.
803                        
804                        NOTE: if files are listed as new that are not
805                        then DO NOT use the reedit as it will destroy
806                        the file history.
807
808                        NOTE: if a file is new reedit will leave the
809                        file checked out so in order to keep the delta
810                        version at 1.1 redelget must be used for
811                        checkin.
812
813                        -m: only reedit files that have more that one
814                            delta as compared to parent file.  New files
815                            will be recreated with comment found in
816                            active list.
817                        -s: silent checkin
818
819        $ME recheckout  Alias for reedit command.
820        $ME reco        Alias for reedit command.
821
822        $ME redelget [-m][-s] [file ...]
823                        Similar to reedit but the file is checked in
824                        when the command is done. This is the command to
825                        use to collapse new files to their initial
826                        1.1 delta (will assign comment in active list).
827        $ME recheckin   Alias for redelget command.
828        $ME reci        Alias for redelget command.
829
830        $ME delete [-f] [file ...]
831                        Delete one or more files from the workspace.
832                        Will delete all files in active list if no file
833                        args.  Note, for files brought over from parent,
834                        this command actually moves the file under the
835                        deleted_files/ subdir so it can be recovered.
836                        For new files this command can remove the file
837                        and file history completely.
838                        (Updates active list if file is in there.)
839                        -f : force delete regardless of warnings 
840                             (non-interactive)
841
842                             Warning, this will completely remove new
843                             files from the workspace.  Will backup
844                             if wx files newer than last backup.
845        $ME rm          Alias for delete command.
846
847        $ME mv file newfile     
848                        Rename file to newfile
849                        (Updates active list with new file name)
850        $ME mv file newdir      
851        $ME mv dir newdir       
852                        Renames dir or file to newdir.  If newdir exists
853                        then dir will be subdir under newdir.  Note,
854                        this renames all files in dir and can take a
855                        while if there are a lot of files affected by
856                        the rename.  (Updates active list)
857
858        $ME reset [-f] [file ...]
859                        Resets file contents and history to that of
860                        parent file.  If the file was renamed locally it
861                        will be renamed to that of the parent.  It does not
862                        work on new files (see uncreate or delete).
863
864                        NOTE: use with care.  If something goes wrong,
865                        do a wx restore from the last backup and copy
866                        wx/tmp/nametable.orig to Codemgr_wsdata/nametable.
867
868======================== Teamware Commands ======================
869        $ME putback [-v][-f][-N][Teamware putback flags, see below][file ...]
870                        Use Teamware putback command to putback either
871                        file(s) specified or *all* active and renamed
872                        files if no file(s) specified.  Will use pbcom
873                        output as the putback comments. 
874                        -f: force non-interactive mode (will not prompt)
875                        -v: pass comments verbatim to putback(see pbcom)
876                        -N: don't check bug synopsis against bug database
877                        Accepts -n, -p pws, -q putback flags ('man putback' 
878                        for more info).
879        $ME pb          alias for putback.
880
881        $ME resolve [Teamware resolve args]
882                        resolve bringover conflicts and reedit merged 
883                        files.  See 'man resolve' for args.
884
885======================== ON Rules checking Commands ======================
886        $ME cstyle      run cstyle over all active .c and .h files
887                        skips files in wx/cstyle.NOT
888        $ME jstyle      run jstyle over all active .java files
889                        skips files in wx/jstyle.NOT
890        $ME hdrchk      run 'hdrchk -a' over all active .h files
891                        skips files in wx/hdrchk.NOT
892        $ME makestyle   run makestyle over all active Makefiles
893        $ME keywords    run keywords over all active files
894                        skips files in wx/keywords.NOT
895        $ME copyright   make sure there is a correct copyright message
896                        that contains the current year
897                        skips files in wx/copyright.NOT
898        $ME cddlchk     make sure there is a current CDDL block in
899                        active files
900                        skips files in wx/cddlchk.NOT
901        $ME rmdelchk    make sure sccs rmdel was not run on active files
902                        (This causes Teamware problems.)
903        $ME deltachk    make sure only 1 sccs delta in active files
904                        (more than 1 breaks gate putback rules unless > 1
905                        bug in putback.)
906        $ME comchk      make sure comments are okay
907        $ME rtichk      make sure RTI is approved for bugs/rfe's listed
908                        in active list comments.  Will skip rtichk if
909                        wx/rtichk.NOT exists.
910        $ME outchk      warn if there are files checked out that aren't in
911                        active list.
912        $ME nits [file ...]
913                        nits checking.  Run cstyle, jstyle, hdrchk, copyright,
914                        cddlchk, and keywords over files to which they are
915                        applicable (makestyle is not currently run
916                        because it seems to be quite broken -- more
917                        noise than data).  This is a subset of pbchk checks
918                        suitable for checking files during development.
919                        Use pbchk before doing the final putback.
920                        Will run checks on all active files if no file args.
921                        Will skip checks for files listed in wx/nits.NOT.
922        $ME pbchk [file ...]
923                        putback check.  Run cstyle, jstyle, hdrchk, copyright,
924                        cddlchk, keywords, rmdelchk, deltachk, comchk, rtichk
925                        and outchk over files to which they are
926                        applicable (makestyle is not currently run
927                        because it seems to be quite broken -- more
928                        noise than data).  Good command to run before
929                        doing a putback.  
930                        Will run checks on all active files if no file args.
931                        Will skip checks for files listed in wx/pbchk.NOT.
932
933======================== Code Review Commands ======================
934        $ME webrev [webrev-args]
935                        generate webrev for active and renamed/deleted files.
936                        Note, uses comments in the active list.  This is the
937                        preferred way of reviewing code.  Arguments to webrev
938                        may also be specified.
939                        Will skip files listed in wx/webrev.NOT
940
941        $ME codereview [-N] [codereview options]
942                        generate environmentally friendly codereview diffs
943                        for all active files.  -N indicates that delta
944                        comments should not be included.
945
946        $ME fullreview [-N] [codereview options]
947                        generate full codereview diffs for all active files.
948                        -N indicates that delta comments should not be
949                        included.
950
951======================== Backup and Restore Commands ======================
952        $ME backup [-i|-n|-z|-b|-t]
953                        make backup copies of all active and renamed files.
954                        -i: info about backups (backup dir and contents)
955                        -n: no compression 
956                        -z: use gzip, faster than bzip2 but less compression.
957                        -b: use bzip2, slower but better compression.
958                        -t: backup if wx files are newer than last backup.
959                        Defaults to the compression of the previous backup.
960        $ME bu          Alias for backup command.
961
962        $ME restore [-f] [backup_dir]   
963                        restore a backup in a workspace (restores both
964                        active files and performs file renames).  A path
965                        to the directory containing the backup to
966                        restore from can be optionally specified.
967                        -f: non-interactive.  Will restore from last backup.
968
969======================== Misc Commands ============================
970        $ME apply <cmd> apply cmd to all active files; for example,
971                        "$ME apply cat" cats every file
972        $ME eval <cmd>  like apply, but more general.  In fact,
973                        "$ME apply cmd" is implemented internally as
974                        "$ME eval 'cmd \$file'".  When using eval,
975                        you can refer to \$dir, \$file, \$filepath,
976                        \$parent, and \$workspace.  For example:
977                        $ME eval 'echo \$dir; sccs prt \$file | more'
978                        will show the sccs history for each active file,
979                        preceded by its directory.
980        $ME grep        search all active files for pattern; equivalent to
981                        "$ME eval 'echo \$filepath; grep pattern \$file'"
982        $ME egrep       see $ME grep
983        $ME sed         see $ME grep
984        $ME nawk        see $ME grep
985
986        $ME dir         echo the $ME directory path (\$workspace/$ME)
987        $ME e <file>    edit the named $ME control file, e.g. "$ME e active".
988                        The editor is \$EDITOR if set, else vi.
989
990        $ME ea          shorthand for "$ME e active" (edit active list).  
991                        Note, the format for each entry in the active
992                        list is:
993
994                        filepath
995                        <empty line> # no spaces allowed
996                        one or more comment lines # no blank lines between.
997                        <empty line> # no spaces allowed, ends the entry.
998
999                        In general, it is best to only edit the active
1000                        list to update comments.  Use the other $ME
1001                        commands like edit or create to update the
1002                        active list when possible.
1003
1004        $ME ws <file>   cat the named workspace control file, i.e.
1005                        \$workspace/Codemgr_wsdata/file
1006        $ME args        shorthand for "$ME ws args"
1007        $ME access      shorthand for "$ME ws access_control"
1008
1009        $ME help        print this usage message
1010        $ME version     print current version of this program
1011EOF
1012	exit 1
1013}
1014
1015list_putback() {
1016	(wx_active; list_renamed -n)|nawk '!seen[$0]++'
1017}
1018
1019list_new() {
1020	# list new files (not found in parent ws)
1021	typeset new
1022
1023	if [[ $1 == '-t' ]]; then
1024		wx_active|
1025		while read new; do
1026			# thorough, skip new cache
1027			if ! lookup_parent $new; then
1028				# no parent, new file
1029				echo "$new"
1030			fi
1031		done
1032	else
1033		while read new; do
1034			# use new cache
1035			if ! lookup_parent $new; then
1036				# no parent, new file
1037				echo "$new"
1038			fi
1039		done < $wxdir/new
1040	fi
1041}
1042
1043list_renamed() {
1044	typeset active current old
1045
1046	if [[ $# -eq 0 ]]; then
1047		cat $wxdir/renamed
1048	elif [[ $1 == '-d' ]]; then
1049		# Only list only deleted file renames
1050		grep '^deleted_files/' $wxdir/renamed
1051	elif [[ $1 == '-n' ]]; then
1052		# Only list only current filenames
1053		cut -f1 -d' ' $wxdir/renamed
1054	elif [[ $1 == '-p' ]]; then
1055		# list parent for current instead of local previous name
1056		while read current old; do
1057			if lookup_parent $current; then
1058				print "$current $parentfile"
1059			else
1060				ring_bell
1061				print -u2 "Warning: cannot find parent file"\
1062					"for $current"
1063			fi
1064		done < $wxdir/renamed
1065	elif [[ $1 == '-a' ]]; then
1066		# Just list active renamed files
1067		for active in $(wx_active); do
1068			grep "^$(escape_re $active) " $wxdir/renamed
1069		done
1070	elif [[ $1 == '-r' ]]; then
1071		wx_active > $wxtmp/alist || fail "Error: cannot create $wxtmp/alist"
1072		# Just list non-active renamed files (just current name)
1073		while read current old; do
1074			grep -q "^$(escape_re $current)$" $wxtmp/alist ||
1075				echo $current
1076		done < $wxdir/renamed
1077		rm -f $wxtmp/alist
1078	else
1079		fail "Invalid flag $i. Run 'wx help' for info."
1080	fi
1081}
1082
1083list_webrev() {
1084	# Output a list of active and renamed files suitable for webrev
1085	for filepath in $( (wx_active; list_renamed -n)|nawk '!seen[$0]++'); do
1086		if renamed; then
1087			echo "$filepath $parentfile"
1088		else
1089			echo "$filepath"
1090		fi
1091	done
1092}
1093
1094wx_webrev() {
1095	typeset origdir=$PWD
1096
1097	cd $workspace
1098	# skip files listed in .NOT files
1099	cat -s $wxdir/$command.NOT >$wxtmp/NOT
1100
1101	( # do the loops in a subshell so there is only one file open
1102	for filepath in $(wx_active); do
1103		if grep -q "^$(escape_re $filepath)$" $wxtmp/NOT; then
1104			print -u2 "$filepath (skipping)"
1105			continue
1106		fi
1107		if renamed; then
1108			echo "$filepath $parentfile"
1109		else
1110			echo "$filepath"
1111		fi
1112		echo "\n$(wx_show_comment)\n"
1113	done
1114
1115	# Do non-active renamed files
1116	for filepath in $(list_renamed -r); do
1117		if grep -q "^$(escape_re $filepath)$" $wxtmp/NOT; then
1118			print -u2 "renamed $filepath (skipping)"
1119			continue
1120		fi
1121		lookup_parent $filepath ||
1122			fail "Error: cannot find parent for $filepath."
1123		if [[ $filepath != $parentfile ]]; then
1124			echo "$filepath $parentfile"
1125			# output empty line, comment, empty line
1126			echo "\nRenamed only (webrev comment generated by "\
1127			    "$ME, not an sccs comment)\n"
1128		else
1129			echo "$filepath"
1130			echo "\nWarning, file in renamed list but has same "\
1131			    "name as parent (not an sccs comment)\n"
1132		fi
1133	done
1134	# End of subshell
1135	) > $wxtmp/webrev.list
1136
1137	# Note that the file list must come last.
1138	${WXWEBREV:-webrev} -w "$@" $wxtmp/webrev.list
1139
1140	cd $origdir
1141}
1142
1143#
1144# list all active files
1145#
1146wx_active() {
1147	# do not print hash by default
1148	typeset rc hash=0
1149
1150	case $1 in
1151		# list only renamed active files
1152		-r) list_renamed -a; return 0;;
1153		# list suitable for putback (both active and renamed)
1154		-p) list_putback; return 0;;
1155		# list suitable for webrev (both active and renamed)
1156		-w) list_webrev; return 0;;
1157		-*) fail "Invalid flag $1. See 'wx help' for more info.";;
1158	esac
1159
1160        if [[ ! -r $wxdir/active ]]; then
1161                fail "Error: cannot read $wxdir/active"
1162        fi
1163
1164	nawk '
1165	{
1166	    # skip blank lines
1167	    while (NF == 0) {
1168		    if (getline == 0)
1169			    next;
1170	    }
1171	    if (NF > 0){
1172		    # print the active filepath
1173		    print $1;
1174		    getline;
1175	    }
1176	    # skip blank lines
1177	    while (NF == 0) {
1178		    if (getline == 0){
1179			    # Error, corrupt active list (missing comment)
1180			    exit 1
1181		    }
1182	    }
1183	    # skip comment
1184	    while (NF > 0) {
1185		    if (getline == 0){
1186			    # Error, blank line after comment missing
1187			    exit 2
1188		    }
1189	    }
1190	}' $wxdir/active
1191
1192	rc=$?
1193	if [[ $rc -eq 1 ]];then
1194		fail "Corrupt active list (missing comment)."
1195	elif [[ $rc -eq 2 ]];then
1196		fail "Corrupt active list (blank line needed after comment)."
1197	fi
1198}
1199
1200#
1201# show the comment for $filepath
1202#
1203wx_show_comment() {
1204	if [[ -f $wxdir/active ]]; then
1205		nawk '
1206		{
1207			filename=$1
1208			getline
1209			while (getline) {
1210				if (length == 0)
1211					next
1212				if (filename == target) {
1213					if ($0 == "NO_COMMENT") {
1214							found = 0
1215							exit 1
1216					}
1217					found = 1
1218					print
1219				}
1220			}
1221		}
1222		END {
1223			if (found == 0)
1224				print "NO_COMMENT"
1225			exit 1 - found
1226		}' target=$filepath $wxdir/active
1227		return $?
1228	else
1229		echo "NO_COMMENT"
1230	fi
1231}
1232
1233bugdb_lookup_monaco() {
1234	typeset buglist=$1
1235	typeset monaco=/net/monaco.sfbay/export/bin/monaco
1236
1237	if [[ ! -x $monaco ]]; then
1238		return 1
1239	fi
1240
1241	$monaco -text <<-EOF
1242	set What =
1243	substr(cr.cr_number, 1, 7), synopsis
1244
1245	set Which =
1246	cr.cr_number in ($buglist)
1247
1248	set FinalClauses =
1249	order by cr.cr_number
1250
1251	do query/CR
1252	EOF
1253}
1254
1255bugdb_compare() {
1256	nawk -v bugdb=$1 -v active=$2 -f - <<-"EOF"
1257	BEGIN {
1258		file = bugdb
1259		while (getline < file) {
1260			synbugdb[$1] = substr($0, length($1) + 1)
1261		}
1262
1263		file = active
1264		while (getline < file) {
1265			synactive = substr($0, length($1) + 1)
1266			# If the synopsis doesn't match the bug database
1267			# and the synopsis isn't what's in the database
1268			# with " (something)" appended, spew a warning.
1269			if (!($1 in synbugdb)) {
1270				print "Bug " $1 " is not in the database"
1271			} else if (synbugdb[$1] != synactive &&
1272				!(index(synactive, synbugdb[$1]) == 1 &&
1273				  substr(synactive, length(synbugdb[$1])) ~ \
1274					/ \([^)]*\)$/)) {
1275				print "Synopsis of " $1 " is wrong:"
1276				print "  should be:" synbugdb[$1]
1277				print "         is:" synactive
1278			}
1279		}
1280	}
1281	EOF
1282}
1283
1284#
1285# Summarize all comments listing, in order:
1286#	 - free-form comments
1287#	 - ARC case comments
1288#	 - Bug comments
1289#
1290# -a will suppress ARC case comments
1291# -b will suppress bug comments
1292# -n will do pbchk processing (give all warnings, no error exit)
1293# -o will suppress free-form (other) comments
1294# -p will do pbcom checking and exit if it finds problems unless -v is given
1295# -u will prevent sorting of the ARC case comments and bug comments
1296# -v will output all comments verbatim (no sorting)
1297# -N will prevent bug lookup, which may take a while.
1298#
1299# Note, be careful when modifying this function as sometimes the output
1300# should go to stdout, as in the case of being called via pbchk and
1301# sometimes the output should go to stderr.  Think about this when
1302# making changes in here.
1303#
1304
1305function wx_summary {
1306	typeset i comment arc arcerr bug bugnospc bugid buglist synopsis \
1307		show_arcs=true \
1308		show_bugs=true \
1309		show_others=true \
1310		verbatim=false \
1311		pbchk=false \
1312		pbcom=false \
1313		cmd=sort \
1314		nolookup=false
1315
1316	while getopts :abnopuvN i; do
1317		  case $i in
1318		  a)	show_arcs=false;;
1319		  b)	show_bugs=false;;
1320		  n)	pbchk=true;;
1321		  o)	show_others=false;;
1322		  p)	pbcom=true;;
1323		  v)	verbatim=true;;
1324		  u)	cmd=cat;;
1325		  N)	nolookup=true;;
1326		  *)	fail "Invalid flag -$OPTARG. See 'wx help' for more"\
1327		  		"info.";;
1328		  esac
1329	done
1330
1331	# Store all the comments in a tmp file
1332	for filepath in $file_list; do
1333		wx_show_comment
1334	done | nawk '!seen[$0]++' > $wxtmp/comments
1335
1336	if grep -q "^NO_COMMENT$" $wxtmp/comments; then
1337		print -u2 "Warning, found NO_COMMENT. Run '$ME ea' to edit the "\
1338			  "comments."
1339		if $pbcom; then
1340			# Don't want to output anything to stdout for a pbcom
1341			# if there is an error.
1342			return 1
1343		fi
1344	fi
1345	if $pbcom && $verbatim; then
1346		# All comments output verbatim.  Note, is hacky because
1347		# of the wx_summary interface.  pbcom calls wx_summary
1348		# with -po assuming other comments shouldn't be output.
1349		show_others=true
1350	fi
1351
1352	# Note, hard tab in the arc regex.  This only recognizes FWARC,
1353	# LSARC and PSARC.  Also, regex must be compat with both egrep
1354	# and nawk.
1355	arc='(FW|LS|PS)ARC[\/ 	][12][0-9][0-9][0-9]\/[0-9][0-9][0-9][^0-9]'
1356	# bug ID must followed by single space
1357	bug='[0-9][0-9][0-9][0-9][0-9][0-9][0-9] '
1358	bugnospc='[0-9][0-9][0-9][0-9][0-9][0-9][0-9][^ ]'
1359
1360	if $show_arcs; then
1361		# Note must use /usr/bin/sort.
1362		if ! egrep "^$arc" $wxtmp/comments |
1363			 sed 's#\([A-Z][A-Z]*ARC\)[/ 	]#\1 #' | sort |
1364			 /usr/bin/sort -cu -k 1,2 2>$wxtmp/error >/dev/null
1365		then
1366			arcerr=$(nawk '{match($0, '"/$arc/"'); \
1367				print substr($0, RSTART, RLENGTH);}' \
1368				$wxtmp/error)
1369
1370			if $pbchk; then
1371				# if pbchk print to stdout
1372				print "Inconsistent ARC summaries for: $arcerr"
1373			else
1374				# else print to stderr
1375				print -u2 \
1376				"Inconsistent ARC summaries for: $arcerr"
1377			fi
1378			if $pbcom && ! $verbatim; then
1379
1380				# Don't want to output anything to
1381				# stdout for a pbcom if there is an
1382				# error.
1383
1384				return 1
1385			fi
1386		fi
1387	fi
1388
1389	if $show_bugs; then
1390		# Treating bug comments with "(nit comment)" at the end as the
1391		# same as the original bug hence the sed and nawk below
1392		# Note hard tabs in sed arg.
1393		# Must use /usr/bin/sort.
1394		if ! grep "^$bug" $wxtmp/comments |sort|
1395			 sed 's/[ 	][ 	]*([^)]*) *$//' |
1396			 nawk '!seen[$0]++'|
1397			 /usr/bin/sort -cnu 2>$wxtmp/error >/dev/null; then
1398			if $pbchk; then
1399				print -n "Inconsistent bug summaries for: "
1400				sed 's#^[^0-9]*\('"${bug}"'\).*#\1#' \
1401				    $wxtmp/error
1402			else
1403				print -nu2 "Inconsistent bug summaries for: "
1404				sed 's#^[^0-9]*\('"${bug}"'\).*#\1#' \
1405				    $wxtmp/error >&2
1406			fi
1407			if $pbcom && ! $verbatim; then
1408				# Don't want to output anything to
1409				# stdout for a pbcom if there is an
1410				# error.
1411				return 1
1412			fi
1413		fi
1414
1415		# Compare bug synopsis in active comments with the bug database.
1416		# If we've been passed -N, then we just never set buglist, thus
1417		# skipping the lookup.
1418		if ! $nolookup; then
1419			grep "^$bug" $wxtmp/comments | sort > $wxtmp/buglist.active
1420			cat $wxtmp/buglist.active | while read bugid synopsis; do
1421				buglist=$buglist,$bugid
1422			done
1423			buglist=${buglist#,}
1424		else
1425			if $pbchk; then
1426				print "Not performing bug synopsis check (be careful)"
1427			else
1428				print -u2 "Not performing bug synopsis check (be careful)"
1429			fi
1430		fi
1431		if [[ -n $buglist ]]; then
1432			bugdb_lookup_monaco $buglist > $wxtmp/buglist.bugdb
1433			if [[ -s $wxtmp/buglist.bugdb && $? -eq 0 ]]; then
1434				bugdb_compare $wxtmp/buglist.bugdb \
1435					$wxtmp/buglist.active > $wxtmp/error
1436				if [[ -s $wxtmp/error ]]; then
1437					if $pbchk; then
1438						cat $wxtmp/error
1439					else
1440						cat $wxtmp/error >&2
1441					fi
1442					if $pbcom && ! $verbatim; then
1443						# Don't want to output anything
1444						# to stdout for a pbcom if there
1445						# is an error.
1446						return 1
1447					fi
1448				fi
1449			else
1450				if $pbchk; then
1451					print "Could not perform bug synopsis check"
1452				else
1453					print -u2 "Could not perform bug synopsis check"
1454					if $pbcom; then
1455						print -u2 "Use -N to skip bug synopsis check (be careful)"
1456					fi
1457				fi
1458				if $pbcom && ! $verbatim; then
1459					# Don't want to output anything
1460					# to stdout for a pbcom if there
1461					# is an error.
1462					return 1
1463				fi
1464			fi
1465		fi
1466
1467		if egrep "^$bugnospc" $wxtmp/comments >$wxtmp/error; then
1468			# must have single space following bug ID
1469			if $pbchk; then
1470				print "\nThese bugs are missing single space following the bug ID"
1471				print "(output prefaced by active file line number ':'):"
1472				while read comment
1473				do
1474					grep -n "^$comment" $wxdir/active
1475				done < $wxtmp/error
1476			else
1477				print -u2 "\nThese bugs are missing single space following the bug ID"
1478				print -u2 "(output prefaced by active file line number ':'):"
1479				while read comment
1480				do
1481					grep -n "^$comment" $wxdir/active >&2
1482				done < $wxtmp/error
1483			fi
1484			if $pbcom && ! $verbatim; then
1485				# Don't want to output anything to
1486				# stdout for a pbcom if there is an
1487				# error.
1488				return 1
1489			fi
1490		fi
1491	fi
1492
1493	# Create warning for pbchk or pbcom.
1494	if $pbchk || ($pbcom && ! $verbatim) &&
1495		egrep -v "^($bug|$bugnospc|$arc|NO_COMMENT$)" $wxtmp/comments\
1496				> $wxtmp/other_comments; then
1497		cat <<-EOF
1498Warning, the following comments are found in your active list
1499that are neither bug or arc cases: 
1500
1501		EOF
1502		cat $wxtmp/other_comments
1503		print -- "\n---- End of active list comment warnings ----"
1504	fi > $wxtmp/comment_warning
1505
1506	if [[ -s $wxtmp/comment_warning ]]; then
1507		if $pbchk; then
1508			# output to stdout, don't exist in case there are more
1509			# warnings.
1510			cat $wxtmp/comment_warning
1511		elif $pbcom && ! $verbatim ; then
1512			# Don't want to output anything to stdout for a pbcom
1513			# if there is an error.
1514			cat $wxtmp/comment_warning >&2
1515			return 1
1516		fi
1517	fi
1518
1519	if $pbchk; then
1520		cd $workspace
1521		# See if sccs delta comment is the same as active list comment
1522		for filepath in $file_list; do
1523			# extract most recent delta comment
1524			sccs prs -d ':C:' $filepath | sort > $wxtmp/com1 ||
1525				fail "sccs prs -d ':C:' $filepath failed."
1526
1527			# Add blank line to active comments so following cmp will
1528			# work properly.
1529			(wx_show_comment; print) | sort > $wxtmp/com2
1530
1531			if ! cmp -s $wxtmp/com1 $wxtmp/com2; then
1532				print "\n$filepath"
1533				print "Warning: current sccs comment does not"\
1534					"match active list comment"
1535				print "(< sccs comment, > active list comment):"
1536				diff $wxtmp/com1 $wxtmp/com2
1537			fi
1538		done
1539		# Just output warnings for pbchk
1540		return 0
1541	fi
1542
1543	if $show_others; then
1544		  if ! $verbatim; then
1545			  # The non-verbatim check should have produced the
1546			  # other_comments file.
1547			  cat $wxtmp/other_comments| $cmd
1548		  else
1549			  # just output comments verbatim then return
1550			  cat $wxtmp/comments
1551			  return 0
1552		  fi
1553	fi
1554
1555	if $show_arcs; then
1556		  egrep "^$arc" $wxtmp/comments | $cmd
1557	fi
1558
1559	if $show_bugs; then
1560		  egrep "^$bug" $wxtmp/comments | $cmd
1561	fi
1562	return 0
1563}
1564
1565update_renamed_file() {
1566	# Try to add a entry to the renamed list.  Note, this stores the new
1567	# name and previous name, not the  parent name as this is more useful in
1568	# detecting cyclic renames.
1569
1570	# ofp: old filepath, nfp: new filepath
1571	typeset ofp=$1 nfp=$2
1572
1573	# remove old and new entries from renamed
1574	egrep -v "^($(escape_re $ofp)|$(escape_re $nfp)) " $wxdir/renamed \
1575		>$wxtmp/renamed
1576	[[ ! -f $wxtmp/renamed ]] && fail "Error: cannot create $wxtmp/renamed"
1577	mv -f $wxtmp/renamed $wxdir/renamed || \
1578		fail "Error: cannot create $wxdir/renamed."
1579
1580	# remove old entries from local nt cache
1581	remove_local_nt_entry $ofp
1582
1583	# Do not update renamed list if the filepath is the same as
1584	# the parent or the file is new.
1585	if lookup_parent $nfp; then
1586		if [[ $parentfile != $nfp ]]; then
1587			print "$nfp $ofp" >> $wxdir/renamed || \
1588				fail "Error: cannot append $wxdir/renamed."
1589
1590			[[ $ACTSORT == sort ]] && do_renamed_sort=true
1591		fi
1592	fi
1593	return 0
1594}
1595
1596update_renamed_dir() {
1597	typeset append pb_files new orig
1598	typeset -i rc
1599
1600	if [[ $# -eq 0 ]]; then
1601		# No args so we need to create the renamed list from
1602		# the source root.
1603		append=false
1604		if [ -r $wxdir/srcroot_dir ]; then
1605			pb_files=$(cat $wxdir/srcroot_dir)
1606		else
1607			pb_files=$DEFAULT_SRCDIR
1608		fi
1609	else
1610		# Assuming one or more filepaths as args
1611		append=true
1612		pb_files="$*"
1613	fi
1614	echo "Updating $ME renamed list... this may take several minutes."
1615
1616	# Get output of putback -n to detect renames elsewhere in
1617	# this script.
1618
1619	get_pb_output $pb_files
1620
1621	nawk '
1622		/^rename from:/{orig_file=$3}
1623		$1 == "to:" {print $2 " " orig_file}' \
1624		$wxtmp/putback.out  >$wxtmp/pb_renames || \
1625			fail "Error, creation of $wxtmp/pb_renames failed."
1626
1627	cp $wxdir/renamed $wxdir/renamed.old ||
1628		fail "Error: cannot create $wxdir/renamed.old."
1629
1630	if $append; then
1631		nawk '!seen[$0]++' $wxtmp/pb_renames $wxdir/renamed \
1632			> $wxtmp/renamed || \
1633			fail "Error: cannot create $wxtmp/renamed."
1634		mv -f $wxtmp/renamed $wxdir/renamed || \
1635			fail "Error: cannot create $wxdir/renamed."
1636	else
1637		mv -f $wxtmp/pb_renames $wxdir/renamed ||
1638			fail "Error: cannot create $wxdir/renamed."
1639	fi
1640
1641	[[ $ACTSORT == sort ]] && do_renamed_sort=true
1642}
1643
1644# returns 0 if a pattern in patternfile matches input path arg
1645pathcheck() {
1646	typeset pattern path=$1 patternfile=$2
1647
1648	while read pattern; do
1649		if [[ $path == $pattern ]]; then
1650			return 0 # pattern matched path
1651		fi
1652	done < $patternfile
1653	return 1 # file path not matched by pattern
1654}
1655
1656#
1657# Evaluate a command for all listed files.  This is the basic building
1658# block for most wx functionality.
1659#
1660
1661wx_eval() {
1662	typeset -i changedir=1
1663
1664	if [[ $# -gt 1 && "$1" == "-r" ]]; then
1665		changedir=0
1666		shift
1667	fi
1668
1669	pre_eval=$*
1670	# skip files listed in .NOT files
1671	cat -s $wxdir/$command.NOT >$wxtmp/NOT
1672	for filepath in $file_list; do
1673		if pathcheck $filepath $wxtmp/NOT; then
1674			print "$filepath (skipping)"
1675		else
1676			cd $workspace
1677			dir=`dirname $filepath`
1678			file=`basename $filepath`
1679			[[ $changedir -eq 1 ]] && cd $dir
1680			eval $pre_eval
1681		fi
1682	done
1683}
1684
1685#
1686# Initialize a workspace for wx.
1687#
1688
1689wx_init() {
1690	typeset srcroot_dir force=false
1691
1692	# check that srcroot is relative to top of workspace
1693	if cd $workspace/$1; then
1694		# normalize the srcroot dir for test below
1695		srcroot_dir=$(/bin/pwd)
1696		srcroot_dir="${srcroot_dir#$workspace/}"
1697		if [[ $srcroot_dir == $workspace ]]; then
1698			# Special case need to set srcroot_dir to
1699			# a relative path but we're at the top of the
1700			# workspace.
1701			srcroot_dir="."
1702		fi
1703	else
1704		fail "Source root '$1' does not exist in workspace"\
1705			"($workspace)."
1706	fi
1707
1708	if [ -d $wxtmp ]; then
1709                if [[ $2 != -f[nqt] ]]; then
1710                        echo "This workspace has already been initialized."
1711                        ok_to_proceed 'Do you really want to re-initialize?'
1712                fi
1713	else
1714		mkdir -p $wxtmp
1715	fi
1716
1717	#
1718	# Make sure to save $srcroot_dir as a path relative to the workspace
1719	# root; an absolute path would break if the workspace name changed.
1720	#
1721	rm -f $wxdir/srcroot_dir
1722	echo $srcroot_dir >$wxdir/srcroot_dir
1723
1724	backup_dir=$HOME/$ME.backup/$workspace_basename
1725	[[ -d $backup_dir ]] || mkdir -p $backup_dir ||
1726		fail "mkdir -p $backup_dir failed."
1727	cd $backup_dir
1728	rm -f $wxdir/backup_dir
1729	pwd >$wxdir/backup_dir || fail "Creation of  $wxdir/backup_dir failed."
1730
1731	touch $wxdir/renamed
1732	touch $wxdir/active
1733	touch $wxdir/new
1734	touch $wxdir/local_nametable
1735
1736	if [[ -z "$2" ]]; then
1737		# Interactive mode
1738		cat << EOF
1739    Pick one of the following update methods:
1740
1741    1) Thorough: Detect any differences between the current workspace
1742       and its parent and update the active, new and renamed lists.  Use
1743       this in workspaces where files have been renamed or deleted or
1744       there are files that are different from the parent that are not
1745       checked out.  Note, files must be under SCCS control in order for
1746       this method to compare them to the parent workspace.
1747
1748    2) Quick: Only update the active list with files that are currently
1749       checked out.  Will not update the renamed list.
1750
1751    3) None: Use this on workspaces where there are no changes between
1752       the workspace and its parent.  This is very quick but will not
1753       update the active, new or renamed lists.
1754
1755EOF
1756		read answer?"Which update method? [1|2|3]: "
1757		case $answer in
1758			1) wx_update;;
1759			2) wx_update -q;;
1760			3) ;;
1761			*) fail "Bad answer: ${answer}. Rerun init command"\
1762				"again";;
1763		esac
1764		yesno "Keep active list sorted by default?"
1765		if [[ "$answer" == 'yes' ]]; then
1766			print "true" > $wxdir/sort_active
1767		else
1768			print "false" > $wxdir/sort_active
1769		fi
1770	else
1771		# non-interactive mode
1772		case $2 in
1773			# forced thorough update
1774			-ft) wx_update; force=true;;
1775			# forced quick update
1776			-fq) wx_update -q; force=true;;
1777			# forced no update
1778			-fn) force=true;;
1779			# invalid arg
1780			-*) fail "$ME $command: unrecognized argument";;
1781		esac
1782		if [[ $3 == -s ]]; then
1783			print "true" > $wxdir/sort_active
1784		else
1785			print "false" > $wxdir/sort_active
1786		fi
1787	fi
1788	print
1789
1790	if [ -s $wxdir/active ]; then
1791		basedir=$workspace
1792		file_list=`wx_active`
1793		if $force; then
1794			# only backup if necessary
1795			print "Will backup wx and active files if necessary"
1796			wx_backup -t
1797		else
1798			print "Making backup copies of all wx and active files"
1799			wx_backup
1800		fi
1801	else
1802		echo "Active list empty, not doing backup."
1803		echo
1804	fi
1805
1806	echo "$ME initialization complete"
1807}
1808
1809#
1810# Find all checked out files
1811#
1812
1813wx_checked_out() {
1814	typeset origdir=$(pwd)
1815	cd $workspace
1816	x=$(ls -t $wsdata/nametable $wxdir/sccs_dirs 2>/dev/null)
1817	if [[ -z $x || "`basename $x`" == nametable ]]; then
1818		if [ -f $wxdir/srcroot_dir ]; then
1819			srcroot_dir=`cat $wxdir/srcroot_dir`
1820		else
1821			srcroot_dir=$DEFAULT_SRCDIR
1822		fi
1823		print -u2 "Workspace nametable changed: sccs_dirs out of date"
1824		print -u2 "Updating $wxdir/sccs_dirs...this may take a few minutes.\n"
1825		rm -f $wxdir/sccs_dirs
1826		find $srcroot_dir -name SCCS -print | sort >$wxdir/sccs_dirs
1827	fi
1828	cd $workspace
1829	rm -f $wxtmp/checked_out
1830	# Note if srcroot_dir = . this must be removed from the front of the
1831	# filepaths.
1832	echo $(sed -e 's,$,/p.*,' $wxdir/sccs_dirs) | \
1833		tr \\040 \\012 | \
1834		grep -v '*' | \
1835		sed -e 's,SCCS/p.,,' |sed -e 's,^\./,,' >$wxtmp/checked_out
1836	cd $origdir
1837}
1838
1839deal_ws_renames() {
1840	# See if any active/renamed files were renamed externally
1841	# (perhaps by bringover?) and try to rename the active entry
1842	# filepath.
1843	typeset fp newfp renamed localfile hash1 hash2 hash3 hash4 \
1844		notrenamed_list origdir=$(pwd)
1845	cd $workspace
1846	list_putback |\
1847	while read fp; do
1848		if [[ ! -f $fp ]]; then
1849			# file not found, suspect rename.
1850			# using renamed for error checking.
1851			renamed=false
1852			# search cached local nt to find old hash info
1853			grep "^$(escape_re $fp) " $wxdir/local_nametable |\
1854			while read localfile hash1 hash2 hash3 hash4; do
1855				# find new filepath
1856				newfp=$(fgrep " $hash1 $hash2 $hash3 $hash4"\
1857					$wsdata/nametable|cut -f1 -d' ')
1858
1859				[[ -z $newfp ]] && continue
1860
1861				if [[ $newfp != $fp ]]; then
1862					update_renamed_file $fp $newfp
1863					rename_active_entry $fp $newfp
1864					echo "\nRenamed active file"\
1865						"$fp to $newfp"
1866					renamed=true
1867					break
1868				fi
1869			done
1870			if ! $renamed; then
1871				if [[ -z $notrenamed_list ]]; then
1872					notrenamed_list="$fp"
1873				else
1874					notrenamed_list="$notrenamed_list\n$fp"
1875				fi
1876			fi
1877		fi
1878	done
1879	if [[ -n $notrenamed_list ]]; then
1880		ring_bell
1881		cat <<-EOF
1882Warning, active file(s):
1883   $(echo $notrenamed_list)
1884not found and cannot be renamed.  Use "$ME ea" to edit the active list to
1885remove these entries if they do not exist in this workspace.
1886		EOF
1887	fi
1888	cd $origdir
1889}
1890
1891#
1892# Old style update the active file list (by appending all checked out files).
1893# This is what the original wx update did.
1894#
1895wx_update_quick() {
1896
1897	# Sort active if requested.
1898	[[ "$1" == "-s" ]] && ACTSORT=sort
1899
1900	wx_checked_out
1901	cd $wxdir
1902	rm -f tmp/files.old tmp/files.new active.new
1903	wx_active >tmp/files.old || fail "Error: cannot create $wxtmp/files.old"
1904	# sed has a hard tab, used to delete lines containing only whitespace
1905	sed '/^[	 ]*$/d' tmp/files.old tmp/checked_out|
1906		nawk '!seen[$0]++' | $ACTSORT > tmp/files.new ||
1907			fail "Error: cannot create $wxtmp/files.new"
1908	cp -f new new.old || fail "Error: cannot create new.old."
1909	while read filepath ; do
1910		add_local_nt_entry $filepath
1911		(echo "$filepath"; echo; wx_show_comment; echo)
1912	done < tmp/files.new > active.new ||
1913		fail "Error: cannot create $wxdir/active.new"
1914
1915	mv -f active active.old
1916	mv -f active.new active
1917
1918	echo
1919	echo "New active file list:"
1920	echo
1921	cat tmp/files.new
1922	echo
1923	echo "Diffs from previous active file list:"
1924	echo
1925	${WXDIFFCMD:-diff} tmp/files.old tmp/files.new
1926	echo "End active diffs =========================="
1927
1928	# Do new file processing after active list is updated.
1929
1930	# Note, the parent nt read check below is hackish because we are
1931	# assuming that lookup_parent needs to read the parent nt and if it
1932	# can't then we want to just issue the warning.
1933	if [[ -n $parent && ! -r $parent/Codemgr_wsdata/nametable ]]; then
1934		echo "\nWarning: cannot read parent nametable, new file list"\
1935			"not output." >&2
1936	else
1937		while read filepath; do
1938			# lookup_parent populates local nt cache
1939			if lookup_parent $filepath; then
1940				remove_new $filepath
1941			else
1942				add_new $filepath
1943			fi
1944		done < tmp/files.new
1945
1946		echo
1947		echo "New new-file list:"
1948		echo
1949		cat new
1950		echo
1951		echo "Diffs from previous new-file list:"
1952		echo
1953		${WXDIFFCMD:-diff} new.old new
1954		echo "End new diffs =========================="
1955	fi
1956}
1957
1958#
1959# Update various lists (active, renamed, new)
1960#
1961
1962wx_update() {
1963	typeset i efp sortarg do_quick=false
1964	typeset -i rc found
1965	# default, update all lists
1966	typeset update_active=true update_renamed=true update_new=true
1967
1968	while getopts :qrs i; do
1969		  case $i in
1970		  q)	do_quick=true;;
1971		  r)	update_active=false
1972			update_new=false;;
1973		  s)	ACTSORT=sort; sortarg="-s";;
1974		  *)	fail "Invalid flag -$OPTARG. See 'wx help' for more"\
1975		  		"info.";;
1976		  esac
1977	done
1978
1979	deal_ws_renames
1980
1981	if $do_quick; then
1982		# Do old school wx update (only for checked out files)
1983		# This is faster but not as thorough.
1984		wx_update_quick $sortarg
1985		return
1986	fi
1987
1988	if [[ -z $parent ]]; then
1989		fail "Error: cannot do thorough update, no parent.  Use 'update -q'"\
1990			"instead."
1991	fi
1992	if [[ ! -r $parent/Codemgr_wsdata/nametable ]]; then
1993		fail "Error: cannot read $parent/Codemgr_wsdata/nametable"
1994	fi
1995
1996	# create tmp/checked_out file which putback -n may not list.
1997	# Do this before putback so it doesn't unnecessarily update the
1998	# sccs dirs cache.
1999	wx_checked_out
2000
2001	# Get output of putback -n to detect renames and active files.
2002	if [ -f $wxdir/srcroot_dir ]; then
2003		get_pb_output $(cat $wxdir/srcroot_dir)
2004	else
2005		get_pb_output $DEFAULT_SRCDIR
2006	fi
2007
2008	cd $wxdir
2009
2010	if $update_renamed; then
2011		if [[ -f renamed ]]; then
2012			mv renamed renamed.old ||
2013			    fail "Error: cannot create $wxdir/renamed.old."
2014		else
2015			touch renamed.old ||
2016			    fail "Error: cannot create $wxdir/renamed.old."
2017		fi
2018
2019		nawk '  /^rename from:/{orig_file=$3}
2020			$1 == "to:" {print $2 " " orig_file}' \
2021			tmp/putback.out |$ACTSORT > tmp/renamed ||\
2022				fail "Error, creation of $wxdir/tmp/renamed failed."
2023			mv -f tmp/renamed renamed || \
2024				fail "Error: cannot create $wxdir/renamed"
2025
2026		for filepath in $(cut -f1 -d' ' renamed); do
2027			add_local_nt_entry $filepath
2028		done
2029
2030		echo "New renamed file list:"
2031		echo
2032		cat renamed
2033		echo
2034		echo "Diffs from previous renamed file list:"
2035		echo
2036		${WXDIFFCMD:-diff} renamed.old renamed
2037		echo "End renamed diffs =========================="
2038	fi
2039
2040	if $update_active; then
2041		# Create active list from putback output.
2042		nawk '/^(update|create): / {print $2};
2043		     /^The following files are currently checked out/ \
2044			{p=1; continue};
2045		     /^"/ {continue};
2046		     NF == 0 {p=0; continue};
2047		     {if (p==1)
2048		     	print $1}' tmp/putback.out |
2049		     sort -u > tmp/active_nawk_out ||
2050		     fail "Error: cannot create $wxtmp/active_nawk_out"
2051
2052		# list files in conflict also
2053		nawk '/^(conflict): / {print $2}' tmp/putback.out |
2054		    sort -u > tmp/conflict_nawk_out ||
2055		    fail "Error: cannot create $wxtmp/conflict_nawk_out"
2056
2057		# Need to read $wsdata/nametable if there are conflicts
2058		if [[ -s tmp/conflict_nawk_out && ! -r $wsdata/nametable ]]
2059		then
2060			fail "Error: cannot read $wsdata/nametable."
2061		fi
2062
2063		# clear the tmp active file
2064		print -n > tmp/active.new || fail "Error: cannot create tmp/active.new."
2065
2066		# store current active list
2067		wx_active > tmp/files.old ||
2068			fail "Error: cannot create $wxtmp/files.old"
2069
2070		# go through all the possible active files (keeping the existing
2071		# ones as well). Note hard tab in sed arg.
2072		for filepath in $(sed '/^[	 ]*$/d' tmp/files.old \
2073			tmp/checked_out tmp/conflict_nawk_out \
2074			tmp/active_nawk_out | nawk '!seen[$0]++' | $ACTSORT); do
2075
2076			efp=$(escape_re $filepath)
2077
2078			if grep -q "^$efp$" tmp/conflict_nawk_out; then
2079
2080				# conflict files have a parent but the
2081				# putback output only shows the parent's
2082				# filename, need to find local name in
2083				# case of rename
2084
2085				grep "^$efp " $parent/Codemgr_wsdata/nametable|\
2086					read localfile hash1 hash2 hash3 hash4
2087				local_file="$(\
2088				    fgrep " $hash1 $hash2 $hash3 $hash4" \
2089					$wsdata/nametable | cut -f1 -d' ')"
2090
2091				# continue if empty string
2092				[[ -z "$local_file" ]] && continue
2093
2094				if ! grep -q "^$local_file" tmp/active.new; then
2095					filepath=$local_file
2096				else
2097					continue
2098				fi
2099			fi
2100			add_local_nt_entry $filepath
2101			(echo $filepath; echo; wx_show_comment; echo)\
2102			    >> tmp/active.new
2103		done
2104
2105		rm -f tmp/active_nawk_out
2106
2107		mv -f active active.old
2108		mv -f tmp/active.new active
2109		wx_active > tmp/files.new
2110
2111		echo
2112		echo "New active file list:"
2113		echo
2114		cat tmp/files.new
2115		echo
2116		echo "Diffs from previous active file list:"
2117		echo
2118		${WXDIFFCMD:-diff} tmp/files.old tmp/files.new
2119		echo "End active diffs =========================="
2120		rm -f tmp/files.old tmp/files.new
2121
2122	fi
2123
2124	# The new list is for caching names of new files to speed up commands
2125	# that list the new files.
2126	if $update_new; then
2127		if [ -f new ]; then
2128			cp -f new new.old || fail "Error: cannot create file new.old."
2129		elif [ ! -f new.old ]; then
2130			touch new.old || fail "Error: cannot create file new.old."
2131		fi
2132		# Create new list from putback output.
2133		nawk '/^create: / {print $2};' tmp/putback.out |
2134			sort -u > tmp/new || fail "Error: cannot create $wxtmp/new."
2135		mv -f tmp/new new
2136		echo
2137		echo "New new-file list:"
2138		echo
2139		cat new
2140		echo
2141		echo "Diffs from previous new-file list:"
2142		echo
2143		${WXDIFFCMD:-diff} new.old new
2144		echo "End new diffs =========================="
2145	fi
2146}
2147
2148wx_edit() {
2149	# Must be called via wx_eval
2150	if [ -f SCCS/p.$file ]; then
2151		echo "$filepath already checked out"
2152		update_active $filepath
2153	elif [ -f $file ]; then
2154		echo $filepath
2155		if sccs edit $silent $file; then
2156			update_active $filepath
2157		else
2158			fail "sccs edit $filepath failed."
2159		fi
2160	else
2161		ring_bell
2162		echo "Warning. file $filepath not found."
2163	fi
2164
2165	[[ $ACTSORT == sort ]] && do_active_sort=true
2166}
2167
2168wx_unedit() {
2169	# Must be called via wx_eval
2170	typeset -i force=0
2171	typeset arg msg
2172
2173	# set positional args to contents of global $args
2174	set -- $args
2175
2176	while getopts :f arg; do
2177		case $arg in
2178			 f) force=1;;
2179			 *) fail "Invalid flag -$OPTARG. See 'wx help' for"\
2180			 	"more info.";;
2181		esac
2182	done
2183
2184	msg="differs from parent file, will remain in active list."
2185
2186	if [[ ! -f SCCS/p.$file ]]; then
2187		echo "$filepath not checked out"
2188	else
2189		if [[ $backup_done -eq 0 ]]; then
2190			if [[ $force -eq 0 ]]; then
2191				yesno "Do you want to backup files first?"
2192				if [[ "$answer" == "yes" ]]; then
2193					wx_backup || fail "Backup failed."
2194				fi
2195			else
2196				# only backup if necessary
2197				print "Will backup wx and active files if necessary"
2198				wx_backup -t || fail "Backup failed."
2199			fi
2200			backup_done=1;
2201
2202			# cd to the dir where the file is in case
2203			# wx_backup took us somewhere else.
2204
2205			cd ${workspace}/$dir
2206		fi
2207
2208		echo $filepath
2209		if sccs unedit $silent $file; then
2210			if [[ $force -eq 1 ]]; then
2211				if is_active $filepath; then
2212					if wx_pnt_filepath $filepath; then
2213						if cmp -s $file $parentfilepath; then
2214							remove_active_entry $filepath
2215						else
2216							print "$filepath $msg"
2217						fi
2218					fi
2219				fi
2220			else
2221				ask_remove_active_entry
2222			fi
2223		fi
2224	fi
2225}
2226
2227wx_create() {
2228	# Must be called via wx_eval
2229	typeset -i checkout=0 force=0
2230	typeset arg
2231
2232	while getopts :fo arg; do
2233		case $arg in
2234			 o) checkout=1;;
2235			 f) force=1;;
2236			 *) fail "Invalid flag -$OPTARG. See 'wx help' for"\
2237			 	"more info.";;
2238		esac
2239	done
2240
2241	if [ ! -f $file ]; then
2242		ring_bell
2243		echo "Error! $filepath is not a file."
2244		return 1
2245	elif [ -f $workspace/deleted_files/$filepath ]; then
2246		ring_bell
2247		cat >&2 <<-EOF
2248Error: a deleted version of $filepath exists.
2249You must undelete the file and edit that version.
2250Run:
2251'cd $workspace'
2252'$ME mv deleted_files/$filepath $filepath'
2253'$ME edit $filepath'
2254		EOF
2255		return 1
2256	elif [[ -n $parent && -f $parent/$filepath ]]; then
2257		ring_bell
2258		cat >&2 <<-EOF
2259Error! $filepath exists in the parent workspace $parent.
2260Choose a different name.
2261		EOF
2262		return 1
2263	elif [[ -n $parent && -f $parent/deleted_files/$filepath ]]; then
2264		ring_bell
2265		cat >&2 <<-EOF
2266Error! a deleted version of $filepath exists in the parent workspace
2267You must undelete the file and edit that version.
2268Run:
2269'cd $workspace'
2270'bringover deleted_files/$filepath'
2271'$ME mv deleted_files/$filepath $filepath'
2272'$ME edit $filepath'
2273		EOF
2274		return 1
2275	elif [ -f SCCS/s.$file ]; then
2276			echo "$filepath already created, active list not"\
2277				"updated." >&2
2278	else
2279		# XXX it would be nice if keyword would work on new files
2280		if ! egrep "$SCCSKEYWORD" $file >/dev/null; then
2281			ring_bell
2282			cat >&2 <<-EOF
2283
2284Warning!!!
2285$filepath 
2286is missing SCCS keywords.  See
2287/net/wizard.eng/export/misc/general_docs/keyword_info.txt
2288for more info.  Note, pay attention to the tabs.
2289			EOF
2290			if [[ $force -ne 1 ]]; then
2291				yesno "Continue with create?"
2292				if [[ "$answer" != 'yes' ]]; then
2293					echo "Aborting create $filepath"
2294					return 1
2295				fi
2296			fi
2297		fi
2298
2299		if ! copyrightchk $file; then
2300			# Sound bell
2301			ring_bell
2302			cat >&2 <<-EOF
2303
2304Warning!!!
2305$filepath 
2306has copyright problems.  See
2307/net/wizard.eng/export/misc/general_docs/golden_rules.txt
2308for more info.
2309			EOF
2310			if [[ $force -ne 1 ]]; then
2311				yesno "Continue with create?"
2312				if [[ "$answer" != 'yes' ]]; then
2313					echo "Aborting create $filepath"
2314					return 1
2315				fi
2316			fi
2317		fi
2318
2319		if ! cddlchk -a $file; then
2320			# Sound bell
2321			ring_bell
2322			cat >&2 <<-EOF
2323
2324Warning!!!
2325$filepath 
2326has CDDL block problems.  See
2327http://www.opensolaris.org/os/community/onnv/devref_toc/devref_7/#7_2_3_nonformatting_considerations
2328for more info.
2329			EOF
2330			if [[ $force -ne 1 ]]; then
2331				yesno "Continue with create?"
2332				if [[ "$answer" != 'yes' ]]; then
2333					echo "Aborting create $filepath"
2334					return 1
2335				fi
2336			fi
2337		fi
2338
2339		if [[ ! -d SCCS ]]; then
2340			mkdir SCCS || fail "Error: cannot create SCCS dir."
2341		fi
2342
2343		if [[ -n "$comment_file" ]]; then
2344			sccs create $silent -y"$(\
2345			    sed '/^[ 	]*$/d' $comment_file)" $file ||
2346				    fail "sccs create $filepath failed."
2347		else
2348			sccs create $silent $file ||
2349				fail "sccs create $filepath failed."
2350		fi
2351		rm -f ,$file
2352		update_active $filepath
2353		add_new $filepath
2354		if [[ $checkout -eq 1 ]]; then
2355			sccs edit $silent $file ||
2356				fail "sccs edit $filepath failed."
2357		fi
2358		[[ $ACTSORT == sort ]] && do_active_sort=true
2359	fi
2360}
2361
2362wx_uncreate() {
2363	# Must be called via wx_eval
2364	# undoes a 'wx create'
2365
2366	typeset efp
2367	typeset -i force=0
2368
2369	case $1 in
2370		-f) force=1;;
2371		-*) fail "$ME $command: unrecognized argument";;
2372	esac
2373
2374	if [[ $backup_done -eq 0 ]]; then
2375		if [[ $force -eq 0 ]]; then
2376			yesno "Do you want to backup files first?"
2377			if [[ "$answer" == 'yes' ]]; then
2378				wx_backup || fail "Backup failed."
2379			fi
2380		else
2381			# only backup if necessary
2382			print "Will backup wx and active files if necessary"
2383			wx_backup -t || fail "Backup failed."
2384		fi
2385		backup_done=1;
2386		cd $workspace/$dir
2387	fi
2388
2389	if [[ ! -f $file ]]; then
2390		echo "$filepath not found, skipping"
2391		return 1
2392	fi
2393
2394	efp=$(escape_re $filepath)
2395
2396	if ! wx_pnt_filepath; then
2397		# This is a new file so let's uncreate it.
2398		answer='no'
2399		if [[ $force -ne 1 ]]; then
2400			cat <<-EOF
2401
2402$filepath appears to be a new file.  
2403Note, $command will remove its SCCS info from your
2404workspace and entry in the active list but will leave
2405the file in your workspace.  
2406
2407			EOF
2408			# yesno sets answer
2409			yesno "Continue $command $filepath?"
2410		else
2411			# forced to yes
2412			answer='yes'
2413		fi
2414
2415		if [[ "$answer" == 'yes' ]]; then
2416			if [[ ! -f SCCS/p.$file ]]; then
2417				sccs edit $file ||
2418				    fail "sccs edit $filepath failed."
2419			fi
2420			rm -f SCCS/s.$file SCCS/p.$file
2421			# For later cleanup on exit
2422			if grep -q "^$efp " $wsdata/nametable 2>/dev/null; then
2423				NEED_WS_CLEAN='y'
2424			fi
2425
2426			if is_active $filepath; then
2427				remove_active_entry $filepath
2428			fi
2429			remove_new $filepath
2430		else
2431			echo "skipping $filepath"
2432		fi
2433	else
2434		echo "Not new, skipping $filepath"
2435	fi # if ! wx_pnt_filepath
2436}
2437
2438wx_reset() {
2439	# Must be called via wx_eval
2440	# resets a local file to the parent version
2441
2442	typeset efp
2443	typeset -i force=0
2444
2445	case $1 in
2446		-f) force=1;;
2447		-*) fail "$ME $command: unrecognized argument";;
2448	esac
2449
2450	if [[ $backup_done -eq 0 ]]; then
2451		if [[ $force -eq 0 ]]; then
2452			yesno "Do you want to backup files first?"
2453			if [[ "$answer" == 'yes' ]]; then
2454				wx_backup || fail "Backup failed."
2455			fi
2456		else
2457			# only backup if necessary
2458			print "Will backup wx and active files if necessary"
2459			wx_backup -t || fail "Backup failed."
2460		fi
2461		backup_done=1;
2462	fi
2463
2464	if [[ ! -f $file ]]; then
2465		print "$filepath not found, skipping"
2466		return 1
2467	fi
2468
2469	efp=$(escape_re $filepath)
2470
2471	if wx_pnt_filepath; then
2472		if [[ $force -ne 1 ]]; then
2473			answer='no' # safe default
2474			cat <<-EOF
2475
2476Regarding: $filepath
2477$command will reset the file contents and sccs history to that of the parent:
2478$parentfilepath
2479and remove the entry from the active and renamed lists.
2480
2481			EOF
2482			if [[ $filepath != $parentfile ]]; then
2483				print "Note: local file will be reset to parent filepath."
2484			fi
2485			# yesno sets answer
2486			yesno "Continue $command $filepath?"
2487		else
2488			# forced to yes
2489			answer='yes'
2490		fi
2491
2492		if [[ "$answer" == 'yes' ]]; then
2493			if is_active $filepath; then
2494				remove_active_entry $filepath
2495			fi
2496			if renamed; then
2497				remove_renamed_entry $filepath
2498			fi
2499			rm -f $file SCCS/[ps].$file
2500			grep -v "^$efp " $wsdata/nametable > $wxtmp/nametable.new || \
2501				fail "Error: cannot create $wxtmp/nametable.new ."
2502			mv -f $wxtmp/nametable.new $wsdata/nametable || \
2503				fail "Error: mv -f $wxtmp/nametable.new $wsdata/nametable failed."
2504
2505			# add to bringover list for more efficient bringover
2506			bofilelist="$bofilelist $parentfile"
2507		else
2508			print -u2 "Skipping $filepath"
2509		fi
2510	else
2511		cat >&2 <<-EOF
2512
2513Warning: skipping $filepath 
2514as it appears to be new. Use 'uncreate' to remove this new file from the
2515workspace.
2516		EOF
2517	fi # if ! wx_pnt_filepath
2518}
2519
2520
2521cyclic_rename() {
2522	# Detect the cyclic rename that causes Teamware problems.
2523	# See 'man workspace' for more info
2524	typeset new_filepath=$1 old_filepath=$2\
2525		found_new=false found_old=false
2526	typeset prev_new prev_old
2527
2528	while read prev_new prev_old; do
2529		if [[ "deleted_files/$new_filepath" == $prev_new &&
2530			  $old_filepath != $prev_new ]]; then
2531
2532			# Cyclic rename
2533			return 0
2534		fi
2535
2536		if [[ $new_filepath == $prev_old && $prev_new != $old_filepath ]]
2537		then
2538			# The new file was the old file of a previous rename
2539			found_new=true
2540			if $found_old; then
2541				# Cyclic rename
2542				return 0
2543			fi
2544		elif [[ $old_filepath == $prev_new &&
2545		    $new_filepath != $prev_old ]]; then
2546
2547			# The old filepath was the new filepath of a
2548			# previous rename and this rename is not an undo
2549			# (new filepath is diff from previous old
2550			# filepath)
2551
2552			found_old=true
2553			if $found_new; then
2554				# Cyclic rename
2555				return 0
2556			fi
2557		fi
2558	done < $wxdir/renamed
2559
2560	# Not a cyclic rename
2561	return 1
2562}
2563
2564wx_delete() {
2565	# Must be called via wx_eval
2566	typeset efp
2567	typeset -i force=0
2568
2569	case $1 in
2570		-f) force=1;;
2571		-*) fail "$ME $command: unrecognized argument";;
2572	esac
2573
2574	if [[ $backup_done -eq 0 ]]; then
2575		if [[ $force -eq 0 ]]; then
2576			yesno "Do you want to backup files first?"
2577			if [[ "$answer" == 'yes' ]]; then
2578				wx_backup || fail "Backup failed."
2579			fi
2580		else
2581			# only backup if necessary
2582			print "Will backup wx and active files if necessary"
2583			wx_backup -t || fail "Backup failed."
2584		fi
2585		backup_done=1;
2586		cd $workspace/$dir
2587	fi
2588
2589	if [[ ! -f $file ]]; then
2590		fail "$filepath isn't a file."
2591	fi
2592
2593	# this is used a couple times so save escape_re value
2594	efp=$(escape_re $filepath)
2595
2596	if wx_pnt_filepath; then
2597		# Not a new file (has a parent)
2598		if is_active $filepath; then
2599			ring_bell
2600			cat >&2 <<-EOF
2601
2602Warning! $filepath 
2603is in active list. You should run
2604"$ME reedit $filepath" 
2605"$ME unedit $filepath" 
2606which should remove it from the active list then run 
2607"$ME $command $filepath".
2608Note, if you have made changes to this file that you want to keep, back
2609it up first.
2610
2611$filepath not deleted.
2612
2613			EOF
2614			return 1
2615		fi
2616		# See if this is already in the renamed list
2617		if grep -q "^deleted_files/$efp " $wxdir/renamed; then
2618			ring_bell
2619			if [[ -f $workspace/deleted_files/$filepath ]]; then
2620				cat >&2  <<-EOF
2621Warning: $filepath
2622has already been deleted.
2623Check for deleted_files/$filepath
2624in $wxdir/renamed .
2625				EOF
2626			else
2627				cat >&2 <<-EOF
2628Warning! the $ME renamed list appears to be corrupt.
2629				EOF
2630				fail "Please run '$ME update -r' and try this"\
2631					"command again."
2632			fi
2633		fi
2634		if workspace filerm $file; then
2635			# we know filerm renames files under deleted_files/
2636			update_renamed_file $filepath deleted_files/$filepath
2637
2638			print "$filepath deleted"
2639			print
2640			print "To recover $filepath do:"
2641			print "'cd $workspace'"
2642			print "'$ME mv deleted_files/$filepath $filepath'"
2643		else
2644			print "There was an error while trying to delete $filepath"
2645		fi
2646	else
2647		# This is a new file so let's remove it.
2648		if is_active $filepath; then
2649			ring_bell
2650			cat >&2 <<-EOF
2651
2652Warning: $filepath 
2653is in active list. You should run
2654"$ME uncreate $filepath" 
2655which should remove it from the active list
2656then run "$ME $command $filepath". 
2657
2658$filepath not deleted.
2659
2660			EOF
2661			return 1
2662		fi
2663
2664		answer='no'
2665		if [[ $force -ne 1 ]]; then
2666			cat <<-EOF
2667
2668Warning: $filepath 
2669appears to be a new file.
2670
2671Do you want to completely remove the file and SCCS info from 
2672your workspace?  (If you answer no, the file will just be 
2673removed from the active list if it's in there.  If you answer 
2674yes, the file and associated SCCS history files will removed 
2675from the active list and also removed entirely from the 
2676workspace.)
2677
2678			EOF
2679			# yesno sets answer
2680			yesno "Completely remove $filepath?"
2681		else
2682			# forced to yes
2683			answer='yes'
2684		fi
2685
2686		if [[ "$answer" == 'yes' ]]; then
2687			rm -f $file SCCS/s.$file
2688			[[ -f SCCS/p.$file ]] && rm -f SCCS/p.$file
2689			echo "$filepath removed from workspace."
2690			# For later cleanup, optional
2691			if grep -q "^$efp " $wsdata/nametable 2>/dev/null; then
2692				NEED_WS_CLEAN='y'
2693			fi
2694		fi
2695		remove_new $filepath
2696	fi
2697}
2698
2699wx_mv() {
2700	# create some local variables to avoid side effects
2701	typeset efp old_filepath new_filepath
2702
2703	cd $workspace
2704
2705	old_filepath=$1
2706	new_filepath=$2
2707
2708	if [[ -d $old_filepath && ( -d $new_filepath || ! -f $new_filepath ) ]]
2709	then
2710
2711		if [[ $(basename $old_filepath) == "SCCS" ]]; then
2712			return
2713		fi
2714
2715		echo "Doing a $command between two directories can take a "\
2716		    "while, please be patient."
2717		# deal with directory to directory move
2718		if [[ -d $new_filepath ]]; then
2719			base="$(basename $old_filepath)/"
2720		else
2721			base=
2722		fi
2723
2724		sccsmv $old_filepath $new_filepath ||
2725		    fail "sccsmv $old_filepath $new_filepath failed."
2726
2727		# remove previous renamed entry
2728		remove_renamed_entry $old_filepath
2729
2730		update_renamed_dir $new_filepath/$base
2731
2732		if grep -q "^$efp/" $wsdata/nametable 2>/dev/null; then
2733			# Old entries in workspace nametable so set this
2734			# to clean up on exit
2735			NEED_WS_CLEAN='y'
2736		fi
2737
2738		# rename path of active entry
2739		sed "s|^$efp/|$new_filepath/$base|" $wxdir/active \
2740			   > $wxtmp/active || fail "Error: cannot create $wxtmp/active."
2741		mv $wxtmp/active $wxdir/active ||
2742			fail "Error: cannot create $wxdir/active."
2743		sed "s|^$efp/|$new_filepath/$base|" $wxdir/new \
2744			   > $wxtmp/new || fail "Error: cannot create $wxtmp/new."
2745		mv $wxtmp/new $wxdir/new || fail "Error: cannot create $wxdir/new."
2746
2747	elif [[ -f $old_filepath && -d $new_filepath ]]; then
2748		wx_mv_file $old_filepath $new_filepath/$(basename $old_filepath)
2749	elif [[ -f $old_filepath && ! -f $new_filepath ]]; then
2750		wx_mv_file $old_filepath $new_filepath
2751	elif [[ ! -f $old_filepath ]]; then
2752		fail "Error! $old_filepath not found."
2753	elif [[ -f $new_filepath ]]; then
2754		fail "Error! $new_filepath exists."
2755	fi
2756}
2757
2758wx_mv_file() {
2759	# store literal filepath in local var. to avoid side effects
2760	typeset efp old_filepath new_filepath
2761
2762	cd $workspace
2763
2764	old_filepath=$1
2765	new_filepath=$2
2766
2767	if [[ ! -f $old_filepath ]]; then
2768		fail "Error! $old_filepath does not exist."
2769
2770	elif [[ -f $new_filepath ]]; then
2771		fail "Error! $new_filepath already exists."
2772
2773	else
2774		if cyclic_rename $new_filepath $old_filepath; then
2775			fail "Cyclic renamed detected. See 'man workspace'"\
2776				"for more info."
2777		fi
2778
2779		if workspace filemv $old_filepath $new_filepath; then
2780			update_renamed_file $old_filepath $new_filepath
2781			efp=$(escape_re $old_filepath)
2782			if is_active $old_filepath; then
2783				# In active list so update list with new
2784				# file name
2785				rename_active_entry $old_filepath $new_filepath
2786			fi
2787			if grep -q "^$efp$" $wxdir/new; then
2788				remove_new $old_filepath
2789				add_new $new_filepath
2790			fi
2791			if grep -q "^$efp " $wsdata/nametable 2>/dev/null; then
2792				NEED_WS_CLEAN=y
2793			fi
2794		else
2795			echo "There was an error renaming $old_filepath to "\
2796			    "$new_filepath"
2797		fi
2798	fi
2799}
2800
2801sccs_rmdel_done() {
2802	# Note there are literal tabs in the []'s below so be careful when
2803	# editing.
2804
2805	# file not in SCCS so return false (1)
2806	[[ ! -f SCCS/s.$file ]] && return 1
2807
2808	if wx_pnt_filepath; then
2809		sccs prt -a $parentfilepath > $wxtmp/parenthist
2810		sccs prt -a $file > $wxtmp/filehist
2811		diff $wxtmp/parenthist $wxtmp/filehist |
2812			grep '^> R [0-9]\.[0-9]'|
2813			egrep -v ' (Codemgr|Fake)[ 	]' |
2814			sed 's/^> //' > $wxtmp/newrmdels
2815		[[ ! -f $wxtmp/newrmdels ]] && fail "Error: cannot create $wxtmp/newrmdels"
2816		rm -f $wxtmp/parenthist $wxtmp/filehist
2817	else
2818		# New file, no parent
2819		sccs prt -a $file |
2820		    egrep -v \
2821			'^R [0-9]+(\.[0-9]+)+[ 	].* (Codemgr|Fake)[ 	]' |
2822		    egrep '^R [0-9]+(\.[0-9]+)+[ 	]' > $wxtmp/newrmdels
2823		[[ ! -f $wxtmp/newrmdels ]] && fail "Error: cannot create $wxtmp/newrmdels"
2824	fi
2825
2826	if [[ -s $wxtmp/newrmdels ]]; then
2827		cat $wxtmp/newrmdels
2828		rm -f $wxtmp/newrmdels
2829		# an rmdel was done so return true
2830		return 0
2831	else
2832		rm -f $wxtmp/newrmdels
2833		# no rmdel was done so return false
2834		return 1
2835	fi
2836}
2837
2838rmdelchk() {
2839	# returns 0 (success) if an rmdel was done.
2840	# Should be called via wx_eval().
2841	if sccs_rmdel_done ; then
2842		ring_bell
2843		cat <<-EOF
2844
2845Warning, it looks like 'sccs rmdel' was run on $filepath
2846This will cause a problem for Teamware.  Please fix this.
2847Note, this can be fixed by doing '$ME reedit $filepath'
2848		EOF
2849		return 0
2850	else
2851		return 1
2852	fi
2853}
2854
2855wx_delget() {
2856	typeset -i force=0
2857	typeset arg comment_found=false
2858
2859	while getopts :f arg; do
2860		case $arg in
2861			 f) force=1;;
2862			 *) fail "Invalid flag -$OPTARG. See 'wx help' for"\
2863			 	"more info.";;
2864		esac
2865	done
2866
2867	if [[ (! -f SCCS/s.$file && ! -f SCCS/p.$file) ||
2868		(-f SCCS/s.$file &&  -f SCCS/p.$file) ]]; then
2869
2870		# Check for keywords unless force is set or file is in
2871		# keywords.NOT
2872		if [[ $force -ne 1 ]] && [ -f SCCS/p.$file ] &&
2873			! grep -q "^$(escape_re $filepath)$" \
2874				$wxdir/keywords.NOT 2>/dev/null &&
2875			! keywords -p $file; then
2876
2877			ring_bell
2878			cat <<-EOF
2879
2880The keywords check has detected a problem with $filepath
2881If this check should be skipped for this file, put the filepath in
2882${wxdir}/keywords.NOT.
2883See /net/wizard.eng/export/misc/general_docs/keyword_info.txt
2884for more info about keywords.  Note, pay attention to the tabs.
2885
2886			EOF
2887			yesno "Continue with $command for $filepath?"
2888			if [[ "$answer" != 'yes' ]]; then
2889				echo "Aborting $command $filepath\n"
2890				return
2891			fi
2892		fi
2893
2894		[[ -f $wxtmp/comment ]] && rm $wxtmp/comment
2895		if [[ -n "$comment_file" ]]; then
2896			# note hard tab in sed r.e.
2897			sed '/^[ 	]*$/d' $comment_file > $wxtmp/comment &&
2898			    comment_found=true
2899		else
2900			wx_show_comment >$wxtmp/comment && comment_found=true
2901		fi
2902		if $comment_found; then
2903			echo $filepath
2904			cat $wxtmp/comment
2905			if [[ -f SCCS/s.$file ]]; then
2906				# file history so check in file
2907				sccs delget $silent -y"`cat $wxtmp/comment`" \
2908					$file ||
2909					fail "sccs delget failed $filepath."
2910			else
2911				# no file history so create file
2912				sccs create $silent -y"`cat $wxtmp/comment`" \
2913					$file ||
2914					fail "sccs create $filepath failed."
2915				rm -f ,$file
2916			fi
2917			[[ -n "$comment_file" ]] &&
2918			    update_active_comment $filepath
2919		else
2920			ring_bell
2921			print "\nError: no comments (NO_COMMENT) registered for $filepath"
2922			if [[ $force -ne 1 ]] ; then
2923				yesno "Invoke ${EDITOR:-vi} to edit"\
2924					"$wxdir/active"'?'
2925				if [ "$answer" == 'yes' ]; then
2926					${EDITOR:-vi} $wxdir/active
2927					wx_delget
2928				else
2929					fail "Edit $wxdir/active and try again."
2930				fi
2931			else
2932				fail "Edit $wxdir/active and try again."
2933			fi
2934		fi
2935	elif [[ -f SCCS/s.$file && ! -f SCCS/p.$file ]]; then
2936		echo "$filepath already checked in"
2937	elif [[ ! -f SCCS/s.$file && -f SCCS/p.$file ]]; then
2938		fail "Error, $filepath is missing SCCS/s.$file ."
2939	fi
2940}
2941
2942wx_get() {
2943	if [[ -f SCCS/s.$file ]]; then
2944		sccs get $args -s $file || fail "sccs get $file failed."
2945	else
2946		ring_bell
2947		echo "$filepath not in SCCS"
2948	fi
2949}
2950
2951wx_info() {
2952	if [ -f SCCS/p.$file ]; then
2953		if [[ -w $file ]]; then
2954			echo "$filepath (checked out)"
2955		else
2956			ring_bell
2957			echo "$filepath (Warning, inconsistent state."
2958			echo "   SCCS/p.$file exists but $file is readonly.)"
2959		fi
2960	elif [[ ! -f $file ]]; then
2961		ring_bell
2962		echo "$filepath (Warning, not found in $workspace)"
2963	elif [[ ! -f SCCS/s.$file ]]; then
2964		ring_bell
2965		echo "$filepath (Warning, not in SCCS)"
2966	else
2967		echo "$filepath (checked in)"
2968	fi
2969	echo "Check-in comment:"
2970	wx_show_comment
2971	if [ -f SCCS/s.$file ]; then
2972		echo "Most recent delta: \c"
2973		sccs prt -y $file
2974	fi
2975	echo "File name status:"
2976	if renamed; then
2977		# old is set by renamed
2978		echo "Locally renamed, parent file = $parentfile"
2979	elif lookup_parent $filepath; then
2980		# parentfile is set by lookup_parent
2981		if [[ "$filepath" != "$parentfile" ]]; then
2982			echo "In parent ws, file renamed to: $parentfile"
2983		else
2984			echo "Same as parent."
2985		fi
2986	else
2987		echo "New file (does not exist in parent ws)."
2988	fi
2989	echo
2990}
2991
2992get_multi_deltas() {
2993	# Get list of files with more that one delta when putback.
2994	# set global multi_delta_list.
2995	if ! deltachk >/dev/null 2>&1; then
2996		multi_delta_list="$multi_delta_list $filepath"
2997	fi
2998}
2999
3000wx_reedit() {
3001	typeset -i numkids=`workspace children | wc -l`
3002	typeset i newfiles only_multideltas=false
3003
3004	case $1 in
3005		-m)	only_multideltas=true;;
3006		-*)	fail "Invalid flag $1. See 'wx help' for more"\
3007		  		"info.";;
3008	esac
3009
3010	if [[ ! -f $wsdata/nametable ]]; then
3011		echo "$wsdata/nametable not found, all files assumed new."
3012		ok_to_proceed "Okay to continue with $command?"
3013	elif [[ ! -r $wsdata/nametable ]]; then
3014		fail "Error: cannot read $wsdata/nametable."
3015	fi
3016
3017	if $only_multideltas; then
3018		# get_multi_deltas sets multi_delta_list
3019		wx_eval get_multi_deltas
3020		# set file_list for wx_eval wx_reedit_file below
3021		file_list=$multi_delta_list
3022	fi
3023
3024	cd $workspace
3025
3026	for i in $file_list; do
3027		if [[ ! -f $i ]]; then
3028			fail "$i does not exist."
3029		fi
3030		if ! lookup_parent $i; then
3031			if [[ -z $newfiles ]]; then
3032				newfiles="$i"
3033			else
3034				newfiles="$newfiles\n$i"
3035			fi
3036		fi
3037	done
3038
3039	if [[ -n $newfiles ]]; then
3040		# If there are some new files, give user a warning
3041		cat <<-EOF | ${PAGER:-more}
3042
3043$ME thinks following files are new (not in parent workspace) and will
3044reset their file histories to version 1.1 (exit if this list isn't correct):
3045$(echo $newfiles)
3046
3047		EOF
3048		ok_to_proceed "Okay to continue with $command?"
3049		if ! $CHECKIN; then
3050			cat <<-EOF
3051
3052Hint, use '$ME redelget' to collapse/reset new file histories to version
30531.1 since '$ME $command' will check out the file and '$ME delget' always
3054increments the file version doing the check in.
3055
3056			EOF
3057		fi
3058	fi
3059
3060	if [ $numkids -gt 0 ]; then
3061		echo "WARNING: This workspace has the following children:"
3062		echo
3063		workspace children
3064		echo
3065		echo "The reedit command will coalesce all children's deltas"
3066		echo "into one, losing all delta comments in the process."
3067		ok_to_proceed 'Are you sure you want to proceed?'
3068	fi
3069	echo
3070	yesno "Do you want to backup files first?"
3071	if [[ "$answer" == 'yes' ]]; then
3072		wx_backup || fail "Backup failed."
3073	fi
3074
3075	echo "$command beginning..."
3076	echo
3077	wx_eval wx_reedit_file
3078	echo
3079	echo "$command complete"
3080	echo
3081	[[ $ACTSORT == sort ]] && do_active_sort=true
3082}
3083
3084wx_reedit_file() {
3085	# Must be called via wx_eval
3086	typeset comment_found=false
3087
3088	if [[ ! -f $file ]]; then
3089		echo "$file does not exist.  Can not reedit $file"
3090		return
3091	fi
3092
3093	echo $filepath
3094	# Is there a parent file?
3095	if wx_pnt_filepath; then
3096		rm -f $wxtmp/s.$file
3097		cp -p $parentsdot $wxtmp/s.$file ||
3098			fail "Error: cannot cp $parentsdot $wxtmp/s.$file ."
3099
3100		# get the latest parent delta and comment and filter out
3101		# certain fields removing trailing spaces
3102
3103		p_delta="$(sccs prt -y $parentsdot|expand -1|grep 'SCCS'|\
3104				   cut -f'4,5,6,9-' -d' '|sed 's/  *$//')"
3105
3106		if [[ -z "$p_delta" ]]; then
3107			ring_bell
3108			echo "Warning ${command}: skipping $filepath,"
3109			echo "cannot get parent delta info"
3110			echo
3111			return 1
3112		fi
3113
3114		# create a list of local deltas in the same format
3115		# also removing trailing spaces
3116		sccs prt $file|expand -1|
3117			nawk '
3118			/^D [0-9]+(\.[0-9]+)+ +[0-9][0-9]\/[0-9][0-9]/ {
3119				if (delta != "") {
3120					# print previous delta info
3121					print delta comment;
3122				}
3123				delta=sprintf("%s %s %s %s ",$3, $4, $5, $8);
3124				comment = "";
3125			}
3126			! /^D [0-9]+(\.[0-9]+)+ +[0-9][0-9]\/[0-9][0-9]/ {
3127				# Add comment lines to comment variable
3128				if (comment == "") {
3129					if (NF > 0) {
3130						comment = $0;
3131					} else {
3132						# empty lines require a space
3133						# in comment.
3134						comment = " ";
3135					}
3136				} else {
3137					if (NF > 0) {
3138						comment = comment " " $0;
3139					} else {
3140						comment = comment " ";
3141					}
3142				}
3143			}
3144			END {
3145				if (delta != "") {
3146					# print last delta info
3147					print delta comment;
3148				}
3149			}' | sed 's/  *$//' > $wxtmp/l_deltas ||
3150			    	fail "Error: cannot create $wxtmp/l_deltas."
3151
3152		# If the latest parent delta doesn't appear in the local file
3153		# then a bringover is required.  Use fgrep because comment may
3154		# have RE chars in it.
3155		if ! fgrep "$p_delta" $wxtmp/l_deltas >/dev/null; then
3156			ring_bell
3157			echo "\nWarning ${command}: skipping $filepath because:"
3158			echo "parent's version of $filepath"
3159			echo "is newer than child's -- bringover required."
3160			echo
3161			return 1
3162		fi
3163
3164		if [ ! -f SCCS/p.$file ]; then
3165			if sccs edit $silent $file; then
3166				update_active $filepath
3167			else
3168				fail "sccs edit $file failed."
3169			fi
3170		fi
3171
3172		# make copy of local file and copy parent's SCCS s. file over
3173		# local.
3174		mv -f $file ${file}.wx_reedit ||
3175			fail "mv -f $file ${file}.wx_reedit failed."
3176		rm -f SCCS/s.$file SCCS/p.$file
3177		cp $wxtmp/s.$file SCCS/s.$file ||
3178			fail "cp $wxtmp/s.$file SCCS/s.$file failed."
3179
3180		if sccs edit $silent $file; then
3181			update_active $filepath
3182		else
3183			fail "sccs edit $file failed."
3184		fi
3185
3186		mv -f ${file}.wx_reedit $file ||
3187			fail "mv -f ${file}.wx_reedit $file failed."
3188
3189		if $CHECKIN; then
3190			wx_delget
3191		fi
3192		touch $file
3193	else
3194		# reediting a new file.
3195		if [[ -f SCCS/s.$file ]]; then
3196			if [[ ! -f SCCS/p.$file ]]; then
3197				# File needs to be checked out
3198				sccs edit $silent $file ||
3199				    fail "sccs edit $file failed."
3200			fi
3201			# clean up SCCS since we are going to create again.
3202			rm -f SCCS/s.$file
3203		fi
3204		# clean up SCCS since we are going to create again.
3205		[[ -f SCCS/p.$file ]] && rm -f SCCS/p.$file
3206
3207		[[ -f $wxtmp/comment ]] && rm $wxtmp/comment
3208		wx_show_comment >$wxtmp/comment && comment_found=true
3209		if $comment_found; then
3210			echo $filepath
3211			cat $wxtmp/comment
3212			rm -f SCCS/s.$file SCCS/p.$file
3213			sccs create $silent -y"`cat $wxtmp/comment`" $file ||
3214			    fail "sccs create $filepath failed."
3215			rm -f ,$file
3216			[[ -n "$comment_file" ]] &&
3217			    update_active_comment $filepath
3218		else
3219			ring_bell
3220			echo "\nError, no comments registered for $filepath"
3221			if [[ $force -ne 1 ]] ; then
3222				yesno "Invoke ${EDITOR:-vi} to edit"\
3223					"$wxdir/active"'?'
3224				if [[ "$answer" == 'yes' ]]; then
3225					${EDITOR:-vi} $wxdir/active
3226					wx_reedit_file
3227				else
3228					fail "Edit $wxdir/active and try again."
3229				fi
3230			else
3231				fail "Edit $wxdir/active and try again."
3232			fi
3233		fi
3234
3235		if $CHECKIN; then
3236			# No need to check out file.
3237			return
3238		fi
3239
3240		if sccs edit $silent $file; then
3241			update_active $filepath
3242			add_new $filepath
3243		else
3244			fail "sccs edit $file failed."
3245		fi
3246	fi
3247}
3248
3249#
3250# Warn if there are sccs delta issues
3251#
3252deltachk() {
3253	# must be run via wx_eval
3254	typeset -i numdeltas
3255	typeset newfile checkedout=false
3256
3257	if wx_pnt_filepath; then
3258		# find number of deltas by subtracting the number in the parent
3259		# from the local file (note the literal Control-A in the grep
3260		# R.E.s below).
3261		(( numdeltas = $(grep '^d D' SCCS/s.$file|wc -l) - \
3262				    $(grep '^d D' $parentsdot|wc -l) ))
3263		newfile=false
3264	else
3265		# checking a new file (note the literal Control-A in the grep
3266		# R.E.)
3267		numdeltas=$(grep '^d D' SCCS/s.$file|wc -l)
3268		newfile=true
3269	fi
3270
3271	if [[ -z $numdeltas ]]; then
3272		cat <<-EOF
3273
3274Warning: the local file:
3275$filepath
3276does not appear to have a sccs delta history file or the sccs delta
3277history file is corrupt.  If the local file is new try using:
3278"cd $dir"
3279"$ME create $file"
3280
3281If the file is not new (exists in parent):
3282"cd $dir"
3283Save a copy of the local file
3284Remove the SCCS/[ps].$file history files
3285"bringover $filepath"
3286"$ME edit $file"
3287Then carefuly merge the saved copy of local file with the
3288file brought over from parent.  Hint: twmerge is a good merge 
3289tool.
3290		EOF
3291		return 1
3292	fi
3293
3294	[[ -f SCCS/p.$file ]] && checkedout=true
3295
3296	# Note the use of hard tabs in the messages
3297	case $numdeltas in
3298		0)	if $checkedout; then
3299                                # file is checked out so assume there
3300                                # will be 1 delta when checked in.
3301				return 0
3302			else
3303				if [[ -n $parentfilepath ]];  then
3304					if cmp -s $file $parentfilepath; then
3305						cat <<-EOF
3306
3307Warning: the local file:
3308$filepath
3309and the parent file:
3310$parentfilepath
3311content are identical.  There are no new deltas in the local file.
3312If this file is no longer required in the active list use:
3313"cd $dir"
3314"$ME reset $file"
3315to remove file from the wx state files (active list, etc...)
3316						EOF
3317					else
3318						cat <<-EOF
3319
3320Warning: the local file:
3321$filepath
3322and the parent file:
3323$parentfilepath
3324have the same number of deltas but contents differ.  A bringover may be
3325required before putback.
3326						EOF
3327					fi
3328				else
3329					cat <<-EOF
3330
3331Warning: the local file:
3332$filepath
3333is new but doesn't appear to contain any deltas.  The SCCS delta history file
3334may need to be recreated.  If so: 
3335"cd $dir"
3336"rm SCCS/s.$file"
3337"$ME create $file"
3338					EOF
3339				fi
3340				return 1
3341			fi ;;
3342
3343		1)	if $checkedout; then
3344				cat <<-EOF
3345
3346Regarding $filepath
3347Warning! There may be more than 1 delta when you check this file in
3348(currently checked out).  Run '$ME redelget' on this file to collapse
3349the deltas and check in with 1 delta unless putting back > 1 bug.
3350				EOF
3351				return 1
3352			else
3353				return 0
3354			fi ;;
3355
3356		-*) # a negative number means the parent has more deltas
3357
3358			cat <<-EOF
3359
3360Regarding $filepath
3361Warning! The parent file has more deltas than the local file.
3362You should bringover the local file to fix this.
3363			EOF
3364			;;
3365
3366		*)	if $newfile && $checkedout; then
3367				cat <<-EOF
3368
3369Regarding $filepath
3370Warning! There may be more than 1 delta when you check this file in
3371(currently checked out).  Run '$ME redelget' on this file to collapse
3372the deltas and check in with 1 delta unless putting back > 1 bug.
3373				EOF
3374			else
3375				cat <<-EOF
3376
3377Regarding $filepath
3378Warning! There is more than 1 delta.  Run:
3379'cd $dir; $ME redelget $file' 
3380to collapse the deltas on this file and check in with 1 delta unless
3381putting back > 1 bug.
3382				EOF
3383			fi
3384			return 1;;
3385	esac
3386}
3387
3388wx_cstyle() {
3389	case $file in
3390		*.[ch])	;;
3391		*)	return;;
3392	esac
3393	((CSTYLE_INDEX = CSTYLE_INDEX + 1))
3394	(cd $workspace;
3395	 cstyle ${CSTYLE_FLAGS} $args $filepath >\
3396	    $wxtmp/wx.cstyle.$CSTYLE_INDEX) &
3397}
3398
3399wx_jstyle() {
3400	case $file in
3401		*.java)	;;
3402		*)	return;;
3403	esac
3404	((JSTYLE_INDEX = JSTYLE_INDEX + 1))
3405	(cd $workspace;
3406	 jstyle ${JSTYLE_FLAGS} $args $filepath >\
3407	    $wxtmp/wx.jstyle.$JSTYLE_INDEX) &
3408}
3409
3410wx_find_compression_progs() {
3411	gzip=/usr/bin/gzip
3412	if [[ ! -x $gzip && -n "$GZIPBIN" ]]; then
3413		gzip=$GZIPBIN
3414	fi
3415
3416	bzip2=/usr/bin/bzip2
3417	if [[ ! -x $bzip2 && -n "$BZIP2BIN" ]]; then
3418		bzip2=$BZIP2BIN
3419	fi
3420}
3421
3422wx_get_backup_dir() {
3423	typeset backup_dir_file
3424	# if backup_dir hasn't been set already...
3425	if [[ -z "$backup_dir" ]]; then
3426		# use the backup dir specifier in the wx/
3427		backup_dir_file=$wxdir/backup_dir
3428		if [[ ! ( -f $backup_dir_file && -r $backup_dir_file &&
3429			-s $backup_dir_file ) ]]; then
3430			fail "$backup_dir_file: missing, empty, or not readable"
3431		fi
3432		backup_dir=`cat $backup_dir_file`
3433	fi
3434	if [[ ! ( -d $backup_dir && -x $backup_dir && -r $backup_dir ) ]]; then
3435		fail "$backup_dir: missing, not a directory, or bad permissions"
3436	fi
3437}
3438
3439#
3440# This code requires that the files (n.sdot, n.pdot and n.clear) for a given
3441# backup have the same extension (.tar, .tar.gz, or .tar.bz2).  It also
3442# disallows the existance of two incarnations of the same file (i.e.
3443# n.clear.tar and n.clear.tar.gz)
3444#
3445# It's up to the user to straighten things out if the above conditions are
3446# violated.  The only time that is a problem is if they are trying to
3447# restore a version which violates the above rules.
3448#
3449#  Takes one argument, the version number.
3450#
3451#  Returns:
3452#	(return code)	0 if exists and consistent,
3453#			1 if not found,
3454#			2 if inconsistent
3455#	b_clear, b_sdot, b_pdot	On success, the full path to the clear, sdot
3456#	and pdot files comp, ext	The compression program for and
3457#	extension of said files
3458#
3459wx_check_backup() {
3460	typeset _new _b_new _renamed _b_renamed _active _b_active \
3461		_local_nt _b_local_nt found bad
3462
3463	_version=$1
3464	clear=$_version.clear.tar
3465	sdot=$_version.sdot.tar
3466	pdot=$_version.pdot.tar
3467	not=$_version.not.tar
3468	_renamed=$_version.renamed
3469	_b_renamed=$backup_dir/$_renamed
3470	_new=$_version.new
3471	_b_new=$backup_dir/$_new
3472	_active=$_version.active
3473	_b_active=$backup_dir/$_active
3474	_local_nt=$_version.local_nametable
3475	_b_local_nt=$backup_dir/$_local_nt
3476	_sort=$_version.sort_active
3477	_b_sort=$backup_dir/$_sort
3478	found=false
3479	bad=false
3480	#
3481	# these arrays must be in sync with:
3482	# 1. the immediately following _count variable
3483	# 2. wx_find_last_backup's egrep expression
3484	# 3. wx_backup's "$args" handling.
3485	# 4. wx_find_compression_progs's programs
3486	#
3487	set -A _comps	""  "$gzip" "$bzip2"
3488	set -A _extns	""  ".gz"   ".bz2"
3489	_count=3
3490
3491	idx=0
3492	while [[ $idx -lt $_count ]] ; do
3493		_ext=${_extns[$idx]}
3494		_comp=${_comps[$idx]}
3495		_clear=$clear$_ext
3496		_sdot=$sdot$_ext
3497		_pdot=$pdot$_ext
3498		_b_clear=$backup_dir/$_clear
3499		_b_sdot=$backup_dir/$_sdot
3500		_b_pdot=$backup_dir/$_pdot
3501
3502		if [[ -f $_b_clear || -f $_b_sdot ]]; then
3503			if $found; then
3504				echo "$backup_dir: both $_version.*.tar$ext "\
3505					 "and $_version.*.tar$_ext exist"
3506				bad=true
3507			else
3508				ext=$_ext
3509				comp=$_comp
3510				found=true
3511			fi
3512		fi
3513
3514		if [[ -f $_b_clear && ! -f $_b_sdot ]]; then
3515			echo "$backup_dir: $_clear exists; $_sdot does not"
3516			bad=true
3517		elif [[ ! -f $_b_clear && -f $_b_sdot ]]; then
3518			echo "$backup_dir: $_sdot exists; $_clear does not"
3519			bad=true
3520		elif [[ ! -f $_b_sdot && -f $_b_pdot ]]; then
3521			echo "$backup_dir: $_pdot exists; $_sdot does not"
3522			bad=true
3523		fi
3524		idx=`expr $idx + 1`
3525	done
3526
3527	if [[ ! -f $_b_renamed && -f $_b_active ]]; then
3528		# Can determine compression only
3529		return 1
3530	fi
3531
3532	if [[ -f $_b_renamed && -f $_b_active && -f $_b_new &&
3533		-f $_b_local_nt ]]; then
3534		found=true
3535	else
3536		bad=true
3537	fi
3538
3539	$bad && return 2
3540	$found || return 1
3541
3542	b_renamed=$_b_renamed
3543	b_new=$_b_new
3544	b_active=$_b_active
3545	b_local_nt=$_b_local_nt
3546
3547	if [[ -f $backup_dir/$clear$ext && -f $backup_dir/$sdot$ext ]]; then
3548		b_clear=$backup_dir/$clear$ext
3549		b_sdot=$backup_dir/$sdot$ext
3550	else
3551		b_clear=
3552		b_sdot=
3553	fi
3554
3555	# It's not an error if this doesn't exist.
3556	if [[ -f $backup_dir/$pdot$ext ]]; then
3557		b_pdot=$backup_dir/$pdot$ext
3558	else
3559		b_pdot=
3560	fi
3561	# It's not an error if this doesn't exist.
3562	if [[ -f $backup_dir/$not ]]; then
3563		b_not_files=$backup_dir/$not
3564	else
3565		b_not_files=
3566	fi
3567	# It's not an error if this doesn't exist.
3568	if [[ -f $_b_sort ]]; then
3569		b_sort=$_b_sort
3570	else
3571		b_sort=
3572	fi
3573
3574	return 0
3575}
3576
3577#
3578# finds the number of the last backup.
3579#
3580# Returned in $result, which is -1 if no backups are found
3581#
3582wx_find_last_backup() {
3583	#
3584	# The list of extensions in the egrep expression must be in sync
3585	# with wx_check_backup's arrays
3586	#
3587	result=`ls -1 $backup_dir | egrep \
3588	    '^[0-9][0-9]*\.((pdot|sdot|clear)\.tar($|\.gz$|\.bz2$)|active|renamed|new|local_nametable$)'| \
3589	    sed 's/^\([0-9][0-9]*\)\..*$/\1/'| sort -rn | head -1`
3590
3591	[[ -n "$result" ]] # fail if result is empty
3592}
3593
3594#
3595# wx_do_backup
3596# Returns 0 on successful backup, 1 if nothing to backup, 2 any other
3597# error.
3598#
3599
3600wx_do_backup() {
3601	_type=$1	# type of files (for user)
3602	_out=$2		# file to write to
3603	_comp="$3"	# compression program, or empty for no compression
3604	_evalarg=$4	# arg to wx_eval to get the correct file list
3605	typeset backupfiles=$(wx_eval "$_evalarg")
3606
3607	echo
3608	echo "Saving $_type files to $_out"
3609	echo
3610
3611	if [[ -z $backupfiles ]]; then
3612		echo "Note, nothing to backup."
3613		return 1
3614	fi
3615
3616	if [[ -n "$_comp" ]]; then
3617		( tar cvf - $backupfiles 2>$BACKUP_ERRORS || \
3618		    rm -f $_out ) | $_comp -9 -c > $_out || rm -f $_out
3619	else
3620		tar cvf $_out $backupfiles 2>$BACKUP_ERRORS ||
3621		    rm -f $_out
3622	fi
3623
3624	[[ -f "$_out" ]] || return 2	# $_out is removed on any error
3625
3626	return 0
3627}
3628
3629wx_do_restore() {
3630	_type=$1		# type of files (for user)
3631	_in=$2		  # file to read from
3632	_comp="$3"	  # uncompressing program
3633	echo
3634	echo "Restoring $_type files from $_in"
3635	echo
3636
3637	if [[ -n "$_comp" ]]; then
3638		#
3639		# if decompression fails, echo a bad value to make tar fail
3640		#
3641		($_comp -dc < $_in || echo "fail") | tar xvpf - || return 1
3642	else
3643		tar xvpf $_in || return 1
3644	fi
3645	return 0
3646}
3647
3648#
3649# do renames in a workspace from a backup set
3650#
3651
3652wx_do_renames() {
3653	typeset _in=$1 # file to read from
3654
3655	if [[ ! -f $wsdata/nametable ]]; then
3656		echo "$wsdata/nametable not found, not doing renames."
3657		return 0
3658	fi
3659
3660	echo
3661	echo "Restoring renamed files from $_in"
3662	echo
3663
3664	# Note this assumes we're staring in $workspace
3665
3666	while read new hash1 hash2 hash3 hash4; do
3667		# get current local file name
3668		current=$(grep " $hash1 $hash2 $hash3 $hash4$" \
3669				$wsdata/nametable | cut -f1 -d' ')
3670
3671		if [[ -z $current ]]; then
3672			# nothing to rename
3673			continue
3674		fi
3675
3676		if [[ "$new" == "$current" ]]; then
3677			# rename not needed
3678			continue
3679		fi
3680
3681		if [[ ! -f $new ]]; then
3682			if [[ ! -d $(dirname $new) ]]; then
3683				mkdir -p $(dirname $new) ||
3684					fail "Error: cannot create dir $(dirname $new)"
3685			fi
3686			echo "Renaming current workspace file $current to $new"
3687			workspace filemv $current $new
3688		else
3689			if [[ -f $current ]]; then
3690				ring_bell
3691				cat >&2 <<-EOF
3692
3693Warning: $current 
3694and $new 
3695files both exist in current workspace with the
3696same hash.  The restored renamed list should be recreated by running:
3697'$ME update -r'
3698Skipping rename of $current 
3699to $new
3700				EOF
3701
3702			fi
3703		fi
3704	done < $_in
3705
3706	return 0
3707}
3708
3709wx_backup() {
3710	typeset orig_file_list ws_file back_file
3711	typeset newer=false
3712	typeset origdir=$PWD
3713
3714	case $1 in
3715		-i)	wx_get_backup_dir
3716			echo "Backup dir is $backup_dir"
3717			ls -ltr $backup_dir
3718			echo "Backup dir is $backup_dir"
3719			cd $origdir
3720			return ;;
3721		-t) 	newer=true
3722                        # backup if wx files are newer than last backup.
3723                        # Implies use of default compression and no
3724                        # interaction.  Doing shift so case further down
3725                        # won't see -t.
3726			shift;;
3727	esac
3728	# save state in case wx_backup called from another command.
3729	orig_file_list=$file_list
3730
3731	# we always backup the active files.
3732	file_list=$(wx_active)
3733
3734	if [[ ! -s $wxdir/renamed && -z $file_list ]]; then
3735		echo "There isn't anything to backup."
3736                file_list=$orig_file_list
3737		return 0
3738	fi
3739
3740	# must be in workspace to do backup
3741	cd $workspace || fail "Error: cannot cd $workspace"
3742
3743	if $newer; then
3744		# get latest wx state files and active files but skip
3745		# wx/tmp and wx/*.old files.
3746		ws_file=$(ls -1t $wxdir/!(tmp|*.old) $file_list|head -1)
3747		# get latest backup.
3748		wx_get_backup_dir
3749		back_file=$(ls -1t $backup_dir/*|head -1)
3750		if [[ ( -z "$back_file" && -n "$ws_file" ) || \
3751			(( -n "$back_file" && -n "$ws_file" ) && \
3752			$ws_file -nt $back_file ) ]]
3753		then
3754			: # continue with backup
3755		else
3756			print "Skipping backup, last backup newer than wx"\
3757				"files."
3758                        file_list=$orig_file_list
3759			cd $origdir
3760			return 0
3761		fi
3762	fi
3763
3764	wx_find_compression_progs
3765	wx_get_backup_dir
3766
3767	if [[ ! -w $backup_dir ]]; then
3768		fail "$backup_dir: not writable"
3769	fi
3770
3771	if wx_find_last_backup; then
3772		prev_backup=$result
3773		version=`expr $result + 1`
3774	else
3775		prev_backup=
3776		version=0
3777	fi
3778
3779	#
3780	# This must be in sync with wx_check_backup's arrays
3781	#
3782	case $1 in
3783		-n) ext=;	comp=;;
3784		-z) ext=.gz;	comp=$gzip;;
3785		-b) ext=.bz2; comp=$bzip2;;
3786		"-") shift;; # treat this as use default compression
3787		"") ;; # treat this as use default compression
3788		-??*) fail "$ME $command: only accepts a single argument";;
3789		*)  fail "$ME $command: unrecognized argument";;
3790	esac
3791
3792	if [[ -z "$1" ]]; then
3793		#
3794		# default to the compression of the previous backup
3795		#
3796		if [[ -z "$prev_backup" ]]; then
3797			ext=
3798			comp=
3799		else
3800			wx_check_backup $prev_backup
3801			# A return of 1 is okay
3802			if [ $? -gt 1 ]; then
3803				echo "$backup_dir/$prev_backup.*: "\
3804					"cannot determine previous "\
3805					"compression."
3806				if $newer; then
3807                                        # Assume we want backup.
3808					answer="yes"
3809				else
3810					yesno "Proceed with no "\
3811						"compression?"
3812				fi
3813				if [[ $answer == "no" ]]; then
3814					echo "No backup done."
3815                                        file_list=$orig_file_list
3816					cd $origdir
3817					return
3818				fi
3819				ext=
3820				comp=
3821			fi
3822		fi
3823	fi
3824
3825	if [[ -n "$comp" && ! -x "$comp" ]]; then
3826		echo "${comp}: missing.  defaulting to no compression"
3827		ext=
3828		comp=
3829	fi
3830
3831	b_clear=$backup_dir/$version.clear.tar$ext
3832	b_sdot=$backup_dir/$version.sdot.tar$ext
3833	b_pdot=$backup_dir/$version.pdot.tar$ext
3834	b_local_nt=$backup_dir/$version.local_nametable
3835	b_active=$backup_dir/$version.active
3836	b_renamed=$backup_dir/$version.renamed
3837	b_new=$backup_dir/$version.new
3838	b_not_files=$backup_dir/$version.not.tar
3839	b_sort=$backup_dir/$version.sort_active
3840
3841	#
3842	# If anything goes wrong, clean up after ourselves
3843	#
3844	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
3845
3846	fail_msg='failed.  Cleaning up. '
3847
3848	#
3849	# It is not a hard error for the SCCS/s.file to be missing.  We just
3850	# let the user know what's going on.
3851	#
3852	sdot_cmd='
3853_sdot="SCCS/s.$file";
3854_file="$dir/$_sdot";
3855if [[ -f $_sdot ]]; then
3856	echo "$_file";
3857else
3858	echo "$_file: not found" >&2;
3859fi
3860'
3861	pdot_cmd='
3862_pdot="SCCS/p.$file";
3863_sdot="SCCS/s.$file";
3864_file="$dir/$_pdot";
3865if [[ -f $_pdot ]]; then
3866	echo "$_file";
3867elif [[ ! -f $_sdot ]]; then
3868	echo "$_file: not checked in" >&2;
3869elif [[ -w $file ]]; then
3870	echo "$_file: not found but $file is writable!" >&2;
3871fi
3872'
3873	# Do this first in case there are no active files
3874	echo
3875	echo "Saving renamed file list to $b_renamed"
3876	echo
3877	cp $wxdir/renamed $b_renamed || fail "$b_renamed: $fail_msg"
3878
3879	if [[ -f $wxdir/local_nametable ]]; then
3880		echo
3881		echo "Saving local_nametable to $b_local_nt"
3882		echo
3883		cp $wxdir/local_nametable $b_local_nt || \
3884			fail "$b_local_nt: $fail_msg"
3885	fi
3886
3887	if [[ -f $wxdir/sort_active ]]; then
3888		print
3889		print "Saving sort_active to $b_active"
3890		print
3891		cp $wxdir/sort_active $b_sort || fail "$b_sort: $fail_msg"
3892	fi
3893
3894	if ls wx/*.NOT >/dev/null 2>&1; then
3895		echo
3896		echo "Saving .NOT files to $b_not_files"
3897		echo
3898		tar -cf $b_not_files wx/*.NOT || fail "$b_not_files: $fail_msg"
3899	fi
3900
3901	# Are there any active files to backup?
3902	if [[ -n $file_list ]]; then
3903		wx_do_backup 'clear' $b_clear "$comp" 'echo $filepath' ||
3904			fail "$b_clear: $fail_msg"
3905
3906		wx_do_backup 'sdot' $b_sdot "$comp" "$sdot_cmd" ||
3907			fail "$b_sdot: $fail_msg"
3908
3909		echo
3910		echo "Saving new list to $b_new"
3911		echo
3912		cp $wxdir/new $b_new || fail "$b_new: $fail_msg"
3913
3914		# It's not fatal if the backup error for pdot files is
3915		# 'no files to backup'.  This is because it's possible
3916		# that the active files aren't checked out so there
3917		# won't be any pdot files.
3918		wx_do_backup 'pdot (if any)' $b_pdot "$comp" "$pdot_cmd"
3919		if [[ $? -gt 1 ]]; then
3920			fail "$b_pdot: $fail_msg $(cat $BACKUP_ERRORS)"
3921		fi
3922	fi
3923
3924	echo
3925	echo "Saving active file list to $b_active"
3926	echo
3927	cp $wxdir/active $b_active || fail "$b_active: $fail_msg"
3928
3929	trap - 0 1 2 3 15
3930
3931	rm -f $BACKUP_ERRORS
3932
3933	# restore file_list state.
3934	file_list=$orig_file_list
3935
3936	cd $origdir
3937	return 0
3938}
3939
3940wx_restore() {
3941	typeset force=0
3942
3943	case $1 in
3944		-f) force=1;;
3945		-*) fail "Invalid flag $1. See 'wx help' for more  info.";;
3946	esac
3947
3948	if [[ $force -eq 0 ]]; then
3949		cat <<-EOF
3950
3951Warning, the restore command will overwrite several files including the
3952active and renamed lists.  This could be a problem if you have made
3953changes to your workspace and $ME related files following the last
3954backup.  It may be a good idea to run:
3955
3956$ME update
3957
3958after the restore so that the active and renamed lists are updated with
3959the new changes in the workspace.
3960
3961Also, restore may perform workspace renames in this workspace if it
3962finds that the existing file has a pathname that differs from that in
3963the backup being restored.
3964
3965		EOF
3966		ok_to_proceed "Do you really want to do the restore?"
3967	fi
3968
3969	wx_find_compression_progs
3970	wx_get_backup_dir
3971
3972	if wx_find_last_backup; then
3973		version=$result
3974	else
3975		fail "$backup_dir: no backups found"
3976	fi
3977
3978	if [[ $force -eq 0 ]]; then
3979		ask 'Version to restore from' $version
3980		version=$answer
3981	fi
3982
3983	#
3984	# wx_check_backup sets $comp, $b_clear, and $b_sdot when successful
3985	#
3986	if wx_check_backup $version; then
3987		:
3988	else
3989		if [[ $? -eq 2 ]]; then
3990			fail "$backup_dir/$version.*: unable to restore"\
3991				"inconsistent version"
3992		else
3993			fail "$backup_dir: Unable to find version $version"
3994		fi
3995	fi
3996
3997	b_active=$backup_dir/$version.active
3998
3999	if [[ -n "$comp" && ! -x "$comp" ]]; then
4000		fail "${comp}: missing -- cannot decompress $b_clear"
4001	fi
4002
4003	# must be in workspace to do restore
4004	cd $workspace || fail "Error: cannot cd $workspace"
4005
4006	[[ -f $b_renamed ]] || fail "$b_renamed: missing"
4007	[[ -f $b_new     ]] || fail "$b_new: missing"
4008	[[ -f $b_active  ]] || fail "$b_active: missing"
4009
4010	[[ -r $b_renamed ]] || fail "$b_renamed: not readable"
4011	[[ -r $b_new     ]] || fail "$b_new: not readable"
4012	[[ -r $b_active  ]] || fail "$b_active: not readable"
4013
4014	if [[ -f $b_clear ]]; then
4015		[[ -r $b_clear ]] || fail "$b_clear: not readable"
4016	fi
4017	if [[ -f $b_sdot ]]; then
4018		[[ -r $b_sdot ]] || fail "$b_sdot: not readable"
4019	fi
4020	if [[ -f $b_pdot ]]; then
4021		[[ -r $b_pdot ]] || fail "$b_pdot: not readable"
4022	fi
4023	if [[ -f $b_local_nt ]]; then
4024		[[ -r $b_local_nt ]] || fail "$b_local_nt: not readable"
4025	fi
4026	if [[ -f $b_not_files ]]; then
4027		[[ -r $b_not_files ]] || fail "$b_not_files: not readable"
4028	fi
4029	if [[ -f $b_sort ]]; then
4030		[[ -r $b_sort ]] || fail "$b_sort: not readable"
4031	fi
4032
4033	#
4034	# If something goes wrong, we need to make sure they notice, so
4035	# we make the message quite visible, and echo a BELL.
4036	#
4037	fail_msg='Extraction failed.
4038
4039	*DANGER* *DANGER* workspace could be corrupted *DANGER* *DANGER*'
4040
4041	cp $b_renamed $wxdir/renamed || fail "$wxdir/renamed: $fail_msg"
4042	cp $b_new $wxdir/new || fail "$wxdir/new: $fail_msg"
4043	cp $b_active $wxdir/active || fail "$wxdir/active: $fail_msg"
4044	cp $b_local_nt $wxdir/local_nametable ||
4045		fail "$wxdir/local_nametable: $fail_msg"
4046	if [[ -n $b_sort ]]; then
4047		cp $b_sort $wxdir/sort_active || \
4048			fail "$wxdir/sort_active: $fail_msg"
4049	fi
4050
4051	# Need to move active files that are renamed in current ws back to
4052	# their name in the active list to avoid two copies of the file
4053	# occuring when the clear files are restored below.
4054	wx_do_renames $wxdir/local_nametable ||
4055		fail "$wxdir/local_nametable: $fail_msg"
4056
4057	if [[ -n $b_not_files ]]; then
4058		tar -xf $b_not_files || fail "$wx/*.NOT: $fail_msg"
4059	fi
4060	# It's not an error if there is no clear backup.
4061	if [[ -f $b_clear ]]; then
4062		wx_do_restore "clear" $b_clear "$comp" ||
4063		    fail "$b_clear: $fail_msg"
4064	fi
4065	# It's not an error if there is no sdot backup.
4066	if [[ -f $b_sdot ]]; then
4067		wx_do_restore "sdot" $b_sdot "$comp" ||
4068			fail "$b_sdot: $fail_msg"
4069	fi
4070	# It's not an error if there is no pdot backup.
4071	if [[ -f $b_pdot ]]; then
4072		wx_do_restore "pdot" $b_pdot "$comp" ||
4073			fail "$b_pdot: $fail_msg"
4074	fi
4075
4076	# Do some integrity checking
4077	for filepath in $(wx_active); do
4078		if cd ${workspace}/$(dirname $filepath); then
4079			file=$(basename $filepath)
4080
4081			# If file is not writable then assume the
4082			# SCCS/p.file is bogus.  This can happen if a
4083			# file is checked out and a wx restore is done
4084			# and the restored file was not checked out when
4085			# it was backed up.
4086
4087			if [[ ! -w $file && -f SCCS/p.$file ]]; then
4088				ring_bell
4089				cat <<-EOF
4090
4091Warning! $filepath is in inconsistent state.
4092$filepath is not writable and SCCS/p.$file exists.
4093Removing SCCS/p.$file 
4094To edit the file run '$ME edit $filepath'
4095				EOF
4096				rm -f SCCS/p.$file
4097			elif [[ -w $file && ! -f SCCS/p.$file ]]; then
4098				ring_bell
4099				cat <<-EOF
4100
4101Warning! $filepath is in inconsistent state.
4102$filepath is writable 
4103but there is no SCCS/p.$file
4104
4105				EOF
4106				yesno "Should this file be checked out?"
4107				if [[ "$answer" == 'yes' ]]; then
4108					if mv $file $wxtmp; then
4109						if sccs edit $file; then
4110							update_active $filepath
4111						fi
4112						mv -f $wxtmp/$file $file
4113					fi
4114				else
4115					ask_remove_active_entry
4116					echo "Setting $filepath read only"
4117					chmod ugo-w $file
4118				fi
4119			fi
4120		else
4121			ring_bell
4122			echo "\nWarning! Could not check sccs state of "\
4123			    "$filepath"
4124		fi
4125	done
4126}
4127
4128wx_fullreview() {
4129	if wx_pnt_filepath; then
4130		:
4131	else
4132		parentfilepath=/dev/null
4133	fi
4134	if $show_comments && wx_show_comment > $wxdir/comment; then
4135		comm=-y"`cat $wxdir/comment`"
4136		codereview "$comm" $args $parentfilepath $workspace/$filepath
4137	else
4138		codereview $args $parentfilepath $workspace/$filepath
4139	fi
4140}
4141
4142#
4143# Check on RTI status for bug ID's found in active list comments.
4144#
4145
4146wx_rtichk() {
4147	# gate contains the gate dir, not full path
4148	typeset gate=${parent##*/}
4149	typeset -i rc=0
4150	typeset nolookup opt
4151
4152	if [[ -f $wxdir/rtichk.NOT ]]; then
4153		print "\nSkipping RTI check:"
4154		return
4155	else
4156		print "\nDoing RTI check:"
4157	fi
4158
4159	while getopts :N opt; do
4160		case $opt in
4161			N) nolookup='-N' ;;
4162			*) fail "Invalid flag -$OPTARG." ;;
4163		esac
4164	done
4165
4166	# Note, rtichk needs a gate arg to correctly determine status.
4167	if [[ -z $gate ]]; then
4168		cat >&2 <<-EOF
4169Warning: cannot find a parent gate, skipping RTI checking.
4170		EOF
4171	fi
4172
4173	# Use wx_summary to output bug ID's in active list comments,
4174	# redirecting warnings about non-bug ID's to file for later use.
4175	set -A bugs $(wx_summary -ao $nolookup 2>$wxtmp/bugwarnings|cut -f1 -d' ')
4176	rtichk -g $gate ${bugs[@]}
4177	rc=$?
4178
4179	if [[ -s $wxtmp/bugwarnings ]]; then
4180		cat <<-EOF
4181
4182There are issues with the bug ID format in the 
4183$wxdir/active file.
4184Please fix the following and run rtichk again:
4185
4186		EOF
4187		cat $wxtmp/bugwarnings
4188		((rc = 1))
4189	fi
4190	if [[ ${#bugs} -eq 0 ]]; then
4191		print "\nWarning: no bug ID's in active list."
4192	fi
4193	return $rc
4194}
4195
4196#
4197# Do a Teamware putback of active and renamed files.
4198#
4199wx_putback() {
4200	# Use pbargs array to store Teamware putback args.
4201	# verbatim is for -v verbatim flag which doesn't get passed to
4202	# putback.
4203	set -A pbargs
4204	typeset i verbatim pbfiles narg=false force=false
4205	typeset nolookup=false
4206
4207	if $FILES_PASSED; then
4208		# use the user specified files
4209		pbfiles=$file_list
4210	else
4211		# use the pblist (active and renamed)
4212		pbfiles=$(wx_active -p)
4213	fi
4214
4215	while getopts :fnp:qvN i; do
4216		case $i in
4217			# Force the putback (no user interaction)
4218			f)	force=true;;
4219
4220			n)	narg=true
4221				pbargs[${#pbargs[@]}]="-$i" ;;
4222
4223			q)	pbargs[${#pbargs[@]}]="-$i" ;;
4224
4225			p)	pbargs[${#pbargs[@]}]="-$i"
4226				pbargs[${#pbargs[@]}]="$OPTARG"
4227				# setting parent for user prompt below
4228				parent="$OPTARG" ;;
4229
4230				# -v doesn't get passed to putback.
4231			v)	verbatim='-v';;
4232
4233			N)	nolookup='-N';;
4234
4235		  	*)	fail "Invalid flag -$OPTARG. See 'wx help'"\
4236					"for more info.";;
4237		esac
4238	done
4239
4240	if ! $narg; then
4241		# real putback
4242
4243		# get pb comments, will be used later.
4244		if ! wx_summary -p $verbatim $nolookup >$wxtmp/pb_comments; then
4245			# Fail if comments have problems.
4246			fail "\nError, improper comments found. Use -v"\
4247				"to bypass this check."
4248		fi
4249
4250		if ! $force; then
4251			# not force so give more warning.
4252			( # using subshell to capture stdout to file.
4253			cat <<-EOF
4254Remember to run '$ME pbchk' before doing a final putback (esp. if
4255doing a putback to an official ON gate).  Make sure your workspace 
4256parent ($parent) is correct.  
4257It's a good idea to check your code diffs before putback ('$ME pdiffs').
4258
4259Also, run '$ME $command -n' and check for conflicts before doing the
4260final putback.
4261			EOF
4262
4263			if [[ -z "$(wx_summary -bo 2>/dev/null)" ]]; then
4264				cat <<-EOF
4265
4266Don't forget the ARC ID info in your active list comments if there is an
4267ARC case associated with your putback.
4268				EOF
4269			fi
4270
4271			echo "\nThe putback comment will be:"
4272			cat $wxtmp/pb_comments
4273			print "========== End of putback comments ======="
4274
4275			# Output warning if RTI isn't approved.
4276			wx_rtichk $nolookup
4277			print "========== End of RTI check output ======="
4278
4279			cat <<-EOF
4280
4281The following files will be used for putback:
4282$pbfiles
4283
4284			EOF
4285			) | ${PAGER:-more}
4286
4287			ok_to_proceed "Do you really want to"\
4288				"'$PUTBACK ${pbargs[@]}' to $parent?"
4289		fi
4290	fi
4291
4292	# Do the putback, passing in active list comments if required.
4293	# putback both active and renamed/deleted files.
4294	cd $workspace
4295	if $narg; then
4296		# Don't use putback comment if -n is given (trial putback)
4297		$PUTBACK "${pbargs[@]}" $pbfiles
4298	else
4299		# feed active list comments into real putback
4300		wx_summary $verbatim $nolookup |$PUTBACK "${pbargs[@]}" $pbfiles
4301	fi
4302	return
4303}
4304
4305outchk() {
4306
4307	# List files that are checked out but not in active list.
4308	typeset outfile do_header=true
4309
4310	wx_checked_out >/dev/null
4311	# if $wxtmp/checked_out is 0 bytes then return
4312	[[ -s $wxtmp/checked_out ]] || return
4313
4314	sort $wxtmp/checked_out > $wxtmp/co_sort
4315	wx_active | sort > $wxtmp/active_sort
4316	for outfile in $(comm -12 $wxtmp/active_sort $wxtmp/co_sort); do
4317		if $do_header; then
4318			echo "\nWarning, the following active list files are"\
4319			    "checked out:"
4320			do_header=false
4321		fi
4322		echo "$outfile"
4323	done
4324	do_header=true
4325	for outfile in $(comm -13 $wxtmp/active_sort $wxtmp/co_sort); do
4326		if $do_header; then
4327			cat <<-EOF
4328
4329Warning, the following files are checked out but not in active list
4330(Run "$ME update -q" to add them to the active list): 
4331			EOF
4332			do_header=false
4333		fi
4334		echo "$outfile"
4335	done
4336	rm -f $wxtmp/co_sort $wxtmp/active_sort
4337}
4338
4339#
4340# run Teamware resolve and do reedit only on merged files.
4341#
4342wx_resolve() {
4343	typeset merged_file
4344
4345	# clear the file_list, will be set below
4346	file_list=
4347
4348	grep -v '^VERSION ' $wsdata/conflicts > $wxtmp/conflicts
4349	[[ ! -f $wxtmp/conflicts ]] && fail "Error: cannot create $wxtmp/conflicts"
4350
4351	# run Teamware resolve
4352	resolve $* || fail "Teamware resolve failed."
4353
4354	# resolve will remove files from $wsdata/conflicts when
4355	# successfully merged.
4356
4357	# set file_list to files that were merged.
4358	for merged_file in $(cat $wxtmp/conflicts); do
4359		if ! grep -q '^'"$(escape_re $merged_file)"'$' \
4360		    $wsdata/conflicts; then
4361			# set file_list for wx_eval later.
4362			file_list="$file_list $merged_file"
4363		fi
4364	done
4365
4366	if [[ -n $file_list ]]; then
4367		ok_to_proceed "Re-edit merged files to collapse merge deltas?"
4368		echo "Re-editing merged files"
4369		echo
4370		wx_eval wx_reedit_file
4371		echo
4372		echo "Re-edit complete"
4373		echo
4374	else
4375		echo "No merged files to re-edit."
4376	fi
4377}
4378
4379#########################################################################
4380#
4381# Main
4382#
4383
4384#
4385# Do some initial sanity checking and set up.
4386#
4387
4388# Set the lang to standard English so wx doesn't get confused.
4389export LC_ALL=C
4390
4391# Turn on debugging output early
4392if [[ "$*" == *' -D'*( *) ]]; then
4393	typeset -ft $(typeset +f)
4394	set -x
4395fi
4396
4397ME=$(basename $0)
4398export ME
4399
4400if [[ -d /usr/xpg4/bin ]]; then
4401	# Want to use xpg4 versions of fgrep and grep
4402	PATH=/usr/xpg4/bin:/usr/bin:/usr/sbin:/usr/ccs/bin:$PATH
4403else
4404	fail "Error: directory /usr/xpg4/bin not found."
4405fi
4406
4407unset CDPATH	# if set "cd" will print the new directory on stdout
4408		# which screws up wx_eval.
4409
4410DEFAULT_SRCDIR=usr
4411
4412if [[ $# -eq 0 || "$1" == help ]]; then
4413	# output usage now to avoid unnecessary checking below.
4414	wx_usage
4415fi
4416
4417if [[ "$1" == version ]]; then
4418	# output version now to avoid unnecessary checking below.
4419	version
4420	exit 0
4421fi
4422
4423#
4424# Check to make sure we're not being run from within a Mercurial repo
4425#
4426if hg root >/dev/null 2>&1; then
4427	fail "Error: wx does not support Mercurial repositories.\n"\
4428"Please see http://opensolaris.org/os/community/tools/hg"
4429fi
4430
4431whence workspace >/dev/null || fail "Error: cannot find workspace command in \$PATH."
4432
4433# Note, PUTBACK can be set to "cm_env -g -o putback" to use Casper Dik's
4434# turbo-dir.flp scripts to speed up thorough updates.
4435PUTBACK=${PUTBACK:='putback'}
4436BRINGOVER=${BRINGOVER:='bringover'}
4437
4438dot=$PWD
4439
4440if [[ -n "$CODEMGR_WS" ]]; then
4441	# ws was used.
4442	# normalize the workspace name.
4443	workspace=$(cd $CODEMGR_WS && workspace name)
4444
4445	# If the current dir is in a workspace check that it is the same
4446	# as CODEMGR_WS.
4447	if [[ -n "$(workspace name)" ]]; then
4448		if [[ "$(/bin/pwd)/" != "$workspace/"* ]]; then
4449			cat <<-EOF
4450
4451Warning, $ME will use $ME files in workspace $workspace (the current
4452directory is not in this workspace).
4453			EOF
4454			ok_to_proceed "Okay to proceed?"
4455		fi
4456	fi
4457else
4458	# If current dir is in a workspace then use output of workspace
4459	# name as current ws.
4460	workspace=$(workspace name)
4461	if [[ -n "$workspace" ]]; then
4462		CODEMGR_WS=$workspace
4463		export CODEMGR_WS
4464	fi
4465fi
4466
4467if [[ -z "$workspace" ]]; then
4468	fail "No active workspace, run \"ws <workspace>\" or"\
4469		"\"cd <workspace>\"."
4470fi
4471
4472workspace_basename=`basename $workspace`
4473wxdir=${WXDIR:-$workspace/wx}
4474wxtmp=$wxdir/tmp
4475wsdata=$workspace/Codemgr_wsdata
4476node=`uname -n`
4477
4478if [ -f $wsdata/parent ]; then
4479	parent=`tail -1 $wsdata/parent`
4480else
4481	parent=
4482fi
4483if [[ $parent == *:* ]]; then
4484	parentdir=${parent#*:}
4485	parentnode=${parent%%:*}
4486	if [[ $parentnode == $node ]]; then
4487		parent=$parentdir
4488	else
4489		parent=/net/$parentnode$parentdir
4490	fi
4491fi
4492
4493# Store backup state
4494backup_done=0
4495
4496# store state if new files are deleted
4497NEED_WS_CLEAN='n'
4498
4499# XXX doing this because keywords doesn't work on new files
4500# Note doing the echo so the tabs are apparent,
4501# % is escaped to prevent keyword expansion by sccs commands on this file.
4502SCCSKEYWORD=$(echo "ident\t+\"(\%Z\%\%M\%\t+\%I\%|\%W\%)\t+\%E\% SMI\"")
4503
4504# file that contains comments for use in create and checkin
4505comment_file=
4506# mode for updating comments in active list
4507comment_mode="replace"
4508
4509CSTYLE_FLAGS=${CSTYLE_FLAGS:='-P -p -c'}
4510JSTYLE_FLAGS=${JSTYLE_FLAGS:='-p'}
4511
4512BACKUP_ERRORS=/tmp/${ME}_berrors_$(/usr/xpg4/bin/id -un)_$$
4513
4514# global for reedit command, don't checkin by default
4515CHECKIN=false
4516
4517# Indicates that the parent nametable cache needs refreshing
4518need_pnt_refresh=true
4519
4520# Indicate whether file args were specified
4521FILES_PASSED=false
4522
4523# Used to store files that have more than one delta compared to parent
4524multi_delta_list=
4525
4526# Used to bringover any files just before exit of wx
4527bofilelist=
4528
4529# should codereviews include delta comments?
4530show_comments=true
4531
4532# Determines if active list should be sorted by default
4533# If sort_active contains true then we sort the active list on updates.
4534if [[ -r $wxdir/sort_active && "$(cat $wxdir/sort_active)" == "true" ]]; then
4535	ACTSORT=sort
4536else
4537	ACTSORT=cat
4538fi
4539
4540# These are set depending on what needs sorting
4541do_renamed_sort=false
4542do_active_sort=false
4543
4544# Places to search for approved RTIs
4545RTIDIRS="/net/wizard.eng/export/consolidation/rtiroute/newrtis
4546	/net/wizard.eng/export/consolidation/rtiroute/oldrtis
4547	/net/onstc.eng/export/stc/Rtitool/consolidation/rtiroute/newrtis
4548	/net/onstc.eng/export/stc/Rtitool/consolidation/rtiroute/oldrtis"
4549
4550# Places to search for approved Patch RTIs
4551PRTIDIRS="/net/wizard.eng/export/consolidation/rtiroute/newprtis
4552	/net/wizard.eng/export/consolidation/rtiroute/oldprtis"
4553
4554export workspace parent wxdir file dir filepath backup_done DEFAULT_SRCDIR
4555
4556#
4557# main section
4558#
4559
4560# Get wx command
4561command=$1
4562comlist=$command
4563shift
4564# throw away -D flag after command assigned as this flag was processed earlier
4565[[ "$1" == '-D' ]] && shift
4566
4567case $command in
4568	apply|eval)	subcommand=$1; shift;;
4569	grep|egrep|sed|nawk)	pattern=$1; shift;;
4570	nits)	comlist="cstyle jstyle hdrchk copyright cddlchk keywords";
4571		echo "Note, nits is a subset of pbchk checks.";;
4572	pbchk)	comlist="cstyle jstyle hdrchk copyright cddlchk keywords"
4573		comlist="$comlist rmdelchk deltachk comchk rtichk outchk";;
4574esac
4575
4576orig_args="$@"
4577silent=
4578args=
4579file_list=
4580typeset tmp_file_list tmp_args
4581
4582#
4583# Some subcommands pass through all arguments.
4584#
4585case $command in
4586	webrev)	args="$orig_args"; shift $#;;
4587esac
4588
4589# Parse args
4590while [ $# -gt 0 ]; do
4591	case $1 in
4592		-c|-C)
4593			if [[ $command == @(delget|checkin|ci|create) ]]; then
4594				# set global comment_file
4595				[[ "$1" == "-C" ]] && comment_mode="append"
4596				comment_file=$2;
4597				if [[ $comment_file != '/'* ]]; then
4598					comment_file="$(pwd)/$comment_file"
4599				fi
4600				if [[ -z "$comment_file" ]]; then
4601					fail "Missing comment file."\
4602						"Run 'wx help' for info."
4603				fi
4604				[[ ! -r "$comment_file" ]] &&
4605				    fail "Can not read comment file"\
4606				    	"$comment_file."
4607				echo "Using comment file $comment_file"\
4608					"for comments."
4609				# shift past the comment_file arg
4610				shift
4611			elif [[ $1 == '-C' && $command == 'diffs' ||
4612				$command == 'tdiffs' &&
4613				-z $WXDIFFCMD ]]; then
4614				if [[ $2 != +([0-9]) ]]; then
4615					# provide default context value for
4616					# compat with old wx
4617					args="$args -C5"
4618				else
4619					args="$args -C$2"
4620					# shift past context lines arg
4621					shift
4622				fi
4623			else
4624				args="$args $1"
4625			fi;;
4626		-p) 	if [[ $command == @(putback|pb) ]]; then
4627				if workspace access $2 >/dev/null; then
4628					# 2nd arg is a workspace
4629					args="$args $1 $2"
4630				else
4631					fail "$2 not a workspace."\
4632						"Run 'wx help' for info."
4633				fi
4634				# shift past the parent ws arg
4635				shift
4636			else
4637				# for other commands -p doesn't have a arg
4638				args="$args $1"
4639			fi;;
4640		-r) 	if [[ $command == @(get|extract) ]]; then
4641				# 2nd arg is the version #
4642				args="$args $1 $2"
4643				# shift past 2nd arg
4644				shift
4645			else
4646				# for other commands -r doesn't have a arg
4647				args="$args $1"
4648			fi;;
4649		-s) 	if [[ "$command" == @(update|init) ]]; then
4650                                args="$args $1"
4651                        else
4652                                silent=-s
4653                        fi;;
4654		-N)	if [[ "$command" == @(codereview|fullreview) ]]; then
4655				show_comments=false
4656			else
4657				args="$args $1"
4658			fi ;;
4659		-*) 	args="$args $1";;
4660		*)  	if [[ -z "$file_list" ]]; then
4661				file_list="$1"
4662			else
4663				file_list="$file_list $1"
4664			fi;;
4665	esac
4666	shift
4667done
4668
4669if [[ "$command" == "init" ]]; then
4670	if [ -z "$file_list" ]; then
4671		file_list=$DEFAULT_SRCDIR
4672	fi
4673	wx_init $file_list $args
4674	exit
4675fi
4676
4677if [ ! -d $wxdir/tmp ]; then
4678	echo "Workspace does not appear to be initialized for $ME."
4679	echo "The initialization process will create a few files under"
4680	echo "$wxdir but will not otherwise affect your workspace."
4681	ok_to_proceed 'OK to proceed?'
4682
4683	ask "Where is the root of the source code in this workspace?" \
4684		$DEFAULT_SRCDIR
4685	# wx_init modifies file_list so save current value
4686	tmp_file_list=$file_list
4687	file_list=
4688	# save off args and set to null to avoid side effects
4689	tmp_args=$args
4690	args=
4691	wx_init $answer
4692	# restore original file list and cd to original dir in case there's
4693	# a command to execute.
4694	file_list=$tmp_file_list
4695	args=$tmp_args
4696	cd $dot
4697fi
4698
4699if [[ ! -f $wxdir/local_nametable ]]; then
4700	touch $wxdir/local_nametable
4701fi
4702
4703# Doing this for backward compat since old wx doesn't have a renamed list
4704if [[ ! -f $wxdir/renamed ]]; then
4705	# if 'wx update' or 'wx update -r' is the command then skip
4706	# renamed list creation since it will happen anyway.
4707	if [[ "$command" != "update" ]] || [[ "$args" == *'-q'* ]]; then
4708		ring_bell
4709		cat <<-EOF
4710
4711$ME needs to create a renamed file list.  If you are sure that no files
4712were renamed or deleted in the current workspace then answer no to the
4713following question.
4714
4715		EOF
4716		yesno "Okay to search for renamed files (can be slow)?"
4717		if [[ "$answer" == "yes" ]]
4718		then
4719			wx_update -r
4720		else
4721			touch $wxdir/renamed
4722		fi
4723	fi
4724fi
4725
4726# Doing this for backward compat since old wx doesn't have a new list
4727if [[ ! -f $wxdir/new ]]; then
4728	ring_bell
4729	cat <<-EOF
4730
4731$ME needs to create a new-file list (cache names of newly created
4732files).  Please be patient.
4733	EOF
4734	# Avoid a putback -n which is slow, just use lookup_parent()
4735	touch $wxdir/new || fail "Error: cannot create $wxdir/new list"
4736	wx_active |
4737		while read filepath; do
4738			if ! lookup_parent $filepath; then
4739				add_new $filepath
4740			fi
4741		done
4742	echo "\nNew files:"
4743	cat $wxdir/new
4744	echo
4745fi
4746
4747if [[ "$command" == @(restore|backup|bu) ]]; then
4748	# If the backup dir was specified as a file arg...
4749	if [ -n "$file_list" ]; then
4750		backup_dir=$(echo "$file_list"|cut -f1 -d' ')
4751	fi
4752	# unset file_list since this file arg has been processed here.
4753	unset file_list
4754elif [[ "$command" == "ea" ]]; then
4755	# Do this command before wx_active is run because the active list
4756	# may be corrupt.
4757	cd $wxdir
4758	exec ${EDITOR-vi} active
4759elif [[ "$command" == @(unedit|uncheckout|unco) ]]; then
4760	if [[ -z "$file_list" && $args != *-f ]]; then
4761		echo "$ME will $command all active files which may remove"\
4762		    "them from the active list."
4763		ok_to_proceed 'Do you REALLY want to do this?'
4764	fi
4765	cp $wxdir/active $wxdir/active.old
4766elif [[ "$command" == @(bugs|arcs) ]]; then
4767	# -v verbatim is not valid for these commands
4768	if [[ "$args" == *'-v'* ]]; then
4769		fail "Invalid flag -v. Run 'wx help' for info."
4770	fi
4771elif [[ "$command" == "create" ]]; then
4772	if [ -z "$file_list" ]; then
4773		fail "$command missing file arg(s). Run 'wx help' for info."
4774	fi
4775
4776	cp $wxdir/active $wxdir/active.old ||
4777		fail "Error could not backup $wxdir/active"
4778elif [[ "$command" == @(delget|checkin|ci) && -n "$comment_file" ]]; then
4779	cp $wxdir/active $wxdir/active.old ||
4780		fail "Error could not backup $wxdir/active"
4781elif [[ "$command" == @(mv) ]]; then
4782	if [[ $(echo "$file_list"|wc -w) -ne 2 ]]; then
4783		fail "$command requires two args. Run 'wx help' for info."
4784	fi
4785
4786	cp $wxdir/active $wxdir/active.old ||
4787		fail "Error could not backup $wxdir/active"
4788	cp $wxdir/renamed $wxdir/renamed.old ||
4789		fail "Error could not backup $wxdir/renamed"
4790elif [[ "$command" == @(delete|rm) ]]; then
4791
4792	if [ -z "$file_list" ]; then
4793		echo "$ME will try to delete all active files which may "\
4794		    "remove them from the active list."
4795		ok_to_proceed 'Do you REALLY want to do this?'
4796	fi
4797
4798	cp $wxdir/active $wxdir/active.old ||
4799		fail "Error: could not backup $wxdir/active"
4800	cp $wxdir/renamed $wxdir/renamed.old ||
4801		fail "Error: could not backup $wxdir/renamed"
4802elif [[ "$command" == reset ]]; then
4803	cp $wsdata/nametable $wxtmp/nametable.orig || \
4804		fail "Error: cp $wsdata/nametable $wxtmp/nametable.orig failed."
4805fi
4806
4807if [ -z "$file_list" ]; then
4808	basedir=$workspace
4809	file_list=$(wx_active) || fail
4810else
4811	base_file_list=$file_list
4812	file_list=
4813	for basefile in $base_file_list; do
4814		# normalize the filepaths
4815		if [[ -d $basefile ]]; then
4816			basedir=$(cd $basefile && /bin/pwd)
4817			abspath=$basedir
4818		else
4819			basedir=$(cd $(dirname $basefile) && /bin/pwd)
4820			abspath=$basedir/$(basename $basefile)
4821		fi
4822		if [[ ! -d $basedir ]]; then
4823			fail "Error: Path to $basefile does not exist."
4824                elif [[ $(cd $basedir; workspace name) != $workspace ]]; then
4825			fail "Error: $basefile isn't in current workspace: $workspace."
4826
4827		fi
4828		filepath=${abspath##$workspace/}
4829		if [[ -z "$file_list" ]]; then
4830			file_list="$filepath"
4831		else
4832			file_list="$file_list $filepath"
4833		fi
4834	done
4835	FILES_PASSED=true
4836fi
4837
4838if [[ "$command" == @(nits|pbchk) ]]; then
4839	tmp_list=
4840	# skip nits/pbchk checks for files listed in $command.NOT
4841	if [[ -f $wxdir/${command}.NOT ]]; then
4842		for _a_file in $file_list; do
4843			if grep -q "^$(escape_re $_a_file)$" \
4844				$wxdir/${command}.NOT
4845			then
4846				echo "skipping $command checks for "\
4847				    "$_a_file (skipping)"
4848			else
4849				tmp_list="$tmp_list $_a_file"
4850			fi
4851		done
4852		file_list=${tmp_list# }
4853	fi
4854	[[ -z $file_list ]] && exit 0
4855fi
4856
4857# This is where the commands are executed.
4858for command in $comlist; do
4859cd $dot
4860case $command in
4861	list|active) wx_active $args ;;
4862	pblist)	wx_active -p;;
4863	renamed)	list_renamed $args ;;
4864	new)	list_new $args;;
4865	update)	wx_update $args;;
4866	out)	wx_checked_out; cat $wxtmp/checked_out;;
4867	diffs)	wx_eval -r 'print -- "\n------- $filepath -------\n";
4868			sccs get -s -p -k $filepath |
4869			${WXDIFFCMD:-diff} $args - $filepath';;
4870	tdiffs)	## As diffs but also shows new files.
4871		if [[ -r $wxdir/new ]]; then
4872		    ## Read names of new files into space separated list.
4873		    while read new_file
4874		    do
4875			new_files="${new_files}${new_file} "
4876		    done < $wxdir/new
4877		else
4878		    new_files=""
4879		fi
4880		## For new files a comparison is made with /dev/null thus
4881		## all lines will appear to have been added.
4882		wx_eval -r 'print -- "\n------- $filepath -------\n";
4883		    if [[ ${new_files} == *"${filepath} "* ]]; then
4884			${WXDIFFCMD:-diff} $args /dev/null $filepath;
4885		    else
4886			sccs get -s -p -k $filepath |
4887			${WXDIFFCMD:-diff} $args - $filepath;
4888		    fi';;
4889	pdiffs|tpdiffs)
4890		## Parent Diffs - Compare with parent file.  For
4891		## 'tpdiffs' when the parent file does not exist the
4892		## child file is assumed new and compared to
4893		## /dev/null; thus all lines will appear to have been
4894		## added.
4895		wx_eval '
4896		print -- "\n------- $filepath -------\n";
4897		if wx_pnt_filepath; then
4898			echo "Index: $filepath";
4899			${WXDIFFCMD:-diff} $args $parentfilepath
4900			    $workspace/$filepath;
4901		    elif [[ $command == 'tpdiffs' ]]; then
4902			${WXDIFFCMD:-diff} $args /dev/null
4903			    $workspace/$filepath;
4904		    else
4905			print "New file (does not exist in parent).";
4906		    fi';;
4907	pvi)	wx_eval '
4908		echo $filepath;
4909		if wx_pnt_filepath; then
4910			${EDITOR-vi} $args $parentfilepath;
4911		else
4912			echo "New file (does not exist in parent)";
4913		fi';;
4914	edit|checkout|co)	wx_eval wx_edit;;
4915	unedit|uncheckout|unco)	wx_eval wx_unedit;;
4916	create)		wx_eval wx_create $args;;
4917	uncreate)	wx_eval wx_uncreate $args;;
4918	delete|rm)	wx_eval wx_delete $args;;
4919	mv)		wx_mv $file_list;;
4920	delget|checkin|ci)	wx_eval wx_delget $args;;
4921	get|extract)	wx_eval wx_get;;
4922	reset)		wx_eval wx_reset $args;;
4923	putback|pb)	wx_putback $args;;
4924	resolve)	wx_resolve $args;;
4925	prt)		wx_eval 'sccs prt $args $file';;
4926	comments)	wx_eval 'echo $filepath; echo; wx_show_comment; echo';;
4927	bugs)   wx_summary -ao $args;;
4928	arcs)   wx_summary -bo $args;;
4929	pbcom)  wx_summary -po $args;;
4930	info)	wx_eval wx_info;;
4931	reedit|recheckout|reco)  wx_reedit $args;;
4932	redelget|recheckin|reci) CHECKIN=true; wx_reedit $args;;
4933	cstyle) echo "\nDoing cstyle check:"
4934		rm -f $wxtmp/wx.cstyle.*;
4935		export CSTYLE_INDEX=0;
4936		wx_eval wx_cstyle;
4937		wait;
4938		sort -k 1,1 -k 2,2n $wxtmp/wx.cstyle.* 2> /dev/null
4939		;;
4940	jstyle) echo "\nDoing jstyle check:"
4941		rm -f $wxtmp/wx.jstyle.*;
4942		export JSTYLE_INDEX=0;
4943		wx_eval wx_jstyle;
4944		wait;
4945		sort -k 1,1 -k 2,2n $wxtmp/wx.jstyle.* 2> /dev/null
4946		;;
4947	hdrchk)	echo "\nDoing header format check:";
4948		cd $workspace;
4949		hdrchk_files=;
4950		for filepath in $file_list ; do
4951			if [[ "$filepath" == *.h ]]; then
4952				if [[ -s $wxdir/${command}.NOT ]] &&
4953					grep -q "^$(escape_re $filepath)$" \
4954						$wxdir/${command}.NOT
4955				then
4956					echo "$filepath (skipping)"
4957				else
4958					hdrchk_files="$hdrchk_files $filepath"
4959				fi
4960			fi
4961		done
4962		hdrchk -a $args $hdrchk_files ;;
4963	makestyle) echo "\nDoing makestyle check:";
4964		cd $workspace; mlist=$(wx_active | grep '[Mm]akefile');
4965		[[ -n "$mlist" ]] && makestyle $args $mlist;;
4966
4967	keywords)
4968		echo "\nDoing keywords check:";
4969		cd $workspace;
4970		keyword_files=;
4971		for filepath in $file_list ; do
4972			if [[ -s $wxdir/${command}.NOT ]] &&
4973				grep -q "^$(escape_re $filepath)$" \
4974					$wxdir/${command}.NOT
4975			then
4976				echo "$filepath (skipping)"
4977			else
4978				keyword_files="$keyword_files $filepath"
4979			fi
4980		done
4981		keywords -p $args $keyword_files;;
4982
4983	rmdelchk) echo "\nDoing sccs rmdel check:"; wx_eval rmdelchk;;
4984
4985	rtichk) wx_rtichk;;
4986
4987	deltachk)  echo "\nDoing multi delta check:"; wx_eval deltachk;;
4988	copyright) echo "\nDoing copyright check:";
4989		cd $workspace;
4990		copyright_files=;
4991		for filepath in $file_list; do
4992			if [[ -s $wxdir/${command}.NOT ]] &&
4993				grep -q "^$(escape_re $filepath)$" \
4994					$wxdir/${command}.NOT
4995			then
4996				echo "$filepath (skipping)"
4997			else
4998				copyright_files="$copyright_files $filepath"
4999			fi
5000		done
5001		copyrightchk $copyright_files;;
5002
5003	cddlchk)
5004		echo "\nDoing CDDL block check:";
5005		cd $workspace;
5006		cddlnot="";
5007		if [[ -s $wxdir/${command}.NOT ]]; then
5008			cddlnot="-x $wxdir/${command}.NOT"
5009		fi
5010
5011		#
5012		# Split the file list into new files and existing files.
5013		# New files must have a CDDL block whereas existing files don't
5014		# necessarily have to have a block, but if they do it must be
5015		# valid.  Both sets of files are subject to cddlchk.NOT
5016		# exception processing.
5017		#
5018		old=""
5019		new=""
5020		for filepath in $file_list; do
5021			if wx_pnt_filepath $filepath; then
5022				old="$old $filepath"
5023			else
5024				new="$new $filepath"
5025			fi
5026		done
5027		[[ ! -z $new ]] && cddlchk $cddlnot $args -a $new
5028		[[ ! -z $old ]] && cddlchk $cddlnot $args $old
5029		;;
5030	comchk)	 echo "\nDoing comments check:"; wx_summary -n 2>&1;;
5031	outchk)	 echo "\nDoing out check:"; outchk;;
5032	backup|bu) wx_backup $args;;
5033	restore) wx_restore $args;;
5034	apply)	wx_eval "$subcommand \$file";;
5035	eval)	wx_eval "$subcommand";;
5036	grep|egrep)
5037		   wx_eval '
5038		   if egrep -s '\'$pattern\'' $file;
5039		   then
5040			   echo $filepath;
5041			   $command $args '\'$pattern\'' $file;
5042		   fi';;
5043	nawk|sed)
5044		wx_eval 'echo $filepath; $command $args '\'$pattern\'' $file';;
5045	codereview) args="-e $args"; wx_eval wx_fullreview;;
5046	fullreview) wx_eval wx_fullreview;;
5047	webrev) wx_webrev $args;;
5048	dir)	echo $wxdir;;
5049	e)	cd $wxdir; exec ${EDITOR-vi} $orig_args;;
5050	ws)	cd $wsdata; cat $orig_args;;
5051	args)	cat $wsdata/args;;
5052	access)	cat $wsdata/access_control;;
5053	*)	ring_bell;
5054		echo "Command not found. Run 'wx help' for command list.";
5055		exit 1;;
5056esac
5057
5058done
5059
5060if [[ $NEED_WS_CLEAN == 'y' ]]; then
5061	# clean up the nametable
5062	print -u2 "Running workspace updatenames to clean up nametable, may"\
5063	    "take a while."
5064	workspace updatenames >&2
5065fi
5066
5067if [[ -n $bofilelist ]]; then
5068	$BRINGOVER $bofilelist
5069fi
5070
5071# save sorting for last for some speed up.
5072if [[ $ACTSORT == sort ]]; then
5073	if $do_renamed_sort; then
5074		sort_renamed
5075	fi
5076	if $do_active_sort; then
5077		sort_active
5078	fi
5079fi
5080