open(STDERR, ">&STDOUT") || die "'can't dup stdout" ;

$| = 1 ;
select((select(STDOUT), $! = 1)[0]);   # unbuffer output
select((select(STDERR), $! = 1)[0]);   # unbuffer output

$show_esc  = 1;

###$s = 'command /c cat x' ;
###$s = 'c:\win95\command.com /c cat x | grep -i mak' ;
###$s = 'cat x | grep -i mak' ;
###$s = 'cat xxxx 2>&1 > x' ;
###print "$s\n" ;
###system($s) ;
###print $? / 256 . "\n" ;
###die ;

$ctrl_break = 0;
sub dokill {
	print "detected ^C...($?)\n" ;
	$ctrl_break = 1;
	$SIG{'INT'} = 'dokill' ;
}

$SIG{'INT'} = 'dokill' ;

$punt = 1;	# Punt command to COMMAND?

if (!($dir=$ENY{'TELNET_DIR'})) {
	$dir = &get_pwd ;
}
$dir =~ s=\\=/=g ;
chdir($dir) ;

$last_pwd = &get_pwd ;

&do_banner ;

sub clear {
	local($i);
	print "\r";
	for ($i = 0; $i < 79; ++$i) {
		print ' ';
	}
	print "\r";
}

sub read_keys {
	local($ch, $s, $cmd);
	local($cix);

        printf("%c", 0x80);	# echo off
	$cix = $ix;
	$esc = '';
	while (!$ctrl_break && read(STDIN,$ch,1)) {
		if ($ch eq "\n" || $ch eq "\r") {
			##printf("\\%c", ord($ch) + ord('@'));
			print $ch;
			$cmd .= $ch;
			last;
		}
		if ($ch eq "\b") {
			print "$ch $ch";
			$cmd = substr($cmd, 0, length($cmd)-1);
			next;
		}
		if ($esc ne '') {
			if ($esc eq "\033" && $ch eq "\033") { # ESC-ESC
				$esc = '';
				$cmd .= "\033";
				print "\r";
				last;
			}
			if ($esc eq "\033" && $ch eq 'O') {
				$esc .= $ch;
				next;
			}
			if ($esc eq "\033O" && $ch eq 'A') { # ^
				$esc = '';
				--$cix;
				$cix = 1 if ($cix < 1);
				$cmd = $history[$cix];
				--$ix; &clear; &prompt; print $cmd;
				next;
			}
			if ($esc eq "\033O" && $ch eq 'B') { # v
				$esc = '';
				++$cix;
				$cix = $ix if ($cix > $ix);
				$cmd = $history[$cix];
				--$ix; &clear; &prompt; print $cmd;
				next;
			}
			if ($esc eq "\033O" && $ch eq 'D') { # >
				$esc = '';
				next;
			}
			if ($esc eq "\033O" && $ch eq 'C') { # <
				$esc = '';
				next;
			}
			print "\[";
			print substr($esc, 1);
			$cmd .= $esc;
			$esc = '';
		}
		if ($ch eq "\033") {
			$esc = $ch;
			next;
		}
		##printf("ord(%s) = %d\n",$ch,ord($ch));
		if ($show_esc && ord($ch) < ord(' ')) {
			printf("\\%c", ord($ch) + ord('@'));
		} else {
			print $ch;
		}
		$cmd .= $ch;
	}
        printf("%c", 0x81); # echo on
	return $cmd;
}

sub source_file {
	local($file) = @_;
	local($cmds, @cmds, $s);

	$file =~ s=\\=/=g ;
	if (open(F,"<$file")) {
		read(F, $cmds, 1000000);
		close(F);
		@cmds = split(/\n/, $cmds) ;
		foreach $s (@cmds)  {
			next if ($s =~ /^\s*$/);
			print "----- $s\n";
			&process_cmd_line($s);
		}
	} else {
		print "$file: No such file\n";
	}
}

$last_cmd = '' ;
$ix = 0 ;
for (&prompt ;         ; &prompt)  {
	$cmd = &read_keys ;
        last if ($cmd eq '');
	&process_cmd_line($cmd);
}

