blob: 7a19ccd29d91ff2a116118b66c54042623da1056 (
plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
|
with import <stockholm/lib>;
{ utillinux, writeDash }:
opt-spec: cmd-spec: let
cmd = cmd-spec opts;
cmd-script =
if typeOf cmd == "set"
then "exec ${cmd}"
else cmd;
opts = mapAttrs (name: value: value // rec {
long = value.long or (replaceStrings ["_"] ["-"] name);
ref = value.ref or "\"\$${varname}\"";
switch = value.switch or false;
varname = value.varname or (replaceStrings ["-"] ["_"] name);
}) opt-spec;
# true if b requires a to define its default value
opts-before = a: b:
test ".*[$]${stringAsChars (c: "[${c}]") a.varname}\\>.*" (b.default or "");
opts-list = let
sort-out = toposort opts-before (attrValues opts);
in
if sort-out ? result
then sort-out.result
else throw "toposort output: ${toJSON sort-out}";
wrapper-name =
if typeOf cmd == "set" && cmd ? name
then "${cmd.name}-getopt"
else "getopt-wrapper";
in writeDash wrapper-name ''
set -efu
wrapper_name=${shell.escape wrapper-name}
# TODO
for i in "$@"; do
case $i in
-h|--help)
${concatStringsSep "\n" (mapAttrsToList (name: opt: /* sh */ ''
printf ' %-16s %s\n' \
--${shell.escape opt.long} \
${shell.escape (opt.description or "undocumented flag")}
'') opts)}
exit
esac
done
${concatStringsSep "\n" (mapAttrsToList (name: opt: /* sh */ ''
unset ${opt.varname}
'') opts)}
args=$(${utillinux}/bin/getopt \
-l ${shell.escape
(concatMapStringsSep ","
(opt: opt.long + optionalString (!opt.switch) ":")
(filter (opt: opt.long != null)
(attrValues opts)))} \
-n "$wrapper_name" \
-o "" \
-s sh \
-- "$@")
if \test $? != 0; then exit 1; fi
eval set -- "$args"
while :; do
case $1 in
${concatStringsSep "\n" (mapAttrsToList (name: opt: /* sh */ ''
--${opt.long})
${if opt.switch then /* sh */ ''
${opt.varname}=true
shift
'' else /* sh */ ''
${opt.varname}=$2
shift 2
''}
;;
'') (filterAttrs
(_: opt: opt.long != null)
opts))}
--)
shift
break
esac
done
${concatMapStringsSep "\n"
(opt: /* sh */ ''
if \test "''${${opt.varname}+1}" != 1; then
printf '%s: missing mandatory option '--%s'\n' \
"$wrapper_name" \
${shell.escape opt.long}
error=1
fi
'')
(filter
(x: ! hasAttr "default" x)
(attrValues opts))}
if test "''${error+1}" = 1; then
exit 1
fi
${concatMapStringsSep "\n"
(opt: /* sh */ ''
if \test "''${${opt.varname}+1}" != 1; then
${opt.varname}=${opt.default}
fi
'')
(filter
(hasAttr "default")
opts-list)}
${concatStringsSep "\n" (mapAttrsToList (name: opt: /* sh */ ''
export ${opt.varname}
'') opts)}
${cmd-script} "$@"
''
|