;#	-*-Perl-*-
;#
;#	expand.pl - filename and variable expansion for SoftList, version 3.0
;#
;#	Daniel Smith, dansmith@autodesk.com
;#
;#	There's so much going on with filename and variable expansion that
;#	this deserves its own file, and its own special brand of insanity :-)
;#
;#	There are many cases with filename expansion:
;#
;#	1) an easy one.  someone types "cd pe<expand>".  Expanding in the
;#	current directory is straightforward.
;#
;#	2) introduction of special characters.  someone
;# 	types "cd ./pe<expand>".
;#	As soon as we see '.', we are either dealing with a hidden file or
;#	directory, or, a relative path of some sort.


;# likely that I'll rewrite this from scratch..Wed Nov  4 11:26:07 PST 1992


;# Sun Feb 16 13:42:46 PST 1992
;#
;# I'm currently unhappy with this, and am going to leave it for a little
;# while and come back to it.  It's a mess.
;#
;# First off, the current word needs to be narrowed down better.  It should
;# not matter where the cursor is (beginning, middle, end).  If the cursor
;# is in the word, then we should find the whole word...
;#
;# The first_pass code is pretty good.  It narrows down all of the cases
;# I can think of at this time.  Now I just need code to deal with all of
;# them.  I need to seperate out a lot of the redundant stuff, like finding
;# the longest expansion for two or more possible matches...that one happens
;# all over the place...
;#
;# On the other hand, for casual use this is good enough for now.  The goal
;# is to get it as good as the expansion in tcsh.
;#
;# A couple of other goals, as long as I am writing...
;# One is to be able to expand scalar, list, associative array, and subroutine
;# references.  This won't be easy, but then I don't think it will be as
;# hard as trying to handle all of the filename expansion cases.  Just takes
;# lots of code :-)
;#
;# Another goal is to have a sub-mode to handle multiple matches.  The way
;# it would work is that when you get more than one match, you will be able
;# to use the TAB key to traverse through all of the matches.  Any other
;# key would exit out of that sub-mode and get fed into whatever is handling
;# the current line editing.
;#

#	Some notes on all of this
#
#	ok, we have our word from the current line.  Let's dive into
#	an unbelievable behind the scenes world of trying to make sense
#	of it...
#
#	some cases:
#
#	* we're on whitespace, so we simply show the directory entries
#	in the current directory.  If all of the entries share some common
#	prefix, we'll expand that
#
#	* it's local to the directory, and we can just expand the filename
#
#	* it's local to the directory, but there is more than one match,
#	so we expand as far as we can, and show the entries that matched
#
#	* it's local to the directory, and it's a directory name.  In that
#	case, we'll tack on a '/'.  If there is more than one match though,
#	we just expand...
#
#	* it's a pathname with any sort of '/' in it.  Do our best to
#	separate the directory component from any filename.  Look for
#	lone '/'.  Completely ignore Apollo "//" in this implementation.


;#	references into $cmds after this point get here directly
$cmds{"expand"} = '&expand';

;#	expand
;#
;#	first pass, need to handle expansions in middle of line, full paths,
;#	and a few other cases...
sub expand {
    local($char_under_cursor);
    #local(@local_filenames);
    local(@current_line_words);
    local($our_word);
    
    push(@current_line_words,
	 split(/\s+/, substr($cur_line, 0, $cur_pos)));
    
    if ($cur_pos > 0) {
	$char_under_cursor = substr($cur_line, $cur_pos - 1);
	if ($char_under_cursor =~ /\s/) {
	    $our_word = '.';
	} else {
	    $our_word = pop(@current_line_words);
	}
    }
    
    # make wild assumptions (set variables)
    &first_pass($our_word);
    
    # a few easy cases....
    # this one is right here...
    if ($local_entry) {
	&do_local;
	return 1;
    }
    
    # the root '/'
    if ($root_path) {
	opendir (THISDIR, "/");
	@local_filenames = (readdir (THISDIR));
	closedir (THISDIR);
	print TTYOUT "\n@local_filenames\n";
	&redraw_line;
	return 1;
    }
    
    # no filename hanging off of path...
    if ($all_path) {
	opendir (THISDIR, $our_word);
	@local_filenames = (readdir (THISDIR));
	closedir (THISDIR);
	if ($our_word !~ /\/$/) {
	    $ans = '/';
	    &self_insert;
	}
	&redraw_line;
	return 1;
    }
    
    # ok, now we start handling tougher ones...
    
    if ($abs_path) {
	local ($path) = $our_word;
	$where_slash = rindex($path, "/");	
	$dirpath = substr($path, 0, $where_slash);
	
	if (length($dirpath) == 0) {
	    &do_root_plus_entry;
	    return 1;
	} else {
	    if (opendir(THISDIR, $dirpath)) {
		local(@fnames) = ();
		
		@rfn = readdir (THISDIR);
		closedir(THISDIR);
		foreach (@rfn) {
		    push(@fnames, "$dirpath/$_");
		}
		@local_filenames = sort grep(/^$path/, @fnames);
		if ($#local_filenames == 0) {
		    foreach $ans (split(//,
					substr($local_filenames[0],
					       length($our_word)))) {
			&self_insert;
		    }
		} else {
		    print TTYOUT "\n\n\n we get @local_filenames\n";
		}
		&redraw_line;
	    }
	    return 1;
	}
    }
    1;
}

