line |
stmt |
bran |
cond |
sub |
pod |
time |
code |
1
|
|
|
|
|
|
|
package Shell::POSIX::Select; |
2
|
|
|
|
|
|
|
|
3
|
|
|
|
|
|
|
our $VERSION = '0.09'; |
4
|
|
|
|
|
|
|
|
5
|
|
|
|
|
|
|
# TODO: Portable-ize tput stuff |
6
|
|
|
|
|
|
|
|
7
|
|
|
|
|
|
|
# TODO: Dump user's code-block with same line numbers shown in error |
8
|
|
|
|
|
|
|
# messages for debugging ease |
9
|
|
|
|
|
|
|
|
10
|
|
|
|
|
|
|
# TODO: Add option to embolden menu numbers, to distinguish them from |
11
|
|
|
|
|
|
|
# choices that are also numbers |
12
|
|
|
|
|
|
|
|
13
|
|
|
|
|
|
|
# See documentation and copyright notice below =pod section below |
14
|
|
|
|
|
|
|
|
15
|
|
|
|
|
|
|
# Not using Exporter.pm; doing typeglob-based exporting, |
16
|
|
|
|
|
|
|
# using adapted code from Damian's Switch.pm |
17
|
|
|
|
|
|
|
our ( @EXPORT_OK ); |
18
|
|
|
|
|
|
|
our ($Reply, $Heading, $Prompt); |
19
|
|
|
|
|
|
|
@EXPORT_OK = qw( $Heading $Prompt $Reply $Eof ); |
20
|
|
|
|
|
|
|
|
21
|
|
|
|
|
|
|
our ( $U_WARN, $REPORT, $DEBUG, $DEBUG_default, $_DEBUG, ); |
22
|
|
|
|
|
|
|
our ( $U_WARN_default, $_import_called, $U_DEBUG, $DEBUG_FILT ); |
23
|
|
|
|
|
|
|
our ( $sdump, $cdump, $script ); |
24
|
|
|
|
|
|
|
# |
25
|
|
|
|
|
|
|
our ( @ISA, @EXPORT, $PRODUCTION, $LOGGING, $PKG, $INSTALL_TESTING,$ON,$OFF, $BOLD, $SGR0, $COLS ); |
26
|
|
|
|
|
|
|
|
27
|
|
|
|
|
|
|
# What is the maximum number of columns that the user wants to see |
28
|
|
|
|
|
|
|
# on-screen? By default, no maximum -- the number of columns will be |
29
|
|
|
|
|
|
|
# determined by the width of the terminal and the length of the meny |
30
|
|
|
|
|
|
|
# item strings. |
31
|
|
|
|
|
|
|
our $MaxColumns = 99; |
32
|
|
|
|
|
|
|
push @EXPORT_OK, '$MaxColumns'; |
33
|
|
|
|
|
|
|
|
34
|
|
|
|
|
|
|
BEGIN { |
35
|
26
|
|
|
26
|
|
123
|
$PKG = __PACKAGE__ ; |
36
|
26
|
|
|
|
|
57
|
$LOGGING = 0; |
37
|
|
|
|
|
|
|
|
38
|
|
|
|
|
|
|
$SIG{TERM}=$SIG{QUIT}=$SIG{INT}= sub { |
39
|
0
|
0
|
|
|
|
0
|
$DEBUG and warn caller(1), "\n"; |
40
|
|
|
|
|
|
|
# must disable reverse-video, if it was turned on |
41
|
0
|
0
|
0
|
|
|
0
|
defined $ON and $ON ne "" and do { |
42
|
0
|
0
|
0
|
|
|
0
|
my $reset=($SGR0 || $OFF); defined $reset and warn "$reset\n"; |
|
0
|
|
|
|
|
0
|
|
43
|
|
|
|
|
|
|
}; |
44
|
0
|
0
|
|
|
|
0
|
$DEBUG and warn "$0: killed by signal\n"; |
45
|
0
|
|
|
|
|
0
|
exit 111; # means, killed by signal |
46
|
26
|
|
|
|
|
1257
|
}; |
47
|
26
|
50
|
|
|
|
156
|
! defined $_import_called and $_import_called = 0; |
48
|
26
|
|
|
|
|
1271
|
( $script = $0 ) =~ s|^.*/||; |
49
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
|
} |
51
|
|
|
|
|
|
|
|
52
|
|
|
|
|
|
|
sub import ; # advance declaration |
53
|
|
|
|
|
|
|
|
54
|
26
|
|
|
26
|
|
11808
|
use File::Spec::Functions (':ALL'); |
|
26
|
|
|
|
|
23522
|
|
|
26
|
|
|
|
|
4477
|
|
55
|
|
|
|
|
|
|
|
56
|
26
|
|
|
26
|
|
196
|
use File::Spec::Functions 0.7; |
|
26
|
|
|
|
|
569
|
|
|
26
|
|
|
|
|
2178
|
|
57
|
26
|
|
|
26
|
|
13710
|
use Filter::Simple 0.84; |
|
26
|
|
|
|
|
637249
|
|
|
26
|
|
|
|
|
174
|
|
58
|
|
|
|
|
|
|
|
59
|
|
|
|
|
|
|
# Damian's been fixing bugs as I report them, so best to have recent version |
60
|
|
|
|
|
|
|
# This is the oldest version that I know works pretty well |
61
|
26
|
|
|
26
|
|
1724
|
use Text::Balanced 1.97 qw(extract_variable extract_bracketed); |
|
26
|
|
|
|
|
399
|
|
|
26
|
|
|
|
|
1339
|
|
62
|
|
|
|
|
|
|
|
63
|
26
|
|
|
26
|
|
171
|
use Carp; |
|
26
|
|
|
|
|
58
|
|
|
26
|
|
|
|
|
11349
|
|
64
|
|
|
|
|
|
|
|
65
|
|
|
|
|
|
|
$U_DEBUG=1; |
66
|
|
|
|
|
|
|
$U_DEBUG=0; |
67
|
|
|
|
|
|
|
|
68
|
|
|
|
|
|
|
$DEBUG_FILT=4; |
69
|
|
|
|
|
|
|
# $DEBUG_FILT=0; |
70
|
|
|
|
|
|
|
|
71
|
|
|
|
|
|
|
# $DEBUG=1; # force verbosity level for debugging messages |
72
|
|
|
|
|
|
|
$DEBUG=0; # force verbosity level for debugging messages |
73
|
|
|
|
|
|
|
|
74
|
|
|
|
|
|
|
$REPORT=1; # report subroutines when entered |
75
|
|
|
|
|
|
|
# $REPORT=0; # report subroutines when entered |
76
|
|
|
|
|
|
|
|
77
|
|
|
|
|
|
|
$DEBUG > 0 and warn "Logging is $LOGGING\n"; |
78
|
|
|
|
|
|
|
|
79
|
|
|
|
|
|
|
# controls messages and carp vs. warn (but that doesn't do much) |
80
|
|
|
|
|
|
|
$PRODUCTION=1; |
81
|
|
|
|
|
|
|
$PRODUCTION and $REPORT=$DEBUG_FILT=$DEBUG=0; |
82
|
|
|
|
|
|
|
|
83
|
|
|
|
|
|
|
$DEBUG and disable_buffering(); |
84
|
|
|
|
|
|
|
|
85
|
|
|
|
|
|
|
sub _WARN; sub _DIE; |
86
|
|
|
|
|
|
|
|
87
|
|
|
|
|
|
|
local $_; # avoid clobbering user's by accident |
88
|
|
|
|
|
|
|
|
89
|
|
|
|
|
|
|
|
90
|
|
|
|
|
|
|
$Shell::POSIX::Select::_default_style='K'; # default loop-style is Kornish |
91
|
|
|
|
|
|
|
$Shell::POSIX::Select::_default_prompt= "\nEnter number of choice:"; |
92
|
|
|
|
|
|
|
# I detest the shell's default prompt! |
93
|
|
|
|
|
|
|
$Shell::POSIX::Select::_bash_prompt ='#?'; |
94
|
|
|
|
|
|
|
$Shell::POSIX::Select::_korn_prompt='#?'; |
95
|
|
|
|
|
|
|
$Shell::POSIX::Select::_generic ='#?'; |
96
|
|
|
|
|
|
|
$Shell::POSIX::Select::_arrows_prompt='>>'; |
97
|
|
|
|
|
|
|
|
98
|
|
|
|
|
|
|
$U_WARN_default = 1; # for enabling user-warnings for bad interactive input |
99
|
|
|
|
|
|
|
|
100
|
|
|
|
|
|
|
# $_import_called > 0 or import(); # ensure initialization of defaults |
101
|
|
|
|
|
|
|
|
102
|
|
|
|
|
|
|
my $subname=__PACKAGE__ ; # for identifying messages from outside sub's |
103
|
|
|
|
|
|
|
|
104
|
|
|
|
|
|
|
my $select2foreach; |
105
|
|
|
|
|
|
|
$select2foreach=1; # just translate select into foreach, for debugging |
106
|
|
|
|
|
|
|
$select2foreach=0; |
107
|
|
|
|
|
|
|
|
108
|
|
|
|
|
|
|
# warn "Setting up video modes\n"; |
109
|
|
|
|
|
|
|
# I know about Term::Cap, but this seems more direct and sufficient |
110
|
|
|
|
|
|
|
|
111
|
|
|
|
|
|
|
$Shell::POSIX::Select::_FILTER_CALLS= $Shell::POSIX::Select::_ENLOOP_CALL_COUNT= $Shell::POSIX::Select::_LOOP_COUNT=0; |
112
|
|
|
|
|
|
|
# Number of select loops detected |
113
|
|
|
|
|
|
|
$DEBUG > 3 and $LOGGING and warn "About to call log_files\n"; |
114
|
|
|
|
|
|
|
|
115
|
|
|
|
|
|
|
$LOGGING and log_files(); # open logfiles, depending on DEBUG setting |
116
|
|
|
|
|
|
|
|
117
|
|
|
|
|
|
|
$DEBUG >2 and warn "Import_called initially set to: $_import_called\n"; |
118
|
|
|
|
|
|
|
|
119
|
|
|
|
|
|
|
FILTER_ONLY |
120
|
|
|
|
|
|
|
code_no_comments => \&filter, |
121
|
|
|
|
|
|
|
all => sub { $LOGGING and print SOURCE }; |
122
|
|
|
|
|
|
|
|
123
|
|
|
|
|
|
|
$DEBUG >2 and warn "Import_called set to: $_import_called\n"; |
124
|
|
|
|
|
|
|
$DEBUG >2 and $Shell::POSIX::Select::_testmode and warn "testmode is $Shell::POSIX::Select::_testmode"; |
125
|
|
|
|
|
|
|
|
126
|
26
|
|
|
26
|
|
222
|
use re 'eval'; |
|
26
|
|
|
|
|
60
|
|
|
26
|
|
|
|
|
163827
|
|
127
|
|
|
|
|
|
|
|
128
|
|
|
|
|
|
|
# Scope for declaration of pre-compiled REs: |
129
|
|
|
|
|
|
|
{ |
130
|
|
|
|
|
|
|
my $RE_kw1 = qr^ |
131
|
|
|
|
|
|
|
(\bselect\b) |
132
|
|
|
|
|
|
|
^x; # extended-syntax, allowing comments, etc. |
133
|
|
|
|
|
|
|
|
134
|
|
|
|
|
|
|
my $RE_kw2 = qr^ |
135
|
|
|
|
|
|
|
\G(\bselect\b) |
136
|
|
|
|
|
|
|
^x; # extended-syntax, allowing comments, etc. |
137
|
|
|
|
|
|
|
|
138
|
|
|
|
|
|
|
my $RE_decl = qr^ |
139
|
|
|
|
|
|
|
(\s* |
140
|
|
|
|
|
|
|
# grab declarator if there |
141
|
|
|
|
|
|
|
(?: \b my \b| \b local \b| \b our \b ) |
142
|
|
|
|
|
|
|
\s*) |
143
|
|
|
|
|
|
|
^x; # extended-syntax, allowing comments, etc. |
144
|
|
|
|
|
|
|
|
145
|
|
|
|
|
|
|
my $RE_kw_and_decl = qr^ |
146
|
|
|
|
|
|
|
\bselect\b |
147
|
|
|
|
|
|
|
\s* |
148
|
|
|
|
|
|
|
( # Next, grab optional declarator and varname if there |
149
|
|
|
|
|
|
|
(?: \b my \b| \b local \b| \b our \b )? |
150
|
|
|
|
|
|
|
\s* |
151
|
|
|
|
|
|
|
)? |
152
|
|
|
|
|
|
|
^x; # extended-syntax, allowing comments, etc. |
153
|
|
|
|
|
|
|
|
154
|
|
|
|
|
|
|
|
155
|
|
|
|
|
|
|
my $RE_list = qr^ |
156
|
|
|
|
|
|
|
\s* |
157
|
|
|
|
|
|
|
( |
158
|
|
|
|
|
|
|
# $RE{balanced}{-parens=>'()'} |
159
|
|
|
|
|
|
|
) |
160
|
|
|
|
|
|
|
^x; # extended-syntax, allowing comments, etc. |
161
|
|
|
|
|
|
|
|
162
|
|
|
|
|
|
|
my $RE_block = qr^ |
163
|
|
|
|
|
|
|
\s* |
164
|
|
|
|
|
|
|
# Is following really beneficial/necessary? I think I needed it in one case - tfm |
165
|
|
|
|
|
|
|
(?= { ) # ensure opposite of } comes next |
166
|
|
|
|
|
|
|
( |
167
|
|
|
|
|
|
|
# now find the code-block |
168
|
|
|
|
|
|
|
# $RE{balanced}{-parens=>'{}'} |
169
|
|
|
|
|
|
|
) |
170
|
|
|
|
|
|
|
^x; # extended-syntax, allowing comments, etc. |
171
|
|
|
|
|
|
|
|
172
|
|
|
|
|
|
|
sub matches2fields; |
173
|
|
|
|
|
|
|
sub enloop_codeblock; |
174
|
|
|
|
|
|
|
|
175
|
|
|
|
|
|
|
sub filter { |
176
|
28
|
|
|
28
|
0
|
356560
|
my $subname = sub_name(); |
177
|
28
|
|
|
|
|
90
|
my $last_call = 0; |
178
|
28
|
|
|
|
|
60
|
my $orig_string=$_; |
179
|
28
|
|
|
|
|
101
|
my $detect_msg=''; |
180
|
|
|
|
|
|
|
|
181
|
28
|
|
|
|
|
82
|
++$::_FILTER_CALLS; |
182
|
|
|
|
|
|
|
|
183
|
28
|
50
|
|
|
|
110
|
$orig_string ne $_ and die "$_ got trashed"; |
184
|
|
|
|
|
|
|
|
185
|
|
|
|
|
|
|
#/(..)/ and warn "Matched chars: '$1'\n"; # prime the pos marker |
186
|
|
|
|
|
|
|
|
187
|
28
|
|
|
|
|
69
|
my $loopnum; |
188
|
|
|
|
|
|
|
# Probably looping out of control if we get this many: |
189
|
28
|
|
|
|
|
51
|
my $maxloops = 25; |
190
|
|
|
|
|
|
|
|
191
|
28
|
|
|
|
|
47
|
my $first_celador; |
192
|
28
|
50
|
|
|
|
93
|
if ( $last_call = ($_ eq "") ) { |
193
|
0
|
|
|
|
|
0
|
return undef ; |
194
|
|
|
|
|
|
|
} |
195
|
|
|
|
|
|
|
else { |
196
|
|
|
|
|
|
|
# TIMJI: Revisit; why is following the default? |
197
|
28
|
|
|
|
|
223
|
$detect_msg="SELECT LOOP DETECTED"; |
198
|
28
|
50
|
|
|
|
84
|
$orig_string ne $_ and die "$_ got trashed"; |
199
|
|
|
|
|
|
|
|
200
|
|
|
|
|
|
|
|
201
|
28
|
50
|
|
|
|
77
|
$DEBUG > 1 and show_subs("****** Pre-Pre-WHILE ****** \n",""); |
202
|
28
|
0
|
33
|
|
|
216
|
$DEBUG > 1 and $LOGGING and print LOG "\$_ is '$_'\n"; |
203
|
|
|
|
|
|
|
|
204
|
28
|
|
|
|
|
67
|
$loopnum=0; |
205
|
28
|
50
|
|
|
|
109
|
$DEBUG > 1 and show_subs("****** Pre-WHILE ****** \n",""); |
206
|
|
|
|
|
|
|
|
207
|
28
|
|
|
|
|
89
|
while (++$loopnum <= $maxloops) { # keep looping until we can't find any more select loops |
208
|
|
|
|
|
|
|
|
209
|
65
|
100
|
|
|
|
184
|
$loopnum == 2 and $first_celador=$_; |
210
|
|
|
|
|
|
|
|
211
|
65
|
50
|
|
|
|
157
|
$DEBUG > 1 and show_subs("****** LOOKING FOR LOOP ****** #$loopnum\n",""); |
212
|
65
|
50
|
|
|
|
156
|
$loopnum > 25 and warn "$subname: Might be stuck in loop\n"; |
213
|
65
|
50
|
|
|
|
138
|
$loopnum > 100 and die "$subname: Probably was stuck in loop\n"; |
214
|
65
|
50
|
33
|
|
|
180
|
$DEBUG > 3 and pos() and warn "pos is currently: ", pos(), "\n"; |
215
|
65
|
|
|
|
|
180
|
pos()=0; |
216
|
65
|
50
|
0
|
|
|
390
|
/\S/ or $LOGGING and |
217
|
|
|
|
|
|
|
print LOG "\$_ is all white space or else empty\n"; |
218
|
|
|
|
|
|
|
|
219
|
|
|
|
|
|
|
# /(..)/ and warn "Matched chars: '$1'\n"; # prime the pos marker |
220
|
|
|
|
|
|
|
|
221
|
65
|
|
|
|
|
154
|
my ($matched, $can_rewrite) = 0; |
222
|
65
|
50
|
|
|
|
215
|
if ($select2foreach) { |
223
|
|
|
|
|
|
|
# simple conversion, for debugging basic ops |
224
|
|
|
|
|
|
|
# change one word, and select loops with all pieces |
225
|
|
|
|
|
|
|
# present are magically rendered syntactically acceptable |
226
|
|
|
|
|
|
|
# NOTE: will break select() usage! |
227
|
0
|
0
|
|
|
|
0
|
s/\bselect\b/foreach /g and $matched = -1; |
228
|
|
|
|
|
|
|
# All these can be handled in one pass, so exit loop |
229
|
0
|
|
|
|
|
0
|
goto FILTER_EXIT; |
230
|
|
|
|
|
|
|
} |
231
|
|
|
|
|
|
|
else { |
232
|
65
|
|
|
|
|
109
|
my $pos; |
233
|
65
|
|
|
|
|
465
|
my ($match, $start_match); |
234
|
65
|
|
|
|
|
0
|
my ($got_kw,$got_decl, $got_loop_var, $got_list, $got_codeblock); |
235
|
65
|
|
|
|
|
152
|
my $iteration=0; |
236
|
68
|
|
|
|
|
156
|
FIND_LOOP: |
237
|
|
|
|
|
|
|
my ($loop_var, $loop_decl, $loop_list, $loop_block)= ("" x 3); |
238
|
|
|
|
|
|
|
|
239
|
68
|
50
|
|
|
|
409
|
$DEBUG_FILT > 0 and warn "Pos initially at ", pos($_), "\n"; |
240
|
|
|
|
|
|
|
|
241
|
68
|
50
|
|
|
|
224
|
!defined pos() and warn "AT FIND_LOOP, POS IS UNDEF\n"; |
242
|
|
|
|
|
|
|
|
243
|
68
|
|
|
|
|
152
|
$match=$got_kw=$got_decl=$got_loop_var=$got_list=$got_codeblock=""; |
244
|
68
|
|
|
|
|
105
|
my $matched=0; # means, currently no detected loops that still need replacement |
245
|
|
|
|
|
|
|
|
246
|
|
|
|
|
|
|
# my $RE = ( $loopnum == 1 ? $RE_kw1 : $RE_kw2 ) ; # second version uses \G |
247
|
68
|
|
|
|
|
97
|
my $RE = $RE_kw1 ; # always restart from the beginning, of incrementally modified program |
248
|
|
|
|
|
|
|
# Same pattern good now, since pos() will have been reset by mod |
249
|
|
|
|
|
|
|
# my $RE = ( $loopnum == 1 ? $RE_kw1 : $RE_kw1 ) ; # second version uses \G |
250
|
68
|
100
|
|
|
|
1145
|
if ( m/$RE/g ) { # try to match keyword, "select" |
251
|
40
|
|
|
|
|
73
|
++$matched ; |
252
|
40
|
|
|
|
|
106
|
$match=$1; |
253
|
40
|
|
|
|
|
90
|
$start_match=pos() - length $1; |
254
|
40
|
|
|
|
|
67
|
$got_kw=1; |
255
|
40
|
50
|
|
|
|
95
|
$DEBUG_FILT > 1 and show_progress($match, pos(), $_); |
256
|
|
|
|
|
|
|
} |
257
|
|
|
|
|
|
|
else { |
258
|
|
|
|
|
|
|
# no more select keywords to process! # LOOP EXIT #1 |
259
|
28
|
|
|
|
|
1620
|
goto FILTER_EXIT; |
260
|
|
|
|
|
|
|
} |
261
|
|
|
|
|
|
|
|
262
|
40
|
|
|
|
|
65
|
$pos=pos(); # remember position |
263
|
|
|
|
|
|
|
|
264
|
40
|
100
|
|
|
|
886
|
if (/\G$RE_decl/g) { |
265
|
11
|
|
|
|
|
24
|
++$matched ; |
266
|
11
|
|
|
|
|
24
|
$loop_decl=$1; |
267
|
11
|
|
|
|
|
36
|
$match.=" $1"; |
268
|
11
|
|
|
|
|
23
|
$got_decl=1; |
269
|
|
|
|
|
|
|
} |
270
|
|
|
|
|
|
|
else { |
271
|
29
|
|
|
|
|
90
|
pos()=$pos; # reset to where we left off |
272
|
|
|
|
|
|
|
} |
273
|
40
|
50
|
|
|
|
199
|
$DEBUG_FILT > 1 and show_progress($match, pos(), $_); |
274
|
|
|
|
|
|
|
|
275
|
40
|
|
|
|
|
109
|
my @rest; |
276
|
40
|
50
|
|
|
|
95
|
$DEBUG_FILT > 0 and warn "POS before ext-var is now ", pos(), "\n"; |
277
|
|
|
|
|
|
|
|
278
|
40
|
|
|
|
|
185
|
( $loop_var, @rest ) = extract_variable( $_ ); |
279
|
40
|
50
|
|
|
|
6851
|
$DEBUG_FILT > 0 and show_subs( "POST- ext-var string is: ", $_, pos(),19); |
280
|
|
|
|
|
|
|
|
281
|
40
|
50
|
|
|
|
180
|
$DEBUG_FILT > 0 and warn "POS after ext-var is now ", pos(), "\n"; |
282
|
|
|
|
|
|
|
|
283
|
40
|
100
|
66
|
|
|
244
|
if (defined $loop_var and $loop_var ne "" ) { |
284
|
26
|
|
|
|
|
81
|
$got_loop_var=1; |
285
|
26
|
50
|
|
|
|
82
|
$DEBUG_FILT > 0 and warn "Got_Loop_Var matched '$loop_var'\n"; |
286
|
26
|
|
|
|
|
72
|
$match.=" $loop_var"; |
287
|
|
|
|
|
|
|
} |
288
|
|
|
|
|
|
|
else { |
289
|
14
|
|
|
|
|
55
|
pos()=$pos; # reset to where we left off |
290
|
14
|
50
|
|
|
|
75
|
$DEBUG_FILT > 0 and warn "extract_variable failed to match\n"; |
291
|
|
|
|
|
|
|
} |
292
|
40
|
50
|
|
|
|
108
|
$DEBUG_FILT > 1 and show_progress($match, pos(), $_); |
293
|
|
|
|
|
|
|
|
294
|
40
|
|
|
|
|
166
|
gobble_spaces(); |
295
|
|
|
|
|
|
|
|
296
|
|
|
|
|
|
|
# $DEBUG_FILT > 0 and warn "Pre-extract_bracketed ()\n"; |
297
|
40
|
|
|
|
|
151
|
( $loop_list, @rest ) = extract_bracketed($_, '()'); |
298
|
40
|
50
|
33
|
|
|
6390
|
if (defined $loop_list and $loop_list ne "") { |
299
|
40
|
|
|
|
|
109
|
++$matched; |
300
|
40
|
|
|
|
|
74
|
$got_list=1; |
301
|
40
|
|
|
|
|
102
|
$match.=" $loop_list"; |
302
|
40
|
50
|
|
|
|
159
|
$DEBUG_FILT > 1 and show_progress($match, pos(), $_); |
303
|
|
|
|
|
|
|
} |
304
|
|
|
|
|
|
|
else { # no loop list; not our kind of select |
305
|
|
|
|
|
|
|
# warn "extract_bracketed failed to match\n"; |
306
|
|
|
|
|
|
|
# If we didn't find loop var, they're probably using |
307
|
|
|
|
|
|
|
# select() function or syscall, not select loop |
308
|
0
|
0
|
|
|
|
0
|
if ($got_loop_var) { |
309
|
0
|
0
|
|
|
|
0
|
$DEBUG_FILT > 3 and |
310
|
|
|
|
|
|
|
warn "$PKG: Found keyword and loop variable, but no ( LIST )!\n", |
311
|
|
|
|
|
|
|
; |
312
|
|
|
|
|
|
|
# "If { } really there, try placing 'no $PKG;' after loop to fix.\n"; |
313
|
|
|
|
|
|
|
} |
314
|
|
|
|
|
|
|
else { |
315
|
0
|
0
|
|
|
|
0
|
$DEBUG_FILT > 3 and warn "$PKG: Found keyword, but no ( LIST )\n", |
316
|
|
|
|
|
|
|
"Must be some other use of the word\n"; |
317
|
|
|
|
|
|
|
} |
318
|
0
|
0
|
|
|
|
0
|
$DEBUG_FILT > 0 and warn "giving up on this match; scanning for next keyword (1)"; |
319
|
0
|
0
|
|
|
|
0
|
if (++$iteration < $maxloops) { |
320
|
0
|
|
|
|
|
0
|
goto FIND_LOOP; |
321
|
|
|
|
|
|
|
} |
322
|
|
|
|
|
|
|
else { |
323
|
0
|
|
|
|
|
0
|
_DIE "$PKG: Maximum iterations reached while looking for select loop #$loopnum"; |
324
|
|
|
|
|
|
|
} |
325
|
|
|
|
|
|
|
} # else |
326
|
|
|
|
|
|
|
|
327
|
40
|
|
|
|
|
133
|
gobble_spaces(); |
328
|
|
|
|
|
|
|
|
329
|
|
|
|
|
|
|
# $DEBUG > 1 and warn " DDD sending to extract_bracketed() ===$_===\n"; |
330
|
40
|
|
|
|
|
143
|
( $loop_block, @rest ) = extract_bracketed($_, '{}'); |
331
|
|
|
|
|
|
|
# $DEBUG > 1 and warn " DDD extract_bracketed returned ===$loop_block===\n"; |
332
|
40
|
100
|
66
|
|
|
9655
|
if (defined $loop_block and $loop_block ne "") { |
333
|
37
|
|
|
|
|
110
|
++$matched; |
334
|
37
|
|
|
|
|
92
|
$got_codeblock=1; |
335
|
37
|
|
|
|
|
124
|
$match.=" $loop_block"; |
336
|
37
|
50
|
|
|
|
128
|
$DEBUG_FILT > 1 and show_progress($match, pos(), $_); |
337
|
|
|
|
|
|
|
} |
338
|
|
|
|
|
|
|
else { |
339
|
|
|
|
|
|
|
# if $var there, can't possibly be select syscall or function use, |
340
|
|
|
|
|
|
|
# so 100% sure there's a problem |
341
|
|
|
|
|
|
|
|
342
|
3
|
50
|
|
|
|
6
|
if ($got_loop_var) { |
343
|
0
|
|
|
|
|
0
|
warn "$PKG: Found loop variable and list, but no code-block!\n", |
344
|
|
|
|
|
|
|
; |
345
|
|
|
|
|
|
|
# "If { } really there, try placing 'no $PKG;' after loop to fix.\n"; |
346
|
|
|
|
|
|
|
} |
347
|
|
|
|
|
|
|
else { |
348
|
3
|
50
|
|
|
|
7
|
$DEBUG_FILT > 3 and warn "$PKG: Found keyword and list,", |
349
|
|
|
|
|
|
|
" but no code-block\n", |
350
|
|
|
|
|
|
|
"Must be some other use of the word\n"; |
351
|
|
|
|
|
|
|
} |
352
|
3
|
50
|
|
|
|
6
|
$DEBUG_FILT > 0 and warn "giving up on this match; scanning for next keyword (2)"; |
353
|
3
|
|
|
|
|
20
|
goto FIND_LOOP; |
354
|
|
|
|
|
|
|
} |
355
|
|
|
|
|
|
|
|
356
|
|
|
|
|
|
|
# and print "list_and_block matched '$&'\n"; |
357
|
|
|
|
|
|
|
# defined $& and $match.=$&; |
358
|
|
|
|
|
|
|
# defined $& and $match.="$1 $2"; |
359
|
|
|
|
|
|
|
#defined $& and ($loop_list, $loop_block) = ($1, $2); |
360
|
|
|
|
|
|
|
|
361
|
37
|
|
|
|
|
66
|
my $end_match; |
362
|
37
|
50
|
|
|
|
113
|
if ( $matched == 0 ) { |
363
|
0
|
|
|
|
|
0
|
die" Can it ever get here?"; |
364
|
0
|
|
|
|
|
0
|
goto FILTER_EXIT; |
365
|
|
|
|
|
|
|
} |
366
|
|
|
|
|
|
|
else { |
367
|
37
|
|
|
|
|
102
|
$end_match=pos(); |
368
|
37
|
|
|
|
|
65
|
$detect_msg=''; |
369
|
37
|
50
|
|
|
|
104
|
if ( $matched == 1 ) { # means "select" keyword only |
370
|
|
|
|
|
|
|
; |
371
|
|
|
|
|
|
|
} |
372
|
37
|
50
|
|
|
|
216
|
if ( $matched == 2 ) { # means "select" plus decl, var, list, or block |
|
|
50
|
|
|
|
|
|
373
|
0
|
|
|
|
|
0
|
$detect_msg="select loop incomplete; "; |
374
|
0
|
0
|
|
|
|
0
|
$got_list or $detect_msg.= "no (LIST) detected\n"; |
375
|
0
|
0
|
|
|
|
0
|
$got_codeblock or $detect_msg.= "no {CODE} detected\n"; |
376
|
|
|
|
|
|
|
} |
377
|
|
|
|
|
|
|
elsif ( $matched >= 3 ) { |
378
|
|
|
|
|
|
|
} |
379
|
|
|
|
|
|
|
} |
380
|
|
|
|
|
|
|
|
381
|
|
|
|
|
|
|
# print "Entire match: $match\n"; |
382
|
|
|
|
|
|
|
# print "Matched Text: ", |
383
|
|
|
|
|
|
|
# substr $_, $start_match, |
384
|
|
|
|
|
|
|
# $end_match-$start_match; |
385
|
|
|
|
|
|
|
|
386
|
37
|
50
|
|
|
|
92
|
if ( $matched > 1 ) { # 1 just means select->foreach conversion |
387
|
37
|
|
|
|
|
69
|
$::_LOOP_COUNT++; # counts # detected select-loops |
388
|
37
|
50
|
|
|
|
97
|
$DEBUG > 0 and |
389
|
|
|
|
|
|
|
warn "$PKG: Set debug to: $Shell::POSIX::Select::DEBUG\n"; |
390
|
|
|
|
|
|
|
} |
391
|
|
|
|
|
|
|
|
392
|
|
|
|
|
|
|
# $can_rewrite indicates whether we matched the crucial |
393
|
|
|
|
|
|
|
# parts that allow replacement of the input -- the list and codeblock |
394
|
|
|
|
|
|
|
# If we got both, the $can_rewrite var shows true now |
395
|
37
|
50
|
|
|
|
122
|
$can_rewrite = $matched >= 2 ? 1 : 0; |
396
|
|
|
|
|
|
|
|
397
|
|
|
|
|
|
|
# $DEBUG > 1 and warn "Calling MATCHES2FIELDS with \$loop_list of $loop_list\n"; |
398
|
|
|
|
|
|
|
# $DEBUG > 1 and warn "Calling MATCHES2FIELDS with \$loop_block of ===$loop_block===\n"; |
399
|
37
|
50
|
|
|
|
116
|
if ($can_rewrite) { |
400
|
37
|
|
|
|
|
112
|
my $replacer = enloop_codeblock |
401
|
|
|
|
|
|
|
matches2fields ( $loop_decl, |
402
|
|
|
|
|
|
|
$loop_var, |
403
|
|
|
|
|
|
|
$loop_list, |
404
|
|
|
|
|
|
|
$loop_block ), |
405
|
|
|
|
|
|
|
$::_LOOP_COUNT; |
406
|
|
|
|
|
|
|
|
407
|
37
|
|
|
|
|
878
|
substr($_, $start_match, ($end_match-$start_match), $replacer ); |
408
|
|
|
|
|
|
|
# print "\n\nModified \$_ is: \n$_\n"; |
409
|
|
|
|
|
|
|
} |
410
|
|
|
|
|
|
|
} |
411
|
|
|
|
|
|
|
} # end while |
412
|
|
|
|
|
|
|
continue { |
413
|
37
|
50
|
|
|
|
188
|
$DEBUG_FILT > 2 and warn "CONTINUING FIND_LOOP\n" ; |
414
|
|
|
|
|
|
|
} |
415
|
|
|
|
|
|
|
#warn "Leaving $subname 1 \n"; |
416
|
|
|
|
|
|
|
} # else |
417
|
|
|
|
|
|
|
FILTER_EXIT: |
418
|
|
|
|
|
|
|
# $Shell::POSIX::Select::filter_output="PRE-LOADING DUMP VAR, loopnum was $loopnum"; |
419
|
28
|
|
|
|
|
53
|
if ( |
420
|
|
|
|
|
|
|
0 # and $DEBUG or $Shell::POSIX::Select::dump_data |
421
|
|
|
|
|
|
|
) { |
422
|
|
|
|
|
|
|
# print TTY "$detect_msg\nCode 222\n" ; |
423
|
|
|
|
|
|
|
# print TTY "Code 222\n" ; |
424
|
|
|
|
|
|
|
if ($loopnum == 1 and |
425
|
|
|
|
|
|
|
$detect_msg !~ /SELECT LOOP DETECTED/ ) { |
426
|
|
|
|
|
|
|
# $DEBUG and print STDERR "copacetic\n"; |
427
|
|
|
|
|
|
|
# exit 222; |
428
|
|
|
|
|
|
|
# We still need to run the program! |
429
|
|
|
|
|
|
|
} |
430
|
|
|
|
|
|
|
else { |
431
|
|
|
|
|
|
|
$DEBUG >2 and print TTY "LOOP DETECTED: $detect_msg\n"; exit 222; |
432
|
|
|
|
|
|
|
} |
433
|
|
|
|
|
|
|
} # if 0 |
434
|
|
|
|
|
|
|
|
435
|
28
|
50
|
|
|
|
98
|
$loopnum > 0 and $Shell::POSIX::Select::filter_output=$_; |
436
|
|
|
|
|
|
|
# Restore original string-like parts of the code: |
437
|
28
|
|
|
|
|
303
|
$Shell::POSIX::Select::filter_output =~ s/$Filter::Simple::placeholder/${$Filter::Simple::components[unpack('N',$1)]}/ge; |
|
180
|
|
|
|
|
316
|
|
|
180
|
|
|
|
|
1264
|
|
438
|
28
|
50
|
|
|
|
146
|
$LOGGING and print USERPROG $_; # $_ unset 2nd call; label starts below |
439
|
28
|
50
|
|
|
|
144
|
$DEBUG_FILT > 2 and _WARN "Leaving $subname on call #$::_FILTER_CALLS\n"; |
440
|
|
|
|
|
|
|
} # end sub filter |
441
|
|
|
|
|
|
|
} # Scope for declaration of filters' REs |
442
|
|
|
|
|
|
|
|
443
|
|
|
|
|
|
|
|
444
|
|
|
|
|
|
|
sub show_progress { |
445
|
0
|
|
|
0
|
0
|
0
|
my $subname = sub_name(); |
446
|
|
|
|
|
|
|
|
447
|
|
|
|
|
|
|
|
448
|
0
|
|
|
|
|
0
|
my ($match, $pos, $string) = @_; |
449
|
|
|
|
|
|
|
|
450
|
0
|
0
|
0
|
|
|
0
|
! defined $match or $match eq "" and warn "$subname: \$match is empty\n"; |
451
|
0
|
|
|
|
|
0
|
show_subs( "Match so far: ", $match, 0, 99); |
452
|
0
|
0
|
|
|
|
0
|
defined $pos and warn "POS is now $pos\n"; |
453
|
0
|
|
|
|
|
0
|
show_subs( "Remaining string: ", $string, $pos, 19); |
454
|
|
|
|
|
|
|
} # show_progress |
455
|
|
|
|
|
|
|
|
456
|
|
|
|
|
|
|
sub show_context { |
457
|
0
|
|
|
0
|
0
|
0
|
my $subname = sub_name(); |
458
|
|
|
|
|
|
|
|
459
|
|
|
|
|
|
|
|
460
|
0
|
|
|
|
|
0
|
my ($left, $match, $right) = @_; |
461
|
|
|
|
|
|
|
|
462
|
0
|
0
|
|
|
|
0
|
$DEBUG > 0 and warn "left/match/right: $left/$match/$right"; |
463
|
|
|
|
|
|
|
|
464
|
0
|
|
|
|
|
0
|
show_subs( "Left is", $left, -10); |
465
|
0
|
|
|
|
|
0
|
show_subs( "Right is", $right, 0, 10); |
466
|
|
|
|
|
|
|
} # show_context |
467
|
|
|
|
|
|
|
|
468
|
|
|
|
|
|
|
# Following sub converts matched elements of users source into the |
469
|
|
|
|
|
|
|
# fields we need: declaration (optional), loop_varname (optional), codeblock |
470
|
|
|
|
|
|
|
|
471
|
|
|
|
|
|
|
sub matches2fields { |
472
|
37
|
|
|
37
|
0
|
83
|
my $subname = sub_name(); |
473
|
37
|
|
|
|
|
78
|
my $default_loopvar = 0; |
474
|
|
|
|
|
|
|
|
475
|
|
|
|
|
|
|
|
476
|
37
|
|
|
|
|
62
|
my ( $debugging_code, $codeblock2, ); |
477
|
37
|
|
|
|
|
96
|
my ( $decl, $loop_var, $values, $codeblock, $fullmatch ) = @_; |
478
|
|
|
|
|
|
|
|
479
|
37
|
|
|
|
|
68
|
$debugging_code = ""; |
480
|
37
|
50
|
|
|
|
128
|
if ($U_DEBUG > 3) { |
481
|
0
|
|
|
|
|
0
|
$debugging_code = "\n# USER-MODE DEBUGGING CODE STARTS HERE\n"; |
482
|
0
|
|
|
|
|
0
|
$debugging_code .= |
483
|
|
|
|
|
|
|
'; $,="/"; warn "Caller is now: ", (caller 0), "\n";'; |
484
|
0
|
|
|
|
|
0
|
$debugging_code .= |
485
|
|
|
|
|
|
|
'warn "Caller 3 is now: ", ((caller 0)[3]), "\n";'; |
486
|
0
|
|
|
|
|
0
|
$debugging_code .= 'warn "\@_ is: @_\n";'; |
487
|
0
|
|
|
|
|
0
|
$debugging_code .= 'warn "\@ARGV is: @ARGV\n";'; |
488
|
|
|
|
|
|
|
|
489
|
|
|
|
|
|
|
# $debugging_code .= |
490
|
|
|
|
|
|
|
# 'warn "\@looplist is : @Shell::POSIX::Select::looplist\n"'; |
491
|
0
|
|
|
|
|
0
|
$debugging_code .= "# USER-MODE DEBUGGING CODE ENDS HERE\n\n"; |
492
|
|
|
|
|
|
|
|
493
|
0
|
|
|
|
|
0
|
$debugging_code .= ""; |
494
|
|
|
|
|
|
|
} |
495
|
|
|
|
|
|
|
|
496
|
37
|
100
|
66
|
|
|
296
|
if ( !defined $values or $values =~ /^\s*\(\s*\)\s*$/ ) { # ( ) is legit syntax |
497
|
|
|
|
|
|
|
# warn "values is undef or vacant"; |
498
|
|
|
|
|
|
|
# Code to let user prog figure out if select loop is in sub, |
499
|
|
|
|
|
|
|
# and if so, selects @_ for default LIST |
500
|
2
|
|
|
|
|
5
|
$values = # supply appropriate default list, depending on programmer's context |
501
|
|
|
|
|
|
|
'defined ((( caller 0 )[3]) and ' . |
502
|
|
|
|
|
|
|
' (( caller 0 )[3]) ne "") ? @_ : @ARGV ' |
503
|
|
|
|
|
|
|
; |
504
|
|
|
|
|
|
|
} |
505
|
|
|
|
|
|
|
|
506
|
|
|
|
|
|
|
|
507
|
37
|
100
|
66
|
|
|
424
|
if ( defined $decl and $decl ne "" and |
|
|
50
|
66
|
|
|
|
|
|
|
100
|
33
|
|
|
|
|
|
|
|
33
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
33
|
|
|
|
|
|
|
|
66
|
|
|
|
|
|
|
|
33
|
|
|
|
|
|
|
|
66
|
|
|
|
|
508
|
|
|
|
|
|
|
defined $loop_var and $loop_var ne "" ) { |
509
|
11
|
50
|
|
|
|
40
|
$LOGGING and print LOG |
510
|
|
|
|
|
|
|
"LOOP: Two-part declaration,", |
511
|
|
|
|
|
|
|
" scoper is: $decl, varname is $loop_var\n"; |
512
|
|
|
|
|
|
|
} |
513
|
|
|
|
|
|
|
elsif ( defined $decl and $decl ne "" and |
514
|
|
|
|
|
|
|
(! defined $loop_var or $loop_var eq "") ) { |
515
|
0
|
0
|
|
|
|
0
|
$LOGGING and print LOG |
516
|
|
|
|
|
|
|
"LOOP: Declaration without variable name: $decl" ; |
517
|
0
|
|
|
|
|
0
|
warn "$PKG: variable declarator ($decl) provided without variable name\n"; |
518
|
0
|
|
|
|
|
0
|
warn "giving up on this match; scanning for next keyword (3)"; |
519
|
0
|
|
|
|
|
0
|
goto FIND_LOOP; |
520
|
|
|
|
|
|
|
} |
521
|
|
|
|
|
|
|
elsif ( defined $loop_var and $loop_var ne "" and |
522
|
|
|
|
|
|
|
(! defined $decl or $decl eq "") ) { |
523
|
15
|
50
|
|
|
|
43
|
$LOGGING and print LOG |
524
|
|
|
|
|
|
|
"LOOP: Variable without declaration (okay): $loop_var" |
525
|
|
|
|
|
|
|
} |
526
|
|
|
|
|
|
|
else { |
527
|
11
|
50
|
|
|
|
30
|
$LOGGING and print LOG "LOOP: zero-word declaration\n"; |
528
|
|
|
|
|
|
|
|
529
|
11
|
|
|
|
|
16
|
my $default_loopvar = 1; |
530
|
11
|
|
|
|
|
26
|
($decl, $loop_var) = qw (local $_); # default loop var; package scope |
531
|
|
|
|
|
|
|
} |
532
|
|
|
|
|
|
|
|
533
|
37
|
100
|
66
|
|
|
265
|
if ( !defined $codeblock or $codeblock =~ /^\s*{\s*}\s*$/ ) { |
534
|
|
|
|
|
|
|
# default codeblock prints the selection; good for grep()-like filtering |
535
|
|
|
|
|
|
|
# NOTE: Following string must start/end with {} |
536
|
2
|
|
|
|
|
8
|
$codeblock = "{ |
537
|
|
|
|
|
|
|
print \"$loop_var\\n\" ; # ** USING DEFAULT CODEBLOCK ** |
538
|
|
|
|
|
|
|
}"; |
539
|
|
|
|
|
|
|
} |
540
|
|
|
|
|
|
|
|
541
|
|
|
|
|
|
|
# I've already extracted what could be a valid variable name, |
542
|
|
|
|
|
|
|
# but the regex was kinda sleazy, so it's time to validate |
543
|
|
|
|
|
|
|
# it using TEXT::BALANCED::extract_variable() |
544
|
|
|
|
|
|
|
# But I found a bug, it rejects $::var*, so exempt that form from check |
545
|
|
|
|
|
|
|
|
546
|
37
|
50
|
33
|
|
|
183
|
unless ($default_loopvar or $loop_var =~ /^\$::\w+/) { |
547
|
|
|
|
|
|
|
# don't check if I inserted it myself, or is in form $::stuff, |
548
|
|
|
|
|
|
|
# which extract_variable() doesn't properly extract |
549
|
37
|
50
|
|
|
|
118
|
$DEBUG > 1 and warn "Pre-extract_variable 3\n"; |
550
|
|
|
|
|
|
|
# Now let's see if Damian likes it: |
551
|
37
|
|
|
|
|
126
|
my ( $loop_var2, @rest ) = extract_variable($loop_var); |
552
|
37
|
50
|
|
|
|
7377
|
if ( $loop_var2 ne $loop_var ) { |
553
|
0
|
0
|
0
|
|
|
0
|
$DEBUG > 1 and |
554
|
|
|
|
|
|
|
warn "$PKG: extracted var diff from parsed var: ", |
555
|
|
|
|
|
|
|
$DEBUG > 0 and warn |
556
|
|
|
|
|
|
|
"$PKG: varname for select loop failed validation", |
557
|
|
|
|
|
|
|
" #$::_LOOP_COUNT: $loop_var\n"; |
558
|
|
|
|
|
|
|
} |
559
|
|
|
|
|
|
|
} |
560
|
|
|
|
|
|
|
else { |
561
|
|
|
|
|
|
|
; |
562
|
|
|
|
|
|
|
} |
563
|
|
|
|
|
|
|
|
564
|
37
|
100
|
|
|
|
106
|
!defined $decl and $decl = ""; |
565
|
|
|
|
|
|
|
# okay for this to be empty string; means user wants it global, or |
566
|
|
|
|
|
|
|
# declared it before loop |
567
|
|
|
|
|
|
|
|
568
|
|
|
|
|
|
|
# make version of \$codeblock without curlies at either end |
569
|
37
|
|
|
|
|
389
|
( $codeblock2 = $codeblock ) =~ s/\A\s*\{\s*|\s*\}\s*\z//g; |
570
|
|
|
|
|
|
|
|
571
|
37
|
50
|
33
|
|
|
225
|
defined $decl and $decl eq 'unset' and undef $decl; # pass as undef |
572
|
|
|
|
|
|
|
# $DEBUG > 1 and warn " DDD matches2fields() is returning codeblock2 ===$codeblock2===\n"; |
573
|
37
|
|
|
|
|
200
|
return ( $decl, $loop_var, $values, $codeblock2, $debugging_code ); |
574
|
|
|
|
|
|
|
} # matches2fields |
575
|
|
|
|
|
|
|
|
576
|
|
|
|
|
|
|
sub enloop_codeblock { |
577
|
|
|
|
|
|
|
# Wraps code implementing select-loop around user-supplied codeblock |
578
|
37
|
|
|
37
|
0
|
100
|
my $subname = sub_name(); |
579
|
|
|
|
|
|
|
|
580
|
37
|
|
|
|
|
105
|
$Shell::POSIX::Select::_ENLOOP_CALL_COUNT++; |
581
|
|
|
|
|
|
|
|
582
|
37
|
|
|
|
|
104
|
my ( $decl, $loop_var, $values, $codestring, $dcode, $loopnum ) = @_; |
583
|
|
|
|
|
|
|
|
584
|
37
|
50
|
33
|
|
|
254
|
(defined $values and $values ne "") or do { |
585
|
0
|
0
|
|
|
|
0
|
$DEBUG > 1 and _WARN "NO VALUES! Using dummy ones"; |
586
|
0
|
|
|
|
|
0
|
$values = '( dummy1, dummy2 )'; |
587
|
|
|
|
|
|
|
}; |
588
|
|
|
|
|
|
|
|
589
|
37
|
100
|
66
|
|
|
280
|
my $declaration = |
590
|
|
|
|
|
|
|
( defined $decl and $decl ne "" ) ? "$decl $loop_var; " . |
591
|
|
|
|
|
|
|
' # LOOP-VAR DECLARATION REQUESTED (perhaps by default)' : |
592
|
|
|
|
|
|
|
" ; # NO DECLARATION OF LOOP-VAR REQUESTED"; |
593
|
|
|
|
|
|
|
|
594
|
37
|
|
|
|
|
105
|
my $arrayname = $PKG . '::looplist'; |
595
|
37
|
|
|
|
|
85
|
my $NL = '\n'; |
596
|
|
|
|
|
|
|
|
597
|
|
|
|
|
|
|
# Now build the code for the user-prog to run |
598
|
37
|
|
|
|
|
63
|
my @parts; |
599
|
|
|
|
|
|
|
# Start new scope first, so if user has LOOP: label before select, |
600
|
|
|
|
|
|
|
# it applies to the whole encapsulated loop |
601
|
|
|
|
|
|
|
# wrapper scope needed so user can LABEL: select(), and not *my* label |
602
|
37
|
|
|
|
|
192
|
push @parts, qq( |
603
|
|
|
|
|
|
|
# Code generated by $PKG v$VERSION, by tim(AT)TeachMePerl.com |
604
|
|
|
|
|
|
|
# NOTA BENE: Line 1 of this segment must start with {, so user can LABEL it |
605
|
|
|
|
|
|
|
{ # **** NEW WRAPPER SCOPE FOR SELECTLOOP #$loopnum **** |
606
|
|
|
|
|
|
|
\$${PKG}::DEBUG > 1 and $loopnum == 1 and |
607
|
|
|
|
|
|
|
warn "LINE NUMBER FOR START OF USER CODE_BLOCK IS: ", __LINE__, "\\n"; |
608
|
|
|
|
|
|
|
_SEL_LOOP$loopnum: { # **** NEW SCOPE FOR SELECTLOOP #$loopnum **** |
609
|
|
|
|
|
|
|
); |
610
|
|
|
|
|
|
|
# warn " DDD LOGGING is now $LOGGING\n"; |
611
|
37
|
50
|
0
|
|
|
100
|
$LOGGING and (print PART1 $parts[0] or _DIE "failed to write to PART1\n"); |
612
|
37
|
50
|
|
|
|
122
|
$DEBUG > 4 and warn "SETTING $arrayname to $values\n"; |
613
|
|
|
|
|
|
|
|
614
|
37
|
|
|
|
|
258
|
push @parts, qq( |
615
|
|
|
|
|
|
|
# critical for values's contents to be resolved in user's scope |
616
|
|
|
|
|
|
|
local \@$arrayname=$values; |
617
|
|
|
|
|
|
|
local \$${PKG}::num_values=\@$arrayname; |
618
|
|
|
|
|
|
|
|
619
|
|
|
|
|
|
|
\$${PKG}::DEBUG > 4 and do { |
620
|
|
|
|
|
|
|
warn "ARRAY VALUES ARE: \@$arrayname\\n"; |
621
|
|
|
|
|
|
|
warn "NUM VALUES is \$${PKG}::num_values\\n"; |
622
|
|
|
|
|
|
|
warn "user-program debug level is \$${PKG}::U_WARN\\n"; |
623
|
|
|
|
|
|
|
}; |
624
|
|
|
|
|
|
|
$declaration # loop-var declaration appears here |
625
|
|
|
|
|
|
|
); |
626
|
|
|
|
|
|
|
|
627
|
37
|
50
|
0
|
|
|
170
|
$LOGGING and (print PART2 $parts[1] or _DIE "failed to write to PART1\n"); |
628
|
|
|
|
|
|
|
|
629
|
37
|
50
|
|
|
|
164
|
$DEBUG > 4 and do { |
630
|
0
|
|
|
|
|
0
|
warn "\$codestring is: $codestring\n"; |
631
|
0
|
|
|
|
|
0
|
warn "\$dcode is: '$dcode'\n"; |
632
|
0
|
|
|
|
|
0
|
warn "\$arrayname is: $arrayname\n"; |
633
|
0
|
0
|
|
|
|
0
|
!defined $Shell::POSIX::Select::_autoprompt and warn "autoprompt is unset"; |
634
|
0
|
0
|
|
|
|
0
|
!defined $codestring and warn "codestring is unset"; |
635
|
|
|
|
|
|
|
}; |
636
|
|
|
|
|
|
|
|
637
|
|
|
|
|
|
|
{ # local scope for $^W mod |
638
|
|
|
|
|
|
|
# getting one pesky "uninit var" warnings I can't resolve |
639
|
37
|
|
|
|
|
74
|
local $^W=0; |
|
37
|
|
|
|
|
282
|
|
640
|
37
|
|
|
|
|
1321
|
push @parts, qq( |
641
|
|
|
|
|
|
|
$dcode; |
642
|
|
|
|
|
|
|
local ( |
643
|
|
|
|
|
|
|
\$${PKG}::Prompt[$loopnum], |
644
|
|
|
|
|
|
|
\$${PKG}::menu |
645
|
|
|
|
|
|
|
) = |
646
|
|
|
|
|
|
|
${PKG}::make_menu( |
647
|
|
|
|
|
|
|
\$${PKG}::Heading || "", |
648
|
|
|
|
|
|
|
\$${PKG}::Prompt || "" , # Might be overridden in make_menu |
649
|
|
|
|
|
|
|
\@$arrayname |
650
|
|
|
|
|
|
|
); |
651
|
|
|
|
|
|
|
|
652
|
|
|
|
|
|
|
# no point in prompting a pipe! |
653
|
|
|
|
|
|
|
local \$${PKG}::do_prompt[$loopnum] = (-t) ? 1 : 0 ; |
654
|
|
|
|
|
|
|
$DEBUG > 2 and warn "do_prompt is \$${PKG}::do_prompt[$loopnum]\\n"; |
655
|
|
|
|
|
|
|
if ( defined \$${PKG}::menu ) { # No list, no iterations! |
656
|
|
|
|
|
|
|
while (1) { # for repeating prompt for selections |
657
|
|
|
|
|
|
|
# localize, so I don't have to reset $Reply for |
658
|
|
|
|
|
|
|
# outer loop on exit from inner |
659
|
|
|
|
|
|
|
local (\$Reply); |
660
|
|
|
|
|
|
|
while (1) { # for validating user's input |
661
|
|
|
|
|
|
|
local \$${PKG}::bad = 0; |
662
|
|
|
|
|
|
|
|
663
|
|
|
|
|
|
|
# local decl suppresses newline on prompt when -l switch turned on |
664
|
|
|
|
|
|
|
{ |
665
|
|
|
|
|
|
|
local \$\\; |
666
|
|
|
|
|
|
|
if (\$${PKG}::do_prompt[$loopnum]) { |
667
|
|
|
|
|
|
|
|
668
|
|
|
|
|
|
|
# When transferring from INNER to OUTER loop, |
669
|
|
|
|
|
|
|
# extra NL before prompt is visually desirable |
670
|
|
|
|
|
|
|
|
671
|
|
|
|
|
|
|
if ( \$${PKG}::_extra_nl) { |
672
|
|
|
|
|
|
|
print STDERR "\\n\\n"; |
673
|
|
|
|
|
|
|
\$${PKG}::_extra_nl=0; |
674
|
|
|
|
|
|
|
} |
675
|
|
|
|
|
|
|
print STDERR |
676
|
|
|
|
|
|
|
"\$${PKG}::menu$NL$ON\$${PKG}::Prompt[$loopnum]$OFF$BOLD "; |
677
|
|
|
|
|
|
|
} |
678
|
|
|
|
|
|
|
} |
679
|
|
|
|
|
|
|
|
680
|
|
|
|
|
|
|
# \$${PKG}::do_prompt=$Shell::POSIX::Select::_autoprompt; |
681
|
|
|
|
|
|
|
# constant prompting depends on style |
682
|
|
|
|
|
|
|
\$${PKG}::do_prompt[$loopnum]= 0; |
683
|
|
|
|
|
|
|
|
684
|
|
|
|
|
|
|
if ( \$${PKG}::dump_data ) { |
685
|
|
|
|
|
|
|
\$Reply = undef; |
686
|
|
|
|
|
|
|
# dump filtered source for comparison against expected |
687
|
|
|
|
|
|
|
print STDERR "copacetic\n"; # ensure some output, and flush pending |
688
|
|
|
|
|
|
|
exit 222; # code for graceful, expected, early exit |
689
|
|
|
|
|
|
|
} |
690
|
|
|
|
|
|
|
else { |
691
|
|
|
|
|
|
|
# \$^W=0; |
692
|
|
|
|
|
|
|
# warn "Waiting for input"; |
693
|
|
|
|
|
|
|
\$Eof=0; |
694
|
|
|
|
|
|
|
\$Reply = ; |
695
|
|
|
|
|
|
|
# warn "Got input"; |
696
|
|
|
|
|
|
|
# \$^W=1; |
697
|
|
|
|
|
|
|
|
698
|
|
|
|
|
|
|
if ( !defined( \$Reply ) ) { |
699
|
|
|
|
|
|
|
defined "$BOLD" and "$BOLD" ne "" and print STDERR "$SGR0"; |
700
|
|
|
|
|
|
|
|
701
|
|
|
|
|
|
|
# need to undef loop var; user may check it! |
702
|
|
|
|
|
|
|
undef $loop_var; |
703
|
|
|
|
|
|
|
|
704
|
|
|
|
|
|
|
# last ${PKG}::_SEL_LOOP$loopnum; # Syntax error! |
705
|
|
|
|
|
|
|
# If returning to outer loop, show the prompt for it |
706
|
|
|
|
|
|
|
# warn "User hit ^D"; |
707
|
|
|
|
|
|
|
if ( $loopnum > 1 and -t ) { # reset prompting for outer loop |
708
|
|
|
|
|
|
|
\$${PKG}::do_prompt[$loopnum-1] = 1; \$${PKG}::_extra_nl=1; |
709
|
|
|
|
|
|
|
} |
710
|
|
|
|
|
|
|
$DEBUG > 2 and warn "Lasting out of _SEL_LOOP$loopnum\\n"; |
711
|
|
|
|
|
|
|
\$Eof=1; |
712
|
|
|
|
|
|
|
last _SEL_LOOP$loopnum; |
713
|
|
|
|
|
|
|
} |
714
|
|
|
|
|
|
|
!defined \$Reply and die "REPLY accessed, while undefined"; |
715
|
|
|
|
|
|
|
chomp \$Reply; |
716
|
|
|
|
|
|
|
|
717
|
|
|
|
|
|
|
# undo emboldening of user input |
718
|
|
|
|
|
|
|
defined "$BOLD" and "$BOLD" ne "" and print STDERR "$SGR0"; |
719
|
|
|
|
|
|
|
|
720
|
|
|
|
|
|
|
#print STDERR "\$${PKG}::menu$NL$ON\$${PKG}::Prompt$OFF$BOLD "; |
721
|
|
|
|
|
|
|
if ( \$Reply eq "" ) { # interpreted as re-print menu request |
722
|
|
|
|
|
|
|
# Empty input is legit, means redisplay menu |
723
|
|
|
|
|
|
|
\$${PKG}::U_WARN > 1 and warn "\\tINPUT IS: empty\\n"; |
724
|
|
|
|
|
|
|
\$${PKG}::bad = \$${PKG}::do_prompt[$loopnum] = 1; |
725
|
|
|
|
|
|
|
} |
726
|
|
|
|
|
|
|
elsif ( \$Reply =~ /\\D/ ) { # shouldn't be any non-digit! |
727
|
|
|
|
|
|
|
\$${PKG}::U_WARN > 0 |
728
|
|
|
|
|
|
|
and warn "\\tINPUT CONTAINS NON-DIGIT: '\$Reply'\\n"; |
729
|
|
|
|
|
|
|
\$${PKG}::bad = 1; # Korn and Bash shell just ignore this case |
730
|
|
|
|
|
|
|
} |
731
|
|
|
|
|
|
|
elsif ( \$Reply < 1 or \$Reply > \$${PKG}::num_values ) { |
732
|
|
|
|
|
|
|
\$${PKG}::U_WARN > 0 |
733
|
|
|
|
|
|
|
and warn |
734
|
|
|
|
|
|
|
"\\t'\$Reply' IS NOT IN RANGE: 1 - \$${PKG}::num_values\\n"; |
735
|
|
|
|
|
|
|
\$${PKG}::bad = 1; # Korn and Bash shell just ignore this case |
736
|
|
|
|
|
|
|
} |
737
|
|
|
|
|
|
|
|
738
|
|
|
|
|
|
|
# warn "BAD is now: \$${PKG}::bad"; |
739
|
|
|
|
|
|
|
\$${PKG}::bad or |
740
|
|
|
|
|
|
|
$DEBUG > 2 and warn "About to last out of Reply Validator Loop\n"; |
741
|
|
|
|
|
|
|
\$${PKG}::bad or last; # REPLY VALIDATOR EXITED HERE |
742
|
|
|
|
|
|
|
} # if for validating user input |
743
|
|
|
|
|
|
|
} # infinite while for validating user input |
744
|
|
|
|
|
|
|
|
745
|
|
|
|
|
|
|
$loop_var = \$$arrayname\[\$Reply - 1]; # set users' variable |
746
|
|
|
|
|
|
|
|
747
|
|
|
|
|
|
|
# USER'S LOOP-BLOCK BELOW |
748
|
|
|
|
|
|
|
$codestring; |
749
|
|
|
|
|
|
|
|
750
|
|
|
|
|
|
|
# USER'S LOOP-BLOCK ABOVE |
751
|
|
|
|
|
|
|
# Making sure there's colon (maybe |
752
|
|
|
|
|
|
|
# even two) after codestring above, |
753
|
|
|
|
|
|
|
# in case user omitted after last |
754
|
|
|
|
|
|
|
# statement in block. I might add |
755
|
|
|
|
|
|
|
# another statement below it someday! |
756
|
|
|
|
|
|
|
$DEBUG > 2 and warn "At end of prompt-repeating loop \n"; |
757
|
|
|
|
|
|
|
} # infinite while for repeating collection of selections |
758
|
|
|
|
|
|
|
$DEBUG and warn "BEYOND end of prompt-repeating loop \n"; |
759
|
|
|
|
|
|
|
} # endif (defined \$${PKG}::menu) |
760
|
|
|
|
|
|
|
else { |
761
|
|
|
|
|
|
|
\$${PKG}::DEBUG > 0 and warn "$PKG: Select Loop #$loopnum has no list, so no iterations\\n"; |
762
|
|
|
|
|
|
|
|
763
|
|
|
|
|
|
|
if ( \$${PKG}::dump_data ) { |
764
|
|
|
|
|
|
|
\$Reply = undef; |
765
|
|
|
|
|
|
|
# dump filtered source for comparison against expected |
766
|
|
|
|
|
|
|
print STDERR "copacetic\n"; # ensure some output, and flush pending |
767
|
|
|
|
|
|
|
exit 222; # code for graceful, expected, early exit |
768
|
|
|
|
|
|
|
} |
769
|
|
|
|
|
|
|
} |
770
|
|
|
|
|
|
|
# return omitted above, to get last expression's value |
771
|
|
|
|
|
|
|
# returned automatically, just like shell's version |
772
|
|
|
|
|
|
|
|
773
|
|
|
|
|
|
|
); # push onto parts ender |
774
|
|
|
|
|
|
|
} # local scope for $^W mod |
775
|
|
|
|
|
|
|
|
776
|
37
|
50
|
0
|
|
|
162
|
$LOGGING and (print PART3 $parts[2] or _DIE "failed to write to PART3\n"); |
777
|
|
|
|
|
|
|
|
778
|
37
|
|
|
|
|
138
|
push @parts, qq( |
779
|
|
|
|
|
|
|
} # **** END NEW SCOPE FOR SELECTLOOP #$loopnum **** |
780
|
|
|
|
|
|
|
} # **** END WRAPPER SCOPE FOR SELECTLOOP #$loopnum **** |
781
|
|
|
|
|
|
|
# vi:ts=2 sw=2: |
782
|
|
|
|
|
|
|
); |
783
|
|
|
|
|
|
|
|
784
|
37
|
50
|
0
|
|
|
115
|
$LOGGING and (print PART4 $parts[3] or _DIE "failed to write to PART4\n"); |
785
|
|
|
|
|
|
|
# Following is portable PART-divider, used to isolate chunk |
786
|
|
|
|
|
|
|
# with unitialized value causing warning |
787
|
|
|
|
|
|
|
# ); push @parts, qq( |
788
|
|
|
|
|
|
|
|
789
|
37
|
|
|
|
|
860
|
return ( join "", @parts ); # return assembled code, for user to run |
790
|
|
|
|
|
|
|
} # enloop_codeblock |
791
|
|
|
|
|
|
|
|
792
|
|
|
|
|
|
|
sub make_menu { |
793
|
22
|
|
|
22
|
0
|
85
|
my $subname = sub_name(); |
794
|
|
|
|
|
|
|
|
795
|
|
|
|
|
|
|
|
796
|
|
|
|
|
|
|
# Replacement of empty list by @_ or @ARGV happens in matches2fields |
797
|
|
|
|
|
|
|
# Here we check to see if we got arguments from somewhere |
798
|
|
|
|
|
|
|
# Note that it's not necesssarily an error if there are no values, |
799
|
|
|
|
|
|
|
# that just means we won't do any iterations |
800
|
|
|
|
|
|
|
|
801
|
22
|
|
|
|
|
71
|
my ($heading) = shift; |
802
|
22
|
|
|
|
|
66
|
my ($prompt) = shift; |
803
|
22
|
|
|
|
|
73
|
my (@values) = @_; |
804
|
22
|
100
|
|
|
|
85
|
unless (@values) { |
805
|
4
|
|
|
|
|
22
|
return ( undef, undef ); # can't make menu out of nothing! |
806
|
|
|
|
|
|
|
} |
807
|
18
|
|
|
|
|
57
|
my ( $l, $l_length ) = 0; |
808
|
18
|
|
|
|
|
36
|
my $count = 5; |
809
|
18
|
|
|
|
|
54
|
my ( $sep, $padding ) = "" x 2; |
810
|
18
|
|
|
|
|
36
|
my $choice = ""; |
811
|
|
|
|
|
|
|
|
812
|
|
|
|
|
|
|
|
813
|
|
|
|
|
|
|
# Find longest string value in selection list |
814
|
18
|
|
|
|
|
43
|
my $v_length = 0; |
815
|
|
|
|
|
|
|
|
816
|
18
|
|
|
|
|
92
|
for ( my $i = 0 ; $i < @values ; $i++ ) { |
817
|
35
|
100
|
|
|
|
165
|
( $l = length $values[$i] ) > $v_length and $v_length = $l; |
818
|
|
|
|
|
|
|
} |
819
|
18
|
0
|
33
|
|
|
111
|
$DEBUG > 3 and $LOGGING and print LOG "Longest value is $v_length chars\n"; |
820
|
|
|
|
|
|
|
|
821
|
|
|
|
|
|
|
# Figure out lengths of labels (numbers on menu selections) |
822
|
18
|
0
|
33
|
|
|
91
|
$DEBUG > 3 and $LOGGING and print LOG "Number of values is ", scalar @values, "\n"; |
823
|
|
|
|
|
|
|
|
824
|
|
|
|
|
|
|
|
825
|
18
|
50
|
|
|
|
158
|
@values >= 10_000 ? $l_length = 5 : |
|
|
50
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
826
|
|
|
|
|
|
|
@values >= 1_000 ? $l_length = 4 : |
827
|
|
|
|
|
|
|
@values >= 100 ? $l_length = 3 : |
828
|
|
|
|
|
|
|
@values >= 10 ? $l_length = 2 : |
829
|
|
|
|
|
|
|
@values > 0 ? $l_length = 1 : |
830
|
|
|
|
|
|
|
undef $l_length; |
831
|
|
|
|
|
|
|
|
832
|
18
|
0
|
33
|
|
|
78
|
$DEBUG > 3 and $LOGGING and print LOG "Label length is $l_length\n"; |
833
|
|
|
|
|
|
|
|
834
|
18
|
50
|
|
|
|
71
|
if ( !defined $l_length ) { return undef; } |
|
0
|
|
|
|
|
0
|
|
835
|
|
|
|
|
|
|
|
836
|
18
|
|
|
|
|
46
|
$sep = "\040\040"; |
837
|
18
|
|
|
|
|
40
|
my $l_sep = length $sep; # separator 'tween pieces |
838
|
|
|
|
|
|
|
|
839
|
|
|
|
|
|
|
# Figure out how many columns per line we can print |
840
|
|
|
|
|
|
|
# 2 is for : after label |
841
|
|
|
|
|
|
|
|
842
|
|
|
|
|
|
|
# TIMJI: Convert to using YUMPY's Term::Size::Heuristic here, later on |
843
|
|
|
|
|
|
|
|
844
|
18
|
|
|
|
|
53
|
my $one_label = ( $l_length + 2 ) + $v_length + $l_sep; |
845
|
18
|
|
|
|
|
79
|
my $columns = int( $COLS / $one_label ); |
846
|
18
|
50
|
|
|
|
107
|
$columns < 1 and $columns = 1; |
847
|
|
|
|
|
|
|
# Do not let the number of columns grow beyond the maximum: |
848
|
18
|
50
|
|
|
|
112
|
if ($MaxColumns < $columns) |
849
|
|
|
|
|
|
|
{ |
850
|
0
|
|
|
|
|
0
|
$columns = $MaxColumns; |
851
|
|
|
|
|
|
|
} # if |
852
|
|
|
|
|
|
|
|
853
|
|
|
|
|
|
|
# $DEBUG > 3 and |
854
|
|
|
|
|
|
|
#HERE |
855
|
18
|
50
|
|
|
|
100
|
$LOGGING and print LOG "T-Cols, Columns, label: $COLS, $columns, $one_label\n"; |
856
|
|
|
|
|
|
|
|
857
|
|
|
|
|
|
|
# Prompt may have been set in import() according to a submitted option; |
858
|
|
|
|
|
|
|
# if so, keep it. If not, use shell's default |
859
|
|
|
|
|
|
|
$prompt = |
860
|
|
|
|
|
|
|
(defined $Shell::POSIX::Select::Prompt and |
861
|
|
|
|
|
|
|
$Shell::POSIX::Select::Prompt ne "") ? |
862
|
|
|
|
|
|
|
$Shell::POSIX::Select::Prompt : |
863
|
|
|
|
|
|
|
defined $ENV{Select_POSIX_Shell_Prompt} ? $ENV{Select_POSIX_Shell_Prompt} : |
864
|
18
|
50
|
66
|
|
|
161
|
$Shell::POSIX::Select::_default_prompt; |
|
|
100
|
|
|
|
|
|
865
|
|
|
|
|
|
|
; |
866
|
|
|
|
|
|
|
|
867
|
18
|
0
|
33
|
|
|
66
|
$DEBUG > 3 and $LOGGING and print LOG "Making menu\n"; |
868
|
|
|
|
|
|
|
|
869
|
|
|
|
|
|
|
{ |
870
|
18
|
|
|
|
|
35
|
local $, = "\n"; |
|
18
|
|
|
|
|
61
|
|
871
|
|
|
|
|
|
|
} |
872
|
|
|
|
|
|
|
|
873
|
18
|
|
|
|
|
36
|
my $menu; |
874
|
18
|
50
|
|
|
|
81
|
$menu = defined $heading ? "${ON}$heading$OFF" : "" ; |
875
|
18
|
|
|
|
|
45
|
$menu.="\n"; |
876
|
|
|
|
|
|
|
# $columns == 0 and die "Columns is zero!"; |
877
|
18
|
|
|
|
|
93
|
for ( my $i = 0, my $j = 1 ; $i < @values ; $i++, $j++ ) { |
878
|
35
|
|
|
|
|
190
|
$menu .= sprintf "%${l_length}d) %-${v_length}s$sep", $j, $values[$i]; |
879
|
35
|
50
|
|
|
|
176
|
$j % $columns or $menu .= sprintf "\n"; # format $count items per line |
880
|
|
|
|
|
|
|
|
881
|
|
|
|
|
|
|
# For 385 line list: |
882
|
|
|
|
|
|
|
# Illegal modulus zero at /pmods/yumpy/Select/Shell/POSIX/Select.pm line 764. |
883
|
|
|
|
|
|
|
|
884
|
|
|
|
|
|
|
} |
885
|
18
|
|
|
|
|
133
|
return ( $prompt, $menu ); |
886
|
|
|
|
|
|
|
} # make_menu |
887
|
|
|
|
|
|
|
|
888
|
|
|
|
|
|
|
sub log_files { |
889
|
0
|
|
|
0
|
0
|
0
|
my $subname = sub_name(); |
890
|
0
|
|
|
|
|
0
|
my ($dir, $sep); |
891
|
|
|
|
|
|
|
|
892
|
|
|
|
|
|
|
|
893
|
0
|
0
|
|
|
|
0
|
if ( $LOGGING == 1 ) { |
|
|
0
|
|
|
|
|
|
894
|
0
|
|
|
|
|
0
|
$dir = tmpdir(); |
895
|
|
|
|
|
|
|
# |
896
|
|
|
|
|
|
|
# USERPROG shows my changes, with control-chars |
897
|
|
|
|
|
|
|
# filling in as placeholders for some pieces. For |
898
|
|
|
|
|
|
|
# debugging purposes, I find it helpful to print that |
899
|
|
|
|
|
|
|
# out ASAP so I have something to look at if the |
900
|
|
|
|
|
|
|
# program bombs out before SOURCE gets written out, |
901
|
|
|
|
|
|
|
# which is the same apart from placeholders being |
902
|
|
|
|
|
|
|
# converted to original data. |
903
|
|
|
|
|
|
|
# |
904
|
0
|
0
|
0
|
|
|
0
|
$DEBUG > 1 and $LOGGING > 0 and warn "Opening log files\n"; |
905
|
0
|
0
|
|
|
|
0
|
open LOG, '>', catfile($dir, 'SELECT_log') or _DIE "Open LOG failed, $!\n"; |
906
|
0
|
0
|
|
|
|
0
|
open SOURCE, '>', catfile($dir, 'SELECT_source') or _DIE "Open SOURCE failed, $!\n"; |
907
|
0
|
0
|
|
|
|
0
|
open USERPROG, '>', catfile($dir, 'SELECT_user_program') or _DIE "Open USERPROG failed, $!\n"; |
908
|
0
|
0
|
|
|
|
0
|
open PART1, '>', catfile($dir, 'SELECT_part1') or _DIE "Open PART1 failed, $!\n"; |
909
|
0
|
0
|
|
|
|
0
|
open PART2, '>', catfile($dir, 'SELECT_part2') or _DIE "Open PART2 failed, $!\n"; |
910
|
0
|
0
|
|
|
|
0
|
open PART3, '>', catfile($dir, 'SELECT_part3') or _DIE "Open PART3 failed, $!\n"; |
911
|
0
|
0
|
|
|
|
0
|
open PART4, '>', catfile($dir, 'SELECT_part4') or _DIE "Open PART4 failed, $!\n"; |
912
|
0
|
|
|
|
|
0
|
$LOGGING++; # to avoid 2nd invocation |
913
|
0
|
0
|
0
|
|
|
0
|
$DEBUG > 1 and $LOGGING > 0 and warn "Finished with log files\n"; |
914
|
|
|
|
|
|
|
} |
915
|
|
|
|
|
|
|
elsif ($LOGGING > 1) { |
916
|
0
|
0
|
|
|
|
0
|
$DEBUG > 0 and warn "$subname: Logfiles opened previously\n"; |
917
|
|
|
|
|
|
|
} |
918
|
|
|
|
|
|
|
else { |
919
|
0
|
0
|
|
|
|
0
|
$DEBUG > 0 and warn "$subname: Logfiles not opened\n"; |
920
|
|
|
|
|
|
|
} |
921
|
|
|
|
|
|
|
} # log_files |
922
|
|
|
|
|
|
|
|
923
|
|
|
|
|
|
|
sub sub_name { |
924
|
320
|
|
|
320
|
0
|
2145
|
my $callers_name = (caller 1)[3] ; |
925
|
320
|
50
|
|
|
|
1027
|
if ( ! defined $callers_name ) { |
926
|
0
|
|
|
|
|
0
|
$callers_name='Main_program'; # must be call from main |
927
|
|
|
|
|
|
|
} |
928
|
|
|
|
|
|
|
else { |
929
|
320
|
|
|
|
|
1810
|
$callers_name =~ s/^.*:://; # strip package name |
930
|
320
|
|
|
|
|
712
|
$callers_name .= '()'; # sub_name -> sub_name() |
931
|
|
|
|
|
|
|
} |
932
|
320
|
|
|
|
|
712
|
return $callers_name; |
933
|
|
|
|
|
|
|
} # sub_name |
934
|
|
|
|
|
|
|
|
935
|
|
|
|
|
|
|
sub _WARN { |
936
|
27
|
|
|
27
|
|
75
|
my $subname = sub_name(); |
937
|
27
|
50
|
|
|
|
7629
|
$PRODUCTION ? carp(@_) : warn (@_); |
938
|
|
|
|
|
|
|
} |
939
|
|
|
|
|
|
|
|
940
|
|
|
|
|
|
|
sub _DIE { |
941
|
0
|
|
|
0
|
|
0
|
my $subname = sub_name(); |
942
|
0
|
0
|
|
|
|
0
|
$DEBUG and warn "$0: In _DIE, with PRODUCTION of $PRODUCTION, arg of @_\n"; |
943
|
0
|
0
|
|
|
|
0
|
$PRODUCTION ? croak(@_) : die (@_); |
944
|
|
|
|
|
|
|
} |
945
|
|
|
|
|
|
|
|
946
|
|
|
|
|
|
|
|
947
|
0
|
|
|
0
|
0
|
0
|
sub ignoring_case { lc $a cmp lc $b } |
948
|
|
|
|
|
|
|
|
949
|
|
|
|
|
|
|
sub import { |
950
|
|
|
|
|
|
|
local $_; |
951
|
|
|
|
|
|
|
my $subname = sub_name(); |
952
|
|
|
|
|
|
|
my %import; |
953
|
|
|
|
|
|
|
$_import_called++; |
954
|
|
|
|
|
|
|
|
955
|
|
|
|
|
|
|
shift; # discard package name |
956
|
|
|
|
|
|
|
|
957
|
|
|
|
|
|
|
$Shell::POSIX::Select::U_WARN = $Shell::POSIX::Select::U_WARN_default; |
958
|
|
|
|
|
|
|
$Shell::POSIX::Select::_style = $Shell::POSIX::Select::_default_style; |
959
|
|
|
|
|
|
|
# $Shell::POSIX::Select::_prompt = |
960
|
|
|
|
|
|
|
# Prompt is now established in make_menu, during run-time |
961
|
|
|
|
|
|
|
$Shell::POSIX::Select::_autoprompt=0; |
962
|
|
|
|
|
|
|
|
963
|
|
|
|
|
|
|
# First, peel off symbols to import, if any |
964
|
|
|
|
|
|
|
# warn "Caller of $subname is ", scalar caller, "\n"; |
965
|
|
|
|
|
|
|
my $user_pkg=caller; |
966
|
|
|
|
|
|
|
# $DEBUG > 2 and |
967
|
|
|
|
|
|
|
for (my $i=0; $i<@_; $i++) { |
968
|
|
|
|
|
|
|
my $found=0; |
969
|
|
|
|
|
|
|
foreach (@EXPORT_OK) { # Handle $Headings, etc. |
970
|
|
|
|
|
|
|
if ($_[$i] eq $_) { $import{$_} = $i; $found++; last; } |
971
|
|
|
|
|
|
|
} |
972
|
|
|
|
|
|
|
# stop as soon as first non-symbol encountered, so as not to |
973
|
|
|
|
|
|
|
# accidentally mess with following hash-style options |
974
|
|
|
|
|
|
|
$found==0 and last; |
975
|
|
|
|
|
|
|
} |
976
|
|
|
|
|
|
|
%import and export($user_pkg, keys %import); # create aliases for user |
977
|
|
|
|
|
|
|
|
978
|
|
|
|
|
|
|
# following gets "attempt to delete unreferenced scalar"! |
979
|
|
|
|
|
|
|
# %import and delete @_[values %import]; |
980
|
|
|
|
|
|
|
# Delete from @_ each |
981
|
|
|
|
|
|
|
map { delete $_[$_] } values %import; # but this works |
982
|
|
|
|
|
|
|
# warn "Numvals in array is ", scalar @_, "\n"; |
983
|
|
|
|
|
|
|
|
984
|
|
|
|
|
|
|
@_= grep defined, @_; # reset, to eliminate extracted imports |
985
|
|
|
|
|
|
|
# warn "Numvals in array is now ", scalar @_, "\n"; |
986
|
|
|
|
|
|
|
# warnings sets user-program debugging level |
987
|
|
|
|
|
|
|
# debug sets module's debuging level |
988
|
|
|
|
|
|
|
my @legal_options = qw( style prompt testmode warnings debug logging ); |
989
|
|
|
|
|
|
|
my %options = |
990
|
|
|
|
|
|
|
hash_options(\@legal_options, @_ ); # style => Korn, etc. |
991
|
|
|
|
|
|
|
|
992
|
|
|
|
|
|
|
|
993
|
|
|
|
|
|
|
my @styles=qw( bash korn ); |
994
|
|
|
|
|
|
|
my @prompts=qw( generic korn bash arrows ); |
995
|
|
|
|
|
|
|
my @testmodes=qw( make foreach ); |
996
|
|
|
|
|
|
|
|
997
|
|
|
|
|
|
|
my $bad; |
998
|
|
|
|
|
|
|
|
999
|
|
|
|
|
|
|
# timji: Loopify this section later, once it gets stable |
1000
|
|
|
|
|
|
|
|
1001
|
|
|
|
|
|
|
# "logging" enables/disables logging of filter output to file |
1002
|
|
|
|
|
|
|
$_ = $ENV{Shell_POSIX_Select_logging} || $options{logging}; |
1003
|
|
|
|
|
|
|
if (defined) { |
1004
|
|
|
|
|
|
|
# unless ( is_unix() ) { |
1005
|
|
|
|
|
|
|
# warn "$PKG\::$subname: logging is only for UNIX-like OSs\n"; |
1006
|
|
|
|
|
|
|
# } |
1007
|
|
|
|
|
|
|
if (/^(\d)$/ and 0 <= $1 and $1 <=1 ) { |
1008
|
|
|
|
|
|
|
$LOGGING = $_; |
1009
|
|
|
|
|
|
|
$DEBUG > 0 and warn "$PKG: Set logging to: $LOGGING\n"; |
1010
|
|
|
|
|
|
|
} |
1011
|
|
|
|
|
|
|
else { |
1012
|
|
|
|
|
|
|
_WARN "$PKG\::$subname: Invalid logging level '$_'\n"; |
1013
|
|
|
|
|
|
|
$DEBUG > 1 and _DIE; |
1014
|
|
|
|
|
|
|
} |
1015
|
|
|
|
|
|
|
} |
1016
|
|
|
|
|
|
|
|
1017
|
|
|
|
|
|
|
# "debug" enables/disables informational messages while running user program |
1018
|
|
|
|
|
|
|
$_ = $ENV{Shell_POSIX_Select_warnings} || $options{warnings}; |
1019
|
|
|
|
|
|
|
$select2foreach=0; |
1020
|
|
|
|
|
|
|
if (defined) { |
1021
|
|
|
|
|
|
|
if (/^\d+$/) { |
1022
|
|
|
|
|
|
|
$Shell::POSIX::Select::U_WARN = $_; |
1023
|
|
|
|
|
|
|
warn "$PKG: Set warnings to: $Shell::POSIX::Select::U_WARN\n"; |
1024
|
|
|
|
|
|
|
} |
1025
|
|
|
|
|
|
|
else { |
1026
|
|
|
|
|
|
|
_WARN "$PKG\::$subname: Invalid warnings level '$_'\n"; |
1027
|
|
|
|
|
|
|
$DEBUG > 1 and _DIE; |
1028
|
|
|
|
|
|
|
} |
1029
|
|
|
|
|
|
|
} |
1030
|
|
|
|
|
|
|
|
1031
|
|
|
|
|
|
|
# "debug" enables/disables informational messages while running user program |
1032
|
|
|
|
|
|
|
$_ = $ENV{Shell_POSIX_Select_debug} || $options{debug}; |
1033
|
|
|
|
|
|
|
if (defined) { |
1034
|
|
|
|
|
|
|
if (/^\d+$/) { |
1035
|
|
|
|
|
|
|
$Shell::POSIX::Select::DEBUG = $_; |
1036
|
|
|
|
|
|
|
} |
1037
|
|
|
|
|
|
|
else { |
1038
|
|
|
|
|
|
|
_WARN "$PKG\::$subname: Invalid debug option '$_'\n"; |
1039
|
|
|
|
|
|
|
$DEBUG > 1 and _DIE; |
1040
|
|
|
|
|
|
|
} |
1041
|
|
|
|
|
|
|
} |
1042
|
|
|
|
|
|
|
|
1043
|
|
|
|
|
|
|
$_=$ENV{Shell_POSIX_Select_style} || $options{style}; |
1044
|
|
|
|
|
|
|
if (defined) { |
1045
|
|
|
|
|
|
|
my $found=0; |
1046
|
|
|
|
|
|
|
foreach my $style (@styles) { |
1047
|
|
|
|
|
|
|
if ($_ =~ /^$style$/i ) { # korn, bash,etc. |
1048
|
|
|
|
|
|
|
# code as K, B, etc. |
1049
|
|
|
|
|
|
|
$Shell::POSIX::Select::_style = uc substr($_,0,1); |
1050
|
|
|
|
|
|
|
$found++; # last one wins |
1051
|
|
|
|
|
|
|
} |
1052
|
|
|
|
|
|
|
} |
1053
|
|
|
|
|
|
|
if (! $found) { |
1054
|
|
|
|
|
|
|
_WARN "$PKG\::$subname: Invalid style option '$_'\n"; |
1055
|
|
|
|
|
|
|
$DEBUG > 1 and _DIE; |
1056
|
|
|
|
|
|
|
} |
1057
|
|
|
|
|
|
|
} |
1058
|
|
|
|
|
|
|
|
1059
|
|
|
|
|
|
|
# Bash automatically shows prompt every time, |
1060
|
|
|
|
|
|
|
# Ksh only does if user enters input of only |
1061
|
|
|
|
|
|
|
my $autoprompt=0; |
1062
|
|
|
|
|
|
|
if ( $Shell::POSIX::Select::_style eq 'K' ) { $autoprompt=0; } |
1063
|
|
|
|
|
|
|
elsif ( $Shell::POSIX::Select::_style eq 'B' ) { $autoprompt=1; } |
1064
|
|
|
|
|
|
|
|
1065
|
|
|
|
|
|
|
$Shell::POSIX::Select::_autoprompt = $autoprompt; |
1066
|
|
|
|
|
|
|
$_ = $ENV{Shell_POSIX_Select_prompt} || $options{prompt} ; |
1067
|
|
|
|
|
|
|
if (defined) { |
1068
|
|
|
|
|
|
|
$_=lc $_; |
1069
|
|
|
|
|
|
|
my $found=0; |
1070
|
|
|
|
|
|
|
foreach my $prompt (sort @prompts) { # sorting, so "generic" choice beats shell-specific ones |
1071
|
|
|
|
|
|
|
if ($_ =~ /^$prompt$/i ) { |
1072
|
|
|
|
|
|
|
$_ eq 'generic' and do { |
1073
|
|
|
|
|
|
|
$DEBUG > 0 and warn "Set generic prompt"; |
1074
|
|
|
|
|
|
|
$Shell::POSIX::Select::_prompt = |
1075
|
|
|
|
|
|
|
$Shell::POSIX::Select::_generic; |
1076
|
|
|
|
|
|
|
++$found and last; |
1077
|
|
|
|
|
|
|
die 33; |
1078
|
|
|
|
|
|
|
}; |
1079
|
|
|
|
|
|
|
$_ eq "korn" and do { |
1080
|
|
|
|
|
|
|
$Shell::POSIX::Select::_prompt = |
1081
|
|
|
|
|
|
|
$Shell::POSIX::Select::_korn_prompt; |
1082
|
|
|
|
|
|
|
$found++; |
1083
|
|
|
|
|
|
|
last; |
1084
|
|
|
|
|
|
|
}; |
1085
|
|
|
|
|
|
|
$_ eq "bash" and do { |
1086
|
|
|
|
|
|
|
$Shell::POSIX::Select::_prompt = |
1087
|
|
|
|
|
|
|
$Shell::POSIX::Select::_bash_prompt; |
1088
|
|
|
|
|
|
|
$found++; |
1089
|
|
|
|
|
|
|
last; |
1090
|
|
|
|
|
|
|
}; |
1091
|
|
|
|
|
|
|
$_ eq "arrows" and do { |
1092
|
|
|
|
|
|
|
$Shell::POSIX::Select::_prompt = |
1093
|
|
|
|
|
|
|
$Shell::POSIX::Select::_arrows_prompt; |
1094
|
|
|
|
|
|
|
$found++; |
1095
|
|
|
|
|
|
|
last; |
1096
|
|
|
|
|
|
|
}; |
1097
|
|
|
|
|
|
|
} |
1098
|
|
|
|
|
|
|
# If not a prompt keyword, must be literal prompt |
1099
|
|
|
|
|
|
|
do { |
1100
|
|
|
|
|
|
|
$Shell::POSIX::Select::_prompt = $_; |
1101
|
|
|
|
|
|
|
$found++; |
1102
|
|
|
|
|
|
|
last; |
1103
|
|
|
|
|
|
|
}; |
1104
|
|
|
|
|
|
|
} |
1105
|
|
|
|
|
|
|
if (! $found) { |
1106
|
|
|
|
|
|
|
_WARN "$PKG\::$subname: Invalid prompt option '$_'\n"; |
1107
|
|
|
|
|
|
|
$DEBUG > 1 and _DIE; |
1108
|
|
|
|
|
|
|
} |
1109
|
|
|
|
|
|
|
} |
1110
|
|
|
|
|
|
|
|
1111
|
|
|
|
|
|
|
$Shell::POSIX::Select::dump_data=0; |
1112
|
|
|
|
|
|
|
$_= $ENV{Shell_POSIX_Select_testmode} || $options{testmode} ; |
1113
|
|
|
|
|
|
|
if (defined) { |
1114
|
|
|
|
|
|
|
my $found=0; |
1115
|
|
|
|
|
|
|
#foreach my $mode ( @testmodes ) { |
1116
|
|
|
|
|
|
|
if ($_ =~ /^make$/i ) { |
1117
|
|
|
|
|
|
|
$Shell::POSIX::Select::_testmode= 'make'; |
1118
|
|
|
|
|
|
|
$Shell::POSIX::Select::dump_data=1; |
1119
|
|
|
|
|
|
|
$found++; |
1120
|
|
|
|
|
|
|
} |
1121
|
|
|
|
|
|
|
elsif ($_ =~ /^foreach$/i ) { |
1122
|
|
|
|
|
|
|
$Shell::POSIX::Select::_testmode= 'foreach'; |
1123
|
|
|
|
|
|
|
$select2foreach=1; |
1124
|
|
|
|
|
|
|
$found++; |
1125
|
|
|
|
|
|
|
} |
1126
|
|
|
|
|
|
|
else { |
1127
|
|
|
|
|
|
|
$Shell::POSIX::Select::_testmode= ''; |
1128
|
|
|
|
|
|
|
$DEBUG > 2 and _WARN "Unrecognized testmode: $_\n"; |
1129
|
|
|
|
|
|
|
} |
1130
|
|
|
|
|
|
|
#} |
1131
|
|
|
|
|
|
|
if (! $found) { |
1132
|
|
|
|
|
|
|
_WARN "$PKG\::$subname: Invalid testmode option '$_'\n"; |
1133
|
|
|
|
|
|
|
$DEBUG > 1 and _DIE; |
1134
|
|
|
|
|
|
|
} |
1135
|
|
|
|
|
|
|
} |
1136
|
|
|
|
|
|
|
# ENV variable overrides program spec |
1137
|
|
|
|
|
|
|
|
1138
|
|
|
|
|
|
|
( ! defined $Shell::POSIX::Select::_testmode or |
1139
|
|
|
|
|
|
|
$Shell::POSIX::Select::_testmode eq "" ) and |
1140
|
|
|
|
|
|
|
$Shell::POSIX::Select::_testmode = ""; |
1141
|
|
|
|
|
|
|
|
1142
|
|
|
|
|
|
|
$DEBUG > 2 and warn "37 Testmode set to $Shell::POSIX::Select::_testmode\n"; |
1143
|
|
|
|
|
|
|
|
1144
|
|
|
|
|
|
|
$LOGGING and log_files(); |
1145
|
|
|
|
|
|
|
|
1146
|
|
|
|
|
|
|
$ENV{Shell_POSIX_Select_reference} and |
1147
|
|
|
|
|
|
|
$Shell::POSIX::Select::dump_data = 'Ref_Data'; |
1148
|
|
|
|
|
|
|
|
1149
|
|
|
|
|
|
|
# Don't assume /dev/tty will work on user's platform! |
1150
|
|
|
|
|
|
|
if ( $Shell::POSIX::Select::dump_data ) { |
1151
|
|
|
|
|
|
|
|
1152
|
|
|
|
|
|
|
# must ensure all output gets flushed to dumpfile before exiting |
1153
|
|
|
|
|
|
|
disable_buffering(); |
1154
|
|
|
|
|
|
|
|
1155
|
|
|
|
|
|
|
#if ( ! $PRODUCTION ) { |
1156
|
|
|
|
|
|
|
$Shell::POSIX::Select::_TTY=0; |
1157
|
|
|
|
|
|
|
# What's the OS-portable equivalent of "/dev/tty" in the above? |
1158
|
|
|
|
|
|
|
if ( -c '/dev/tty' ) { |
1159
|
|
|
|
|
|
|
if ( open TTY, '> /dev/tty' ) { |
1160
|
|
|
|
|
|
|
$Shell::POSIX::Select::_TTY=1; |
1161
|
|
|
|
|
|
|
} |
1162
|
|
|
|
|
|
|
else { |
1163
|
|
|
|
|
|
|
_WARN "Open of /dev/tty failed, $!\n"; |
1164
|
|
|
|
|
|
|
} |
1165
|
|
|
|
|
|
|
} |
1166
|
|
|
|
|
|
|
#} |
1167
|
|
|
|
|
|
|
|
1168
|
|
|
|
|
|
|
$sdump = qq/$script.sdump/; |
1169
|
|
|
|
|
|
|
if ($Shell::POSIX::Select::dump_data =~ /[a-z]/i) |
1170
|
|
|
|
|
|
|
{ |
1171
|
|
|
|
|
|
|
$sdump = catfile($Shell::POSIX::Select::dump_data, $sdump .'_ref'); |
1172
|
|
|
|
|
|
|
} |
1173
|
|
|
|
|
|
|
else |
1174
|
|
|
|
|
|
|
{ |
1175
|
|
|
|
|
|
|
# TODO: probably should put it in the same |
1176
|
|
|
|
|
|
|
# folder as the original program being |
1177
|
|
|
|
|
|
|
# analyzed, rather than '.': |
1178
|
|
|
|
|
|
|
$sdump = catfile('.', $sdump); |
1179
|
|
|
|
|
|
|
} |
1180
|
|
|
|
|
|
|
($cdump = $sdump) =~ s/$script\.sdump/$script.cdump/; # make code-dump name too |
1181
|
|
|
|
|
|
|
|
1182
|
|
|
|
|
|
|
# HERE next two lines squelch |
1183
|
|
|
|
|
|
|
|
1184
|
|
|
|
|
|
|
|
1185
|
|
|
|
|
|
|
# Make reference copies of dumps for distribution, or test copies, |
1186
|
|
|
|
|
|
|
# depending on ENV{reference} set or testmode=make |
1187
|
|
|
|
|
|
|
close STDERR or |
1188
|
|
|
|
|
|
|
die "$PKG-END(): Failed to close 'STDERR', $!\n"; |
1189
|
|
|
|
|
|
|
open STDERR, "> $sdump" or |
1190
|
|
|
|
|
|
|
die "$PKG-END(): Failed to open '$sdump' for writing, $!\n"; |
1191
|
|
|
|
|
|
|
|
1192
|
|
|
|
|
|
|
open STDOUT, ">&STDERR" or |
1193
|
|
|
|
|
|
|
die "$PKG-END(): Failed to dup STDOUT to STDERR, $!\n"; |
1194
|
|
|
|
|
|
|
} |
1195
|
|
|
|
|
|
|
|
1196
|
|
|
|
|
|
|
( $ON , $OFF , $BOLD , $SGR0 , $COLS ) = |
1197
|
|
|
|
|
|
|
display_control ($Shell::POSIX::Select::dump_data); |
1198
|
|
|
|
|
|
|
1; |
1199
|
|
|
|
|
|
|
} # import |
1200
|
|
|
|
|
|
|
|
1201
|
|
|
|
|
|
|
sub export { # appropriated from Switch.pm |
1202
|
8
|
|
|
8
|
0
|
19
|
my $subname = sub_name(); |
1203
|
|
|
|
|
|
|
|
1204
|
|
|
|
|
|
|
# $offset = (caller)[2]+1; |
1205
|
8
|
|
|
|
|
17
|
my $pkg = shift; |
1206
|
26
|
|
|
26
|
|
2006
|
no strict 'refs'; |
|
26
|
|
|
|
|
64
|
|
|
26
|
|
|
|
|
47652
|
|
1207
|
|
|
|
|
|
|
# All exports are scalard vars, so strip sigils and poke in package name |
1208
|
8
|
|
|
|
|
20
|
foreach ( map { s/^\$//; $_ } @_ ) { # must change $Reply to Reply, etc. |
|
11
|
|
|
|
|
30
|
|
|
11
|
|
|
|
|
34
|
|
1209
|
11
|
|
|
|
|
61
|
*{"${pkg}::$_"} = |
1210
|
11
|
|
|
|
|
21
|
\${ "Shell::POSIX::Select::$_" }; |
|
11
|
|
|
|
|
34
|
|
1211
|
|
|
|
|
|
|
# "Shell::POSIX::Select::$_"; |
1212
|
|
|
|
|
|
|
} |
1213
|
|
|
|
|
|
|
# *{"${pkg}::__"} = \&__ if grep /__/, @_; |
1214
|
8
|
|
|
|
|
20
|
1; |
1215
|
|
|
|
|
|
|
} |
1216
|
|
|
|
|
|
|
|
1217
|
|
|
|
|
|
|
sub hash_options { |
1218
|
27
|
|
|
27
|
0
|
49
|
my $ref_legal_keys = shift; |
1219
|
27
|
|
|
|
|
58
|
my %options = @_ ; |
1220
|
27
|
|
|
|
|
59
|
my $num_options=keys %options; |
1221
|
27
|
|
|
|
|
43
|
my %options2 ; |
1222
|
|
|
|
|
|
|
|
1223
|
27
|
|
|
|
|
58
|
my $subname = sub_name(); |
1224
|
|
|
|
|
|
|
|
1225
|
|
|
|
|
|
|
|
1226
|
|
|
|
|
|
|
|
1227
|
27
|
100
|
|
|
|
81
|
if ($num_options) { |
1228
|
|
|
|
|
|
|
my @legit_options = |
1229
|
1
|
|
|
|
|
12
|
grep { "@$ref_legal_keys" =~ /\b $_ \b/x } |
|
1
|
|
|
|
|
17
|
|
1230
|
|
|
|
|
|
|
sort ignoring_case keys %options; |
1231
|
|
|
|
|
|
|
|
1232
|
|
|
|
|
|
|
my @illegit_options = |
1233
|
1
|
|
|
|
|
4
|
grep { "@$ref_legal_keys" !~ /\b $_ \b/x } |
|
1
|
|
|
|
|
10
|
|
1234
|
|
|
|
|
|
|
sort ignoring_case keys %options; |
1235
|
|
|
|
|
|
|
|
1236
|
|
|
|
|
|
|
@options2{sort ignoring_case @legit_options} = |
1237
|
1
|
|
|
|
|
5
|
@options{sort ignoring_case @legit_options } ; |
1238
|
|
|
|
|
|
|
{ # scope for local change to $, |
1239
|
1
|
|
|
|
|
2
|
local $,=' '; |
|
1
|
|
|
|
|
1
|
|
1240
|
1
|
50
|
|
|
|
4
|
if ($num_options > keys %options2) { # options filtered out? |
1241
|
0
|
|
|
|
|
0
|
my $msg= "$PKG\::$subname:\n Invalid options: " ; |
1242
|
0
|
|
|
|
|
0
|
$msg .= "@illegit_options\n"; |
1243
|
0
|
|
|
|
|
0
|
_DIE; # Can't be conditional on DEBUG setting, |
1244
|
|
|
|
|
|
|
# because that comes after this sub returns! |
1245
|
|
|
|
|
|
|
} |
1246
|
|
|
|
|
|
|
} |
1247
|
|
|
|
|
|
|
|
1248
|
|
|
|
|
|
|
} |
1249
|
|
|
|
|
|
|
|
1250
|
27
|
|
|
|
|
90
|
return %options2; |
1251
|
|
|
|
|
|
|
} |
1252
|
|
|
|
|
|
|
|
1253
|
|
|
|
|
|
|
sub show_subs { |
1254
|
|
|
|
|
|
|
# show sub-string in reverse video, primarily for debugging |
1255
|
0
|
|
|
0
|
0
|
0
|
my $subname = sub_name(); |
1256
|
|
|
|
|
|
|
|
1257
|
0
|
0
|
|
|
|
0
|
@_ >= 1 or die "${PKG}\::subname: no arguments\n" ; |
1258
|
0
|
|
0
|
|
|
0
|
my $msg=shift || ''; |
1259
|
0
|
|
0
|
|
|
0
|
my $string=(shift || ''); |
1260
|
0
|
|
0
|
|
|
0
|
my $start=(shift || 0); |
1261
|
0
|
|
0
|
|
|
0
|
my $length=(shift || 9999); |
1262
|
|
|
|
|
|
|
|
1263
|
0
|
|
|
|
|
0
|
$string =~ s/[^[[:alpha:]\d\s]]/-/g; # control-chars screw up printing |
1264
|
|
|
|
|
|
|
# warn "Calling substr for parms $string/$start/$length\n"; |
1265
|
0
|
|
|
|
|
0
|
warn "$msg", $ON, substr ($string, $start, $length), $OFF, "\n"; |
1266
|
|
|
|
|
|
|
} |
1267
|
|
|
|
|
|
|
|
1268
|
|
|
|
|
|
|
sub gobble_spaces { |
1269
|
80
|
|
|
80
|
0
|
209
|
my $subname = sub_name(); |
1270
|
|
|
|
|
|
|
|
1271
|
80
|
|
|
|
|
183
|
my $pos=pos(); # remember current position |
1272
|
80
|
100
|
|
|
|
260
|
if (/\G\s+/g) { |
1273
|
74
|
50
|
|
|
|
195
|
$DEBUG_FILT > 1 and |
1274
|
|
|
|
|
|
|
warn "$subname: space gobbler matched '$&' of length ", length $&, "\n" ; |
1275
|
|
|
|
|
|
|
} |
1276
|
|
|
|
|
|
|
else { |
1277
|
6
|
50
|
|
|
|
26
|
$DEBUG_FILT > 1 and warn "$subname: space gobbler matched nothing\n"; |
1278
|
6
|
|
|
|
|
16
|
pos()=$pos; # reset to prior position |
1279
|
|
|
|
|
|
|
} |
1280
|
80
|
|
|
|
|
157
|
$pos=pos(); # identify current position |
1281
|
|
|
|
|
|
|
} |
1282
|
|
|
|
|
|
|
|
1283
|
|
|
|
|
|
|
sub display_control { |
1284
|
27
|
|
|
27
|
0
|
67
|
my $subname = sub_name(); |
1285
|
|
|
|
|
|
|
|
1286
|
27
|
|
|
|
|
68
|
my $flag=shift; |
1287
|
27
|
|
|
|
|
63
|
my ( $on , $off , $bold , $sgr0 , $cols ) ; |
1288
|
|
|
|
|
|
|
|
1289
|
|
|
|
|
|
|
# in "make" or "reference" testmodes, mustn't clutter output with coloration |
1290
|
|
|
|
|
|
|
# Disable screen manips for reference source-code dumps |
1291
|
27
|
50
|
|
|
|
73
|
unless ( $flag ) { |
1292
|
0
|
0
|
0
|
|
|
0
|
if ( is_unix() and |
|
|
|
0
|
|
|
|
|
1293
|
|
|
|
|
|
|
defined $ENV{TERM} and |
1294
|
|
|
|
|
|
|
! system 'tput -V >/dev/null 2>&1' ) { |
1295
|
|
|
|
|
|
|
# Always need column count |
1296
|
|
|
|
|
|
|
# for menu sizing |
1297
|
0
|
0
|
|
|
|
0
|
$cols=`tput cols`; defined $COLS and chomp ($COLS) ; |
|
0
|
|
|
|
|
0
|
|
1298
|
0
|
0
|
|
|
|
0
|
if ($flag ne 'make') { |
1299
|
0
|
|
|
|
|
0
|
$on=`tput smso`; |
1300
|
0
|
|
0
|
|
|
0
|
$off=`tput rmso` || `tput sgr0`; |
1301
|
0
|
|
|
|
|
0
|
$bold=`tput bold`; # for prettifying screen captures |
1302
|
0
|
|
|
|
|
0
|
$sgr0=`tput sgr0`; # for prettifying screen captures |
1303
|
|
|
|
|
|
|
} |
1304
|
|
|
|
|
|
|
} |
1305
|
|
|
|
|
|
|
else { |
1306
|
|
|
|
|
|
|
} |
1307
|
0
|
0
|
|
|
|
0
|
$DEBUG > 2 and warn "Returning $on , $off , $bold , sgr0 , $cols \n"; |
1308
|
|
|
|
|
|
|
} |
1309
|
27
|
|
50
|
|
|
404
|
return ($on || "", $off || "", $bold || "", $sgr0 || "", $cols || 80); |
|
|
|
50
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
|
|
50
|
|
|
|
|
1310
|
|
|
|
|
|
|
} |
1311
|
|
|
|
|
|
|
|
1312
|
|
|
|
|
|
|
END { # END block |
1313
|
|
|
|
|
|
|
# sdump means screen-dump, cdump means code-dump |
1314
|
26
|
50
|
|
26
|
|
187
|
if ( $Shell::POSIX::Select::dump_data ) { |
1315
|
26
|
50
|
|
|
|
181
|
if ( $ENV{Shell_POSIX_Select_reference} ) { |
1316
|
|
|
|
|
|
|
} |
1317
|
|
|
|
|
|
|
else { |
1318
|
|
|
|
|
|
|
} |
1319
|
26
|
|
|
|
|
92
|
my $pwd=curdir(); |
1320
|
|
|
|
|
|
|
# $Shell::POSIX::Select::_TTY and |
1321
|
|
|
|
|
|
|
# dump filtered source, for reference or analysis |
1322
|
26
|
50
|
|
|
|
2693
|
unless (open SOURCE, "> $cdump") { |
1323
|
0
|
0
|
0
|
|
|
0
|
$Shell::POSIX::Select::_TTY and |
1324
|
|
|
|
|
|
|
print TTY "$PKG-END(): Failed to open '$cdump' for writing, $!\n" and |
1325
|
|
|
|
|
|
|
warn "$PKG-END(): Failed to open '$cdump' for writing, $!\n" ; |
1326
|
0
|
|
|
|
|
0
|
die; |
1327
|
|
|
|
|
|
|
} |
1328
|
26
|
50
|
50
|
|
|
1094
|
defined $Shell::POSIX::Select::filter_output and |
1329
|
|
|
|
|
|
|
(print SOURCE $Shell::POSIX::Select::filter_output or |
1330
|
|
|
|
|
|
|
die "$PKG-END(): Failed to write to '$cdump', $!\n"); |
1331
|
|
|
|
|
|
|
# system "ls -li $cdump $sdump"; |
1332
|
|
|
|
|
|
|
} |
1333
|
|
|
|
|
|
|
|
1334
|
|
|
|
|
|
|
# Screen dumping now arranged in sub import() |
1335
|
|
|
|
|
|
|
# open SCREEN, "> $script.sdump" or |
1336
|
|
|
|
|
|
|
# die "$PKG-END(): Failed to open '$script.sdump' for writing, $!\n"; |
1337
|
|
|
|
|
|
|
|
1338
|
|
|
|
|
|
|
else { |
1339
|
0
|
0
|
0
|
|
|
0
|
defined $SGR0 and $SGR0 ne "" and print STDERR "$SGR0"; # ensure turned off |
1340
|
0
|
0
|
0
|
|
|
0
|
$DEBUG > 1 and $LOGGING and print LOG "\n$PKG finished\n"; |
1341
|
0
|
|
|
|
|
0
|
print STDERR "\n"; # ensure shell prompt starts on fresh line |
1342
|
|
|
|
|
|
|
} |
1343
|
26
|
|
|
|
|
0
|
exit 0; |
1344
|
|
|
|
|
|
|
} |
1345
|
|
|
|
|
|
|
|
1346
|
|
|
|
|
|
|
sub is_unix { |
1347
|
0
|
0
|
|
0
|
0
|
0
|
if ( |
1348
|
|
|
|
|
|
|
# I'm using the $^O from File::Spec, which oughta know |
1349
|
|
|
|
|
|
|
# and guessing at others; help! |
1350
|
|
|
|
|
|
|
$^O =~ /^(MacOS|MSWin32|os2|VMS|epoc|NetWare|dos|cygwin)$/ix |
1351
|
|
|
|
|
|
|
) { |
1352
|
0
|
0
|
|
|
|
0
|
$DEBUG > 2 |
1353
|
|
|
|
|
|
|
and warn "Operating System not UNIX;", $^O, "\n"; |
1354
|
|
|
|
|
|
|
} |
1355
|
|
|
|
|
|
|
else { |
1356
|
0
|
0
|
|
|
|
0
|
$DEBUG > 2 |
1357
|
|
|
|
|
|
|
and warn "Operating System reported as ", $^O, "\n"; |
1358
|
|
|
|
|
|
|
} |
1359
|
0
|
0
|
|
|
|
0
|
return defined $1 ? 0 : 1 ; |
1360
|
|
|
|
|
|
|
} |
1361
|
|
|
|
|
|
|
|
1362
|
|
|
|
|
|
|
sub disable_buffering { |
1363
|
|
|
|
|
|
|
|
1364
|
27
|
|
|
27
|
0
|
133
|
my $old_fh = select (STDERR); |
1365
|
27
|
|
|
|
|
127
|
$|=1; |
1366
|
27
|
|
|
|
|
91
|
select ($old_fh); |
1367
|
27
|
|
|
|
|
48
|
return 0; |
1368
|
|
|
|
|
|
|
} |
1369
|
|
|
|
|
|
|
|
1370
|
|
|
|
|
|
|
=pod |
1371
|
|
|
|
|
|
|
|
1372
|
|
|
|
|
|
|
=head1 NAME |
1373
|
|
|
|
|
|
|
|
1374
|
|
|
|
|
|
|
Shell::POSIX::Select - The POSIX Shell's "select" loop for Perl |
1375
|
|
|
|
|
|
|
|
1376
|
|
|
|
|
|
|
=head1 PURPOSE |
1377
|
|
|
|
|
|
|
|
1378
|
|
|
|
|
|
|
This module implements the C |
1379
|
|
|
|
|
|
|
for Perl. |
1380
|
|
|
|
|
|
|
That loop is unique in two ways: it's by far the friendliest feature of any UNIX shell, |
1381
|
|
|
|
|
|
|
and it's the I UNIX shell loop that's missing from the Perl language. Until now! |
1382
|
|
|
|
|
|
|
|
1383
|
|
|
|
|
|
|
What's so great about this loop? It automates the generation of a numbered menu |
1384
|
|
|
|
|
|
|
of choices, prompts for a choice, proofreads that choice and complains if it's invalid |
1385
|
|
|
|
|
|
|
(at least in this enhanced implementation), and executes a code-block with a variable |
1386
|
|
|
|
|
|
|
set to the chosen value. That saves a lot of coding for interactive programs -- |
1387
|
|
|
|
|
|
|
especially if the menu consists of many values! |
1388
|
|
|
|
|
|
|
|
1389
|
|
|
|
|
|
|
The benefit of bringing this loop to Perl is that it obviates the |
1390
|
|
|
|
|
|
|
need for future programmers |
1391
|
|
|
|
|
|
|
to reinvent the I wheel. |
1392
|
|
|
|
|
|
|
|
1393
|
|
|
|
|
|
|
=head1 SYNOPSIS |
1394
|
|
|
|
|
|
|
|
1395
|
|
|
|
|
|
|
=for comment |
1396
|
|
|
|
|
|
|
Resist temptation to add more spaces in line below; they cause bad wrapping for text-only document version |
1397
|
|
|
|
|
|
|
|
1398
|
|
|
|
|
|
|
=for comment |
1399
|
|
|
|
|
|
|
The damn CPAN html renderer, which doesn't respond to |
1400
|
|
|
|
|
|
|
=for html or =for HTML directives (!), can't get the following |
1401
|
|
|
|
|
|
|
right! It's showing the B<> codes! Postscript and text work fine. |
1402
|
|
|
|
|
|
|
B |
1403
|
|
|
|
|
|
|
[ [ my | local | our ] scalar_var ] |
1404
|
|
|
|
|
|
|
B<(> [LIST] B<)> |
1405
|
|
|
|
|
|
|
B<{> [CODE] B<}> |
1406
|
|
|
|
|
|
|
|
1407
|
|
|
|
|
|
|
select [ [my|local|our] scalar_var ] ( [LIST] ) { [CODE] } |
1408
|
|
|
|
|
|
|
|
1409
|
|
|
|
|
|
|
In the above, the enclosing square brackets I<(not typed)> identify optional elements, and vertical bars separate mutually-exclusive choices: |
1410
|
|
|
|
|
|
|
|
1411
|
|
|
|
|
|
|
The required elements are the keyword C |
1412
|
|
|
|
|
|
|
the I, and the I. |
1413
|
|
|
|
|
|
|
See L<"SYNTAX"> for details. |
1414
|
|
|
|
|
|
|
|
1415
|
|
|
|
|
|
|
=head1 ELEMENTARY EXAMPLES |
1416
|
|
|
|
|
|
|
|
1417
|
|
|
|
|
|
|
NOTE: All non-trivial programming examples shown in this document are |
1418
|
|
|
|
|
|
|
distributed with this module, in the B directory. |
1419
|
|
|
|
|
|
|
L<"ADDITIONAL EXAMPLES">, covering more features, are shown below. |
1420
|
|
|
|
|
|
|
|
1421
|
|
|
|
|
|
|
=head2 ship2me.plx |
1422
|
|
|
|
|
|
|
|
1423
|
|
|
|
|
|
|
use Shell::POSIX::Select; |
1424
|
|
|
|
|
|
|
|
1425
|
|
|
|
|
|
|
select $shipper ( 'UPS', 'FedEx' ) { |
1426
|
|
|
|
|
|
|
print "\nYou chose: $shipper\n"; |
1427
|
|
|
|
|
|
|
last; |
1428
|
|
|
|
|
|
|
} |
1429
|
|
|
|
|
|
|
ship ($shipper, $ARGV[0]); # prints confirmation message |
1430
|
|
|
|
|
|
|
|
1431
|
|
|
|
|
|
|
B |
1432
|
|
|
|
|
|
|
|
1433
|
|
|
|
|
|
|
ship2me.plx '42 hemp toothbrushes' # program invocation |
1434
|
|
|
|
|
|
|
|
1435
|
|
|
|
|
|
|
1) UPS 2) FedEx |
1436
|
|
|
|
|
|
|
|
1437
|
|
|
|
|
|
|
Enter number of choice: 2 |
1438
|
|
|
|
|
|
|
|
1439
|
|
|
|
|
|
|
You chose: FedEx |
1440
|
|
|
|
|
|
|
Your order has been processed. Thanks for your business! |
1441
|
|
|
|
|
|
|
|
1442
|
|
|
|
|
|
|
|
1443
|
|
|
|
|
|
|
=head2 ship2me2.plx |
1444
|
|
|
|
|
|
|
|
1445
|
|
|
|
|
|
|
This variation on the preceding example shows how to use a custom menu-heading and interactive prompt. |
1446
|
|
|
|
|
|
|
It also presents all menus in one column. |
1447
|
|
|
|
|
|
|
|
1448
|
|
|
|
|
|
|
use Shell::POSIX::Select qw($Heading $Prompt $MaxColumns); |
1449
|
|
|
|
|
|
|
|
1450
|
|
|
|
|
|
|
$Heading = 'Select a Shipper' ; |
1451
|
|
|
|
|
|
|
$Prompt = 'Enter Vendor Number: ' ; |
1452
|
|
|
|
|
|
|
$MaxColumns = 1; |
1453
|
|
|
|
|
|
|
select $shipper ( 'UPS', 'FedEx' ) { |
1454
|
|
|
|
|
|
|
print "\nYou chose: $shipper\n"; |
1455
|
|
|
|
|
|
|
last; |
1456
|
|
|
|
|
|
|
} |
1457
|
|
|
|
|
|
|
ship ($shipper, $ARGV[0]); # prints confirmation message |
1458
|
|
|
|
|
|
|
|
1459
|
|
|
|
|
|
|
B |
1460
|
|
|
|
|
|
|
|
1461
|
|
|
|
|
|
|
ship2me2.plx '42 hemp toothbrushes' |
1462
|
|
|
|
|
|
|
|
1463
|
|
|
|
|
|
|
Select a Shipper |
1464
|
|
|
|
|
|
|
|
1465
|
|
|
|
|
|
|
1) UPS 2) FedEx |
1466
|
|
|
|
|
|
|
|
1467
|
|
|
|
|
|
|
Enter Vendor Number: 2 |
1468
|
|
|
|
|
|
|
|
1469
|
|
|
|
|
|
|
You chose: FedEx |
1470
|
|
|
|
|
|
|
Your order has been processed. Thanks for your business! |
1471
|
|
|
|
|
|
|
|
1472
|
|
|
|
|
|
|
|
1473
|
|
|
|
|
|
|
=head1 SYNTAX |
1474
|
|
|
|
|
|
|
|
1475
|
|
|
|
|
|
|
=head2 Loop Structure |
1476
|
|
|
|
|
|
|
|
1477
|
|
|
|
|
|
|
Supported invocation formats include the following: |
1478
|
|
|
|
|
|
|
|
1479
|
|
|
|
|
|
|
use Shell::POSIX::Select ; |
1480
|
|
|
|
|
|
|
|
1481
|
|
|
|
|
|
|
select () { } # Form 0 |
1482
|
|
|
|
|
|
|
select () { CODE } # Form 1 |
1483
|
|
|
|
|
|
|
select (LIST) { CODE } # Form 2 |
1484
|
|
|
|
|
|
|
select $var (LIST) { CODE } # Form 3 |
1485
|
|
|
|
|
|
|
select my $var (LIST) { CODE } # Form 4 |
1486
|
|
|
|
|
|
|
select our $var (LIST) { CODE } # Form 5 |
1487
|
|
|
|
|
|
|
select local $var (LIST) { CODE } # Form 6 |
1488
|
|
|
|
|
|
|
|
1489
|
|
|
|
|
|
|
|
1490
|
|
|
|
|
|
|
If the loop variable is omitted (as in I I<0>, I<1> and I<2> above), |
1491
|
|
|
|
|
|
|
it defaults to C<$_>, Cized to the loop's scope. |
1492
|
|
|
|
|
|
|
If the LIST is omitted (as in I I<0> and I<1>), |
1493
|
|
|
|
|
|
|
C<@ARGV> is used by default, unless the loop occurs within a subroutine, in which case |
1494
|
|
|
|
|
|
|
C<@_> is used instead. |
1495
|
|
|
|
|
|
|
If CODE is omitted (as in I |
1496
|
|
|
|
|
|
|
it defaults to a statement that B the loop variable. |
1497
|
|
|
|
|
|
|
|
1498
|
|
|
|
|
|
|
The cases shown above are merely examples; all reasonable permutations are permitted, including: |
1499
|
|
|
|
|
|
|
|
1500
|
|
|
|
|
|
|
select $var ( ) { CODE } |
1501
|
|
|
|
|
|
|
select local $var (LIST) { } |
1502
|
|
|
|
|
|
|
|
1503
|
|
|
|
|
|
|
The only form that's I allowed is one that specifies the loop-variable's declarator without naming the loop variable, as in: |
1504
|
|
|
|
|
|
|
|
1505
|
|
|
|
|
|
|
select our () { } # WRONG! Must name variable with declarator! |
1506
|
|
|
|
|
|
|
|
1507
|
|
|
|
|
|
|
=head2 The Loop variable |
1508
|
|
|
|
|
|
|
|
1509
|
|
|
|
|
|
|
See L<"SCOPING ISSUES"> for full details about the implications |
1510
|
|
|
|
|
|
|
of different types of declarations for the loop variable. |
1511
|
|
|
|
|
|
|
|
1512
|
|
|
|
|
|
|
=head2 The $Reply Variable |
1513
|
|
|
|
|
|
|
|
1514
|
|
|
|
|
|
|
When the interactive user responds to the C |
1515
|
|
|
|
|
|
|
with a valid input (i.e., a number in the correct range), |
1516
|
|
|
|
|
|
|
the variable C<$Reply> is set within the loop to that number. |
1517
|
|
|
|
|
|
|
Of course, the actual item selected is usually of great interest than |
1518
|
|
|
|
|
|
|
its number in the menu, but there are cases in which access to this |
1519
|
|
|
|
|
|
|
number is useful (see L<"menu_ls.plx"> for an example). |
1520
|
|
|
|
|
|
|
|
1521
|
|
|
|
|
|
|
=head1 OVERVIEW |
1522
|
|
|
|
|
|
|
|
1523
|
|
|
|
|
|
|
This loop is syntactically similar to Perl's |
1524
|
|
|
|
|
|
|
C loop, and functionally related, so we'll describe it in those terms. |
1525
|
|
|
|
|
|
|
|
1526
|
|
|
|
|
|
|
foreach $var ( LIST ) { CODE } |
1527
|
|
|
|
|
|
|
|
1528
|
|
|
|
|
|
|
The job of C is to run one iteration of CODE for each LIST-item, |
1529
|
|
|
|
|
|
|
with the current item's value placed in Cized C<$var> |
1530
|
|
|
|
|
|
|
(or if the variable is missing, Cized C<$_>). |
1531
|
|
|
|
|
|
|
|
1532
|
|
|
|
|
|
|
select $var ( LIST ) { CODE } |
1533
|
|
|
|
|
|
|
|
1534
|
|
|
|
|
|
|
In contrast, the C |
1535
|
|
|
|
|
|
|
LIST-items on the screen, prompts for (numerical) input, and then runs an iteration |
1536
|
|
|
|
|
|
|
with C<$var> being set that number's LIST-item. |
1537
|
|
|
|
|
|
|
|
1538
|
|
|
|
|
|
|
In other words, C |
1539
|
|
|
|
|
|
|
C loop. |
1540
|
|
|
|
|
|
|
And that's cool! What's I so cool is that |
1541
|
|
|
|
|
|
|
C |
1542
|
|
|
|
|
|
|
the Perl language. I |
1543
|
|
|
|
|
|
|
|
1544
|
|
|
|
|
|
|
This module implements the C |
1545
|
|
|
|
|
|
|
("POSIX") shells for Perl. |
1546
|
|
|
|
|
|
|
It accomplishes this through Filter::Simple's I |
1547
|
|
|
|
|
|
|
allowing the programmer to blithely proceed as if this control feature existed natively in Perl. |
1548
|
|
|
|
|
|
|
|
1549
|
|
|
|
|
|
|
The Bash and Korn shells differ slightly in their handling |
1550
|
|
|
|
|
|
|
of C |
1551
|
|
|
|
|
|
|
This implementation currently follows the Korn shell version most closely |
1552
|
|
|
|
|
|
|
(but see L<"TODO-LIST"> for notes on planned enhancements). |
1553
|
|
|
|
|
|
|
|
1554
|
|
|
|
|
|
|
=head1 ENHANCEMENTS |
1555
|
|
|
|
|
|
|
|
1556
|
|
|
|
|
|
|
Although the shell doesn't allow the loop variable to be omitted, |
1557
|
|
|
|
|
|
|
for compliance with Perlish expectations, |
1558
|
|
|
|
|
|
|
the C |
1559
|
|
|
|
|
|
|
(as does the native C loop). See L<"SYNTAX"> for details. |
1560
|
|
|
|
|
|
|
|
1561
|
|
|
|
|
|
|
The interface and behavior of the Shell versions has been retained |
1562
|
|
|
|
|
|
|
where deemed desirable, |
1563
|
|
|
|
|
|
|
and sensibly modified along Perlish lines elsewhere. |
1564
|
|
|
|
|
|
|
Accordingly, the (primary) default LIST is B<@ARGV> (paralleling the Shell's B<"$@">), |
1565
|
|
|
|
|
|
|
menu prompts can be customized by having the script import and set B<$Prompt> |
1566
|
|
|
|
|
|
|
(paralleling the Shell's B<$PS3>), |
1567
|
|
|
|
|
|
|
and the user's response to the prompt appears in the |
1568
|
|
|
|
|
|
|
variable B<$Reply> (paralleling the Shell's B<$REPLY>), |
1569
|
|
|
|
|
|
|
Cized to the loop. |
1570
|
|
|
|
|
|
|
|
1571
|
|
|
|
|
|
|
A deficiency of the shell implementation is the |
1572
|
|
|
|
|
|
|
inability of the user to provide a I for each C |
1573
|
|
|
|
|
|
|
Sure, the |
1574
|
|
|
|
|
|
|
shell programmer can B a heading before the loop is entered and the |
1575
|
|
|
|
|
|
|
menu is displayed, but that approach doesn't help when an I is |
1576
|
|
|
|
|
|
|
reentered on departure from an I, |
1577
|
|
|
|
|
|
|
because the B preceding the I won't be re-executed. |
1578
|
|
|
|
|
|
|
|
1579
|
|
|
|
|
|
|
A similar deficiency surrounds the handling of a custom prompt string, and |
1580
|
|
|
|
|
|
|
the need to automatically display it on moving from an inner loop |
1581
|
|
|
|
|
|
|
to an outer one. |
1582
|
|
|
|
|
|
|
|
1583
|
|
|
|
|
|
|
To address these deficiencies, this implementation provides the option of having a heading and prompt bound |
1584
|
|
|
|
|
|
|
to each C |
1585
|
|
|
|
|
|
|
|
1586
|
|
|
|
|
|
|
Headings and prompts are displayed in reverse video on the terminal, |
1587
|
|
|
|
|
|
|
if possible, to make them more visually distinct. |
1588
|
|
|
|
|
|
|
|
1589
|
|
|
|
|
|
|
Some shell versions simply ignore bad input, |
1590
|
|
|
|
|
|
|
such as the entry of a number outside the menu's valid range, |
1591
|
|
|
|
|
|
|
or alphabetic input. I can't imagine any argument |
1592
|
|
|
|
|
|
|
in favor of this behavior being desirable when input is coming from a terminal, |
1593
|
|
|
|
|
|
|
so this implementation gives clear warning messages for such cases by default |
1594
|
|
|
|
|
|
|
(see L<"Warnings"> for details). |
1595
|
|
|
|
|
|
|
|
1596
|
|
|
|
|
|
|
After a menu's initial prompt is issued, some shell versions don't |
1597
|
|
|
|
|
|
|
show it again unless the user enters an empty line. |
1598
|
|
|
|
|
|
|
This is desirable in cases where the menu is sufficiently large as to |
1599
|
|
|
|
|
|
|
cause preceding output to scroll off the screen, and undesirable otherwise. |
1600
|
|
|
|
|
|
|
Accordingly, an option is provided to enable or disable automatic prompting |
1601
|
|
|
|
|
|
|
(see L<"Prompts">). |
1602
|
|
|
|
|
|
|
|
1603
|
|
|
|
|
|
|
This implementation always issues a fresh prompt |
1604
|
|
|
|
|
|
|
when a terminal user submits EOF as input to a nested C |
1605
|
|
|
|
|
|
|
In such cases, experience shows it's critical to reissue the |
1606
|
|
|
|
|
|
|
menu of the outer loop before accepting any more input. |
1607
|
|
|
|
|
|
|
|
1608
|
|
|
|
|
|
|
=head1 SCOPING ISSUES |
1609
|
|
|
|
|
|
|
|
1610
|
|
|
|
|
|
|
If the loop variable is named and provided with a I (C, C, or C), |
1611
|
|
|
|
|
|
|
the variable is scoped within the loop using that type of declaration. |
1612
|
|
|
|
|
|
|
But if the variable is named but lacks a declarator, |
1613
|
|
|
|
|
|
|
no declaration is applied to the variable. |
1614
|
|
|
|
|
|
|
|
1615
|
|
|
|
|
|
|
This allows, for example, |
1616
|
|
|
|
|
|
|
a variable declared as private I to be accessible |
1617
|
|
|
|
|
|
|
from within the loop, and beyond it, |
1618
|
|
|
|
|
|
|
and one declared as private I to be confined to it: |
1619
|
|
|
|
|
|
|
|
1620
|
|
|
|
|
|
|
select my $loopvar ( ) { } |
1621
|
|
|
|
|
|
|
print "$loopvar DOES NOT RETAIN last value from loop here\n"; |
1622
|
|
|
|
|
|
|
------------------------------------------------------------- |
1623
|
|
|
|
|
|
|
my $loopvar; |
1624
|
|
|
|
|
|
|
select $loopvar ( ) { } |
1625
|
|
|
|
|
|
|
print "$loopvar RETAINS last value from loop here\n"; |
1626
|
|
|
|
|
|
|
|
1627
|
|
|
|
|
|
|
With this design, |
1628
|
|
|
|
|
|
|
C |
1629
|
|
|
|
|
|
|
native C loop, which nowadays employs automatic |
1630
|
|
|
|
|
|
|
localization. |
1631
|
|
|
|
|
|
|
|
1632
|
|
|
|
|
|
|
foreach $othervar ( ) { } # variable localized automatically |
1633
|
|
|
|
|
|
|
print "$othervar DOES NOT RETAIN last value from loop here\n"; |
1634
|
|
|
|
|
|
|
|
1635
|
|
|
|
|
|
|
select $othervar ( ) { } # variable in scope, or global |
1636
|
|
|
|
|
|
|
print "$othervar RETAINS last value from loop here\n"; |
1637
|
|
|
|
|
|
|
|
1638
|
|
|
|
|
|
|
This difference in the treatment of variables is intentional, and appropriate. |
1639
|
|
|
|
|
|
|
That's because the whole point of C |
1640
|
|
|
|
|
|
|
is to let the user choose a value from a list, so it's often |
1641
|
|
|
|
|
|
|
critically important to be able to see, even outside the loop, |
1642
|
|
|
|
|
|
|
the value assigned to the loop variable. |
1643
|
|
|
|
|
|
|
|
1644
|
|
|
|
|
|
|
In contrast, it's usually considered undesirable and unnecessary |
1645
|
|
|
|
|
|
|
for the value of the |
1646
|
|
|
|
|
|
|
C loop's variable to be visible outside the loop, because |
1647
|
|
|
|
|
|
|
in most cases it will simply be that of the last element in the list. |
1648
|
|
|
|
|
|
|
|
1649
|
|
|
|
|
|
|
Of course, in situations where the |
1650
|
|
|
|
|
|
|
C-like behavior of implicit Cization is desired, |
1651
|
|
|
|
|
|
|
the programmer has the option of declaring the C |
1652
|
|
|
|
|
|
|
variable as C. |
1653
|
|
|
|
|
|
|
|
1654
|
|
|
|
|
|
|
Another deficiency of the Shell versions is that it's difficult for the |
1655
|
|
|
|
|
|
|
programmer to differentiate between a |
1656
|
|
|
|
|
|
|
C |
1657
|
|
|
|
|
|
|
versus the loop detecting EOF on input. |
1658
|
|
|
|
|
|
|
To correct this situation, |
1659
|
|
|
|
|
|
|
the variable C<$Eof> can be imported and checked for a I value |
1660
|
|
|
|
|
|
|
upon exit from a C |
1661
|
|
|
|
|
|
|
|
1662
|
|
|
|
|
|
|
=head1 IMPORTS AND OPTIONS |
1663
|
|
|
|
|
|
|
|
1664
|
|
|
|
|
|
|
=head2 Syntax |
1665
|
|
|
|
|
|
|
|
1666
|
|
|
|
|
|
|
use Shell::POSIX::Select ( |
1667
|
|
|
|
|
|
|
'$Prompt', # to customize per-menu prompt |
1668
|
|
|
|
|
|
|
'$Heading', # to customize per-menu heading |
1669
|
|
|
|
|
|
|
'$MaxColumns', # to limit visual number of columns of choices |
1670
|
|
|
|
|
|
|
'$Eof', # T/F for Eof detection |
1671
|
|
|
|
|
|
|
# Variables must come first, then key/value options |
1672
|
|
|
|
|
|
|
prompt => 'Enter number of choice:', # or 'whatever:' |
1673
|
|
|
|
|
|
|
style => 'Bash', # or 'Korn' |
1674
|
|
|
|
|
|
|
warnings => 1, # or 0 |
1675
|
|
|
|
|
|
|
debug => 0, # or 1-5 |
1676
|
|
|
|
|
|
|
logging => 0, # or 1 |
1677
|
|
|
|
|
|
|
testmode => , # or 'make', or 'foreach' |
1678
|
|
|
|
|
|
|
); |
1679
|
|
|
|
|
|
|
|
1680
|
|
|
|
|
|
|
I The values shown for options are the defaults, except for C, which doesn't have one. |
1681
|
|
|
|
|
|
|
|
1682
|
|
|
|
|
|
|
=head2 Prompts |
1683
|
|
|
|
|
|
|
|
1684
|
|
|
|
|
|
|
There are two ways to customize the prompt used to solicit choices from |
1685
|
|
|
|
|
|
|
C |
1686
|
|
|
|
|
|
|
all loops, or the C<$Prompt> variable, which can be set independently for |
1687
|
|
|
|
|
|
|
each loop. |
1688
|
|
|
|
|
|
|
|
1689
|
|
|
|
|
|
|
=head3 The prompt option |
1690
|
|
|
|
|
|
|
|
1691
|
|
|
|
|
|
|
The C option is intended for use in |
1692
|
|
|
|
|
|
|
programs that either contain a single C |
1693
|
|
|
|
|
|
|
content to use the same prompt for every loop. |
1694
|
|
|
|
|
|
|
It allows a custom interactive prompt to be set in the B |
1695
|
|
|
|
|
|
|
|
1696
|
|
|
|
|
|
|
The prompt string should not end in a whitespace character, because |
1697
|
|
|
|
|
|
|
that doesn't look nice when the prompt is highlighted for display |
1698
|
|
|
|
|
|
|
(usually in I). |
1699
|
|
|
|
|
|
|
To offset the cursor from the prompt's end, |
1700
|
|
|
|
|
|
|
I is inserted automatically |
1701
|
|
|
|
|
|
|
after display highlighting has been turned off. |
1702
|
|
|
|
|
|
|
|
1703
|
|
|
|
|
|
|
If the environment variable C<$ENV{Shell_POSIX_Select_prompt}> |
1704
|
|
|
|
|
|
|
is present, |
1705
|
|
|
|
|
|
|
its value overrides the one in the B |
1706
|
|
|
|
|
|
|
|
1707
|
|
|
|
|
|
|
The default prompt is "Enter number of choice:". |
1708
|
|
|
|
|
|
|
To get the same prompt as provided by the Korn or Bash shell, |
1709
|
|
|
|
|
|
|
use C<< prompt =>> Korn >> or C<< prompt => Bash >>. |
1710
|
|
|
|
|
|
|
|
1711
|
|
|
|
|
|
|
=head3 The $Prompt variable |
1712
|
|
|
|
|
|
|
|
1713
|
|
|
|
|
|
|
The programmer may also modify the prompt during execution, |
1714
|
|
|
|
|
|
|
which may be desirable with nested loops that require different user instructions. |
1715
|
|
|
|
|
|
|
This is accomplished by |
1716
|
|
|
|
|
|
|
importing the $Prompt variable, and setting it to the desired prompt string |
1717
|
|
|
|
|
|
|
before entering the loop. Note that imported variables have to be listed |
1718
|
|
|
|
|
|
|
as the initial arguments to the C |
1719
|
|
|
|
|
|
|
See L<"order.plx"> for an example. |
1720
|
|
|
|
|
|
|
|
1721
|
|
|
|
|
|
|
NOTE: If the program's input channel is not connected to a terminal, |
1722
|
|
|
|
|
|
|
prompting is automatically disabled |
1723
|
|
|
|
|
|
|
(since there's no point in soliciting input from a I!). |
1724
|
|
|
|
|
|
|
|
1725
|
|
|
|
|
|
|
=head2 $Heading |
1726
|
|
|
|
|
|
|
|
1727
|
|
|
|
|
|
|
The programmer has the option of binding a heading to each loop's menu, |
1728
|
|
|
|
|
|
|
by importing C<$Heading> and setting it just before entering the associated loop. |
1729
|
|
|
|
|
|
|
See L<"order.plx"> for an example. |
1730
|
|
|
|
|
|
|
|
1731
|
|
|
|
|
|
|
=head2 $Eof |
1732
|
|
|
|
|
|
|
|
1733
|
|
|
|
|
|
|
A common concern with the Shell's C |
1734
|
|
|
|
|
|
|
cases where a loop ends due to EOF detection, versus the execution of C |
1735
|
|
|
|
|
|
|
(like Perl's C). |
1736
|
|
|
|
|
|
|
Although the Shell programmer can check the C<$REPLY> variable to make |
1737
|
|
|
|
|
|
|
this distinction, this implementation localizes its version of that variable |
1738
|
|
|
|
|
|
|
(C<$Reply>) to the loop, |
1739
|
|
|
|
|
|
|
obviating that possibility. |
1740
|
|
|
|
|
|
|
|
1741
|
|
|
|
|
|
|
Therefore, to make EOF detection as convenient and easy as possible, |
1742
|
|
|
|
|
|
|
the programmer may import C<$Eof> and check it for a |
1743
|
|
|
|
|
|
|
I value after a C |
1744
|
|
|
|
|
|
|
See L<"lc_filename.plx"> for a programming example. |
1745
|
|
|
|
|
|
|
|
1746
|
|
|
|
|
|
|
=head2 Number of Columns |
1747
|
|
|
|
|
|
|
|
1748
|
|
|
|
|
|
|
By default, the visual length of each option is examined, |
1749
|
|
|
|
|
|
|
and the list is spread across as many columns as will reasonably fit in the terminal. |
1750
|
|
|
|
|
|
|
You can override this behavior by importing and setting C<$MaxColumns> |
1751
|
|
|
|
|
|
|
to the maximum number of columns you wish to display. |
1752
|
|
|
|
|
|
|
See Scripts/max_columns_1.plx in the distribution as an example. |
1753
|
|
|
|
|
|
|
|
1754
|
|
|
|
|
|
|
=head2 Styles |
1755
|
|
|
|
|
|
|
|
1756
|
|
|
|
|
|
|
The C |