sub process_cmd_line {
	local($cmd) = @_;
	local($save_cmd, $s, $edit);

	chop $cmd while ($cmd =~ /[\n\r]$/) ;

	while (1) {
		($cmd, $edit) = &escapes($cmd) ;
		last if (!$edit) ;
		&prompt ;
		print "$cmd";
		$s = <STDIN> ;
		chop $s while ($s =~ /[\n\r]$/) ;
		$cmd = "$cmd$s" ;
	}
	if ($cmd =~ /^\^/) {
		if ($cmd =~ /^\^([^^]+)\^(.*)/) {
			$edit = 1 ;
			$from = $1 ;
			$to = $2 ;
			$from =~ s/([^a-zA-Z])/\\\1/g ;
			$to =~ s/\^*$// ;
			$cmd = $last_cmd ;
			#print "'$cmd' =~ s/$from/$to/\n" ;
			eval { $cmd =~ s/$from/$to/ ; } ;
		} else {
			print "Modifier failed.\n";
			--$ix ;
			next ;
		}
	}
	while ($cmd =~ /!([0-9]+)/) {
		$edit = 1 ;
		$i = $1 ;
		$h = $history[$i] ;
		$cmd =~ s/!$i/$h/g ;
	}
	if ($cmd =~ /!!/ || $cmd =~ /!\$/)  {
		$edit = 1 ;
		$cmd =~ s/!!/$last_cmd/g ;
		$n = @f = split(/\s+/, $last_cmd) ;
		$lastf = $f[$n-1] ;
		$cmd =~ s/!\$/$lastf/g ;
	}
	if ($edit && $cmd =~ /:p$/) {
		$cmd =~ s/:p$// ;
		$last_cmd = $cmd ;
		--$ix ;
		$edit = 2 ;
	}
	print "$cmd\n" if ($edit) ;
	next if ($edit > 1) ;
	if ($cmd =~ /^\s*$/) {
		--$ix ;
		next ;
	}
	else {
		$history[$ix] = $cmd ;
	}

	if ($cmd =~ /^exit$/)  {
		exit ;
	}
	elsif ($cmd =~ /^h$/)  {
		$ix1 = $ix - 100 ;
		$ix1 = 1 if ($ix1 < 1) ;
		$ix2 = $ix - 1 ;
		$ix2 = 1 if ($ix2 < 1) ;
		for ($i = $ix1; $i <= $ix2; ++$i) {
			printf("%6d  %s\n", $i, $history[$i]) ;
		}
	}
	elsif ($cmd =~ /^set\s*([^=]+)\s*=\s*(.*)$/)  {
		$ENV{$1} = "$2" ;
	}
	elsif ($cmd =~ /^pd$/)  {
	        $last_cmd = $cmd ;
		$x = $pwd ;
		$dir = $last_pwd ;
		if (!chdir($dir)) {
			print "$dir: No such directory\n"
		} else {
			$pwd = &get_pwd ;
			$last_pwd = $x ;
			print "$pwd $x\n" ;
		}
	}
	elsif ($cmd =~ /^pd/)  {
	        $last_cmd = $cmd ;
		$x = $pwd ;
		($cmd,$dir) = split(/\s+/,$cmd) ;
		$dir =~ s=\\=/=g ;
		if (!chdir($dir)) {
			print "$dir: No such directory\n"
		} else {
			$pwd = &get_pwd ;
			$last_pwd = $x ;
		}
	}
	elsif ($cmd =~ /^source/)  {
		($save_cmd, $file) = split(/\s+/,$cmd) ;
		&source_file($file);
	        $last_cmd = $save_cmd ;
		$history[$ix] = $cmd ;
	}
	elsif ($cmd =~ /^cd/)  {
	        $last_cmd = $cmd ;
		($cmd,$dir) = split(/\s+/,$cmd) ;
		$dir =~ s=\\=/=g ;
		print "$dir: No such directory\n" if (!chdir($dir)) ;
	}
	elsif ($cmd =~ /^[a-zA-Z]:$/)  {
	        $last_cmd = $cmd ;
		$dir = $cmd ;
		print "$dir: No such directory\n" if (!chdir($dir)) ;
	}
	elsif ($cmd =~ /^pwd$/)  {
	        $last_cmd = $cmd ;
		$pwd = &get_pwd ;
		print "$pwd\n" ;
	}
	elsif (substr($cmd,0,1) eq ".")  {
	        $last_cmd = $cmd ;
		eval substr($cmd,1,999) ;
		print "\n" ;
	}
	else {
	        $last_cmd = $cmd ;
		&do_cmd($cmd) ;
	}
}

sub do_banner {
	eval '$user = Win32::LoginName' ;
	eval '$domain = Win32::DomainName' ;
	eval '$host = Win32::NodeName' ;
	$host =~ y/A-Z/a-z/ ;
	eval '($string, $major, $minor, $build, $id) = Win32::GetOSVersion' ;
	$os = ("Win32", "Windows 95", "Windows NT")[$id] ;
	$build = sprintf("0x%x",$build);
	$ver = "(v$major.$minor build $build)" if ($build != 0) ;
	print "$os $ver\n" ;
	print "User: $user  Host: $host  Domain: $domain\n" if ($user ne '') ;
	print "\n" ;
}