;#	otherdir
;#
;#	be brave in the face of a potentially bizarre path
sub otherdir {
    local($path) = @_;
    
    $where_slash = rindex($path, "/");	
    
    if ($where_slash == -1) {
	return 0;
    }
    # a case of this would be "/u<expand>"
    if ($where_slash == 0 && ! -d $path) {
	$dirpath = "/";
    } else {
	if (-d $path) {
	    $dirpath = $path;
	} else {
	    $dirpath = substr($path, 0, $where_slash);
	}
    }
    
    if (opendir(THISDIR, $dirpath)) {
	local(@fnames) = ();
	
	@rfn = readdir (THISDIR);
	closedir(THISDIR);
	if ($dirpath =~ /^\/$/) {
	    foreach (@rfn) {
		push(@fnames, "/$_");
	    }
	} else {
	    foreach (@rfn) {
		push(@fnames, "$dirpath/$_");
	    }
	}
	if (-d $path) {
	    $our_word = '.';
	} else {
	    $our_word = '/' . $our_word;
	}
	@local_filenames = sort grep(/^$path/, @fnames);
	return 1;
    }
    0;
}

sub first_pass {
    local($suspect) = @_;
    
    $has_path = $abs_path = $root_path = $all_path = $parent_path = 
	$local_entry = $local_dir = $expand_scalar = $expand_list = 
	    $expand_assoc = $expand_sub = 0;
    
    if ($suspect =~ /\//) {	# a path in there...
	$has_path = 1;
	if ($suspect =~ /^\//) { # absolute path
	    $abs_path = 1;
	}
	if (length($suspect) == 1) { # really absolute (root)
	    $root_path = 1;
	}
	if (-d $suspect) {	# it's all a path
	    $all_path = 1;
	}
    } elsif ($suspect =~ /\.\./) {	# up a level
	$parent_path = 1;
    } else {			# call it local for now...
	$local_entry = 1;
	if (-d $suspect) {
	    $local_dir = 1;
	}
    }
    
    ;#	and for future use...
    ;#	the way this may work is to grab substr's and eval them
    if ($first =~ /\$/) {	# possibly expand a scalar
	$expand_scalar = 1;
    }
    if ($first =~ /@/) {	# possibly expand a list
	$expand_list = 1;
    }
    if ($first =~ /[%]/) {	# possibly expand associative array
	;#$expand_assoc = 1;
    }
    if ($first =~ /\&/) {	# possibly run a subroutine reference
	;#$expand_sub = 1;
    }
    
    
    0 &&
	print TTYOUT "
has_path $has_path
abs_path $abs_path
root_path $root_path
all_path  $all_path
parent_path $parent_path
local_entry  $local_entry
local_dir  $local_dir
expand_scalar $expand_scalar
expand_list $expand_list
expand_assoc $expand_assoc
expand_sub $expand_sub

	";
    
}


sub do_local {
    opendir (THISDIR, ".");
    @local_filenames = sort grep(/^$our_word/, readdir (THISDIR));
    closedir (THISDIR);
    
    if ($#local_filenames == 0) {
	foreach $ans (split(//, substr($local_filenames[0],
				       length($our_word)))) {
	    &self_insert;
	}
    } elsif ($#local_filenames == -1) {
	&cli_error("\(none\)");
    } else {
	# attempt to expand the shortest match
	local ($loop) = 0;
	$same = 1;
	while ($same) {
	    if ($loop > 0) {
		$last_base = $base;
	    }
	    $base = substr($local_filenames[0], 0, $loop + 1);
	    foreach (@local_filenames) {
		if ($base !~ substr($_, 0, $loop + 1)) {
		    $same = 0;
		    last;
		}
	    }
	    $loop++;
	}
	if (length($last_base) > 0) {
	    foreach $ans (split(//,
				substr($last_base, length($our_word)))) {
		
		&self_insert;
	    }
	}
	print TTYOUT "\n@local_filenames\n";
	&redraw_line;
    }
    1;
    
}


sub do_root_plus_entry {
    opendir(ROOTDIR, '/');
    local(@fnames) = ();
    @rfn = readdir (ROOTDIR);
    closedir(ROOTDIR);
    foreach (@rfn) {
	push(@fnames, "/$_");
    }
    @local_filenames = sort grep(/^$path/, @fnames);
    if ($#local_filenames == 0) {
	foreach $ans (split(//, substr($local_filenames[0],
				       length($our_word)))) {
	    &self_insert;
	}
    } else {
	# attempt to expand the shortest match
	local ($loop) = 0;
	$same = 1;
	while ($same) {
	    if ($loop > 0) {
		$last_base = $base;
	    }
	    $base = substr($local_filenames[0], 0, $loop + 1);
	    foreach (@local_filenames) {
		if ($base !~ substr($_, 0, $loop + 1)) {
		    $same = 0;
		    last;
		}
	    }
	    $loop++;
	}
	if (length($last_base) > 0) {
	    foreach $ans (split(//, substr($last_base,
					   length($our_word)))) {
		
		&self_insert;
	    }
	}
	&redraw_line;
    }
}
1;
