#!/bin/sh
# this is SoftList_shar.04 (part 4 of SoftList 3.0 Beta)
# do not concatenate these parts, unpack them in order with /bin/sh
# file help.pl continued
#
touch 2>&1 | fgrep '[-amc]' > /tmp/s3_touch$$
if [ -s /tmp/s3_touch$$ ]
then
    TOUCH=can
else
    TOUCH=cannot
fi
rm -f /tmp/s3_touch$$
CurArch=4
if test ! -r s3_seq_.tmp
then echo "Please unpack part 1 first!"
     exit 1; fi
( read Scheck
  if test "$Scheck" != $CurArch
  then echo "Please unpack part $Scheck next!"
       exit 1;
  else exit 0; fi
) < s3_seq_.tmp || exit 1
echo "x - Continuing file help.pl"
sed 's/^X//' << 'SHAR_EOF' >> help.pl
X        C-l	redraw_line
X        C-r	redraw_line
X        C-t	transpose
X        C-u	kill_whole_line
X        C-w	kill_backward_word
X        C-x	kill_to_beginning
X        ESC	escape_prefix
X        BS	backward_delete_char
X        DEL	backward_delete_char
X
X        ESC-!	run_shell
X        ESC-b	backward_word
X        ESC-d	kill_forward_word
X        ESC-f	forward_word
X        ESC-DEL	kill_backward_word
X        ESC-BS	kill_backward_word
X        ESC-ESC	expand_known_word
X
X	The line editor is always in "Insert Mode", meaning that you can
Xgo backwards on the line and insert characters at any point just by typing.
XTry typing a few characters, hitting C-a, and then typing a few more.  Now,
Xto get to the end of the line, enter C-e.  To have SoftList act on your
Xline, hit the Return key.
X
X	If you are in the middle of typing a line, and need to run a shell
Xcommand to look something up, enter ESC-! (Escape Exclamation).  When
Xyou are finished with your shell, SoftList will redraw the line you were
Xworking on, and you can continue entering it.
X+++
X	}
X	&pager (*help_editing_str);
X	1;
X}
X
X
Xsub help_keystrokes {
X        if (! defined ($help_keystrokes_str)) {
X		$help_keystrokes_str = <<"+++";
X	
X
X		Menu Keystrokes
X
X	SoftList is intended to be very interactive, by responding to each
Xkeystroke in each menu without requiring you to hit Return.  Whenever you
Xsee a line like:
X
X        b       batch in/out
X
X	It means that the key in the first column will get you to the
Xmenu or function shown next to it.  You will see the prompt "your choice >> "
Xwhenever SoftList is expecting a single keystroke.
X
X	Quitting SoftList
X
X	You quit SoftList by pressing 'q' at the Main Menu.  From elsewhere
Xin the program, you need to get to the Main Menu if you want to exit this
Xway.  Another possibility is to type "exit" in the Perl Shell. (See the
Xhelp item "The Perl Shell" for more info)
X
X	Pushing and Popping Menus
X
X	Every time you go into a menu, SoftList remembers where you came
Xfrom.  This can be thought of as "pushing" a menu.  To "pop" back out, tap
Xthe spacebar.  A quick example would be:  from the Main Menu, type 'a'
Xto go to the Add Menu.  Now, tap the spacebar, and you will be back in
Xthe Main Menu.  There are some additional keystrokes available that are
Xnot shown in the menus, that are intended to be helpful shortcuts:
X
X	Pushing a Main Menu:
X	From any menu, you can get to the Main Menu by entering 'ESC' (the
X"Escape" key).
X
X	Pushing a Perl Shell:
X	From any menu, you can push a Perl Shell by entering '@' (the
X"at sign").
X
X	Pushing a Bourne Shell:
X	From any menu, you can push a Bourne Shell by entering '!' (an
Xexclamation).
X
X	Going back to a Previous Menu:
X	From any menu, enter '-', and you will return to the last menu you
Xwere at.
X
X	For more info about Line editing (helpful in the Perl Shell), see
Xthe "Editing Lines" item.  There is also help available on the Perl Shell.
X+++
X	}
X	&pager (*help_keystrokes_str);
X	1;
X}
X
X
Xsub help_references {
X        if (! defined ($help_references_str)) {
X		$help_references_str = <<"+++";
X	
X	the references help is not yet defined
X+++
X	}
X	&pager (*help_references_str);
X	1;
X}
X
Xsub help_about {
X        if (! defined ($help_about_str)) {
X		$help_about_str = <<"+++";
X	
X$about_str
X
X	SoftList, a generalized list keeper and Perl CLI
X	
X	This is the successor to SoftList.  It will handle the
X"Current Versions Of Software" list, as well as any other simple
Xlist (phone list, CD listing, etc.) you care to make up.  I can be
Xcontacted via the email address below.
X
X	If you enjoy using this environment, send me a postcard from
Xyour neck of the woods.  If this is being used in a commercial
Xenterprise, financial feedback will help to assure patches and
Xcontinued development :-)
X				   
XDaniel Smith
XP.O. Box 613
XSausalito, CA
X94966
Xemail: dansmith@autodesk.com				   
X
X
X+++
X
X
X
X	}
X	&pager(*help_about_str);
X	1;
X}
SHAR_EOF
echo "File help.pl is complete"
chmod 0644 help.pl || echo "restore of help.pl fails"
if [ $TOUCH = can ]
then
    touch -am 1205004592 help.pl
