<?xml version="1.0" encoding="windows-1252"?>
<node id="124663" title="'h' - a Win32 command-line history" created="2001-11-11 12:53:05" updated="2005-08-11 08:24:39">
<type id="1748">
sourcecode</type>
<author id="5706">
osfameron</author>
<data>
<field name="doctext">
&lt;code&gt;
=head1 NAME

C&lt;h&gt; - a command line historian...

=head1 DESCRIPTION

A 'history' script for the Windows command line.  While Unix shells allow
quite flexible history calling: grepping for patterns etc., Windows only
allows you to either tab up and down using the arrow cursors, or use F7 and
F9 to select (and execute) an entry from a list or recall a numbered entry
(and edit/execute).

C&lt;h&gt; offers this additional facility of being able to grep for commands fitting 
a particular pattern.  

It is basically a wrapper around the standard C&lt;doskey&gt; command in Win32, using
Aldo Calpini's Win32::Console to generate the selective command history.

=head1 SYNOPSIS

 h             shows history of commands
 h dir         shows history of all commands matching 'dir'
 h /jpe?g/     regular expression on history

The list of all matching commands will be returned: you are prompted with
the most recent command, for example:

 Re-run (ESC to cancel)&gt; perldoc h.pl

The last of the matching commands will be automatically
placed into your command prompt and you can edit and run
as normal.  Using the UP and DOWN arrow keys will tab 
through the selected list.

=cut

use strict; use warnings;
use Win32::Console;
my $cons=Win32::Console-&gt;new(STD_INPUT_HANDLE);

# Help: currently invoked using Perldoc - could change to Pod::Usage I suppose
if (defined $ARGV[0] &amp;&amp; $ARGV[0]=~/^[\/-][hH?]/) {
	close BAT;
	system "perldoc $0";
	exit;
}

my $OPT=$ENV{HIST_OPT} || '';
# I'm being lazy (and probably inefficient) by
# just parsing the options when needed using a regexp. 
# (and by not accepting switches on the command line)

=head1 OPTIONS

The environment variable HIST_OPT can be set
to a string containing various options.

 OPTIONS
     /v         Verbose
     /limit=10  Show only 10 most recent options
     /nodup     Ignore duplicates.  (Alternatively you may prefer to
                set this on a Shell basis 
                (Alt-Space - Properties - Options - "Discard Old Duplicates"

for example (from the commandline)

     set hist_opt=/verbose /nodup

=cut

my @hist=split /\n/, `doskey /history`; # latest last.

@hist=grep (!/^h\b/i, @hist); # remove any references to hist itself
                              # I used to have an option to do this
                              # but reckon it's a sensible default!

if ($OPT=~/[\/-]nodup/i) {
	my %seen;
	@hist = reverse grep (! $seen{$_}++ , reverse @hist);
	# I'm reversing the list so that if there are duplicates, we see the
	# most recent time only
}

if ($OPT=~/[\/-]limit=(\d+)/i) {
	@hist=@hist[-$1..-1];
}

if (! @ARGV) {
	show_hist('ALL', @hist);
} else {
	my $grep='';
	if ($ARGV[0]=~m{^/(.*?)/?$}) {
		$grep=qr{$1};
	} else {
		$grep=$ARGV[0];
	}
	my @grep = grep (/$grep/, @hist);
	show_hist($grep, @grep);
}

close BAT;

###############

sub show_hist {
	my ($what, @hist)=@_;
	print "Options: $OPT\nHistory: $what\n==============\n" if $OPT=~/[-\/][vV]/;
	my $line=0; my $dummy;
	if (@hist) {
		for (@hist) {
		# write each line of the history to STDIN, followed by
		# a NEWLINE chr(13) (except for the last one).
		# The $dummy variable reads what we've written... but
		# the option is still preserved in doskeys's command
		# history so you can tab up and down the list with UP
		# and DOWN arrows
			if ($line) {
				write_input(chr(13));
				$dummy=&lt;STDIN&gt;;
			}
			print ++$line.": ";
			write_input($_); 
		}
		print $hist[-1];
	} else {
		print "-- no commands --\n";
	}
	print "\n";
}



sub write_input {
	my $string=shift;
	for (split //, $string) {
		# This doesn't quite seem to be the format listed in Win32::Console
		# documentation at all: it is supposed to take the same format
		# as the return value of Input, which doesn't work!
		# 1 = Keyboard input
		# 1 = keydown, 0 = keyup (if we miss the second event, we won't
		#                         push multiples of the same character...)
		# 1 = Repeat count
		# Virtual Key code  } Doesn't appear to matter what 
		# Virtual Scan code } goes in these !
		# Char              } 
		# Control Key State - this is the parameter that I am passing
		#                     the ASCII code for the character with.
		
		$cons-&gt;WriteInput(1,1,1,1,1,1,ord($_));
		$cons-&gt;WriteInput(1,0,1,1,1,1,ord($_));
	}
}

=head1 AUTHOR, VERSION, COPYRIGHT, WARNING

 hakim@earthling.net
 version 0.04
 This is TEST CODE.  No warranty is implied.  Use at your own risk.

Please contact me for any comments or questions, I will try to help, however I cannot guarantee any particular support.

=head1 BUGS and CHANGES

=over 4

=item 1

(2001-11-10) FIXED:  
No means of editing the command line (apart from copying and pasting it).
My copy of Term::ReadLine doesn't support the 'addhistory' option which
would have been ideal... (2001-11-11) used Win32::Console's WriteInput
method.  Maybe Term::Readline could be patched to use a similar method to
implement this for Windows?

=item 2

(2001-11-11) FIXED: (see item 5)
If the command history is edited it isn't itself propagated to doskey /history
(because it's run from a batch file...) so it won't be visible to later
hist calls.  This also means that rerunning a command will not make it
appear later in the history list.

=item 3

(2001-11-11) CHANGED:
Renamed from C&lt;hist&gt; to C&lt;h&gt; as hist is too much typing...

=item 4

(2001-11-11) OPEN:
Has only been tested on Win2K so far.

=item 5

(2001-11-11) CHANGED:
Got rid of the whole batch wrapper concept: realised that as the history is
passed back into STDIN, I no longer need to input the command myself!

=item 6
(2001-11-11) OPEN:
Because I'm adding things into STDIN, the order of the history appears to be
messed up.  Fine if you're grepping for specifics but annoying if you wanted
a chronological view.

=back

=cut
&lt;/code&gt;</field>
<field name="codedescription">
&lt;p&gt;A command line history recall for Win32.  Tries to emulate some of the functionality of Unix versions.  I'm quite pleased with the use of [cpan://Win32::Console] to emulate [cpan://Term::Readline]'s (non-functional) addhistory method. (but happy to get suggestions of better ways to do this!!)&lt;/p&gt;

&lt;b&gt;Note:&lt;/b&gt; If this can be done better, safer etc. please let me know - I will welcome review, criticism etc.!
&lt;p&gt;&lt;b&gt;Update:&lt;/b&gt; Replaced the batch wrapper (which is redundant since I'm just writing to STDIN...).  On the other hand, this also means that we lose the ability to recall a
numbered item.  hmmm.</field>
<field name="codecategory">
Win32 Stuff</field>
<field name="codeauthor">
hakim@earthling.net</field>
</data>
</node>
