1 // written in the D programming language
2
3 module options;
4
5 import std.string;
6 import std.ascii : isDigit;
7 import std.utf;
8 import std.conv : to;
9 private import std.process : getenv;
10 debug (1) {
11 import std.stdio;
12 }
version(unittest)13 version (unittest) {
14 import std.stdio;
15 private import std.process : setenv, unsetenv;
16 }
17
18 import config;
19 import dihelp;
20 import digetoptn;
21 import dispopts;
22
23 private immutable string DI_ALL_FORMAT = "MTS\n\tO\n\tbuf13\n\tbcvpa\n\tBuv2\n\tiUFP";
24 private immutable string DI_POSIX_FORMAT = "SbuvpM";
25 private immutable string DI_DEFAULT_SORT = "m";
26
27 struct Options {
28 bool csvOutput = false;
29 bool displayAll = false;
30 bool localOnly = false;
31 bool includeLoopback = false;
32 bool printHeader = true;
33 bool quotaCheck = true;
34 bool displayTotal = false;
35 bool debugHeader = false;
36 bool unknown = false;
37 short debugLevel = 0;
38 string formatString = DI_DEFAULT_FORMAT;
39 string sortType = DI_DEFAULT_SORT;
40 string zoneDisplay;
41 bool[string] includeList;
42 bool[string] excludeList;
43 }
44
45 int
getDIOptions(string[]args,ref Options opts,ref DisplayOpts dispOpts)46 getDIOptions (string[] args, ref Options opts, ref DisplayOpts dispOpts) {
47 int idx;
48 string s;
49
50 if ((s = getenv ("DIFMT")) != null)
51 {
52 opts.formatString = s;
53 }
54
55 // gnu df
56 if ((s = getenv ("POSIXLY_CORRECT")) != null)
57 {
58 dispOpts.dbsstr = "512";
59 opts.formatString = DI_POSIX_FORMAT;
60 dispOpts.posixCompat = true;
61 }
62
63 // bsd df
64 if ((s = getenv ("BLOCKSIZE")) != null)
65 {
66 dispOpts.dbsstr = s;
67 }
68
69 // gnu df
70 if ((s = getenv ("DF_BLOCK_SIZE")) != null)
71 {
72 dispOpts.dbsstr = s;
73 }
74
75 if ((s = getenv ("DI_ARGS")) != null)
76 {
77 processOpts (s, opts, dispOpts);
78 }
79
80 idx = processOpts (args, opts, dispOpts);
81
82 return idx;
83 }
84
85 unittest {
86 bool fail;
87 int failures;
88 int tcount;
89 Options opts;
90 DisplayOpts dispOpts;
91
92 unsetenv ("DI_ARGS");
93 unsetenv ("BLOCKSIZE");
94 unsetenv ("DF_BLOCK_SIZE");
95 unsetenv ("POSIXLY_CORRECT");
96
97 ++tcount;
98 fail = false;
99 opts = opts.init;
100 setenv ("DIFMT", "SMT", true);
101 getDIOptions ([""], opts, dispOpts);
102 if (opts.formatString != "SMT") { fail = true; }
103 if (fail)
104 {
105 ++failures;
106 writefln ("# %s: fail: %s", "getDIOptions:", "Env: DIFMT");
107 writefln (" expected: %s got %s", "SMT", opts.formatString);
108 }
109 unsetenv ("DIFMT");
110
111 ++tcount;
112 fail = false;
113 opts = opts.init;
114 setenv ("POSIXLY_CORRECT", " ", true);
115 getDIOptions ([""], opts, dispOpts);
116 if (dispOpts.dbsstr != "512") { fail = true; }
117 if (opts.formatString != DI_POSIX_FORMAT) { fail = true; }
118 if (dispOpts.posixCompat != true) { fail = true; }
119 if (fail)
120 {
121 ++failures;
122 writefln ("# %s: fail: %s", "getDIOptions:", "Env: POSIXLY_CORRECT");
123 writefln (" expected: %s got %s", "512", dispOpts.dbsstr);
124 writefln (" expected: %s got %s", DI_POSIX_FORMAT, opts.formatString);
125 writefln (" expected: %s got %s", true, dispOpts.posixCompat);
126 }
127 unsetenv ("POSIXLY_CORRECT");
128
129 ++tcount;
130 fail = false;
131 opts = opts.init;
132 setenv ("DF_BLOCK_SIZE", "kB", true);
133 getDIOptions ([""], opts, dispOpts);
134 if (dispOpts.dbsstr != "kB") { fail = true; }
135 if (fail)
136 {
137 ++failures;
138 writefln ("# %s: fail: %s", "getDIOptions:", "Env: DF_BLOCK_SIZE");
139 writefln (" expected: %s got %s", "kB", dispOpts.dbsstr);
140 }
141 unsetenv ("DF_BLOCK_SIZE");
142
143 ++tcount;
144 fail = false;
145 opts = opts.init;
146 setenv ("BLOCKSIZE", "kB", true);
147 getDIOptions ([""], opts, dispOpts);
148 if (dispOpts.dbsstr != "kB") { fail = true; }
149 if (fail)
150 {
151 ++failures;
152 writefln ("# %s: fail: %s", "getDIOptions:", "Env: BLOCKSIZE");
153 writefln (" expected: %s got %s", "kB", dispOpts.dbsstr);
154 }
155 unsetenv ("BLOCKSIZE");
156
157 if (failures > 0) {
158 write ("unittest: options: getDIOptions: ");
159 writefln ("failed: %d of %d", failures, tcount);
160 }
161 }
162
163 private:
164
165 int
processOpts(string str,ref Options opts,ref DisplayOpts dispOpts)166 processOpts (string str, ref Options opts, ref DisplayOpts dispOpts)
167 {
168 return processOpts (["dummy"] ~ split(strip(str)), opts, dispOpts);
169 }
170
171 int
processOpts(string[]args,ref Options opts,ref DisplayOpts dispOpts)172 processOpts (string[] args, ref Options opts, ref DisplayOpts dispOpts)
173 {
174 int idx;
175 OptInfo[string] options;
176
177 with (opts) {
178 auto delA = delegate (string arg, string val)
179 { formatString = DI_ALL_FORMAT; };
180 auto delB = delegate (string arg, string val)
181 { processBaseSize (val, dispOpts); };
182 auto delg = delegate (string arg, string val)
183 { dispOpts.dbsstr = "g"; };
184 auto delh = delegate (string arg, string val)
185 { dispOpts.dbsstr = "h"; };
186 auto delH = delegate (string arg, string val)
187 { dispOpts.dbsstr = "H"; };
188 auto delhelp = delegate (string arg, string val)
189 { usage(); exit (0); };
190 auto delI = delegate (string arg, string val)
191 { processIncludeList (val, opts); };
192 auto delk = delegate (string arg, string val)
193 { dispOpts.dbsstr = "k"; };
194 auto delm = delegate (string arg, string val)
195 { dispOpts.dbsstr = "m"; };
196 auto delP = delegate (string arg, string val)
197 { if (dispOpts.dbsstr != "k") { dispOpts.dbsstr = "512"; }
198 formatString = DI_POSIX_FORMAT;
199 dispOpts.posixCompat = true; };
200 auto dels = delegate (string arg, string val)
201 { sortType = processSort (val); };
202 auto delsi = delegate (string arg, string val)
203 { dispOpts.baseDispSize = size1000;
204 dispOpts.baseDispIdx = idx1000;
205 dispOpts.dbsstr = "H"; };
206 auto delversion = delegate (string arg, string val)
207 { dispVersion(); exit (0); };
208 auto delx = delegate (string arg, string val)
209 { processExcludeList (val, opts); };
210 auto delZ = delegate (string arg, string val)
211 { zoneDisplay = "all"; };
212
213 goinit (options, "-A", GETOPTN_FUNC_NOARG, cast(void *) null, delA);
214 goinit (options, "-a", GETOPTN_BOOL, cast(void *) &displayAll, null);
215 goinit (options, "--all", GETOPTN_ALIAS, cast(void *) &"-a", null);
216 goinit (options, "-b", GETOPTN_ALIAS, cast(void *) &"-B", null );
217 goinit (options, "--block-size", GETOPTN_ALIAS, cast(void *) &"-B", null );
218 goinit (options, "-B", GETOPTN_FUNC_ARG, cast(void *) null, delB );
219 goinit (options, "-c", GETOPTN_BOOL, cast(void *) &csvOutput, null );
220 goinit (options, "--csv-output", GETOPTN_ALIAS, cast(void *) &"-c", null );
221 goinit (options, "-d", GETOPTN_STRING, cast(void *) &dispOpts.dbsstr, null );
222 goinit (options, "-f", GETOPTN_STRING, cast(void *) &formatString, null );
223 goinit (options, "--format-string", GETOPTN_ALIAS, cast(void *) &"-f", null );
224 goinit (options, "-F", GETOPTN_ALIAS, cast(void *) &"-I", null );
225 goinit (options, "-g", GETOPTN_FUNC_NOARG, cast(void *) null, delg );
226 goinit (options, "-h", GETOPTN_FUNC_NOARG, cast(void *) null, delh );
227 goinit (options, "-H", GETOPTN_FUNC_NOARG, cast(void *) null, delH );
228 goinit (options, "--help", GETOPTN_FUNC_NOARG, cast(void *) null, delhelp );
229 goinit (options, "--human-readable", GETOPTN_ALIAS, cast(void *) &"-H", null );
230 goinit (options, "-?", GETOPTN_ALIAS, cast(void *) &"--help", null );
231 goinit (options, "-i", GETOPTN_ALIAS, cast(void *) &"-x", null );
232 goinit (options, "-I", GETOPTN_FUNC_ARG, cast(void *) null, delI );
233 goinit (options, "--inodes", GETOPTN_IGNORE, cast(void *) null, null );
234 goinit (options, "-k", GETOPTN_FUNC_NOARG, cast(void *) null, delk );
235 goinit (options, "-l", GETOPTN_BOOL, cast(void *) &localOnly, null );
236 goinit (options, "--local", GETOPTN_ALIAS, cast(void *) &"-l", null );
237 goinit (options, "-L", GETOPTN_BOOL, cast(void *) &includeLoopback, null );
238 goinit (options, "-m", GETOPTN_FUNC_NOARG, cast(void *) null, delm );
239 goinit (options, "-n", GETOPTN_BOOL, cast(void *) &printHeader, null );
240 goinit (options, "--no-sync", GETOPTN_IGNORE, cast(void *) null, null );
241 goinit (options, "-P", GETOPTN_FUNC_NOARG, cast(void *) null, delP );
242 goinit (options, "--portability", GETOPTN_ALIAS, cast(void *) &"-P", null );
243 goinit (options, "--print-type", GETOPTN_IGNORE, cast(void *) null, null );
244 goinit (options, "-q", GETOPTN_BOOL, cast(void *) "aCheck, null );
245 goinit (options, "-s", GETOPTN_FUNC_ARG, cast(void *) null, dels );
246 goinit (options, "--si", GETOPTN_FUNC_NOARG, cast(void *) null, delsi );
247 goinit (options, "--sync", GETOPTN_IGNORE, cast(void *) null, null );
248 goinit (options, "-t", GETOPTN_BOOL, cast(void *) &displayTotal, null );
249 goinit (options, "--total", GETOPTN_ALIAS, cast(void *) &"-t", null );
250 goinit (options, "--type", GETOPTN_ALIAS, cast(void *) &"-I", null );
251 goinit (options, "-v", GETOPTN_IGNORE, cast(void *) null, null );
252 goinit (options, "--version", GETOPTN_FUNC_NOARG, cast(void *) null, delversion );
253 goinit (options, "-w", GETOPTN_SHORT, cast(void *) &dispOpts.width, null );
254 goinit (options, "-W", GETOPTN_SHORT, cast(void *) &dispOpts.inodeWidth, null );
255 goinit (options, "-x", GETOPTN_FUNC_ARG, cast(void *) null, delx );
256 goinit (options, "--exclude-type", GETOPTN_ALIAS, cast(void *) &"-x", null );
257 goinit (options, "-X", GETOPTN_SHORT, cast(void *) &debugLevel, null );
258 goinit (options, "-z", GETOPTN_STRING, cast(void *) &zoneDisplay, null );
259 goinit (options, "-Z", GETOPTN_FUNC_NOARG, cast(void *) null, delZ );
260
261 idx = getoptn (GETOPTN_LEGACY, args, options);
262 }
263 return idx;
264 }
265
version(unittest)266 version (unittest) {
267 mixin template MoptionsTest(T) {
268 void
269 test (string l, bool expected,
270 string o, ref T var, T rv, int ri)
271 {
272 int i;
273 bool fail;
274
275 ++tcount;
276 opts = opts.init;
277 i = processOpts (o, opts, dispOpts);
278 //writeln ("### OA:i:", i, ":ri:", ri);
279 //writeln ("### OA:var:", var, ":rv:", rv);
280 if (var != rv) { fail = true; }
281 if (i != ri) { fail = true; }
282 if (fail)
283 {
284 ++failures;
285 writefln ("# %s: fail: %s", "processOpts:", l);
286 writefln (" expected: %s got %s", rv, var);
287 writefln (" expected: %s got %s", ri, i);
288 }
289 }
290 }
291 }
292
293 unittest {
294 int failures;
295 int tcount;
296 Options opts;
297 DisplayOpts dispOpts;
298
299 string s;
300 mixin MoptionsTest!bool tbool;
301 mixin MoptionsTest!string tstr;
302 mixin MoptionsTest!real treal;
303 mixin MoptionsTest!short tshort;
304 mixin MoptionsTest!(bool[string]) tbaa;
305
with(opts)306 with (opts) {
307 tbool.test ("-a: displayAll", true, "-a", displayAll, true, 2);
308 tstr.test ("-d k: dispOpts.dbsstr", true, "-d k", dispOpts.dbsstr, "k", 3);
309 tstr.test ("-k: dispOpts.dbsstr", true, "-k", dispOpts.dbsstr, "k", 2);
310 tstr.test ("-d g: dispOpts.dbsstr", true, "-d g", dispOpts.dbsstr, "g", 3);
311 tstr.test ("-g: dispOpts.dbsstr", true, "-g", dispOpts.dbsstr, "g", 2);
312 tstr.test ("-d m: dispOpts.dbsstr", true, "-d m", dispOpts.dbsstr, "m", 3);
313 tstr.test ("-m: dispOpts.dbsstr", true, "-m", dispOpts.dbsstr, "m", 2);
314 tstr.test ("-d h: dispOpts.dbsstr", true, "-d h", dispOpts.dbsstr, "h", 3);
315 tstr.test ("-h: dispOpts.dbsstr", true, "-h", dispOpts.dbsstr, "h", 2);
316 tstr.test ("-d H: dispOpts.dbsstr", true, "-d H", dispOpts.dbsstr, "H", 3);
317 tstr.test ("-H: dispOpts.dbsstr", true, "-H", dispOpts.dbsstr, "H", 2);
318 tstr.test ("-A: formatString", true, "-A", formatString, DI_ALL_FORMAT, 2);
319 treal.test ("-b 1000: dispOpts.baseDispSize", true, "-b 1000", dispOpts.baseDispSize, 1000.0, 3);
320 treal.test ("-b 1024: dispOpts.baseDispSize", true, "-b 1024", dispOpts.baseDispSize, 1024.0, 3);
321 treal.test ("-b d: dispOpts.baseDispSize", true, "-b d", dispOpts.baseDispSize, 1000.0, 3);
322 treal.test ("-b k: dispOpts.baseDispSize", true, "-b k", dispOpts.baseDispSize, 1024.0, 3);
323 tstr.test ("-f: formatString", true, "-f SMbuvpT", formatString, "SMbuvpT", 3);
324 tbaa.test ("-I nfs: includeList", true, "-I nfs", includeList, ["nfs":true], 3);
325 tbaa.test ("-I nfs,jfs: includeList", true, "-I nfs,jfs", includeList, ["nfs":true,"jfs":true], 3);
326 tbaa.test ("-I nfs -I jfs: includeList", true, "-I nfs -I jfs", includeList, ["nfs":true,"jfs":true], 5);
327 tbool.test ("-l: localOnly", true, "-l", localOnly, true, 2);
328 tbool.test ("-L: includeLoopback", true, "-L", includeLoopback, true, 2);
329 tbool.test ("-n: printHeader", true, "-n", printHeader, false, 2);
330 tstr.test ("-P: dispOpts.dbsstr", true, "-P", dispOpts.dbsstr, "512", 2);
331 tbool.test ("-P: dispOpts.posixCompat", true, "-P", dispOpts.posixCompat, true, 2);
332 tstr.test ("-P: formatString", true, "-P", formatString, DI_POSIX_FORMAT, 2);
333 tstr.test ("-P -k: dispOpts.dbsstr", true, "-P -k", dispOpts.dbsstr, "k", 3);
334 tstr.test ("-k -P: dispOpts.dbsstr", true, "-k -P", dispOpts.dbsstr, "k", 3);
335 tbool.test ("-q: quotaCheck", true, "-q", quotaCheck, false, 2);
336 tstr.test ("-s r: sortType", true, "-s r", sortType, "rm", 3);
337 tstr.test ("-s t: sortType", true, "-s t", sortType, "tm", 3);
338 tstr.test ("-s trsrm: sortType", true, "-s trsrm", sortType, "trsrm", 3);
339 tbool.test ("-t: displayTotal", true, "-t", displayTotal, true, 2);
340 tshort.test ("-w 12: dispOpts.width", true, "-w 12", dispOpts.width, 12, 3);
341 tshort.test ("-W 12: dispOpts.inodeWidth", true, "-W 12", dispOpts.width, 12, 3);
342 tbaa.test ("-x nfs: excludeList", true, "-x nfs", excludeList, ["nfs":true], 3);
343 tbaa.test ("-x nfs,jfs: excludeList", true, "-x nfs,jfs", excludeList, ["nfs":true,"jfs":true], 3);
344 tbaa.test ("-x nfs -x jfs: excludeList", true, "-x nfs -x jfs", excludeList, ["nfs":true,"jfs":true], 5);
345 tstr.test ("-z some: zoneDisplay", true, "-z some", zoneDisplay, "some", 3);
346 tstr.test ("-Z: zoneDisplay", true, "-Z", zoneDisplay, "all", 2);
347 tshort.test ("-X 4: debugLevel", true, "-X 4", debugLevel, 4, 3);
348 }
349
350 if (failures > 0) {
351 write ("unittest: options: processOpts: ");
352 writefln ("failed: %d of %d", failures, tcount);
353 }
354 }
355
356 void
processBaseSize(string arg,ref DisplayOpts dispOpts)357 processBaseSize (string arg, ref DisplayOpts dispOpts)
358 {
359 size_t i = 0;
360 dchar c;
361
362 with (dispOpts) {
363 c = decode (arg, i);
364 if (isDigit (c)) {
365 baseDispSize = to!(typeof(baseDispSize))(arg);
366 baseDispIdx = idx1000; // unknown, really
367 if (baseDispSize == 1024.0) {
368 baseDispIdx = idx1024;
369 }
370 }
371 else if (arg == "k") {
372 baseDispSize = size1024;
373 baseDispIdx = idx1024;
374 }
375 else if (arg == "d" || arg == "si") {
376 baseDispSize = size1000;
377 baseDispIdx = idx1000;
378 }
379 }
380 }
381
382 string
processSort(string arg)383 processSort (string arg)
384 {
385 string sortType;
386
387 sortType = arg;
388 if (arg == "r") { // backwards compatibility
389 sortType = "rm";
390 }
391 if (arg == "t") { // add some additional sorting...
392 sortType = "tm";
393 }
394 return sortType;
395 }
396
397 void
processIncludeList(string arg,ref Options opts)398 processIncludeList (string arg, ref Options opts)
399 {
400 foreach (str; split (arg, ",")) {
401 opts.includeList[str] = true;
402 }
403 }
404
405 void
processExcludeList(string arg,ref Options opts)406 processExcludeList (string arg, ref Options opts)
407 {
408 foreach (str; split (arg, ",")) {
409 opts.excludeList[str] = true;
410 }
411 }
412