fi
set `wc -c help.pl`;Wc_c=$1
if test "$Wc_c" != "19618"
then echo original size 19618, current size $Wc_c;fi
# ============= lists.pl ==============
echo "x - extracting lists.pl (Text)"
sed 's/^X//' << 'SHAR_EOF' > lists.pl &&
X#	list menu routines
X#
X#	a hodgepodgery where we let the user go wild...
X#
X#	The routines here enable users to load or create new definitions
X#	for lists,  We do a fair amount of error checking, and give the
X#	user three different [1] ways of defining a new list.
X#
X#	[1] with the forthcoming addition of parsing an existing
X#	record to derive a list definition from it...
X#
X#	This really should be in a package of its own, a lot of it
X#	is improv, or scat, perl.
X#
X$list_template_file = '';
X
X#	main list menu
X%list_menu_cmds = (
X	'?',    '$arg = "l"; eval $cmds{"help"}',
X	'c',    '&list_create_edit',
X	'i',    '&list_create_interactive',
X	'l',    '&list_load',
X	'!',    '&quick_shell',
X	' ',    '&pop_level',
X	'@',    'eval $cmds{"eval_perl_expression"}',
X	'-',    '&push_previous_menu',
X);
X
X#	for the interactive menu
X%list_creation_cmds = (
X	'?',    '$arg = "i"; eval $cmds{"help"}',
X        'd',    '&list_creation_input("DESCRIPTION")',
X        'g',    '&list_creation_input("GLOBAL_SEARCH")',
X        'l',    '&list_creation_input("LOCAL_VAR")',
X        'm',    '&list_creation_input("MAIN_KEY")',
X        'r',    '&list_creation_input("DYNAMIC_REF")',
X        't',    '&list_creation_input("LIST_TITLE")',
X        '0',    '&list_show_fields', 
X        'R',    '$unresolved_list = ""; &list_reset_wd',
X        'U',    '&list_interactive_update',
X	'!',    '&quick_shell',
X	' ',    '&pop_level',
X	'@',    'eval $cmds{"eval_perl_expression"}',
X);
X
X#	register our list creation input routine to keys 1..9
Xforeach $i (1..9) {
X    $list_creation_cmds{$i} = "&list_creation_data_field($i)";
X}
X
X#	as with every other SoftListian menu...
X$list_menu_cmds{pack("c", 27)} = '&main_menu';           # ESC
X$list_creation_cmds{pack("c", 27)} = '&main_menu';       # ESC
X
X
X#	list_menu - create and switch among lists
Xsub list_menu {
X	push (@menu_stack, "&list_menu");
X        while ($menu_stack[$#menu_stack] =~ "&list_menu") {
X		print TTYOUT <<"+++";
X$clear_str
X                List Menu $init_message 
X
X	current list: $list_syms{"LIST_TITLE"}
X
X        ?       help
X
X	c       create a new list (editor)
X        i       interactively create a new list
X        l       load and use a list
X
X+++
X
X            print TTYOUT "\tyour choice >> "; &flush;
X	    read (TTYIN, $ans, 1);
X	    eval ($list_menu_cmds{$ans}) || 
X				print TTYOUT "hey, no such option: $ans\n";
X	}
X	1;
X}
X
X
X#	list_create_edit - use an editor to define list template
Xsub list_create_edit {
X    print TTYOUT <<"EOS";
Xcreate...
X$clear_str
X	Creating a List...
X
X        You may create an entirely new list here.  Just type in a
Xshort name such as "PhoneList" or "coffee types" or "CD Collection".
XAny spaces will get converted to "_" (you will end up with "coffee_types",
X"CD_Collection", etc.).
X
X        You'll use an editor ("$ENV{'EDITOR'}") to define a list template.
X
X        Don't worry, documentation on how to define the list will be
Xthere with you in the editor.
X
X        If you don't want to do this, just press the RETURN key...
XEOS
X    &list_get_list_name(".template");
X    if ($list_template_file =~ /^\.template$/) {
X	print TTYOUT "...skipping..."; sleep 1;
X	return 1;
X    }
X    local ($pushd_arg[0]) = $DIRS{'CATALOG'};
X    &pushd(*main_dir, *pushd_arg);
X
X    open(LIST_TEMPLATE, "> $DIRS{'CATALOG'}/$list_template_file") ||
X	    warn "can't open template file...\n";
X
X		print LIST_TEMPLATE <<"Instructions";
X##	List Template file: $list_template_file
X##
X##      Note that all lines starting with "##" (such as this one) are
X##	just comments containing documentation.  All comment lines are
X##	ignored when the list is defined.  Most of this file is
X##	documentation.  You will be making changes to define a
X##	LIST_TITLE, a MAIN_KEY, one or more DATA_FIELDS, and
X##	possibly how to handle GLOBAL_SEARCH and DESCRIPTION.
X##	This is all that you need to define most lists.  Additional
X##	customization is possible and is outlined farther on in
X##	the file, but most of the time you can simply save your list
X##	and quit the editor before that point.
X
X
X##	LIST_TITLE
X##
X##	Lists have a title that you will see in menus.  This can and
X##	should be a longer, more descriptive phrase than the one you used
X##	for the name of the template.  For this and every other field,
X##	leave the first word (i.e. "LIST_TITLE") alone.  SoftList
X##	needs it to do its work.
X##
X##	A sample title line would look like:
X##	LIST_TITLE Current Versions Of Software
X##
X##	Go ahead and change the list title:
X
XLIST_TITLE  Change me to the list title
X
X
X##	A note on naming of keystrokes and data fields:
X##
X##	The fields that you make up will show up in the Add and
X##	Search menus.  You should use unique, lowercase keystrokes for
X##	the fields you define.  SoftList uses uppercase letters in the
X##	Add and Search menus.  This gives you more freedom to choose
X##	keystrokes, and also helps to separate SoftList operations
X##	from choice of fields.  SoftList reserves some other keystrokes
X##	for its own use.  These are talked about in the "Custom Fields"
X##	section.
X##
X##	This field also takes a letter for which keystroke to use
X##	in menus.  It's a good idea to use the first letter of the field
X##	as the keystroke (like 'n' for Name, 'p' for Phone Number, etc.).
X##	If you specify an uppercase letter, SoftList will coerce it
X##	to be lowercase.
X
X
X##	MAIN_KEY
X##
X##	Lists have one field that will be used for quick lookups.
X##	For an address list, this would often be the "Name" field.
X##      This is called the MAIN_KEY.  Another way you might want to
X##	think about this is: "the primary data field".
X##
X##	A few possible MAIN_KEY fields:
X##	MAIN_KEY l location
X##	MAIN_KEY a address
X##	MAIN_KEY d date
X##
X##	pick your MAIN_KEY field here (edit the keystroke and field name):
X
XMAIN_KEY a address
X
X##	if you used the 'g' or 'd' key, you will need to change
X##	the GLOBAL_SEARCH or DESCRIPTION field below.
X
X
X##	DATA_FIELD
X##
X##	Now we will specify additional data fields.  The DATA_FIELD uses
X##	the same format as the MAIN_KEY.  You may "comfortably" use
X##	a total of 8 data fields.  Beyond that, you will run out of
X##	screen space for your menus on a 24 line terminal.  Here are
X##	some sample DATA_FIELD lines.  Just uncomment them if you want
X##	to use them (leading space is fine, it'll be ignored):
X##
X##	DATA_FIELD c contact
X##	DATA_FIELD c city
X##	DATA_FIELD n name
X##	DATA_FIELD m model
X##	DATA_FIELD b band
X##	DATA_FIELD a author
X##
X##	specify one or more data fields:
X
XDATA_FIELD d dogs
XDATA_FIELD c cats
X
X##	if you used the 'g' or 'd' key, you will need to change
X##	the GLOBAL_SEARCH or DESCRIPTION field below.
X
X
X##	GLOBAL_SEARCH
X##
X##	SoftList gives you a way of searching for a pattern in
X##	records, regardless of field.  This global search is
X##	seen in the Search Menu, and by default it is:
X##	GLOBAL_SEARCH g globally
X##
X##	If you want to use the 'g' keystroke for one of the data
X##	fields, then you need to change the one used for the
X##	global search function.  You may uncomment one of the
X##	sample lines, *or* change the definition below.  In
X##	case you do both, the last one wins.
X
XGLOBAL_SEARCH g globally
X
X
X##	some alternatives:
X##	GLOBAL_SEARCH * globally
X##	GLOBAL_SEARCH a all
X##	GLOBAL_SEARCH e everywhere
X##	GLOBAL_SEARCH e entire record
X
X
X##	DESCRIPTION
X##
X##	SoftList records may have a short description or summary
X##	attached to them.  In the Add Menu, there is an item
X##	for adding the description or summary to a record.  It is
X##	defined as:
X##	DESCRIPTION d description
X##
X##	If you wish to use the 'd' keystroke for one of the data
X##	fields, then you need to change the one used for the description
X##	function.  You may uncomment one of the sample lines, *or*
X##	change the definition below.  In case you do both, the last one wins.
X
XDESCRIPTION d description
X
X##	some alternatives:
X##	DESCRIPTION a attachment
X##	DESCRIPTION < additional
X##	DESCRIPTION < description
X##	DESCRIPTION c comments
X##	DESCRIPTION s summary
X##	DESCRIPTION m more info
X
X
X##	You may be done!
X##
X##	That is all you need to do for most lists!  Here's a quick
X##	checklist to see if you can save this file and successfully
X##	get a list definition, or if you will need to change another
X##	thing or two that is farther on in this file.
X##
X##	* you specified a LIST_TITLE such as:
X##	LIST_TITLE Famous Cats in History
X##
X##	* you specified a MAIN_KEY, such as:
X##	MAIN_KEY f feline name
X##
X##	* you should specify only one MAIN_KEY.  If you specify more
X##	than one, then the last one in the file wins.
X##
X##	* you specified one or more DATA_FIELD's, such as:
X##	DATA_FIELD a age
X##	DATA_FIELD b breed
X##
X##	* Important: if you used the keystroke(s) 'g' or 'd' for one of
X##	the data fields, then you should have changed the definition
X##	for GLOBAL_SEARCH or DESCRIPTION so that there isn't a conflict.
X##
X##	* Also Important: did you make a data field called "dynamic"?
X##	If so, you will need to change the default way that the
X##	field DYNAMIC_REF is handled.  It is the first filed mentioned
X##	in the Custom Fields section.
X##
X##	If this all looks fine, then you may save this file and exit the
X##	editor now, and you should get a new list.  If you want to
X##      look into some customization, read on...
X
X
X##	Custom Fields
X##
X##	At this time, there is an extra field that can be used as part of
X##	a list template.  Later, support will be added that will enable
X##	you to specify alternate Add, Search, Help, Spool routines, and more.
X
X
X##	DYNAMIC_REF
X##
X##	SoftList gives each record the ability to refer to some file
X##	or to perform some action.  Presumably this would be used to
X##	demonstrate or elaborate on the record.  For example. in a list
X##	of different breeds of cats, we might have a line like this in
X##	a record:
X##
X##	dynamic: _X11_BIN/xv _BITMAPS/russian_blue.gif
X##
X##	From the Search Menu, this reference would be detected, and
X##	depending on how the paths _X11_BIN and _BITMAPS resolved,
X##	and availablity of files, we would have the option of seeing
X##	a bitmap of the Russian Blue breed.  In a nutshell, we're
X##	setting aside a field which can be used after a search to
X##	obtain relevant information outside of SoftList.  The default
X##	definition of this field is:
X##	DYNAMIC_REF dynamic
X##
X##	If you used a data field with the name of "dynamic", then
X##	you will want to change the word used here.  A record may
X##	have multiple dynamic references, but they must all be referred
X##	to the same way within any one type of list.
X
XDYNAMIC_REF dynamic
X
X##	some alternatives:
X##	DYNAMIC_REF dref
X##	DYNAMIC_REF references
X##	DYNAMIC_REF external
X##	DYNAMIC_REF link
X
X
X##	Keys Reserved by SoftList...
X##
X##	These characters are used by SoftList in menus:
X##
X##	ESCape	- used to push the Main Menu
X##	Space	- used to pop a menu
X##	!	- quick shell
X##	-	- push previous menu
X##	?	- help
X##
X##	You should stick to using lowercase letters or numbers when
X##	defining menus.
XInstructions
X    
X    close (LIST_TEMPLATE);
X    system ("$ENV{'EDITOR'} $DIRS{'CATALOG'}/$list_template_file");
X
X    &list_load_template($list_template_file);
X    &popd(*main_dir_stack);
X    1;
X}
X
X
X#	list_load_template - load a template and make a definition from it
Xsub list_load_template {
X    local($list_template_file) = @_;
X    local(%template_filter);
X
X    local($which_field, $field_data);
X
X    # the scope of these next two extends to the routines we'll call
X    local($dfield_count) = 0;	# number of data fields seen
X    local($locvar_count) = 0;	# number of local variable fields seen
X
X    open(LIST_TEMPLATE, "< $list_template_file") ||
X	warn "can't open $list_template_file: $!\n";
X
X    # we really want to deal with a %list_data array directly.  The
X    # data we get from a template is in a form that's convenient for
X    # users to edit.  Here we convert it to %list_data, without going
X    # through a myriad of if-then-elsif's...
X
X    %template_filter = (
X	"LIST_TITLE",	"&list_load_template_simple",
X	"MAIN_KEY",	"&list_load_template_simple",
X	"DATA_FIELD",   "&list_load_template_data",
X	"GLOBAL_SEARCH","&list_load_template_simple",
X	"DYNAMIC_REF",  "&list_load_template_simple",
X	"DESCRIPTION",  "&list_load_template_simple",
X	"LOCAL_VAR",    "&list_load_template_locvar",
X    );
X
X
X    # start writing out our %list_data array...
X    $list_def_file = &basename($list_template_file, ".template");
X    $list_def_file .= ".pl";
X
X    &list_start_def_file($list_def_file);
X
X    # skip most of the lines which are either comments or newlines.  What
X    # is left over from that is very likely to be the name of a field.
X    while(<LIST_TEMPLATE>) {
X	next if (/##/);
X	next if (/^$/);
X        chop;
X
X	s/^\s+//;		# kill leading ws
X        ($which_field, $field_data) = split(/[ \t]/, $_, 2);
X	$field_data =~ s="=\\"=g; # you should be able to type '"'
X
X        if ($template_filter{$which_field}) {
X	    $fstr = "$template_filter{$which_field}(\"$which_field\",
X					\"$field_data\")";
X	    eval $fstr;
X	}
X    }
X
X    &list_write_def_file;
X    
X
X    close(LIST_TEMPLATE);
X    %list_data = ();	# kill off old definition
X    %list_syms = ();
X    %list_data = %working_definition;
X
X    # parse_definition will holler about conflicting keystrokes...
X    &parse_definition;
X    1;
X}
X
X#	load template helper routines...
Xsub list_load_template_simple {
X    local($which_field, $field_data) = @_;
X    $working_definition{$which_field} = $field_data;
X    1;
X}
X
Xsub list_load_template_data {
X    local($which_field, $field_data) = @_;
X    $dfield_count++;
X    $working_definition{"$which_field $dfield_count"} = $field_data;
X    1;
X}
X
Xsub list_load_template_locvar {
X    local($field_data) = @_;
X    1;
X}
X
X
X#	list_create_interactive - create a template via menu input
Xsub list_create_interactive {
X    print TTYOUT "
X
X        Going to interactively define a list.  Use the '?' key for help...";
X
X    &list_get_list_name(".pl");
X    if ($list_name =~ /^$/) {
X	$list_name = "unnamed";
X    }
X    if ($list_name =~ "unnamed") {
X	&list_reset_wd;
X    }
X    &list_create_interactive_menu;
X    1;
X}
X
Xsub list_create_interactive_menu {
X    push (@menu_stack, "&list_create_interactive_menu");
X    $unresolved_list = '';
X    while ($menu_stack[$#menu_stack] =~ "&list_create_interactive_menu") {
X	print TTYOUT <<"+++";
X$clear_str
X                Creating a List ($list_name)...
X
X        ?       help
X	
X        t       title of list          "$working_definition{'LIST_TITLE'}"
X        m       main key field         "$working_definition{'MAIN_KEY'}"
X        1-9     edit/add data field
X        0       scroll through data fields
X        r       dynamic reference      "$working_definition{'DYNAMIC_REF'}"
X        g       global search label    "$working_definition{'GLOBAL_SEARCH'}"
X        d       description label      "$working_definition{'DESCRIPTION'}"
X
X        R       reset fields
X        U       update, generate the list
X
X+++
X
X        print TTYOUT "\tyour choice >> "; &flush;
X	read (TTYIN, $ans, 1);
X	if ($ans =~ / /) {
X	    $unresolved_list = "true";
X	}
X	print TTYOUT "$ans..."; &flush;
X	eval ($list_creation_cmds{$ans}) || 
X				print TTYOUT "hey, no such option: $ans\n";
X
X    }
X    1;
X}
X
Xsub list_creation_input {
X    local($which_field) = @_;
X
X    @command_history = @perl_history;
X    &get_input("\n\n\tenter info for $which_field >> ");
X    $working_definition{"$which_field"} = $command_history[$#command_history];
X    @perl_history = @command_history;
X    1;
X}
X
X
Xsub list_creation_data_field {
X    local($which_field) = @_;
X    local($tmp_str);
X    @command_history = @perl_history;
X
X    # first, a sanity check...we really don't want to deal with
X    # skipping around from DATA_FIELD 1 to DATA_FIELD 5 if there
X    # isn't a 2, 3, and 4!  We also don't want to annoy the user with some
X    # "don't hit the '5' key with only two fields you goofball!" message.
X    # A solution is to check how many fields we *really* have so far,
X    # spit out a message that we are adjusting and that we understand
X    # that the user really meant to press a key closer to 1 and that
X    # they just slipped (:-), and get on with it...
X
X    $actual_fields = grep(/DATA_FIELD/, keys %working_definition);
X    if (($which_field - 1) > $actual_fields) {
X	print TTYOUT <<"EOS";
X
X
X        hmm...there are only $actual_fields data fields so far, adjusting...
XEOS
X    	$which_field = $actual_fields + 1;
X    }
X
X    $force_input = $working_definition{"DATA_FIELD $which_field"};
X    &get_input("\n\n\tenter info for DATA_FIELD $which_field >> ");
X    $tmp_str = $command_history[$#command_history];
X    if ($tmp_str !~ /^$/) {
X#	&inspect_field(*tmp_str);
X        $working_definition{"DATA_FIELD $which_field"} = $tmp_str;
X    } else { 
X        &arrive("nothing entered..."); sleep 1; &depart;
X    }
X    @perl_history = @command_history;
X    1;
X}
X
X
X#	list_load - load a template or definition (.pl) %list_data file
X#
X#	what happens here is that we take whichever file is newer
X#
X#	This isn't bulletproof, but it should handle a Super Soaker...
Xsub list_load {
X    local ($pushd_arg[0]) = $DIRS{'CATALOG'};
X    local($try_template) = 0;
X    &pushd (*main_dir_stack, *pushd_arg);
X
X
X    $list_file = &file_prefix($list_file);
X    &assure_set(*list_file, "unnamed");
X    $force_input = $list_file;
X    &file_str_prompt("\tlist name >> ");
X    $list_file = $filename_history[$#filename_history];
X
X    # later on, we will put something in here to remap the '?' key so
X    # that it spits out which lists are available...
X
X    if ($list_file =~ /^$/) {
X	print TTYOUT "...no file, skipping..."; sleep 1;
X	&popd(*main_dir_stack);
X	return 1;
X    }
X 
X    #	what is the filename extension they type in?
X    $the_ext = &extension($list_file);
X
X    # once again, canonicalize..
X    $list_file = &file_prefix($list_file);
X    $lf_pl = $list_file . ".pl";
X    $lf_tmpl = $list_file . ".template";
X
X    $most_recent = &newest_file($lf_pl, $lf_tmpl);
X
X    # ok, at this point, we have one of the following situations:
X    #
X    # no file	                           - punt
X    # user asked specifically for .pl and:
X    # template only, or template newer than definition - make a definition
X    # user asked specifically for .template:
X    # overwrite the .pl after confirmation?
X
X    # a definition?
X    if ($most_recent =~ $lf_pl) {
X	print TTYOUT "...loading definition $lf_pl..."; &flush;
X	eval `cat $lf_pl`;
X	&parse_definition;
X	&popd(*main_dir_stack);
X	return 1;
X    }
X
X    # a newer template, or definition doesn't exist yet?
X    if ($most_recent =~ $lf_tmpl) {
X	print TTYOUT "...loading template $lf_tmpl..."; &flush;
X        &list_load_template($lf_tmpl);
X	&popd(*main_dir_stack);
X	return 1;
X    }
X
X    &popd (*main_dir_stack);	
X    1;
X}
X
Xsub list_interactive_update {
X    %list_data = %working_definition;
X    &parse_definition;
X    &list_start_def_file($list_name);
X    &list_write_def_file;
X    1;
X}
X
Xsub list_reset_wd {
X    if ($unresolved_list =~ /^$/) {
X	%working_definition = %list_data;
X	$num_data_fields = 0;
X    }
X    1;
X}
X
Xsub list_show_fields {
X    print TTYOUT "\n\n\t0 to keep scrolling, any other key returns to menu\n";
X    print TTYOUT "\tDATA_FIELD ";
X    local($ans) = "0";
X    local($tmp_str);
X    local($scroll_field) = 0;
X
X    $actual_fields = grep(/DATA_FIELD/, keys %working_definition);
X    while ($ans =~ "0") {
X	$scroll_field++;
X	if ($scroll_field > $actual_fields) {
X	    $scroll_field = 1;
X	}
X	
X	# not strictly needed...just avoiding a long line...
X	$tmp_str = $working_definition{"DATA_FIELD $scroll_field"};
X	&arrive("$scroll_field is \"$tmp_str\"...");
X        read (TTYIN, $ans, 1);
X        &depart;
X    }
X    1;
X}
X
Xsub list_get_list_name {
X    local($type) = @_;
X    local ($pushd_arg[0]) = $DIRS{'CATALOG'};
X    &pushd (*main_dir_stack, *pushd_arg);
X    $list_template_file = &basename($list_template_file, $type);
X
X    if ((! defined($list_template_file)) ||
X	$list_template_file =~ /^.template$/) {
X	$list_template_file = "unnamed";
X    } else {
X	$list_template_file = &basename($list_template_file, ".template");
X    }
X    $force_input = $list_template_file;
X
X    @command_history = @perl_history;
X    &get_input("\tenter the list name to create >> ");
X    $list_template_file = $command_history[$#command_history];
X    $list_template_file =~ y/0-9a-zA-Z-_/_/cs;
X    # a little sanity check in case the user typed the .template suffix
X    $list_template_file = &basename($list_template_file, ".template");
X    $list_name = $list_template_file;
X    $list_template_file = "$list_template_file.template";
X    &popd (*main_dir_stack);	
X    1;
X}
X
Xsub inspect_field {
X#    local(*the_field) = @_;
X
X #   print "in inspect field we see $the_field\n"; sleep 2;
X
X
X    1;
X}
X
Xsub list_start_def_file {
X    local($the_file) = @_;
X
X    print TTYOUT "writing to $the_file....\n"; sleep 2;
X    open(LIST_DEF, "> $the_file") ||
X 	warn "can't open $the_file: $!\n";
X    $date_str = `date`;
X
X    print LIST_DEF "
X# $the_file - list definition file
X# this file created by SoftList on $date_str
X
X\%list_data = (
X";
X    1;
X}
X
X
Xsub list_write_def_file {
X    while (($key, $val) = each %working_definition) {
X	print LIST_DEF "\t\"$key\", \"$val\",\n";
X    }
X    print LIST_DEF "\n);";
X    close(LIST_DEF); 
X    1;
X}
X1;
SHAR_EOF
chmod 0644 lists.pl || echo "restore of lists.pl fails"
if [ $TOUCH = can ]
then
    touch -am 1205004592 lists.pl
fi
set `wc -c lists.pl`;Wc_c=$1
if test "$Wc_c" != "21084"
then echo original size 21084, current size $Wc_c;fi
# ============= logo ==============
echo "x - extracting logo (Text)"
sed 's/^X//' << 'SHAR_EOF' > logo &&
X
X
X                      __
X    //\         _|_  _  \\     _   '
X __//  /\   //   |  ' `  || ' /   _|_
X  --  /  | ||-   |  |-|  || | `_   |
X   ---   | ||_   |  | |  || |   |  |
X          \ __ _'__'___  || | __'  |
X          \_ _'__'____  \_'______'____
X
X
X
X             /\    /\
X            / \\  / \\
X           /   \\/   \\
X          /           \\  M e t a L i s t
X                       \\
X                         -----------------
X
X
X
X        the one being used....
X
X                             ____
X             /\    /\           \\
X            / \\  / \\           \\
X           /   \\/   \\           \\
X          /           \\  M e t a  \\  L i s t
X         /             \\           \\
X        /                ---------   \\___________
X
X
X
X
X
X
X
X                    ____
X                   //
X                  //
X                  \\  ___
X                   \\  //
X                    \\//
X                     //____   S o f t L i s t...
X                    //----
X              _____//
X
X
X
X
X
X
X                      ______
X                    /______/
X                   //
X                  //
X                 //
X                /-------  //
X                -----//  //
X                  --//  //--
X                 --//  //--
X                --//  //---
X           ------//  /-------
X           -------  --------
X
X
X
X        first take at embossing...
X
X
X                     ______/           __/
X                    /                 /              /
X                   /                 /              /
X                  /                 /     /        /
X                ------    _   /    __/   __/      /       _/   __/
X                      /  /   /    /     /        /            /
X                     /  /   /    /     /        /        /  __
X                    /      /    /     /        /        /      /
X             ______/  ____/   _/    _/      _______/  _/  ____/
X
X
X
X
X        tighten up the 'L'...
X
X
X
X                ______/           __/
X               /                 /            /
X              /                 /            /
X             /                 /     /      /                   /
X           ------    _   /    __/   __/    /       _/   __/    __/
X                 /  /   /    /     /      /            /      /
X                /  /   /    /     /      /        /  __      /
X               /      /    /     /      /        /      /   /
X        ______/  ____/   _/    _/    _______/  _/  ____/  _/
X
X
X
X
Xlittle crosses?
X
X                ______/           __/
X               /                 /             /
X              /                 /             /
X             /                 /       /     /                   /
X           ------    _   /    __/ .   __/   /      _/   __/ .   __/
X                 /  /   /    /      /      /           /       /
X                /  /   /    /      /      /       /  __       /
X               /  /   /    /      /      /       /      /    /
X        ______/  ____/   _/     _/    _______/ _/  ____/   _/
X
X
X
X
Xscrunched..
X
X                ______/       __/
X               /             /       /
X              /             /       /
X             /             /   /   /              /
X           ------   _   / __/ __/ /     _/  __/   __/
X                 / /   / /   /   /         /    /
X                / /   / /   /   /      /  __   /
X               / /   / /   /   /      /     / /
X        ______/ ____/_/  _/ _______/_/ ____/_/
X
X
X
X
X
SHAR_EOF
chmod 0644 logo || echo "restore of logo fails"
if [ $TOUCH = can ]
then
    touch -am 1205004592 logo
fi
set `wc -c logo`;Wc_c=$1
if test "$Wc_c" != "3426"
then echo original size 3426, current size $Wc_c;fi
# ============= parse_definition.pl ==============
echo "x - extracting parse_definition.pl (Text)"
sed 's/^X//' << 'SHAR_EOF' > parse_definition.pl &&
X#	parse_template.pl
X
X%list_data = (
X	"LIST_TITLE",   "Current Versions Of Software",
X	"MAIN_KEY",	"p package",
X	"DATA_FIELD 1",	"c contact",
X	"DATA_FIELD 2",	"l location",
X	"GLOBAL_SEARCH","g globally",
X	"DYNAMIC_REF",  "dynamic",
X	"DESCRIPTION",	"d description",
X	"LOCAL_VAR 1",	"\$show_description = 0",
X);
X
X
X#	here's where we decide how to parse the different %list_data entries
X%parse_routines = (
X	"MAIN_KEY", 		"&parse_primary",
X	"DATA_FIELD",		"&parse_data_field",
X	"GLOBAL_SEARCH",	"&parse_global_search",
X	"DYNAMIC_REF",		"&parse_dref",
X	"DESCRIPTION",		"&parse_description",
X	"LOCAL_VAR",		"&parse_local_var",
X
X        "LIST_TITLE",           0,
X	"CORE_ADD_ROUTINES",	0,
X	"CORE_SEARCH_ROUTINES",	0,
X	"CORE_HELP_ROUTINES",	0,
X
X	"INPUT_SPOOL_ROUTINES",	0,
X	"INPUT_SPOOL_FILES",	0,
X	"INPUT_SPOOL_DIRS",	0,
X);
X
X
X
X
Xsub parse_definition {
X    $num_data_fields = 0;
X    %check_key = ();
X    $list_syms{"RESET_LIST"} = ();
X    $list_syms{"LIST_TITLE"} = $list_data{"LIST_TITLE"};
X    foreach $ld_item (keys %list_data) {
X	($routine_item, $ignore) = split(' ', $ld_item);
X	$fstr = "$parse_routines{$routine_item}(\"$list_data{$ld_item}\")";
X	eval $fstr;
X    }
X    $list_syms{"ADD_MENU_STRS"} = ();
X    $list_syms{"SEARCH_MENU_STRS"} = ();
X    &init_add_menu_strs;
X    1;
X}
X
Xsub parse_primary {
X    local($key_str) = @_;
X    
X    ($keystroke, $key_str) = split(' ', $key_str);
X    &make_data_input_routine($keystroke, $key_str, "MAIN_VAL");
X    $list_syms{"MAIN"} = $key_str;
X    $list_syms{"MAIN_KS"} = $keystroke;
X    $list_syms{"MAIN_VAL"} = '';
X    &parse_check_key($keystroke, "MAIN_KEY ($key_str)");
X    1;
X}
X
Xsub parse_data_field {
X    local($key_str) = @_;
X    
X    $num_data_fields++;
X    ($keystroke, $key_str) = split(/[ \t]+/, $key_str);
X
X    &make_data_input_routine($keystroke, $key_str,
X			     "DATA_VAL" . $num_data_fields);
X    &make_data_search_routine($keystroke, $key_str);
X
X    $list_syms{"DATA_" . $num_data_fields} = $key_str;
X    $list_syms{"DATA_KS" . $num_data_fields} = $keystroke;
X    $list_syms{"DATA_VAL" . $num_data_fields} = '';
X    &parse_check_key($keystroke, "DATA_FIELD $num_data_fields ($key_str)");
X    1;
X}
X
Xsub parse_global_search { 
X    1;
X}
X
Xsub parse_dref {
X    local($dref_str) = @_;
X    $list_syms{"DYNAMIC_REF"} = $dref_str;
X    1;
X}
X
Xsub parse_description {
X    local($desc_str) = @_;
X    
X    ($keystroke, $desc_str) = split(' ', $desc_str);
X    
X    $list_syms{"DESCRIPTION"} = $desc_str;
X    $list_syms{"DESCRIPTION_KS"} = $keystroke;
X    $list_syms{"DESCRIPTION_STAT"} = "not edited";
X    &make_description_input_routine($keystroke);
X    &parse_check_key($keystroke, "DESCRIPTION ($desc_str)");
X    1;
X}
X
Xsub parse_local_var {
X
X    1;
X}
X
X
Xsub parse_check_key {
X    local($keystroke, $desc_str) = @_;
X
X    if (defined($check_key{$keystroke})) {
X	print TTYOUT <<EOS;
X
X	hey! "$keystroke" for "$desc_str" interferes with
X             "$keystroke" for "$check_key{$keystroke}"!
X
X        This is something that should be corrected in the template, or
Xyour list will not behave properly.
X
XEOS
X        print TTYOUT "\tpress any key..."; &flush;
X	read (TTYIN, $ans, 1);
X    } else {
X	$check_key{$keystroke} = $desc_str;
X    }
X    1;
X}
X
X1;
SHAR_EOF
chmod 0644 parse_definition.pl || echo "restore of parse_definition.pl fails"
if [ $TOUCH = can ]
then
    touch -am 1205140192 parse_definition.pl
fi
set `wc -c parse_definition.pl`;Wc_c=$1
if test "$Wc_c" != "3182"
then echo original size 3182, current size $Wc_c;fi
# ============= references.pl ==============
echo "x - extracting references.pl (Text)"
sed 's/^X//' << 'SHAR_EOF' > references.pl &&
X#
X#	references.pl - create or follow dynamic references
X#
X#	Daniel Smith, dansmith@autodesk.com, Mid Olympics, February 1992
X
X# these lines for SoftList...
X$cmds{"dynamic_ref_expand"} = '&dynamic_ref_expand';
X$cmds{"reference_menu"} = '&reference_menu';
X$cmds {"find_references"} = '&find_references';
X
X
X%reference_menu_cmds = (
X    '?',    '$arg = "r"; eval $cmds{"help"}',
X);
X
X
X
Xsub reference_menu {
X		&do_callbacks("references");
X
X		print TTYOUT <<"+++";
X$clear_str
X
X                References Menu $init_message 
X
X                total references: $total_references
X
X        ?       help
X
X
X+++
X
X		print TTYOUT "\tyour choice >> "; &flush;
X	# for debugging.
X
X	foreach (keys %dynamic_references) {
X	print TTYOUT "\n$dynamic_key_field{$_} = \"$dynamic_references{$_}\"\n";
X		
X		&dynamic_ref_expand($dynamic_references{$_});
X
X	}
X		read (TTYIN, $ans, 1);
X		eval ($add_menu_cmds{$ans}) || 
X				print TTYOUT "hey, no such option: $ans\n";
X
X	1;
X}
X
X
Xsub dynamic_ref_expand {
X	local ($dynamic_path) = @_;
X	local (@keyword);
X	local ($found_it) = 0;
X
X	($keyword, $ignore) = split('/', $dynamic_path);
X	$keyword =~ s/^dynamic:\s+//;
X	$remainder = substr($dynamic_path, index($dynamic_path, "/"));
X
X	if ($dynamic_paths{$keyword}) {
X		foreach (split(':', $dynamic_paths{$keyword})) {
X			$real_path = $_ . $remainder;
X			if (-e $real_path) {
X				$found_it++;
X				last;
X			}
X		}
X		$found_it && print TTYOUT "\nwe get \"$real_path\" \n";
X	}
X	#return $found_it;
X	1;
X}
X
X
Xsub find_references {
X	local ($pushd_arg[0]) = $DIRS{'DATA'};
X	%dynamic_references = ();
X	$total_references = 0;
X	&pushd (*main_dir_stack, *pushd_arg);
X	foreach $file (@found_records) {
X		local (@cur_dynamic_lines) = ();
X		open (CUR_FILE, $file);
X		while (<CUR_FILE>) {
X
X			# change this line so as not to be softlist dependent
X			if (m!^package:!) {
X				chop;
X				s/^package:\s+//;
X				$dynamic_key_field{$file} = $_;
X			}
X			
X			if (m!^dynamic:!) {
X				chop;	# no NL spoken here, nor "dynamic:"
X				s/^dynamic:\s+//;	# and the WS goes too
X				push(@cur_dynamic_lines, $_);
X				$total_references++;
X			}
X		}
X
X		if ($#cur_dynamic_lines != -1) {
X			$dynamic_references{$file} = (@key_field,
X							@cur_dynamic_lines);
X		}
X		close(CUR_FILE);
X	}
X	if ($total_references) {
X		$reference_str = "R       references";
X		$reference_hdr_str = ", with $total_references reference";
X		if ($total_references > 1) {
X			$reference_hdr_str .= 's';
X		}
X		$search_menu_cmds{'R'} = '&reference_menu';
X	} else {
X		$reference_str = '';
X		$reference_hdr_str = '';
X		$search_menu_cmds{'R'} = '0';
X	}
X	&popd (*main_dir_stack);
X	1;
X
X}
X
X#	references_init - usually called from search_menu.pl
Xsub references_init {
X	$refs_init = 1;
X}
X1;
SHAR_EOF
chmod 0755 references.pl || echo "restore of references.pl fails"
if [ $TOUCH = can ]
then
    touch -am 1205004592 references.pl
fi
set `wc -c references.pl`;Wc_c=$1
if test "$Wc_c" != "2666"
then echo original size 2666, current size $Wc_c;fi
# ============= search_menu.pl ==============
echo "x - extracting search_menu.pl (Text)"
sed 's/^X//' << 'SHAR_EOF' > search_menu.pl &&
X#       update $cmds so that we just load once
X$cmds{"search_menu"} = '&search_menu';
X
X%search_menu_cmds = (
X	'?',	'$arg = "s"; eval $cmds{"help"}',
X
X	'g',	'&search_globally',
X	'p',	'&search_package',
X
X	'A',	'eval $cmds{"add_menu"}',
X	'E',	'&record_actions',
X	'M',	'&record_actions',
X	'C',	'&reset_variables',
X	'V',	'&record_actions',
X	'P',	'&record_actions',
X	'R',	'0',
X	'!',	'&quick_shell',
X	' ',	'&pop_level',
X	'@',	'eval $cmds{"eval_perl_expression"}',
X	'-',	'&push_previous_menu',
X);
X
X#	'l',	'&search_location',	'c',	'&search_contact',
X
X$search_menu_cmds{pack("c", 27)} = '&main_menu';	# ESC
X
X#	search_menu - entry point for searches
Xsub search_menu {
X
X    #	ay yi yi... we need to have references.pl loaded so that
X    #	we can register callbacks to find_references.  Going through
X    #	$cmds and trying to call a callback wont work because you
X    #	get a very strange looking function call that bombs
X    if (! defined ($refs_init)) {
X	&load_routine ("references.pl", "references_init");
X    }
X    push (@menu_stack, "&search_menu");
X    while ($menu_stack[$#menu_stack] =~ "&search_menu") {
X	&do_callbacks("search");
X	print TTYOUT <<"+++";
X$clear_str
X		Search/Edit Menu $init_message
X
X        $num_data_files data files total${last_found_str}${reference_hdr_str}
X
X	?       help
X
X        Search by:              Actions:
X
X        p       package         A       add
X        c       contact         C       clear patterns
X        l       location        E       edit
X        g       globally        M       mail
X                                P       print
X                                V       view
X				$reference_str
X
X        Current Search Pattern: "$search_pattern"
X
X+++
X        print TTYOUT "\tyour choice >> "; &flush;
X        read (TTYIN, $ans, 1);
X
X        eval ($search_menu_cmds{$ans}) ||
X            print TTYOUT "no such option: $ans\n";
X
X    }
X    1;
X}
X
X
X#	record_actions - manipulate the found records
X#
X# this will do any number of things to the found files, depending on
X# what $ans is set to...
Xsub record_actions {
X    local ($pushd_arg[0]) = $DIRS{'DATA'};
X    
X    # first, build a list of filenames...
X    @found_files = ();
X    # note: hey! this loop could be a one liner, but would it be any faster?
X    foreach $file (@found_records) {
X	push (@found_files, $file);
X    }
X
X    &pushd (*main_dir_stack, *pushd_arg);
X    eval $record_actions{$ans};
X    &popd (*main_dir_stack);
X    1;
X}
X
X
X#	search_package - search by name
Xsub search_package {
X    print TTYOUT "\n\n";
X    @found_records = ();
X    &add_callback("eval $cmds{'find_references'}");
X    @command_history = @package_history;
X    &get_input("\tenter package search pattern >> ");
X    $search_pattern = $command_history[$#command_history];
X    @key_records = grep(/$search_pattern/, keys %all_packages);
X    foreach $package_key (@key_records) {
X	push (@found_records, $all_packages{$package_key});
X    }
X    @package_history = @command_history;
X    $last_found_str=", last search found ".($#found_records + 1);
X    1;
X}
X
X
Xsub make_data_search_routine {
X    local($the_keystroke, $the_routine_name) = @_;
X
X    # register with our search menu
X    $search_menu_cmds{"$the_keystroke"} = "&search_SR_$the_routine_name";
X
X    eval <<EOS;
Xsub search_SR_$the_routine_name {
X    \&add_callback("eval \$cmds{'find_references'}");
X    print TTYOUT "\n\n";
X    \@command_history = \@${the_routine_name}_history;
X    \&get_input("\tenter $the_routine_name search pattern >> ");
X    \$search_pattern =
X    "${the_routine_name}:.*".\$command_history[\$#command_history];
X					       \&file_search(\$search_pattern);
X    \$last_found_str=", last search found ".(\$#found_records + 1);
X    \@${the_routine_name}_history = \@command_history;
X    1;
X}
XEOS
X    1;
X}
X
X
X#	search_globally - grep for pattern in all current data files
Xsub search_globally {
X    &add_callback("eval $cmds{'find_references'}");
X    print TTYOUT "\n\n";
X    
X    # picking the @package_history is a good shot, but it's also bogus.
X    # what should happen here is to refer to a global history (all of the
X    # lines typed in throughout the script...
X    
X    @command_history = @package_history;
X    &get_input("\tenter global search pattern >> ");
X    print TTYOUT "..."; &flush;
X    $search_pattern = $command_history[$#command_history];
X    &file_search($search_pattern);
X    $last_found_str=", last search found ".($#found_records + 1);
X    @package_history = @command_history;
X    1;
X}
X
X
X#	file_search - do an actual search through data files
Xsub file_search {
X    local ($pattern) = @_;
X    ($pattern = $pattern) =~ s/!/\\!/g;
X    local ($pushd_arg[0]) = $DIRS{'DATA'};
X    @found_records = ();
X    
X    &pushd (*main_dir_stack, *pushd_arg);
X    foreach $file (values %all_packages) {
X	open (CUR_FILE, $file);
X	while (<CUR_FILE>) {
X	    if (m!$pattern!) {
X		push (@found_records, $file);
X		last;
X	    }
X	}
X	close(CUR_FILE);
X    }
X    &popd (*main_dir_stack);
X    1;
X}
X1;
SHAR_EOF
chmod 0644 search_menu.pl || echo "restore of search_menu.pl fails"
if [ $TOUCH = can ]
then
    touch -am 1205134992 search_menu.pl
fi
set `wc -c search_menu.pl`;Wc_c=$1
if test "$Wc_c" != "4927"
then echo original size 4927, current size $Wc_c;fi
# ============= utils.pl ==============
echo "x - extracting utils.pl (Text)"
sed 's/^X//' << 'SHAR_EOF' > utils.pl &&
X#
X#	utils.pl - utility routines developed for SoftList 3.0
X#
X#	Daniel Smith, 1992
X#
X#	Routines in this file are not specific to SoftList
X
X
X#	load_routine - load routines from files on the fly, and execute them
Xsub load_routine {
X    local ($routine);
X    
X    $routine = $_[1];
X    require $DIRS{'LIB'}."/$_[0]";
X    if (defined $_[2]) {	# pass an arg
X	&$routine($_[2]);
X    } else {
X	&$routine;
X    }
X}
X
X#	call - set up a reference into %cmds
X#
X#	this is just a syntatic wrapper to make some aliases look better
Xsub call {
X    $arg = $_[1];
X    eval $cmds{$_[0]};
X}
X
X#	file_prompt
Xsub file_prompt {
X    print TTYOUT "\n\n";
X    @command_history = @filename_history;
X    &get_input("\tenter filename");
X    @filename_history = @command_history;
X    return 1;
X}
X
X
X#	file_str_prompt - use the arg as prompt...
Xsub file_str_prompt {
X    local($our_prompt) = @_;
X    print TTYOUT "\n\n";
X    @command_history = @filename_history;
X    &get_input($our_prompt);
X    @filename_history = @command_history;
X    return 1;
X}
X
X
X#	quick_shell - push a bourne shell
Xsub quick_shell {
X    &cbreak_off;
X    system "/bin/sh";
X    &cbreak_on;
X}
X
X
X#	sh - run a shell command
X
X#	all kinds of room for improvement here...
Xsub sh {
X    print TTYOUT "\n";
X    &cbreak_off;
X    $found_pipe = 0;
X    foreach (@_) {
X	(/.*\|.*/) && $found_pipe++;
X    }
X    
X    # try to handle pipes...
X    if ($found_pipe > 0) {
X	open (SH_CMD, "| @_");
X	while (<SH_CMD>) {
X	    print TTYOUT;
X	}
X	close (SH_CMD);
X    } else {
X	system (@_);
X    }
X    &cbreak_on;
X}
X
X
X#	sh_record - run shell command and store output
X# not working yet...
X
Xsub sh_record {
X	&cbreak_off;
X	open (SH_CMD, "@_ |") || warn "can't open @_\n";
X
X	while (<SH_CMD>) {
X		print TTYOUT "$_ $_\n";
X		$sh_recording .= $_;
X	}
X	close (SH_CMD);
X	&cbreak_on;
X}
X	
X	
X
X#	crc - generate checksum
X#
X#	not much use for this at the moment, although I did write an
X#	alias (crc16) to call it.  Keep it around for SoftList, may be of use.
Xsub crc {
X        open (THE_FILE, "< @_") || warn "can't open @_\n";
X        $save_line_mode = $/;
X        undef $/;
X        $checksum = unpack("%16C*", <THE_FILE>);
X        $/ = $save_line_mode;
X}
X
X
X#	these next two routines should probably be replaced by portable
X#	perl equivalents
Xsub cbreak_on {
X	if ($BSD) {
X	    system "stty -echo cbreak </dev/tty >/dev/tty 2>&1";
X	}
X	else {
X	    system "stty", 'cbreak',
X	    system "stty", 'eol', '^A';
X	}
X}
X
Xsub cbreak_off {
X    if ($BSD) {
X	system "stty -cbreak echo </dev/tty >/dev/tty 2>&1";
X    }
X    else {
X	system "stty", 'icanon';
X	system "stty", 'eol', '^@';
X    }
X    print TTYOUT "\n";
X}
X
X
X#	flush - flush output
Xsub flush {
X	$| = 1;
X	print TTYOUT '';
X	$| = 0;
X}
X
X
X
X#	center - center a string
X#
X#	yep, I assume 80 columns...
Xsub center {
X	return sprintf("%s%s", ' ' x ((80 - length(@_[0])) / 2), @_[0]);
X}
X
X#	push_previous_menu
Xsub push_previous_menu {
X	eval $last_used;
X	return 1;
X}
X
X
X#	pop_level - go back a menu level
Xsub pop_level {
X	if ($#menu_stack > 0) {
X		$last_used = pop (@menu_stack);
X	}
X	1;
X}
X
X
X#	cd - a chdir which updates $ENV{'PWD'}
X#
X#	cd turned out to be more of a challenge than pushd and popd!  What
X#	messed with my head was the varargs situation...the case of "cd"
X#	by itself resolving to home, versus "cd somedir".  I'm punting.
X#	from this point forward, cd will *always* modify @main_dir_stack,
X#	and will always take one argument as its target.  Simplifies things
X#	a lot here, and it's trivial to make an alias check for the $HOME case.
X#	Mon Dec 30 23:55:47 PST 1991
Xsub cd {
X	local ($cd_target) = @_;
X	local ($ret) = 0;
X
X	if (&chdir_cdpath ($cd_target)) {
X		$ret++;
X	}
X	$ENV{'PWD'} = &fastcwd;
X	$main_dir_stack[$#main_dir_stack] = $ENV{'PWD'};
X	&set_dir_prompt;
X	$ret;
X}
X
X
X#	set_dir_prompt - construct prompt for CLI
Xsub set_dir_prompt {
X	local ($push_level_prompt) = '';
X	if ($#main_dir_stack) {
X		$push_level_prompt = " ($#main_dir_stack) ";
X	}
X	$dir_prompt = $ENV{'PWD'}.$push_level_prompt;
X	1;
X}
X
X
X#	chdir_cdpath - do a chdir with the aid of $cdpath
Xsub chdir_cdpath {
X	local ($target) = @_;
X	local ($found) = 0;
X	local ($i) = 0;
X
X	if (! defined (@cdpath)) {
X		@cdpath = ('.', '..', '../..');	# minimal @cdpath
X	}
X
X	# check for variables to expand...
X	# Sun Jan 19 19:57:22 PST 1992
X	if ($target =~ /^\$/) {
X		$target = eval $target;
X	}
X
X	# check for an absolute pathname
X	if ($target =~ /^\//) {
X		if (-d $target) { # give it a shot, else we drop into the loop
X			chdir ($target) && $found++;
X		}
X	}
X
X	while ($found == 0 && $i < $#cdpath) {
X		if (-d $cdpath[$i].'/'.$target) {
X			chdir ($cdpath[$i].'/'.$target) && $found++;
X		} else {
X			$i++;
X		}
X	}
X	$found;
X}
X
X
X
X#	pushd - pushd one or more directories onto a given stack
X#
X#	this routine must be called as:
X#	&pushd(*some_menu_stack, *pushd_targets)
Xsub pushd {
X	local (*which_stack, *pushd_targets) = @_;
X	local ($i);
X
X	if ($#pushd_targets == -1) {	# then we have a simple stack flip
X		if ($#which_stack >= 1) { # then we have something to work with
X			@old_top = pop (@which_stack);
X			@new_top = pop (@which_stack);
X			if (&chdir_cdpath ($new_top[0])){ 
X				$ENV{'PWD'} = &fastcwd;
X				push (@which_stack, @old_top);
X				push (@which_stack, $ENV{'PWD'});
X			} else { # a directory has disappeared out
X				 # from under us..
X				push (@which_stack, @new_top);
X				push (@which_stack, @old_top);
X			}
X		}
X	} else {
X		foreach $push_arg (@pushd_targets) {
X			if (&chdir_cdpath ($push_arg)) {
X				$ENV{'PWD'} = &fastcwd;
X				push(@which_stack, $ENV{'PWD'});
X			} else {
X			;
X			# well? what? I can't just spit out "No such directory"
X			# if this is being called internally...perhaps setting
X			# the return value and leaving it up to whoever called
X			# us to make the decision...
X			}
X		}
X	}
X	&set_dir_prompt;
X	1;	# for now, this may change...
X}
X
X
X#	popd - pop directory from given stack
X#
X#       this routine must be called as:
X#       &popd(*some_menu_stack)
X#
Xsub popd {
X    local (*which_stack) = @_;
X    
X    if ($#which_stack >= 1) {
X	@old_top = pop (@which_stack);
X	if (&chdir_cdpath ($which_stack[$#which_stack])) {
X	    $ENV{'PWD'} = &fastcwd;
X	    $which_stack[$#which_stack] = $ENV{'PWD'}; # dot our i's
X	} else { # a directory has disappeared out from under us..
X	    push (@which_stack, @old_top);
SHAR_EOF
echo "End of SoftList 3.0 Beta part 4"
echo "File utils.pl is continued in part 5"
echo "5" > s3_seq_.tmp
exit 0
-- 
     Daniel Smith, Autodesk, Sausalito, California, (415) 332-2344 x 2580
	      Disclaimer: written by a highly caffeinated mammal
	    dansmith@autodesk.com            dansmith@well.sf.ca.us


------------------------------

** FOR YOUR REFERENCE **

The service addresses, to which questions about the list itself and requests
to be added to or deleted from it should be directed, are as follows:

    Internet: Perl-Users-Request@fuggles.acc.Virginia.EDU
              Perl-Users-Request@uvaarpa.Virginia.EDU
    BITNET:   Perl-Req@Virginia
    UUCP:     ...!uunet!virginia!perl-users-request

You can send mail to the entire list (and comp.lang.perl) via one of these
addresses:

    Internet: Perl-Users@uvaarpa.Virginia.EDU
    BITNET:   Perl@Virginia
    UUCP:     ...!uunet!virginia!perl-users

End of Perl-Users Digest
******************************
--[1701]--
