COVERAGE SUMMARY
FILE SUMMARY
NameExecutedRoutines%ExecutedLines%Unexecuted
/home/matt/eu/rds/include/std/cmdline.e6875.00%31744171.88%124
ROUTINE SUMMARY
RoutineExecutedLinesUnexecuted
cmd_parse()11716172.67%44
local_help()599860.20%39
standardize_opts()699275.00%23
find_opt()394979.59%10
local_abort()050.00%5
show_help()030.00%3
build_commandline()22100.00%0
parse_commandline()22100.00%0
LINE COVERAGE DETAIL
#Executed
1
--****
2
-- == Command Line Handling
3
--
4
-- <>
5
namespace cmdline
6
7
include std/text.e
8
include std/sequence.e as seq
9
include std/map.e
10
include std/error.e
11
include std/os.e
12
include std/io.e
13
include std/console.e
14
include std/types.e
15
16
--****
17
-- === Constants
18
19
public constant
20
--** This option switch does not have a parameter. See [[:cmd_parse]]
213
NO_PARAMETER = 'n',
22
23
--** This option switch does have a parameter. See [[:cmd_parse]]
243
HAS_PARAMETER = 'p',
25
26
--** This option switch is not case sensitive. See [[:cmd_parse]]
273
NO_CASE = 'i',
28
29
--** This option switch is case sensitive. See [[:cmd_parse]]
303
HAS_CASE = 'c',
31
32
--** This option switch must be supplied on command line. See [[:cmd_parse]]
333
MANDATORY = 'm',
34
35
--** This option switch does not have to be on command line. See [[:cmd_parse]]
363
OPTIONAL = 'o',
37
38
--** This option switch must only occur once on the command line. See [[:cmd_parse]]
393
ONCE = '1',
40
41
--** This option switch may occur multiple times on a command line. See [[:cmd_parse]]
423
MULTIPLE = '*',
43
44
--** This option switch triggers the 'help' display. See [[:cmd_parse]]
453
HELP = 'h'
46
47
public constant
483
NO_HELP = -2
49
50
public enum
51
--**
52
-- Additional help routine id. See [[:cmd_parse]]
533
HELP_RID,
54
55
--**
56
-- Validate all parameters (default). See [[:cmd_parse]]
573
VALIDATE_ALL,
58
59
--**
60
-- Do not cause an error for an invalid parameter. See [[:cmd_parse]]
613
NO_VALIDATION,
62
63
--**
64
-- Do not cause an error for an invalid parameter after the
65
-- first extra item has been found. This can be helpful for
66
-- processes such as the Interpreter itself that must deal
67
-- with command line parameters that it is not meant to
68
-- handle. At expansions after the first extra are also disabled.
69
--
70
-- For instance:
71
-- ##eui -D TEST greet.ex -name John -greeting Bye##
72
-- -D TEST is meant for ##eui##, but -name and -greeting options
73
-- are meant for ##greet.ex##. See [[:cmd_parse]]
74
--
75
--
76
-- ##eui @euopts.txt greet.ex @hotmail.com##
77
-- here 'hotmail.com' is not expanded into the command line but
78
-- 'euopts.txt' is.
793
NO_VALIDATION_AFTER_FIRST_EXTRA,
80
81
--**
82
-- Only display the option list in show_help. Do not display other
83
-- information such as program name, options, etc... See [[:cmd_parse]]
843
SHOW_ONLY_OPTIONS,
85
86
--**
87
-- Expand arguments that begin with '@' into the command line. (default)
88
-- For example, @filename will expand the contents of file named 'filename'
89
-- as if the file's contents were passed in on the command line. Arguments
90
-- that come after the first extra will not be expanded when
91
-- NO_VALIDATION_AFTER_FIRST_EXTRA is specified.
923
AT_EXPANSION,
93
94
--**
95
-- Do not expand arguments that begin with '@' into the command line.
96
-- Normally @filename will expand the file names contents as if the
97
-- file's contents were passed in on the command line. This option
98
-- supresses this behavior.
993
NO_AT_EXPANSION,
100
101
--**
102
-- Supply a message to display and pause just prior to abort() being called.
1033
PAUSE_MSG,
104
105
$
106
107
--
108
109
public enum
110
--**
111
-- An index into the ##opts## list. See [[:cmd_parse]]
1123
OPT_IDX,
113
114
--**
115
-- The number of times that the routine has been called
116
-- by cmd_parse for this option. See [[:cmd_parse]]
1173
OPT_CNT,
118
119
--**
120
-- The option's value as found on the command line. See [[:cmd_parse]]
1213
OPT_VAL,
122
123
--**
124
-- The value ##1## if the command line indicates that this option is to remove
125
-- any earlier occurrences of it. See [[:cmd_parse]]
1263
OPT_REV
127
128
129
-- Record fields in 'opts' argument.
130
enum
1313
SHORTNAME = 1,
1323
LONGNAME = 2,
1333
DESCRIPTION = 3,
1343
OPTIONS = 4,
1353
CALLBACK = 5,
1363
MAPNAME = 6
137
1383
sequence pause_msg = ""
139
1400
1410
if length(pause_msg) != 0 then
1420
any_key(pause_msg, 1)
143
end if
144
1450
abort(lvl)
1460
end procedure
147
148
-- Local routine to validate and reformat option records if they are not in the standard format.
14948
15048
integer lExtras = 0 -- Ensure that there is zero or one 'extras' record only.
151
15248
for i = 1 to length(opts) do
153104
sequence opt = opts[i]
154104
integer updated = 0
155
156104
if length(opt) < MAPNAME then
15776
opt &= repeat(-1, MAPNAME - length(opt))
15876
updated = 1
159
end if
160
161104
if sequence(opt[SHORTNAME]) and length(opt[SHORTNAME]) = 0 then
1620
opt[SHORTNAME] = 0
1630
updated = 1
164
end if
165
166104
if sequence(opt[LONGNAME]) and length(opt[LONGNAME]) = 0 then
1670
opt[LONGNAME] = 0
1680
updated = 1
169
end if
170
171104
if atom(opt[LONGNAME]) and atom(opt[SHORTNAME]) then
1722
if lExtras != 0 then
1730
crash("cmd_opts: There must be less than two 'extras' option records.\n")
174
else
1752
lExtras = i
1762
if atom(opt[MAPNAME]) then
1771
opt[MAPNAME] = "extras"
1781
updated = 1
179
end if
180
end if
181
end if
182
183104
if atom(opt[DESCRIPTION]) then
1840
opt[DESCRIPTION] = ""
1850
updated = 1
186
end if
187
188
189104
if atom(opt[OPTIONS]) then
1901
if equal(opt[OPTIONS], HAS_PARAMETER) then
1911
opt[OPTIONS] = {HAS_PARAMETER,"x"}
192
else
1930
opt[OPTIONS] = {}
194
end if
1951
updated = 1
196
else
197103
for j = 1 to length(opt[OPTIONS]) do
19872
if find_from(opt[OPTIONS][j], opt[OPTIONS], j + 1) != 0 then
1990
crash("cmd_opts: Duplicate processing options are not allowed in an option record.\n")
200
end if
20172
end for
202
203103
if find(HAS_PARAMETER, opt[OPTIONS]) then
2047
if find(NO_PARAMETER, opt[OPTIONS]) then
2050
crash("cmd_opts: Cannot have both HAS_PARAMETER and NO_PARAMETER in an option record.\n")
206
end if
207
end if
208
209103
if find(HAS_CASE, opt[OPTIONS]) then
2100
if find(NO_CASE, opt[OPTIONS]) then
2110
crash("cmd_opts: Cannot have both HAS_CASE and NO_CASE in an option record.\n")
212
end if
213
end if
214
215103
if find(MANDATORY, opt[OPTIONS]) then
2164
if find(OPTIONAL, opt[OPTIONS]) then
2170
crash("cmd_opts: Cannot have both MANDATORY and OPTIONAL in an option record.\n")
218
end if
219
end if
220
221103
if find(ONCE, opt[OPTIONS]) then
2222
if find(MULTIPLE, opt[OPTIONS]) then
2230
crash("cmd_opts: Cannot have both ONCE and MULTIPLE in an option record.\n")
224
end if
225
end if
226
227
end if
228
229104
if sequence(opt[CALLBACK]) then
2300
opt[CALLBACK] = -1
2310
updated = 1
232104
elsif not integer(opt[CALLBACK]) then
2330
opt[CALLBACK] = -1
2340
updated = 1
235104
elsif opt[CALLBACK] < 0 then
236102
opt[CALLBACK] = -1
237102
updated = 1
238
end if
239
240104
if sequence(opt[MAPNAME]) and length(opt[MAPNAME]) = 0 then
2410
opt[MAPNAME] = 0
2420
updated = 1
243
end if
244
245104
if atom(opt[MAPNAME]) then
24675
if sequence(opt[LONGNAME]) then
24749
opt[MAPNAME] = opt[LONGNAME]
24826
elsif sequence(opt[SHORTNAME]) then
24926
opt[MAPNAME] = opt[SHORTNAME]
250
else
2510
opt[MAPNAME] = "extras"
252
end if
25375
updated = 1
254
end if
255
256104
if updated then
257103
opts[i] = opt
258
end if
259104
end for
260
261
-- Check for duplicate option records.
26248
for i = 1 to length(opts) do
263104
sequence opt
264104
opt = opts[i]
265104
if sequence(opt[SHORTNAME]) then
26698
for j = i + 1 to length(opts) do
26796
if equal(opt[SHORTNAME], opts[j][SHORTNAME]) then
2680
crash("cmd_opts: Duplicate Short Names (%s) are not allowed in an option record.\n",
269
{ opt[SHORTNAME]})
270
end if
27196
end for
272
end if
273
274104
if sequence(opt[LONGNAME]) then
27574
for j = i + 1 to length(opts) do
276100
if equal(opt[LONGNAME], opts[j][LONGNAME]) then
2770
crash("cmd_opts: Duplicate Long Names (%s) are not allowed in an option record.\n",
278
{opt[LONGNAME]})
279
end if
280100
end for
281
end if
282104
end for
283
284
-- Insert the default 'help' option if one is not already there.
28548
integer has_help = 0
28648
for i = 1 to length(opts) do
28780
if find(HELP, opts[i][OPTIONS]) then
28824
has_help = 1
28924
exit
290
end if
29156
end for
292
29348
if not has_help and add_help_options then
29424
opts = append(opts, {"h", "help", "Display the command options", {HELP}, -1})
29524
opts = append(opts, {"?", 0, "Display the command options", {HELP}, -1})
296
297
-- We have to standardize the above additions
29824
opts = standardize_opts(opts, 0)
299
end if
300
30148
return opts
302
end function
303
30416
305
integer pad_size
306
integer this_size
307
sequence cmd
308
sequence param_name
309
integer has_param
310
integer is_mandatory
31116
integer extras_mandatory = 0
31216
integer extras_opt = 0
313
31416
if std = 0 then
3150
opts = standardize_opts(opts, not equal(add_help_rid, NO_HELP))
316
end if
317
318
-- Calculate the size of the padding required to keep option text aligned.
31916
pad_size = 0
32016
for i = 1 to length(opts) do
32147
this_size = 0
32247
param_name = ""
323
32447
if atom(opts[i][SHORTNAME]) and atom(opts[i][LONGNAME]) then
3250
extras_opt = i
3260
if find(MANDATORY, opts[i][OPTIONS]) then
3270
extras_mandatory = 1
328
end if
329
-- Ignore 'extras' record
3300
continue
331
end if
332
33347
if sequence(opts[i][SHORTNAME]) then
33447
this_size += length(opts[i][SHORTNAME]) + 1 -- Allow for "-"
33547
if find(MANDATORY, opts[i][OPTIONS]) = 0 then
33647
this_size += 2 -- Allow for '[' ']'
337
end if
338
end if
339
34047
if sequence(opts[i][LONGNAME]) then
34131
this_size += length(opts[i][LONGNAME]) + 2 -- Allow for "--"
34231
if find(MANDATORY, opts[i][OPTIONS]) = 0 then
34331
this_size += 2 -- Allow for '[' ']'
344
end if
345
end if
346
34747
if sequence(opts[i][SHORTNAME]) and sequence(opts[i][LONGNAME]) then
34831
this_size += 2 -- Allow for ", " between short and long names
349
end if
350
35147
has_param = find(HAS_PARAMETER, opts[i][OPTIONS])
35247
if has_param != 0 then
3530
this_size += 1 -- Allow for " "
3540
if has_param < length(opts[i][OPTIONS]) then
355
--has_param += 1
3560
if sequence(opts[i][OPTIONS][has_param]) then
3570
param_name = opts[i][OPTIONS][has_param]
358
else
3590
param_name = "x"
360
end if
361
else
3620
param_name = "x"
363
end if
3640
this_size += 2 + length(param_name)
365
end if
366
36747
if pad_size < this_size then
36816
pad_size = this_size
369
end if
37047
end for
37116
pad_size += 3 -- Allow for minimum gap between cmd and its description
372
37316
if not equal(add_help_rid, NO_HELP) then
37416
printf(1, "%s options:\n", {cmds[2]})
375
end if
376
37716
for i = 1 to length(opts) do
37847
if atom(opts[i][SHORTNAME]) and atom(opts[i][LONGNAME]) then
379
-- Ignore 'extras' record
3800
continue
381
end if
382
38347
has_param = find(HAS_PARAMETER, opts[i][OPTIONS])
38447
if has_param != 0 then
3850
if has_param < length(opts[i][OPTIONS]) then
3860
has_param += 1
3870
if sequence(opts[i][OPTIONS][has_param]) then
3880
param_name = opts[i][OPTIONS][has_param]
389
else
3900
param_name = "x"
391
end if
392
else
3930
param_name = "x"
394
end if
395
end if
39647
is_mandatory = (find(MANDATORY, opts[i][OPTIONS]) != 0)
39747
cmd = ""
398
39947
if sequence(opts[i][SHORTNAME]) then
40047
if not is_mandatory then
40147
cmd &= '['
402
end if
40347
cmd &= '-' & opts[i][SHORTNAME]
40447
if has_param != 0 then
4050
cmd &= ' ' & param_name
406
end if
40747
if not is_mandatory then
40847
cmd &= ']'
409
end if
410
end if
411
41247
if sequence(opts[i][LONGNAME]) then
41331
if length(cmd) > 0 then cmd &= ", " end if
41431
if not is_mandatory then
41531
cmd &= '['
416
end if
41731
cmd &= "--" & opts[i][LONGNAME]
41831
if has_param != 0 then
4190
cmd &= '=' & param_name
420
end if
42131
if not is_mandatory then
42231
cmd &= ']'
423
end if
424
end if
42547
puts(1, " " & pad_tail(cmd, pad_size))
42647
puts(1, opts[i][DESCRIPTION] & '\n')
42747
end for
428
42916
if extras_mandatory != 0 then
4300
if length(opts[extras_opt][DESCRIPTION]) > 0 then
4310
puts(1, opts[extras_opt][DESCRIPTION])
4320
puts(1, '\n')
433
else
4340
puts(1, "One or more additional arguments are also required\n")
435
end if
43616
elsif extras_opt > 0 then
4370
if length(opts[extras_opt][DESCRIPTION]) > 0 then
4380
puts(1, opts[extras_opt][DESCRIPTION])
4390
puts(1, '\n')
440
else
4410
puts(1, "One or more additional arguments can be supplied.\n")
442
end if
443
end if
444
44516
if atom(add_help_rid) then
44616
if add_help_rid >= 0 then
44715
puts(1, "\n")
44815
call_proc(add_help_rid, {})
44915
puts(1, "\n")
450
end if
451
else
4520
if length(add_help_rid) > 0 then
4530
puts(1, "\n")
4540
if t_display(add_help_rid) then
4550
add_help_rid = {add_help_rid}
456
end if
457
4580
for i = 1 to length(add_help_rid) do
4590
puts(1, add_help_rid[i])
4600
if add_help_rid[i][$] != '\n' then
4610
puts(1, '\n')
462
end if
4630
end for
464
4650
puts(1, "\n")
466
end if
467
end if
468
46916
end procedure
470
471
472
--****
473
-- === Routines
474
475
--****
476
-- Signature:
477
-- function command_line()
478
--
479
-- Description:
480
-- A **sequence**, of strings, where each string is a word from the command-line that started your program.
481
--
482
-- Returns:
483
-- # The ##path##, to either the Euphoria executable, (eui, eui.exe, euid.exe euiw.exe) or to your bound
484
-- executable file.
485
-- # The ##next word##, is either the name of your Euphoria main file, or
486
-- (again) the path to your bound executable file.
487
-- # Any ##extra words##, typed by the user. You can use these words in your program.
488
--
489
-- There are as many entries as words, plus the two mentioned above.
490
--
491
-- The Euphoria interpreter itself does not use any command-line options. You are free to use
492
-- any options for your own program. It does have [[:command line switches]] though.
493
--
494
-- The user can put quotes around a series of words to make them into a single argument.
495
--
496
-- If you convert your program into an executable file, either by binding it, or translating it to C,
497
-- you will find that all command-line arguments remain the same, except for the first two,
498
-- even though your user no longer types "eui" on the command-line (see examples below).
499
--
500
-- Example 1:
501
--
502
-- -- The user types: eui myprog myfile.dat 12345 "the end"
503
--
504
-- cmd = command_line()
505
--
506
-- -- cmd will be:
507
-- {"C:\EUPHORIA\BIN\EUI.EXE",
508
-- "myprog",
509
-- "myfile.dat",
510
-- "12345",
511
-- "the end"}
512
--
513
--
514
-- Example 2:
515
--
516
-- -- Your program is bound with the name "myprog.exe"
517
-- -- and is stored in the directory c:\myfiles
518
-- -- The user types: myprog myfile.dat 12345 "the end"
519
--
520
-- cmd = command_line()
521
--
522
-- -- cmd will be:
523
-- {"C:\MYFILES\MYPROG.EXE",
524
-- "C:\MYFILES\MYPROG.EXE", -- place holder
525
-- "myfile.dat",
526
-- "12345",
527
-- "the end"
528
-- }
529
--
530
-- -- Note that all arguments remain the same as example 1
531
-- -- except for the first two. The second argument is always
532
-- -- the same as the first and is inserted to keep the numbering
533
-- -- of the subsequent arguments the same, whether your program
534
-- -- is bound or translated as a .exe, or not.
535
--
536
--
537
-- See Also:
538
-- [[:build_commandline]], [[:option_switches]], [[:getenv]], [[:cmd_parse]], [[:show_help]]
539
540
--****
541
-- Signature:
542
-- function option_switches()
543
--
544
-- Description:
545
-- Retrieves the list of switches passed to the interpreter on the command line.
546
--
547
-- Returns:
548
-- A **sequence**, of strings, each containing a word related to switches.
549
--
550
-- Comments:
551
--
552
-- All switches are recorded in upper case.
553
--
554
-- Example 1:
555
--
556
-- euiw -d helLo
557
-- -- will result in
558
-- -- option_switches() being {"-D","helLo"}
559
--
560
--
561
-- See Also:
562
-- [[:Command line switches]]
563
564
--**
565
-- Show help message for the given opts.
566
--
567
-- Parameters:
568
-- # ##opts## : a sequence of options. See the [[:cmd_parse]] for details.
569
-- # ##add_help_rid## : an object. Either a routine_id or a set of text strings.
570
-- The default is -1 meaning that no additional help text will be used.
571
-- # ##cmds## : a sequence of strings. By default this is the output from [[:command_line]]()
572
--
573
-- Comments:
574
-- * ##opts## is identical to the one used by [[:cmd_parse]]
575
-- * ##add_help_rid## can be used to provide additional help text. By default, just
576
-- the option switches and their descriptions will be displayed. However you can
577
-- provide additional text by either supplying a routine_id of a procedure that
578
-- accepts no parameters; this procedure is expected to write text to the stdout
579
-- device. Or you can supply one or more lines of text that will be displayed.
580
--
581
-- Example 1:
582
--
583
-- -- in myfile.ex
584
-- constant description = {
585
-- "Creates a file containing an analysis of the weather.",
586
-- "The analysis includes temperature and rainfall data",
587
-- "for the past week."
588
-- }
589
--
590
-- show_help({
591
-- {"q", "silent", "Suppresses any output to console", NO_PARAMETER, -1},
592
-- {"r", 0, "Sets how many lines the console should display", {HAS_PARAMETER,"lines"}, -1}},
593
-- description)
594
--
595
-- Outputs:
596
-- {{{
597
-- myfile.ex options:
598
-- -q, --silent Suppresses any output to console
599
-- -r lines Sets how many lines the console should display
600
--
601
-- Creates a file containing an analysis of the weather.
602
-- The analysis includes temperature and rainfall data
603
-- for the past week.
604
-- }}}
605
--
606
-- Example 2:
607
--
608
-- -- in myfile.ex
609
-- constant description = {
610
-- "Creates a file containing an analysis of the weather.",
611
-- "The analysis includes temperature and rainfall data",
612
-- "for the past week."
613
-- }
614
-- procedure sh()
615
-- for i = 1 to length(description) do
616
-- printf(1, " >> %s <<\n", {description[i]})
617
-- end for
618
-- end procedure
619
--
620
-- show_help({
621
-- {"q", "silent", "Suppresses any output to console", NO_PARAMETER, -1},
622
-- {"r", 0, "Sets how many lines the console should display", {HAS_PARAMETER,"lines"}, -1}},
623
-- routine_id("sh"))
624
--
625
-- Outputs:
626
-- {{{
627
-- myfile.ex options:
628
-- -q, --silent Suppresses any output to console
629
-- -r lines Sets how many lines the console should display
630
--
631
-- >> Creates a file containing an analysis of the weather. <<
632
-- >> The analysis includes temperature and rainfall data <<
633
-- >> for the past week. <<
634
-- }}}
635
--
636
6370
6380
local_help(opts, add_help_rid, cmds, 0)
6390
end procedure
640
641
---
6428
643
integer slash
644
sequence opt_name
645
object opt_param
6468
integer param_found = 0
6478
integer reversed = 0
648
6498
if length(cmd_text) >= 2 then
650
-- Strip off any enclosing quotes
6514
if cmd_text[1] = '\'' or cmd_text[1] = '"' then
6520
if cmd_text[$] = cmd_text[1] then
6530
cmd_text = cmd_text[2 .. $-1]
654
end if
655
end if
656
end if
657
6588
if length(cmd_text) > 0 then
6598
if find(cmd_text[1], "!-") then
6601
reversed = 1
6611
cmd_text = cmd_text[2 .. $]
662
end if
663
end if
664
6658
if length(cmd_text) < 1 then
6660
return {-1, "Empty command text"}
667
end if
668
6698
opt_name = repeat(' ', length(cmd_text))
6708
opt_param = 0
6718
for i = 1 to length(cmd_text) do
67216
if find(cmd_text[i], ":=") then
6731
opt_name = opt_name[1 .. i - 1]
6741
opt_param = cmd_text[i + 1 .. $]
6751
if length(opt_param) >= 2 then
676
-- Strip off any enclosing quotes
6771
if opt_param[1] = '\'' or opt_param[1] = '"' then
6780
if opt_param[$] = opt_param[1] then
6790
opt_param = opt_param[2 .. $-1]
680
end if
681
end if
682
end if
683
6841
if length(opt_param) > 0 then
6851
param_found = 1
686
end if
687
6881
exit
689
else
69015
opt_name[i] = cmd_text[i]
691
end if
69215
end for
693
6948
if param_found then
6951
if find(lower(opt_param), {"1", "on", "yes", "y", "true", "ok", "+"}) then
6960
opt_param = 1
6971
elsif find(lower(opt_param), {"0", "off", "no", "n", "false", "-"}) then
6980
opt_param = 0
699
end if
700
end if
701
7028
for i = 1 to length(opts) do
70322
if find(NO_CASE, opts[i][OPTIONS]) then
7048
if not equal(lower(opt_name), lower(opts[i][opt_style[1]])) then
7056
continue
706
end if
707
else
70814
if not equal(opt_name, opts[i][opt_style[1]]) then
7098
continue
710
end if
711
end if
712
7138
if find(HAS_PARAMETER, opts[i][OPTIONS]) = 0 then
7144
if param_found then
7150
return {0, "Option should not have a parameter"}
716
end if
717
end if
718
7198
if param_found then
7201
return {i, opt_name, reversed, opt_param}
721
else
7227
if find(HAS_PARAMETER, opts[i][OPTIONS]) = 0 then
7234
return {i, opt_name, reversed, 1 }
724
end if
725
7263
return {i, opt_name, reversed}
727
end if
7280
end for
729
7300
return {0, "Unrecognised"}
731
end function
732
733
--**
734
-- Parse command line options, and optionally call procedures that relate to these options
735
--
736
-- Parameters:
737
-- # ##opts## : a sequence of valid option records: See Comments: section for details
738
-- # ##parse_options## : an optional sequence of parse options: See Parse Options section for details
739
-- # ##cmds## : an optional sequence of command line arguments. If omitted the output from
740
-- ##command_line##() is used.
741
--
742
-- Returns:
743
-- A **map**, containing the options set. The returned map has one special key named "extras"
744
-- which are values passed on the command line that are not part of any option, for instance
745
-- a list of files ##myprog -verbose file1.txt file2.txt##. If any command element begins
746
-- with an @ symbol then that file will be opened and its contents used to add to the command line.
747
--
748
-- Parse Options:
749
-- ##parse_options## can be a sequence of options that will affect the parsing of
750
-- the command line options. Options can be:
751
--
752
-- # ##VALIDATE_ALL## ~-- The default. All options will be validated for all possible errors.
753
-- # ##NO_VALIDATION## ~-- Do not validate any parameter.
754
-- # ##NO_VALIDATION_AFTER_FIRST_EXTRA## ~-- Do not validate any parameter after the first extra
755
-- was encountered. This is helpful for programs such as the Interpreter itself:
756
-- ##eui -D TEST greet.ex -name John##. -D TEST should be validated but anything after
757
-- "greet.ex" should not as it is meant for greet.ex to handle, not eui.
758
-- # ##HELP_RID## ~-- Specify a routine id to call in the event of a parse error (invalid option
759
-- given, mandatory option not given, no parameter given for an option that requires a
760
-- parameter, etc...) or a set of text strings. This can be used to provide additional
761
-- help text. By default, just the option switches and their descriptions will be
762
-- displayed. However you can provide additional text by either supplying a
763
-- routine_id of a procedure that accepts no parameters; this procedure is expected
764
-- to write text to the stdout device. Or you can supply one or more lines of text
765
-- that will be displayed.
766
-- # ##NO_AT_EXPANSION## ~-- Do not expand arguments that begin with '@.'
767
-- # ##AT_EXPANSION## ~-- Expand arguments that begin with '@'. The name that follows @ will be
768
-- opened as a file, read, and each trimmed non-empty line that does not begin with a
769
-- '#' character will be inserted as arguments in the command line. These lines
770
-- replace the original '@' argument as if they had been entered on the original
771
-- command line. \\
772
-- ** If the name following the '@' begins with another '@', the extra
773
-- '@' is removed and the remainder is the name of the file. However, if that
774
-- file cannot be read, it is simply ignored. This allows //optional// files
775
-- to be included on the command line. Normally, with just a single '@', if the
776
-- file cannot be found the program aborts.
777
-- ** Lines whose first non-whitespace character is '#' are treated as a comment
778
-- and thus ignored.
779
-- ** Lines enclosed with double quotes will have the quotes stripped off and the
780
-- result is used as an argument. This can be used for arguments that begin with
781
-- a '#' character, for example.
782
-- ** Lines enclosed with single quotes will have the quotes stripped off and
783
-- the line is then further split up use the space character as a delimiter. The
784
-- resulting 'words' are then all treated as individual arguments on the command
785
-- line.
786
--
787
-- An example of parse options:
788
--
789
-- { HELP_RID, routine_id("my_help"), NO_VALIDATION }
790
--
791
--
792
-- Comments:
793
-- Token types recognized on the command line:
794
-- # a single '-'. Simply added to the 'extras' list
795
-- # a single "~-~-". This signals the end of command line options. What remains of the command
796
-- line is added to the 'extras' list, and the parsing terminates.
797
-- # -shortName. The option will be looked up in the short name field of ##opts##.
798
-- # /shortName. Same as -shortName.
799
-- # -!shortName. If the 'shortName' has already been found the option is removed.
800
-- # /!shortName. Same as -!shortName
801
-- # ~-~-longName. The option will be looked up in the long name field of ##opts##.
802
-- # ~-~-!longName. If the 'longName' has already been found the option is removed.
803
-- # anything else. The word is simply added to the 'extras' list.
804
--
805
-- For those options that require a parameter to also be supplied, the parameter
806
-- can be given as either the next command line argument, or by appending '=' or ':'
807
-- to the command option then appending the parameter data. \\
808
-- For example, **##-path=/usr/local##** or as **##-path /usr/local##**.
809
--
810
-- On a failed lookup, the program shows the help by calling [[:show_help]](##opts##,
811
-- ##add_help_rid##, ##cmds##) and terminates with status code 1.
812
--
813
-- Option records have the following structure:
814
-- # a sequence representing the (short name) text that will follow the "-" option format.
815
-- Use an atom if not relevant
816
-- # a sequence representing the (long name) text that will follow the "~-~-" option format.
817
-- Use an atom if not relevant
818
-- # a sequence, text that describes the option's purpose. Usually short as it is
819
-- displayed when "-h"/"--help" is on the command line. Use an atom if not required.
820
-- # An object ...
821
-- ** If an **atom** then it can be either ##HAS_PARAMETER## or anything
822
-- else if there is no parameter for this option. This format also implies that
823
-- the option is optional, case-sensitive and can only occur once.
824
-- ** If a **sequence**, it can containing zero or more processing flags in any order ...
825
-- *** ##MANDATORY## to indicate that the option must always be supplied.
826
-- *** ##HAS_PARAMETER## to indicate that the option must have a parameter following it.
827
-- You can optionally have a name for the parameter immediately follow the ##HAS_PARAMETER##
828
-- flag. If one isn't there, the help text will show "x" otherwise it shows the
829
-- supplied name.
830
-- *** ##NO_CASE## to indicate that the case of the supplied option is not significant.
831
-- *** ##ONCE## to indicate that the option must only occur once on the command line.
832
-- *** ##MULTIPLE## to indicate that the option can occur any number of times on the command line.
833
-- ** If both ##ONCE## and ##MULTIPLE## are omitted then switches that also have
834
-- ##HAS_PARAMETER## are only allowed once but switches without ##HAS_PARAMETER##
835
-- can have multuple occurances but only one is recorded in the output map.
836
-- # an integer; a [[:routine_id]]. This function will be called when the option is located
837
-- on the command line and before it updates the map. \\
838
-- Use -1 if cmd_parse is not to invoke a function for this option.\\
839
-- The user defined function must accept a single sequence parameter containing four values.
840
-- If the function returns ##1## then the command option does not update the map.
841
-- You can use the predefined index values OPT_IDX, OPT_CNT, OPT_VAL, OPT_REV when
842
-- referencing the function's parameter elements.
843
-- ## An index into the ##opts## list.
844
-- ## The number of times that the routine has been called
845
-- by cmd_parse for this option
846
-- ## The option's value as found on the command line
847
-- ## 1 if the command line indicates that this option is to remove any earlier occurrences of it.
848
--
849
-- When assigning a value to the resulting map, the key is the long name if present,
850
-- otherwise it uses the short name. For options, you must supply a short name,
851
-- a long name or both.
852
--
853
-- If you want ##cmd_parse##() to call a user routine for the extra command line values,
854
-- you need to specify an Option Record that has neither a short name or a long name,
855
-- in which case only the routine_id field is used.
856
--
857
-- For more details on how the command line is being pre-parsed, see [[:command_line]].
858
--
859
-- Example:
860
--
861
-- sequence option_definition
862
-- integer gVerbose = 0
863
-- sequence gOutFile = {}
864
-- sequence gInFile = {}
865
-- procedure opt_verbose( sequence value)
866
-- if value[OPT_VAL] = -1 then -- (-!v used on command line)
867
-- gVerbose = 0
868
-- else
869
-- if value[OPT_CNT] = 1 then
870
-- gVerbose = 1
871
-- else
872
-- gVerbose += 1
873
-- end if
874
-- end if
875
-- end procedure
876
--
877
-- procedure opt_output_filename( sequence value)
878
-- gOutFile = value[OPT_VAL]
879
-- end procedure
880
--
881
-- procedure opt_extras( sequence value)
882
-- if not file_exists(value[OPT_VAL]) then
883
-- show_help(option_definitions, sprintf("Cannot find '%s'", value[OPT_VAL]))
884
-- abort(1)
885
-- end if
886
-- gInFile = append(gInFile, value[OPT_VAL])
887
-- end procedure
888
--
889
-- option_definition = {
890
-- { "v", "verbose", "Verbose output",{NO_PARAMETER}, routine_id("opt_verbose")},
891
-- { "h", "hash", "Calculate hash values",{NO_PARAMETER}, -1},
892
-- { "o", "output", "Output filename",{MANDATORY, HAS_PARAMETER, ONCE} , routine_id("opt_output_filename") },
893
-- { "i", "import", "An import path", {HAS_PARAMETER, MULTIPLE}, -1 },
894
-- { 0, 0, 0, 0, routine_id("opt_extras")}
895
-- }
896
--
897
-- map:map opts = cmd_parse(option_definition)
898
--
899
-- -- When run as: eui myprog.ex -v @output.txt -i /etc/app input1.txt input2.txt
900
-- -- and the file "output.txt" contains the two lines ...
901
-- -- --output=john.txt
902
-- -- '-i /usr/local'
903
-- --
904
-- -- map:get(opts, "verbose") --> 1
905
-- -- map:get(opts, "hash") --> 0 (not supplied on command line)
906
-- -- map:get(opts, "output") --> "john.txt"
907
-- -- map:get(opts, "import") --> {"/usr/local", "/etc/app"}
908
-- -- map:get(opts, "extras") --> {"input1.txt", "input2.txt"}
909
--
910
--
911
-- See Also:
912
-- [[:show_help]], [[:command_line]]
913
91424
915
integer arg_idx, opts_done
916
sequence cmd
917
object param
918
sequence find_result
919
sequence type_
920
integer from_
921
sequence help_opts
922
sequence call_count
92324
integer add_help_rid = -1
92424
integer validation = VALIDATE_ALL
92524
integer has_extra = 0
92624
integer use_at = 1
927
92824
if sequence(parse_options) then
92921
integer i = 1
930
93121
while i <= length(parse_options) do
93240
switch parse_options[i] do
933
case HELP_RID then
93420
if i < length(parse_options) then
93520
i += 1
93620
add_help_rid = parse_options[i]
937
else
9380
crash("HELP_RID was given to cmd_parse with no routine_id")
939
end if
940
941
case VALIDATE_ALL then
9426
validation = VALIDATE_ALL
943
944
case NO_VALIDATION then
9456
validation = NO_VALIDATION
946
947
case NO_VALIDATION_AFTER_FIRST_EXTRA then
9488
validation = NO_VALIDATION_AFTER_FIRST_EXTRA
949
950
case NO_AT_EXPANSION then
9510
use_at = 0
952
953
case AT_EXPANSION then
9540
use_at = 1
955
956
case PAUSE_MSG then
9570
if i < length(parse_options) then
9580
i += 1
9590
pause_msg = parse_options[i]
960
else
9610
crash("PAUSE_MSG was given to cmd_parse with no actually message text")
962
end if
963
964
end switch
96540
i += 1
96640
end while
967
9683
elsif atom(parse_options) then
9693
add_help_rid = parse_options
970
end if
971
97224
opts = standardize_opts(opts)
973
97424
call_count = repeat(0, length(opts))
975
97624
map:map parsed_opts = map:new()
977
97824
map:put(parsed_opts, "extras", {})
979
98024
arg_idx = 2
98124
opts_done = 0
982
983
-- Find if there are any user-defined help options.
98424
help_opts = { "h", "?", "help" }
98524
for i = 1 to length(opts) do
98676
if find(HELP, opts[i][OPTIONS]) then
98748
if sequence(opts[i][SHORTNAME]) then
98848
help_opts = append(help_opts, opts[i][SHORTNAME])
989
end if
99048
if sequence(opts[i][LONGNAME]) then
99124
help_opts = append(help_opts, opts[i][LONGNAME])
992
end if
99348
if find(NO_CASE, opts[i][OPTIONS]) then
9940
help_opts = lower(help_opts)
9950
arg_idx = length(help_opts)
9960
for j = 1 to arg_idx do
9970
help_opts = append(help_opts, upper(help_opts[j]))
9980
end for
999
end if
1000
end if
100176
end for
1002
100324
while arg_idx < length(cmds) do
100445
arg_idx += 1
1005
100645
cmd = cmds[arg_idx]
100745
if length(cmd) = 0 then
10080
continue
1009
end if
1010
101145
if cmd[1] = '@' and use_at then
10123
object at_cmds
10133
sequence at_file
10143
integer j
1015
10163
if length(cmd) > 2 and cmd[2] = '@' then
1017
-- Read in the lines from the optional file.
10182
at_cmds = io:read_lines(cmd[3..$])
10192
if equal(at_cmds, -1) then
1020
-- File didn't exist but this is not an error, so just
1021
-- remove it from the commands.
10221
cmds = cmds[1..arg_idx-1] & cmds[arg_idx+1..$]
10231
arg_idx -= 1
10241
continue
1025
end if
1026
else
1027
-- Read in the lines from the file.
10281
at_cmds = io:read_lines(cmd[2..$])
10291
if equal(at_cmds, -1) then
10300
printf(2, "Cannot access '@' argument file '%s'\n", {cmd[2..$]})
10310
local_help(opts, add_help_rid, cmds, 1)
10320
local_abort(1)
1033
end if
1034
end if
1035
-- Parse the 'at' commands removing comment lines and empty lines,
1036
-- and stripping off any enclosing quotes from lines.
10372
j = 0
10382
while j < length(at_cmds) do
103912
j += 1
104012
at_cmds[j] = trim(at_cmds[j])
104112
if length(at_cmds[j]) = 0 then
10422
at_cmds = at_cmds[1 .. j-1] & at_cmds[j+1 ..$]
10432
j -= 1
1044
104510
elsif at_cmds[j][1] = '#' then
10463
at_cmds = at_cmds[1 .. j-1] & at_cmds[j+1 ..$]
10473
j -= 1
1048
10497
elsif at_cmds[j][1] = '"' and at_cmds[j][$] = '"' and length(at_cmds[j]) >= 2 then
10502
at_cmds[j] = at_cmds[j][2 .. $-1]
1051
10525
elsif at_cmds[j][1] = '\'' and at_cmds[j][$] = '\'' and length(at_cmds[j]) >= 2 then
10531
sequence cmdex = split(' ', at_cmds[j][2 .. $-1], 0, 1) -- Empty words removed.
1054
10551
at_cmds = at_cmds[1..j-1] & cmdex & at_cmds[j+1 .. $]
10561
j = j + length(cmdex) - 1
1057
1058
end if
105912
end while
1060
1061
-- Replace the '@' argument with the contents of the file.
10622
cmds = cmds[1..arg_idx-1] & at_cmds & cmds[arg_idx+1..$]
10632
arg_idx -= 1
10642
continue
1065
end if
1066
106742
if (opts_done or find(cmd[1], CMD_SWITCHES) = 0 or length(cmd) = 1)
1068
then
106918
map:put(parsed_opts, "extras", cmd, map:APPEND)
107018
has_extra = 1
107118
if validation = NO_VALIDATION_AFTER_FIRST_EXTRA then
10725
exit
1073
else
107413
continue
1075
end if
1076
end if
1077
107824
if equal(cmd, "--") then
10790
opts_done = 1
10800
continue
1081
end if
1082
108324
if equal(cmd[1..2], "--") then -- found --opt-name
10847
type_ = {LONGNAME, "--"}
10857
from_ = 3
108617
elsif cmd[1] = '-' then -- found -opt
108717
type_ = {SHORTNAME, "-"}
108817
from_ = 2
1089
else -- found /opt
10900
type_ = {SHORTNAME, "/"}
10910
from_ = 2
1092
end if
1093
109424
if find(cmd[from_..$], help_opts) then
109516
local_help(opts, add_help_rid, cmds, 1)
109616
ifdef UNITTEST then
109716
return 0
1098
end ifdef
10990
local_abort(0)
1100
end if
1101
11028
find_result = find_opt(opts, type_, cmd[from_..$])
1103
11048
if find_result[1] < 0 then
11050
continue -- Couldn't use this command argument for anything.
1106
end if
1107
11088
if find_result[1] = 0 then
11090
if validation = VALIDATE_ALL or
1110
(validation = NO_VALIDATION_AFTER_FIRST_EXTRA and has_extra = 0)
1111
then
1112
-- something is wrong with the option
11130
printf(1, "option '%s': %s\n\n", {cmd, find_result[2]})
11140
local_help(opts, add_help_rid, cmds, 1)
11150
local_abort(1)
1116
end if
1117
11180
continue
1119
end if
1120
11218
sequence opt = opts[find_result[1]]
1122
11238
if find(HAS_PARAMETER, opt[OPTIONS]) != 0 then
11244
if length(find_result) < 4 then
11253
arg_idx += 1
11263
if arg_idx <= length(cmds) then
11273
param = cmds[arg_idx]
11283
if length(param) = 2 and find(param[1], "-/") then
11290
param = ""
1130
end if
1131
else
11320
param = ""
1133
end if
1134
11353
if length(param) = 0 and (validation = VALIDATE_ALL or (
1136
validation = NO_VALIDATION_AFTER_FIRST_EXTRA))
1137
then
11380
printf(1, "option '%s' must have a parameter\n\n", {find_result[2]})
11390
local_help(opts, add_help_rid, cmds, 1)
11400
local_abort(1)
1141
end if
1142
else
11431
param = find_result[4]
1144
end if
1145
else
11464
param = find_result[4]
1147
end if
1148
11498
if opt[CALLBACK] >= 0 then
11502
integer pos = find_result[1]
11512
call_count[pos] += 1
1152
-- OPT_IDX OPT_CNT OPT_VAL OPT_REV
11532
if call_func(opt[CALLBACK], {{find_result[1], call_count[pos], param, find_result[3]}}) = 0 then
11540
continue
1155
end if
1156
end if
1157
11588
if find_result[3] = 1 then
11591
map:remove(parsed_opts, opt[MAPNAME])
1160
else
11617
if find(MULTIPLE, opt[OPTIONS]) = 0 then
11627
if map:has(parsed_opts, opt[MAPNAME]) and (validation = VALIDATE_ALL or
1163
(validation = NO_VALIDATION_AFTER_FIRST_EXTRA))
1164
then
11650
if find(HAS_PARAMETER, opt[OPTIONS]) or find(ONCE, opt[OPTIONS]) then
11660
printf(1, "option '%s' must not occur more than once in the command line.\n\n", {find_result[2]})
11670
local_help(opts, add_help_rid, cmds, 1)
11680
local_abort(1)
1169
end if
1170
else
11717
map:put(parsed_opts, opt[MAPNAME], param)
1172
end if
1173
else
11740
map:put(parsed_opts, opt[MAPNAME], param, map:APPEND)
1175
end if
1176
end if
11778
end while
1178
1179
-- Check that all mandatory options have been supplied.
11808
for i = 1 to length(opts) do
118129
if find(MANDATORY, opts[i][OPTIONS]) then
11822
if atom(opts[i][SHORTNAME]) and atom(opts[i][LONGNAME]) then
11831
if length(map:get(parsed_opts, opts[i][MAPNAME])) = 0 then
11840
puts(1, "Additional arguments were expected.\n\n")
11850
local_help(opts, add_help_rid, cmds, 1)
11860
local_abort(1)
1187
end if
1188
else
11891
if not map:has(parsed_opts, opts[i][MAPNAME]) then
11900
printf(1, "option '%s' is mandatory but was not supplied.\n\n", {opts[i][MAPNAME]})
11910
local_help(opts, add_help_rid, cmds, 1)
11920
local_abort(1)
1193
end if
1194
end if
1195
end if
119629
end for
1197
11988
return parsed_opts
1199
end function
1200
1201
--**
1202
-- Returns a text string based on the set of supplied strings. Typically, this
1203
-- is used to ensure that arguments on a command line are properly formed
1204
-- before submitting it to the shell.
1205
--
1206
-- Parameters:
1207
-- # ##cmds## : A sequence. Contains zero or more strings.
1208
--
1209
-- Returns:
1210
-- A **sequence**, which is a text string. Each of the strings in ##cmds## is
1211
-- quoted if they contain spaces, and then concatenated to form a single
1212
-- string.
1213
--
1214
-- Comments:
1215
-- Though this function does the quoting for you it is not going to protect
1216
-- your programs from globing *, ?. And it is not specied here what happens if you
1217
-- pass redirection or piping characters.
1218
--
1219
-- Example 1:
1220
--
1221
-- s = build_commandline( { "-d", "/usr/my docs/"} )
1222
-- -- s now contains '-d "/usr/my docs/"'
1223
--
1224
--
1225
-- Example 2:
1226
-- You can use this to run things that might be difficult to quote out:
1227
-- Suppose you want to run a program that requires quotes on its
1228
-- command line? Use this function to pass quotation marks:
1229
--
1230
--
1231
-- s = build_commandline( { "awk", "-e", "'{ print $1"x"$2; }'" } )
1232
-- system(s,0)
1233
--
1234
--
1235
-- See Also:
1236
-- [[:parse_commandline]], [[:system]], [[:system_exec]], [[:command_line]]
1237
123813
123913
return flatten(quote( cmds,,'\\'," " ), " ")
1240
end function
1241
1242
--**
1243
-- Parse a command line string breaking it into a sequence of command line
1244
-- options.
1245
--
1246
-- Parameters:
1247
-- # ##cmdline## : Command line sequence (string)
1248
--
1249
-- Returns:
1250
-- A **sequence**, of command line options
1251
--
1252
-- Example 1:
1253
--
1254
-- sequence opts = parse_commandline("-v -f '%Y-%m-%d %H:%M')
1255
-- -- opts = { "-v", "-f", "%Y-%m-%d %H:%M" }
1256
--
1257
--
1258
-- See Also:
1259
-- [[:build_commandline]]
1260
--
1261
12621
12631
return keyvalues(cmdline, " ", ":=", "\"'`", " \t\r\n", 0)
1264
end function