sub do_cmd {
	local($cmd) = @_ ;

	##if ($cmd =~ /[<>]/) {
	##	$result = `$cmd` ;
	##	print "$result\n" if ($result ne '') ;
	##}
	system($cmd) ;
	$status = $? ;
	$exit_status = $status / 256 ;
	if ($exit_status == 255) {
		if (!$punt) {
			$s = $cmd ;
			$s =~ s/\s+.*// ;
			print "$s: Command not found.\n" ;
		} else {
			system("command /c $cmd");
			$status = $? ;
			$exit_status = $status / 256 ;
			if ($exit_status == 255) {
				if ($cmd =~ /[<>|]/) {
					$result = `$cmd` ;
					print "$result\n" if ($result ne '') ;
				} else {
					print "Can't run COMMAND\n" ;
				}
			}
		}
	}
}

sub get_pwd {
	#####$pwd = `cd` ;
	#####chop($pwd) ;
	eval '$pwd = Win32::GetCwd' ;
	if ($@) { $pwd = `cd` ; chop $pwd ; }
	return $pwd ;
}

sub prompt {
	local ($x) ;

	++$ix ;
	$pwd = &get_pwd ;
	$x = $pwd ;
	$x =~ s/^.*[\/\\]// ;
	if ($host ne '') {
		print STDOUT "$host:$x\[$ix\]% " ;
	} else {
		print STDOUT "$x\[$ix\]% " ;
	}
}

sub wildcard  {
	local($orig_name, $list) = @_ ;
	local($pat, $ix, $name, $bslash, $orig_dir, $i, $t, $f, $l, $n, $ok) ;

	$name = $orig_name ;
	$bslash = ($name =~ s=([\\])=/=g) ? 1 : 0 ;
	#$name =~ s=/+$== ;
	if ($name =~ /^(.*)[\/]([^\/]*)$/) {
		$orig_dir = $dirname = $1;
		$pat = $2;
	} elsif ($name =~ /^([a-z][:])(.*)$/) {
		$orig_dir = $dirname = $1;
		$pat = $2;
	} else {
		$orig_dir = '' ;
		$dirname = '.';
		$pat = $name;
	}

	$pat =~ y/A-Z/a-z/ ;
	$dirname = "$dirname/" if ($dirname =~ /^[a-z][:]$/) ;
	##print "Looking in $dirname for /$pat/\n" ;
	$match = '' ;
	if (opendir(D,"$dirname"))  {
		undef %files ;  # be safe
		$n = 0 ;
		$l = length($pat) ;
		while (defined($t=readdir(D)))  {
			next if (substr($t,0,1) eq '.') ;
			$f = substr($t,0,$l) ;
			$f =~ y/A-Z/a-z/ ;
			next if ($f ne $pat) ;
			$orig_t = $t ;
			$t =~ y/A-Z/a-z/ ;
			$files{$t} = $orig_t ;
			++$n ;
		}
		closedir(D) ;

		##foreach $file (keys %files)  { print "\t$files{$file}\n"; }

		if ($n == 0) {
			return ($orig_name, 0) ;
		} elsif ($n == 1) {
			$match = (keys %files)[0] ;
			##$match = $files{$match} ;
			if ($list != 0) {
				print "$files{$match}\n";
			}
		} else {
			for ($i = length($pat);    ; ++$i) {
				$trial_pat = (keys %files)[0] ;
				$trial_pat = substr($trial_pat,0,$i) ;
				$ok = 1 ;
				foreach $file (keys %files)  {
					$f = substr($file,0,$i) ;
					#print "$i $file:'$f'=~/^$trial_pat/" ;
					if ($f eq $trial_pat) {
						#print "->ok=$ok\n" ;
						next ;
					}
					$ok = 0 ;
					#print "->ok=$ok\n" ;
					last ;
				}
				if ($ok == 0) {
					$match = substr($trial_pat,0,$i-1) ;
					last ;
				}
			}
			if ($list != 0) {
				foreach $file (keys %files) {
					print "$files{$file}\t";
				}
				print "\n";
			}
		}
	}
	$dirname =~ s=/+$== ;
	if ($orig_dir eq '') {
		$name = $match ;
	} else {
		$name = "$dirname/$match" ;
	}
	$name =~ s=([/])=\\=g if ($bslash) ;
	return ($name, $n) ;
}

sub escapes
{
	local($cmd) = @_ ;

	if ($cmd =~ /\033$/) {		# ESC
		$show = 0 ;
	} elsif ($cmd =~ /\004$/) {	# ^D
		$show = 1 ;
	} elsif ($cmd =~ /\022$/) {	# ^R
		return ('', 2) ;
	} else {
		return ($cmd, 0) ;
	}
	chop($cmd) ;
	if ($cmd =~ /^(.*\s+)(\S*)$/) {
		$root = $1 ;
		$file = $2 ;
	} else {
		$root = '' ;
		$file = $cmd ;
	}
	($s, $n) = &wildcard($file, $show) ;
	##print "($s, $n) = \&wildcard($file, $show)\n";
	return ($cmd, 0) if (!$n) ;
	return ($cmd, 2) if ($show) ;
	return ("$root$s", 2) ;
}
