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 *) &quotaCheck, 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