/
shell.pm
114 lines (87 loc) · 3.45 KB
/
shell.pm
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
package shell; # the PERL5LIB must be set to favor . before the core directories. Otherwise another Shell.pm is used.
use strict; use warnings;
use helpers;
use FindBin '$Bin'; # other programs such as morbo may also use Bin and ruin our bin.
say "bin is $Bin"; # for debugging purposes only
use File::Path 'make_path';
use Time::Stamp -stamps => { us=>1 }; # micro-seconds=>true
#################################### HELPER FUNCTIONS ####################################
sub subtract{
my ($long,$short) = @_;
return substr( $long, length $short )
}
######################################## NEW SHELL ########################################
sub new{
my $packagename = shift;
my ( $shellname, $room ) = @_;
# we need to see what's going on with the csh and tcsh prompt. ksh too, but different.
# tested on Macbook Air::::::
# interactive mode WORKS for bash, sh, csh, tcsh, ksh, but NOT rbash, dash, static-sh, sh.distrib, bsd-csh, ksh93, zsh, zsh5, rzsh
# non-interactive mode works for bash, zsh, sh (need to do full testing for this category)
# NEED TO IMPLEMENT NON INTERACTIVE MODE ALONGSIDE. BOTH OPTIONS SHOULD BE AVAILABLE.
# interactive mode shows the prompts, non-interactive shows just the outputs
my $outputdirpath = "$Bin/rooms/".$room->name;
make_path($outputdirpath);
my $outputpath = "$outputdirpath/".localstamp().".$shellname";
# open( my $handle, '|-', "$shellname -i &>$outputpath" ) or die "Could not open the $shellname shell!";
open( my $handle, '|-', "$shellname &>$outputpath" ) or die "Could not open the $shellname shell!";
my $prompt = 'MFQCLYDSHREDKZKJ'; # maybe not needed at all.(non interactive)
#print $handle "PS1=$prompt\n";
bless { name=>$shellname, handle=>$handle, prompt=>$prompt, outputpath=>$outputpath } => $packagename;
}
########################################## SHELL ##########################################
sub name{
my $shell = shift;
return $$shell{name}
}
sub handle{
my $shell = shift;
return $$shell{handle}
}
sub close_handle{
my $shell = shift;
$$shell{handle} = undef;
}
sub prompt{
my $shell = shift;
return $$shell{prompt}
}
sub output_path{
my $shell = shift;
return $$shell{outputpath}
}
sub output{
my $shell = shift;
my $outputpath = $shell->output_path;
return `cat < $outputpath`
}
sub output_since{
my $shell = shift;
my ($outbefore) = @_;
# outnow will not be equal to shell->output because the command is in there
# this means that even if output hasn't come out yet, we think it's done! :(
# the solution is to wait for output to end with the PROMPT :)
my $outnow; my $stop; my $PS1 = quotemeta( $shell->prompt ); # assuming the prompt won't change
do{
$outnow = $shell->output;
++$stop;
}
while( $outnow !~ /$PS1$/ and print "stop is $stop. " and $stop < 99 );
unless( defined $outnow ){ die 'outnow is not defined.' } # for debugging only
return subtract( $outnow, $outbefore ) # this is only the new stuff.
}
sub command{ # executes command and returns the new output
my $shell = shift;
my ($command) = @_; # incoming command does not have a \n at the end.
my $outbefore = $shell->output;
my $handle = $shell->handle;
print $handle "$command\n"; # \n is needed at the end of the command to send it, of course.
return $shell->output_since($outbefore);
}
sub close{
my $shell = shift;
$shell->command('exit 0');
close( $shell->handle ) or die 'Could not close the shell by its handle.';
$shell->close_handle; # burn the bridge by undefining the handle. May not be necessary.
}
1