1-- The MIT License (MIT)
2
3-- Copyright (c) 2013 - 2018 Peter Melnichenko
4--                      2019 Paul Ouellette
5
6-- Permission is hereby granted, free of charge, to any person obtaining a copy of
7-- this software and associated documentation files (the "Software"), to deal in
8-- the Software without restriction, including without limitation the rights to
9-- use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
10-- the Software, and to permit persons to whom the Software is furnished to do so,
11-- subject to the following conditions:
12
13-- The above copyright notice and this permission notice shall be included in all
14-- copies or substantial portions of the Software.
15
16-- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17-- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
18-- FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
19-- COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
20-- IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
21-- CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
22
23local function deep_update(t1, t2)
24   for k, v in pairs(t2) do
25      if type(v) == "table" then
26         v = deep_update({}, v)
27      end
28
29      t1[k] = v
30   end
31
32   return t1
33end
34
35-- A property is a tuple {name, callback}.
36-- properties.args is number of properties that can be set as arguments
37-- when calling an object.
38local function class(prototype, properties, parent)
39   -- Class is the metatable of its instances.
40   local cl = {}
41   cl.__index = cl
42
43   if parent then
44      cl.__prototype = deep_update(deep_update({}, parent.__prototype), prototype)
45   else
46      cl.__prototype = prototype
47   end
48
49   if properties then
50      local names = {}
51
52      -- Create setter methods and fill set of property names.
53      for _, property in ipairs(properties) do
54         local name, callback = property[1], property[2]
55
56         cl[name] = function(self, value)
57            if not callback(self, value) then
58               self["_" .. name] = value
59            end
60
61            return self
62         end
63
64         names[name] = true
65      end
66
67      function cl.__call(self, ...)
68         -- When calling an object, if the first argument is a table,
69         -- interpret keys as property names, else delegate arguments
70         -- to corresponding setters in order.
71         if type((...)) == "table" then
72            for name, value in pairs((...)) do
73               if names[name] then
74                  self[name](self, value)
75               end
76            end
77         else
78            local nargs = select("#", ...)
79
80            for i, property in ipairs(properties) do
81               if i > nargs or i > properties.args then
82                  break
83               end
84
85               local arg = select(i, ...)
86
87               if arg ~= nil then
88                  self[property[1]](self, arg)
89               end
90            end
91         end
92
93         return self
94      end
95   end
96
97   -- If indexing class fails, fallback to its parent.
98   local class_metatable = {}
99   class_metatable.__index = parent
100
101   function class_metatable.__call(self, ...)
102      -- Calling a class returns its instance.
103      -- Arguments are delegated to the instance.
104      local object = deep_update({}, self.__prototype)
105      setmetatable(object, self)
106      return object(...)
107   end
108
109   return setmetatable(cl, class_metatable)
110end
111
112local function typecheck(name, types, value)
113   for _, type_ in ipairs(types) do
114      if type(value) == type_ then
115         return true
116      end
117   end
118
119   error(("bad property '%s' (%s expected, got %s)"):format(name, table.concat(types, " or "), type(value)))
120end
121
122local function typechecked(name, ...)
123   local types = {...}
124   return {name, function(_, value) typecheck(name, types, value) end}
125end
126
127local multiname = {"name", function(self, value)
128   typecheck("name", {"string"}, value)
129
130   for alias in value:gmatch("%S+") do
131      self._name = self._name or alias
132      table.insert(self._aliases, alias)
133   end
134
135   -- Do not set _name as with other properties.
136   return true
137end}
138
139local function parse_boundaries(str)
140   if tonumber(str) then
141      return tonumber(str), tonumber(str)
142   end
143
144   if str == "*" then
145      return 0, math.huge
146   end
147
148   if str == "+" then
149      return 1, math.huge
150   end
151
152   if str == "?" then
153      return 0, 1
154   end
155
156   if str:match "^%d+%-%d+$" then
157      local min, max = str:match "^(%d+)%-(%d+)$"
158      return tonumber(min), tonumber(max)
159   end
160
161   if str:match "^%d+%+$" then
162      local min = str:match "^(%d+)%+$"
163      return tonumber(min), math.huge
164   end
165end
166
167local function boundaries(name)
168   return {name, function(self, value)
169      typecheck(name, {"number", "string"}, value)
170
171      local min, max = parse_boundaries(value)
172
173      if not min then
174         error(("bad property '%s'"):format(name))
175      end
176
177      self["_min" .. name], self["_max" .. name] = min, max
178   end}
179end
180
181local actions = {}
182
183local option_action = {"action", function(_, value)
184   typecheck("action", {"function", "string"}, value)
185
186   if type(value) == "string" and not actions[value] then
187      error(("unknown action '%s'"):format(value))
188   end
189end}
190
191local option_init = {"init", function(self)
192   self._has_init = true
193end}
194
195local option_default = {"default", function(self, value)
196   if type(value) ~= "string" then
197      self._init = value
198      self._has_init = true
199      return true
200   end
201end}
202
203local add_help = {"add_help", function(self, value)
204   typecheck("add_help", {"boolean", "string", "table"}, value)
205
206   if self._help_option_idx then
207      table.remove(self._options, self._help_option_idx)
208      self._help_option_idx = nil
209   end
210
211   if value then
212      local help = self:flag()
213         :description "Show this help message and exit."
214         :action(function()
215            print(self:get_help())
216            os.exit(0)
217         end)
218
219      if value ~= true then
220         help = help(value)
221      end
222
223      if not help._name then
224         help "-h" "--help"
225      end
226
227      self._help_option_idx = #self._options
228   end
229end}
230
231local Parser = class({
232   _arguments = {},
233   _options = {},
234   _commands = {},
235   _mutexes = {},
236   _groups = {},
237   _require_command = true,
238   _handle_options = true
239}, {
240   args = 3,
241   typechecked("name", "string"),
242   typechecked("description", "string"),
243   typechecked("epilog", "string"),
244   typechecked("usage", "string"),
245   typechecked("help", "string"),
246   typechecked("require_command", "boolean"),
247   typechecked("handle_options", "boolean"),
248   typechecked("action", "function"),
249   typechecked("command_target", "string"),
250   typechecked("help_vertical_space", "number"),
251   typechecked("usage_margin", "number"),
252   typechecked("usage_max_width", "number"),
253   typechecked("help_usage_margin", "number"),
254   typechecked("help_description_margin", "number"),
255   typechecked("help_max_width", "number"),
256   add_help
257})
258
259local Command = class({
260   _aliases = {}
261}, {
262   args = 3,
263   multiname,
264   typechecked("description", "string"),
265   typechecked("epilog", "string"),
266   typechecked("summary", "string"),
267   typechecked("target", "string"),
268   typechecked("usage", "string"),
269   typechecked("help", "string"),
270   typechecked("require_command", "boolean"),
271   typechecked("handle_options", "boolean"),
272   typechecked("action", "function"),
273   typechecked("command_target", "string"),
274   typechecked("help_vertical_space", "number"),
275   typechecked("usage_margin", "number"),
276   typechecked("usage_max_width", "number"),
277   typechecked("help_usage_margin", "number"),
278   typechecked("help_description_margin", "number"),
279   typechecked("help_max_width", "number"),
280   typechecked("hidden", "boolean"),
281   add_help
282}, Parser)
283
284local Argument = class({
285   _minargs = 1,
286   _maxargs = 1,
287   _mincount = 1,
288   _maxcount = 1,
289   _defmode = "unused",
290   _show_default = true
291}, {
292   args = 5,
293   typechecked("name", "string"),
294   typechecked("description", "string"),
295   option_default,
296   typechecked("convert", "function", "table"),
297   boundaries("args"),
298   typechecked("target", "string"),
299   typechecked("defmode", "string"),
300   typechecked("show_default", "boolean"),
301   typechecked("argname", "string", "table"),
302   typechecked("choices", "table"),
303   typechecked("hidden", "boolean"),
304   option_action,
305   option_init
306})
307
308local Option = class({
309   _aliases = {},
310   _mincount = 0,
311   _overwrite = true
312}, {
313   args = 6,
314   multiname,
315   typechecked("description", "string"),
316   option_default,
317   typechecked("convert", "function", "table"),
318   boundaries("args"),
319   boundaries("count"),
320   typechecked("target", "string"),
321   typechecked("defmode", "string"),
322   typechecked("show_default", "boolean"),
323   typechecked("overwrite", "boolean"),
324   typechecked("argname", "string", "table"),
325   typechecked("choices", "table"),
326   typechecked("hidden", "boolean"),
327   option_action,
328   option_init
329}, Argument)
330
331function Parser:_inherit_property(name, default)
332   local element = self
333
334   while true do
335      local value = element["_" .. name]
336
337      if value ~= nil then
338         return value
339      end
340
341      if not element._parent then
342         return default
343      end
344
345      element = element._parent
346   end
347end
348
349function Argument:_get_argument_list()
350   local buf = {}
351   local i = 1
352
353   while i <= math.min(self._minargs, 3) do
354      local argname = self:_get_argname(i)
355
356      if self._default and self._defmode:find "a" then
357         argname = "[" .. argname .. "]"
358      end
359
360      table.insert(buf, argname)
361      i = i+1
362   end
363
364   while i <= math.min(self._maxargs, 3) do
365      table.insert(buf, "[" .. self:_get_argname(i) .. "]")
366      i = i+1
367
368      if self._maxargs == math.huge then
369         break
370      end
371   end
372
373   if i < self._maxargs then
374      table.insert(buf, "...")
375   end
376
377   return buf
378end
379
380function Argument:_get_usage()
381   local usage = table.concat(self:_get_argument_list(), " ")
382
383   if self._default and self._defmode:find "u" then
384      if self._maxargs > 1 or (self._minargs == 1 and not self._defmode:find "a") then
385         usage = "[" .. usage .. "]"
386      end
387   end
388
389   return usage
390end
391
392function actions.store_true(result, target)
393   result[target] = true
394end
395
396function actions.store_false(result, target)
397   result[target] = false
398end
399
400function actions.store(result, target, argument)
401   result[target] = argument
402end
403
404function actions.count(result, target, _, overwrite)
405   if not overwrite then
406      result[target] = result[target] + 1
407   end
408end
409
410function actions.append(result, target, argument, overwrite)
411   result[target] = result[target] or {}
412   table.insert(result[target], argument)
413
414   if overwrite then
415      table.remove(result[target], 1)
416   end
417end
418
419function actions.concat(result, target, arguments, overwrite)
420   if overwrite then
421      error("'concat' action can't handle too many invocations")
422   end
423
424   result[target] = result[target] or {}
425
426   for _, argument in ipairs(arguments) do
427      table.insert(result[target], argument)
428   end
429end
430
431function Argument:_get_action()
432   local action, init
433
434   if self._maxcount == 1 then
435      if self._maxargs == 0 then
436         action, init = "store_true", nil
437      else
438         action, init = "store", nil
439      end
440   else
441      if self._maxargs == 0 then
442         action, init = "count", 0
443      else
444         action, init = "append", {}
445      end
446   end
447
448   if self._action then
449      action = self._action
450   end
451
452   if self._has_init then
453      init = self._init
454   end
455
456   if type(action) == "string" then
457      action = actions[action]
458   end
459
460   return action, init
461end
462
463-- Returns placeholder for `narg`-th argument.
464function Argument:_get_argname(narg)
465   local argname = self._argname or self:_get_default_argname()
466
467   if type(argname) == "table" then
468      return argname[narg]
469   else
470      return argname
471   end
472end
473
474function Argument:_get_choices_list()
475   return "{" .. table.concat(self._choices, ",") .. "}"
476end
477
478function Argument:_get_default_argname()
479   if self._choices then
480      return self:_get_choices_list()
481   else
482      return "<" .. self._name .. ">"
483   end
484end
485
486function Option:_get_default_argname()
487   if self._choices then
488      return self:_get_choices_list()
489   else
490      return "<" .. self:_get_default_target() .. ">"
491   end
492end
493
494-- Returns labels to be shown in the help message.
495function Argument:_get_label_lines()
496   if self._choices then
497      return {self:_get_choices_list()}
498   else
499      return {self._name}
500   end
501end
502
503function Option:_get_label_lines()
504   local argument_list = self:_get_argument_list()
505
506   if #argument_list == 0 then
507      -- Don't put aliases for simple flags like `-h` on different lines.
508      return {table.concat(self._aliases, ", ")}
509   end
510
511   local longest_alias_length = -1
512
513   for _, alias in ipairs(self._aliases) do
514      longest_alias_length = math.max(longest_alias_length, #alias)
515   end
516
517   local argument_list_repr = table.concat(argument_list, " ")
518   local lines = {}
519
520   for i, alias in ipairs(self._aliases) do
521      local line = (" "):rep(longest_alias_length - #alias) .. alias .. " " .. argument_list_repr
522
523      if i ~= #self._aliases then
524         line = line .. ","
525      end
526
527      table.insert(lines, line)
528   end
529
530   return lines
531end
532
533function Command:_get_label_lines()
534   return {table.concat(self._aliases, ", ")}
535end
536
537function Argument:_get_description()
538   if self._default and self._show_default then
539      if self._description then
540         return ("%s (default: %s)"):format(self._description, self._default)
541      else
542         return ("default: %s"):format(self._default)
543      end
544   else
545      return self._description or ""
546   end
547end
548
549function Command:_get_description()
550   return self._summary or self._description or ""
551end
552
553function Option:_get_usage()
554   local usage = self:_get_argument_list()
555   table.insert(usage, 1, self._name)
556   usage = table.concat(usage, " ")
557
558   if self._mincount == 0 or self._default then
559      usage = "[" .. usage .. "]"
560   end
561
562   return usage
563end
564
565function Argument:_get_default_target()
566   return self._name
567end
568
569function Option:_get_default_target()
570   local res
571
572   for _, alias in ipairs(self._aliases) do
573      if alias:sub(1, 1) == alias:sub(2, 2) then
574         res = alias:sub(3)
575         break
576      end
577   end
578
579   res = res or self._name:sub(2)
580   return (res:gsub("-", "_"))
581end
582
583function Option:_is_vararg()
584   return self._maxargs ~= self._minargs
585end
586
587function Parser:_get_fullname(exclude_root)
588   local parent = self._parent
589   if exclude_root and not parent then
590      return ""
591   end
592   local buf = {self._name}
593
594   while parent do
595      if not exclude_root or parent._parent then
596         table.insert(buf, 1, parent._name)
597      end
598      parent = parent._parent
599   end
600
601   return table.concat(buf, " ")
602end
603
604function Parser:_update_charset(charset)
605   charset = charset or {}
606
607   for _, command in ipairs(self._commands) do
608      command:_update_charset(charset)
609   end
610
611   for _, option in ipairs(self._options) do
612      for _, alias in ipairs(option._aliases) do
613         charset[alias:sub(1, 1)] = true
614      end
615   end
616
617   return charset
618end
619
620function Parser:argument(...)
621   local argument = Argument(...)
622   table.insert(self._arguments, argument)
623   return argument
624end
625
626function Parser:option(...)
627   local option = Option(...)
628   table.insert(self._options, option)
629   return option
630end
631
632function Parser:flag(...)
633   return self:option():args(0)(...)
634end
635
636function Parser:command(...)
637   local command = Command():add_help(true)(...)
638   command._parent = self
639   table.insert(self._commands, command)
640   return command
641end
642
643function Parser:mutex(...)
644   local elements = {...}
645
646   for i, element in ipairs(elements) do
647      local mt = getmetatable(element)
648      assert(mt == Option or mt == Argument, ("bad argument #%d to 'mutex' (Option or Argument expected)"):format(i))
649   end
650
651   table.insert(self._mutexes, elements)
652   return self
653end
654
655function Parser:group(name, ...)
656   assert(type(name) == "string", ("bad argument #1 to 'group' (string expected, got %s)"):format(type(name)))
657
658   local group = {name = name, ...}
659
660   for i, element in ipairs(group) do
661      local mt = getmetatable(element)
662      assert(mt == Option or mt == Argument or mt == Command,
663         ("bad argument #%d to 'group' (Option or Argument or Command expected)"):format(i + 1))
664   end
665
666   table.insert(self._groups, group)
667   return self
668end
669
670local usage_welcome = "Usage: "
671
672function Parser:get_usage()
673   if self._usage then
674      return self._usage
675   end
676
677   local usage_margin = self:_inherit_property("usage_margin", #usage_welcome)
678   local max_usage_width = self:_inherit_property("usage_max_width", 70)
679   local lines = {usage_welcome .. self:_get_fullname()}
680
681   local function add(s)
682      if #lines[#lines]+1+#s <= max_usage_width then
683         lines[#lines] = lines[#lines] .. " " .. s
684      else
685         lines[#lines+1] = (" "):rep(usage_margin) .. s
686      end
687   end
688
689   -- Normally options are before positional arguments in usage messages.
690   -- However, vararg options should be after, because they can't be reliable used
691   -- before a positional argument.
692   -- Mutexes come into play, too, and are shown as soon as possible.
693   -- Overall, output usages in the following order:
694   -- 1. Mutexes that don't have positional arguments or vararg options.
695   -- 2. Options that are not in any mutexes and are not vararg.
696   -- 3. Positional arguments - on their own or as a part of a mutex.
697   -- 4. Remaining mutexes.
698   -- 5. Remaining options.
699
700   local elements_in_mutexes = {}
701   local added_elements = {}
702   local added_mutexes = {}
703   local argument_to_mutexes = {}
704
705   local function add_mutex(mutex, main_argument)
706      if added_mutexes[mutex] then
707         return
708      end
709
710      added_mutexes[mutex] = true
711      local buf = {}
712
713      for _, element in ipairs(mutex) do
714         if not element._hidden and not added_elements[element] then
715            if getmetatable(element) == Option or element == main_argument then
716               table.insert(buf, element:_get_usage())
717               added_elements[element] = true
718            end
719         end
720      end
721
722      if #buf == 1 then
723         add(buf[1])
724      elseif #buf > 1 then
725         add("(" .. table.concat(buf, " | ") .. ")")
726      end
727   end
728
729   local function add_element(element)
730      if not element._hidden and not added_elements[element] then
731         add(element:_get_usage())
732         added_elements[element] = true
733      end
734   end
735
736   for _, mutex in ipairs(self._mutexes) do
737      local is_vararg = false
738      local has_argument = false
739
740      for _, element in ipairs(mutex) do
741         if getmetatable(element) == Option then
742            if element:_is_vararg() then
743               is_vararg = true
744            end
745         else
746            has_argument = true
747            argument_to_mutexes[element] = argument_to_mutexes[element] or {}
748            table.insert(argument_to_mutexes[element], mutex)
749         end
750
751         elements_in_mutexes[element] = true
752      end
753
754      if not is_vararg and not has_argument then
755         add_mutex(mutex)
756      end
757   end
758
759   for _, option in ipairs(self._options) do
760      if not elements_in_mutexes[option] and not option:_is_vararg() then
761         add_element(option)
762      end
763   end
764
765   -- Add usages for positional arguments, together with one mutex containing them, if they are in a mutex.
766   for _, argument in ipairs(self._arguments) do
767      -- Pick a mutex as a part of which to show this argument, take the first one that's still available.
768      local mutex
769
770      if elements_in_mutexes[argument] then
771         for _, argument_mutex in ipairs(argument_to_mutexes[argument]) do
772            if not added_mutexes[argument_mutex] then
773               mutex = argument_mutex
774            end
775         end
776      end
777
778      if mutex then
779         add_mutex(mutex, argument)
780      else
781         add_element(argument)
782      end
783   end
784
785   for _, mutex in ipairs(self._mutexes) do
786      add_mutex(mutex)
787   end
788
789   for _, option in ipairs(self._options) do
790      add_element(option)
791   end
792
793   if #self._commands > 0 then
794      if self._require_command then
795         add("<command>")
796      else
797         add("[<command>]")
798      end
799
800      add("...")
801   end
802
803   return table.concat(lines, "\n")
804end
805
806local function split_lines(s)
807   if s == "" then
808      return {}
809   end
810
811   local lines = {}
812
813   if s:sub(-1) ~= "\n" then
814      s = s .. "\n"
815   end
816
817   for line in s:gmatch("([^\n]*)\n") do
818      table.insert(lines, line)
819   end
820
821   return lines
822end
823
824local function autowrap_line(line, max_length)
825   -- Algorithm for splitting lines is simple and greedy.
826   local result_lines = {}
827
828   -- Preserve original indentation of the line, put this at the beginning of each result line.
829   -- If the first word looks like a list marker ('*', '+', or '-'), add spaces so that starts
830   -- of the second and the following lines vertically align with the start of the second word.
831   local indentation = line:match("^ *")
832
833   if line:find("^ *[%*%+%-]") then
834      indentation = indentation .. " " .. line:match("^ *[%*%+%-]( *)")
835   end
836
837   -- Parts of the last line being assembled.
838   local line_parts = {}
839
840   -- Length of the current line.
841   local line_length = 0
842
843   -- Index of the next character to consider.
844   local index = 1
845
846   while true do
847      local word_start, word_finish, word = line:find("([^ ]+)", index)
848
849      if not word_start then
850         -- Ignore trailing spaces, if any.
851         break
852      end
853
854      local preceding_spaces = line:sub(index, word_start - 1)
855      index = word_finish + 1
856
857      if (#line_parts == 0) or (line_length + #preceding_spaces + #word <= max_length) then
858         -- Either this is the very first word or it fits as an addition to the current line, add it.
859         table.insert(line_parts, preceding_spaces) -- For the very first word this adds the indentation.
860         table.insert(line_parts, word)
861         line_length = line_length + #preceding_spaces + #word
862      else
863         -- Does not fit, finish current line and put the word into a new one.
864         table.insert(result_lines, table.concat(line_parts))
865         line_parts = {indentation, word}
866         line_length = #indentation + #word
867      end
868   end
869
870   if #line_parts > 0 then
871      table.insert(result_lines, table.concat(line_parts))
872   end
873
874   if #result_lines == 0 then
875      -- Preserve empty lines.
876      result_lines[1] = ""
877   end
878
879   return result_lines
880end
881
882-- Automatically wraps lines within given array,
883-- attempting to limit line length to `max_length`.
884-- Existing line splits are preserved.
885local function autowrap(lines, max_length)
886   local result_lines = {}
887
888   for _, line in ipairs(lines) do
889      local autowrapped_lines = autowrap_line(line, max_length)
890
891      for _, autowrapped_line in ipairs(autowrapped_lines) do
892         table.insert(result_lines, autowrapped_line)
893      end
894   end
895
896   return result_lines
897end
898
899function Parser:_get_element_help(element)
900   local label_lines = element:_get_label_lines()
901   local description_lines = split_lines(element:_get_description())
902
903   local result_lines = {}
904
905   -- All label lines should have the same length (except the last one, it has no comma).
906   -- If too long, start description after all the label lines.
907   -- Otherwise, combine label and description lines.
908
909   local usage_margin_len = self:_inherit_property("help_usage_margin", 3)
910   local usage_margin = (" "):rep(usage_margin_len)
911   local description_margin_len = self:_inherit_property("help_description_margin", 25)
912   local description_margin = (" "):rep(description_margin_len)
913
914   local help_max_width = self:_inherit_property("help_max_width")
915
916   if help_max_width then
917      local description_max_width = math.max(help_max_width - description_margin_len, 10)
918      description_lines = autowrap(description_lines, description_max_width)
919   end
920
921   if #label_lines[1] >= (description_margin_len - usage_margin_len) then
922      for _, label_line in ipairs(label_lines) do
923         table.insert(result_lines, usage_margin .. label_line)
924      end
925
926      for _, description_line in ipairs(description_lines) do
927         table.insert(result_lines, description_margin .. description_line)
928      end
929   else
930      for i = 1, math.max(#label_lines, #description_lines) do
931         local label_line = label_lines[i]
932         local description_line = description_lines[i]
933
934         local line = ""
935
936         if label_line then
937            line = usage_margin .. label_line
938         end
939
940         if description_line and description_line ~= "" then
941            line = line .. (" "):rep(description_margin_len - #line) .. description_line
942         end
943
944         table.insert(result_lines, line)
945      end
946   end
947
948   return table.concat(result_lines, "\n")
949end
950
951local function get_group_types(group)
952   local types = {}
953
954   for _, element in ipairs(group) do
955      types[getmetatable(element)] = true
956   end
957
958   return types
959end
960
961function Parser:_add_group_help(blocks, added_elements, label, elements)
962   local buf = {label}
963
964   for _, element in ipairs(elements) do
965      if not element._hidden and not added_elements[element] then
966         added_elements[element] = true
967         table.insert(buf, self:_get_element_help(element))
968      end
969   end
970
971   if #buf > 1 then
972      table.insert(blocks, table.concat(buf, ("\n"):rep(self:_inherit_property("help_vertical_space", 0) + 1)))
973   end
974end
975
976function Parser:get_help()
977   if self._help then
978      return self._help
979   end
980
981   local blocks = {self:get_usage()}
982
983   local help_max_width = self:_inherit_property("help_max_width")
984
985   if self._description then
986      local description = self._description
987
988      if help_max_width then
989         description = table.concat(autowrap(split_lines(description), help_max_width), "\n")
990      end
991
992      table.insert(blocks, description)
993   end
994
995   -- 1. Put groups containing arguments first, then other arguments.
996   -- 2. Put remaining groups containing options, then other options.
997   -- 3. Put remaining groups containing commands, then other commands.
998   -- Assume that an element can't be in several groups.
999   local groups_by_type = {
1000      [Argument] = {},
1001      [Option] = {},
1002      [Command] = {}
1003   }
1004
1005   for _, group in ipairs(self._groups) do
1006      local group_types = get_group_types(group)
1007
1008      for _, mt in ipairs({Argument, Option, Command}) do
1009         if group_types[mt] then
1010            table.insert(groups_by_type[mt], group)
1011            break
1012         end
1013      end
1014   end
1015
1016   local default_groups = {
1017      {name = "Arguments", type = Argument, elements = self._arguments},
1018      {name = "Options", type = Option, elements = self._options},
1019      {name = "Commands", type = Command, elements = self._commands}
1020   }
1021
1022   local added_elements = {}
1023
1024   for _, default_group in ipairs(default_groups) do
1025      local type_groups = groups_by_type[default_group.type]
1026
1027      for _, group in ipairs(type_groups) do
1028         self:_add_group_help(blocks, added_elements, group.name .. ":", group)
1029      end
1030
1031      local default_label = default_group.name .. ":"
1032
1033      if #type_groups > 0 then
1034         default_label = "Other " .. default_label:gsub("^.", string.lower)
1035      end
1036
1037      self:_add_group_help(blocks, added_elements, default_label, default_group.elements)
1038   end
1039
1040   if self._epilog then
1041      local epilog = self._epilog
1042
1043      if help_max_width then
1044         epilog = table.concat(autowrap(split_lines(epilog), help_max_width), "\n")
1045      end
1046
1047      table.insert(blocks, epilog)
1048   end
1049
1050   return table.concat(blocks, "\n\n")
1051end
1052
1053function Parser:add_help_command(value)
1054   if value then
1055      assert(type(value) == "string" or type(value) == "table",
1056         ("bad argument #1 to 'add_help_command' (string or table expected, got %s)"):format(type(value)))
1057   end
1058
1059   local help = self:command()
1060      :description "Show help for commands."
1061   help:argument "command"
1062      :description "The command to show help for."
1063      :args "?"
1064      :action(function(_, _, cmd)
1065         if not cmd then
1066            print(self:get_help())
1067            os.exit(0)
1068         else
1069            for _, command in ipairs(self._commands) do
1070               for _, alias in ipairs(command._aliases) do
1071                  if alias == cmd then
1072                     print(command:get_help())
1073                     os.exit(0)
1074                  end
1075               end
1076            end
1077         end
1078         help:error(("unknown command '%s'"):format(cmd))
1079      end)
1080
1081   if value then
1082      help = help(value)
1083   end
1084
1085   if not help._name then
1086      help "help"
1087   end
1088
1089   help._is_help_command = true
1090   return self
1091end
1092
1093function Parser:_is_shell_safe()
1094   if self._basename then
1095      if self._basename:find("[^%w_%-%+%.]") then
1096         return false
1097      end
1098   else
1099      for _, alias in ipairs(self._aliases) do
1100         if alias:find("[^%w_%-%+%.]") then
1101            return false
1102         end
1103      end
1104   end
1105   for _, option in ipairs(self._options) do
1106      for _, alias in ipairs(option._aliases) do
1107         if alias:find("[^%w_%-%+%.]") then
1108            return false
1109         end
1110      end
1111      if option._choices then
1112         for _, choice in ipairs(option._choices) do
1113            if choice:find("[%s'\"]") then
1114               return false
1115            end
1116         end
1117      end
1118   end
1119   for _, argument in ipairs(self._arguments) do
1120      if argument._choices then
1121         for _, choice in ipairs(argument._choices) do
1122            if choice:find("[%s'\"]") then
1123               return false
1124            end
1125         end
1126      end
1127   end
1128   for _, command in ipairs(self._commands) do
1129      if not command:_is_shell_safe() then
1130         return false
1131      end
1132   end
1133   return true
1134end
1135
1136function Parser:add_complete(value)
1137   if value then
1138      assert(type(value) == "string" or type(value) == "table",
1139         ("bad argument #1 to 'add_complete' (string or table expected, got %s)"):format(type(value)))
1140   end
1141
1142   local complete = self:option()
1143      :description "Output a shell completion script for the specified shell."
1144      :args(1)
1145      :choices {"bash", "zsh", "fish"}
1146      :action(function(_, _, shell)
1147         io.write(self["get_" .. shell .. "_complete"](self))
1148         os.exit(0)
1149      end)
1150
1151   if value then
1152      complete = complete(value)
1153   end
1154
1155   if not complete._name then
1156      complete "--completion"
1157   end
1158
1159   return self
1160end
1161
1162function Parser:add_complete_command(value)
1163   if value then
1164      assert(type(value) == "string" or type(value) == "table",
1165         ("bad argument #1 to 'add_complete_command' (string or table expected, got %s)"):format(type(value)))
1166   end
1167
1168   local complete = self:command()
1169      :description "Output a shell completion script."
1170   complete:argument "shell"
1171      :description "The shell to output a completion script for."
1172      :choices {"bash", "zsh", "fish"}
1173      :action(function(_, _, shell)
1174         io.write(self["get_" .. shell .. "_complete"](self))
1175         os.exit(0)
1176      end)
1177
1178   if value then
1179      complete = complete(value)
1180   end
1181
1182   if not complete._name then
1183      complete "completion"
1184   end
1185
1186   return self
1187end
1188
1189local function base_name(pathname)
1190   return pathname:gsub("[/\\]*$", ""):match(".*[/\\]([^/\\]*)") or pathname
1191end
1192
1193local function get_short_description(element)
1194   local short = element:_get_description():match("^(.-)%.%s")
1195   return short or element:_get_description():match("^(.-)%.?$")
1196end
1197
1198function Parser:_get_options()
1199   local options = {}
1200   for _, option in ipairs(self._options) do
1201      for _, alias in ipairs(option._aliases) do
1202         table.insert(options, alias)
1203      end
1204   end
1205   return table.concat(options, " ")
1206end
1207
1208function Parser:_get_commands()
1209   local commands = {}
1210   for _, command in ipairs(self._commands) do
1211      for _, alias in ipairs(command._aliases) do
1212         table.insert(commands, alias)
1213      end
1214   end
1215   return table.concat(commands, " ")
1216end
1217
1218function Parser:_bash_option_args(buf, indent)
1219   local opts = {}
1220   for _, option in ipairs(self._options) do
1221      if option._choices or option._minargs > 0 then
1222         local compreply
1223         if option._choices then
1224            compreply = 'COMPREPLY=($(compgen -W "' .. table.concat(option._choices, " ") .. '" -- "$cur"))'
1225         else
1226            compreply = 'COMPREPLY=($(compgen -f -- "$cur"))'
1227         end
1228         table.insert(opts, (" "):rep(indent + 4) .. table.concat(option._aliases, "|") .. ")")
1229         table.insert(opts, (" "):rep(indent + 8) .. compreply)
1230         table.insert(opts, (" "):rep(indent + 8) .. "return 0")
1231         table.insert(opts, (" "):rep(indent + 8) .. ";;")
1232      end
1233   end
1234
1235   if #opts > 0 then
1236      table.insert(buf, (" "):rep(indent) .. 'case "$prev" in')
1237      table.insert(buf, table.concat(opts, "\n"))
1238      table.insert(buf, (" "):rep(indent) .. "esac\n")
1239   end
1240end
1241
1242function Parser:_bash_get_cmd(buf, indent)
1243   if #self._commands == 0 then
1244      return
1245   end
1246
1247   table.insert(buf, (" "):rep(indent) .. 'args=("${args[@]:1}")')
1248   table.insert(buf, (" "):rep(indent) .. 'for arg in "${args[@]}"; do')
1249   table.insert(buf, (" "):rep(indent + 4) .. 'case "$arg" in')
1250
1251   for _, command in ipairs(self._commands) do
1252      table.insert(buf, (" "):rep(indent + 8) .. table.concat(command._aliases, "|") .. ")")
1253      if self._parent then
1254         table.insert(buf, (" "):rep(indent + 12) .. 'cmd="$cmd ' .. command._name .. '"')
1255      else
1256         table.insert(buf, (" "):rep(indent + 12) .. 'cmd="' .. command._name .. '"')
1257      end
1258      table.insert(buf, (" "):rep(indent + 12) .. 'opts="$opts ' .. command:_get_options() .. '"')
1259      command:_bash_get_cmd(buf, indent + 12)
1260      table.insert(buf, (" "):rep(indent + 12) .. "break")
1261      table.insert(buf, (" "):rep(indent + 12) .. ";;")
1262   end
1263
1264   table.insert(buf, (" "):rep(indent + 4) .. "esac")
1265   table.insert(buf, (" "):rep(indent) .. "done")
1266end
1267
1268function Parser:_bash_cmd_completions(buf)
1269   local cmd_buf = {}
1270   if self._parent then
1271      self:_bash_option_args(cmd_buf, 12)
1272   end
1273   if #self._commands > 0 then
1274      table.insert(cmd_buf, (" "):rep(12) .. 'COMPREPLY=($(compgen -W "' .. self:_get_commands() .. '" -- "$cur"))')
1275   elseif self._is_help_command then
1276      table.insert(cmd_buf, (" "):rep(12)
1277         .. 'COMPREPLY=($(compgen -W "'
1278         .. self._parent:_get_commands()
1279         .. '" -- "$cur"))')
1280   end
1281   if #cmd_buf > 0 then
1282      table.insert(buf, (" "):rep(8) .. "'" .. self:_get_fullname(true) .. "')")
1283      table.insert(buf, table.concat(cmd_buf, "\n"))
1284      table.insert(buf, (" "):rep(12) .. ";;")
1285   end
1286
1287   for _, command in ipairs(self._commands) do
1288      command:_bash_cmd_completions(buf)
1289   end
1290end
1291
1292function Parser:get_bash_complete()
1293   self._basename = base_name(self._name)
1294   assert(self:_is_shell_safe())
1295   local buf = {([[
1296_%s() {
1297    local IFS=$' \t\n'
1298    local args cur prev cmd opts arg
1299    args=("${COMP_WORDS[@]}")
1300    cur="${COMP_WORDS[COMP_CWORD]}"
1301    prev="${COMP_WORDS[COMP_CWORD-1]}"
1302    opts="%s"
1303]]):format(self._basename, self:_get_options())}
1304
1305   self:_bash_option_args(buf, 4)
1306   self:_bash_get_cmd(buf, 4)
1307   if #self._commands > 0 then
1308      table.insert(buf, "")
1309      table.insert(buf, (" "):rep(4) .. 'case "$cmd" in')
1310      self:_bash_cmd_completions(buf)
1311      table.insert(buf, (" "):rep(4) .. "esac\n")
1312   end
1313
1314   table.insert(buf, ([=[
1315    if [[ "$cur" = -* ]]; then
1316        COMPREPLY=($(compgen -W "$opts" -- "$cur"))
1317    fi
1318}
1319
1320complete -F _%s -o bashdefault -o default %s
1321]=]):format(self._basename, self._basename))
1322
1323   return table.concat(buf, "\n")
1324end
1325
1326function Parser:_zsh_arguments(buf, cmd_name, indent)
1327   if self._parent then
1328      table.insert(buf, (" "):rep(indent) .. "options=(")
1329      table.insert(buf, (" "):rep(indent + 2) .. "$options")
1330   else
1331      table.insert(buf, (" "):rep(indent) .. "local -a options=(")
1332   end
1333
1334   for _, option in ipairs(self._options) do
1335      local line = {}
1336      if #option._aliases > 1 then
1337         if option._maxcount > 1 then
1338            table.insert(line, '"*"')
1339         end
1340         table.insert(line, "{" .. table.concat(option._aliases, ",") .. '}"')
1341      else
1342         table.insert(line, '"')
1343         if option._maxcount > 1 then
1344            table.insert(line, "*")
1345         end
1346         table.insert(line, option._name)
1347      end
1348      if option._description then
1349         local description = get_short_description(option):gsub('["%]:`$]', "\\%0")
1350         table.insert(line, "[" .. description .. "]")
1351      end
1352      if option._maxargs == math.huge then
1353         table.insert(line, ":*")
1354      end
1355      if option._choices then
1356         table.insert(line, ": :(" .. table.concat(option._choices, " ") .. ")")
1357      elseif option._maxargs > 0 then
1358         table.insert(line, ": :_files")
1359      end
1360      table.insert(line, '"')
1361      table.insert(buf, (" "):rep(indent + 2) .. table.concat(line))
1362   end
1363
1364   table.insert(buf, (" "):rep(indent) .. ")")
1365   table.insert(buf, (" "):rep(indent) .. "_arguments -s -S \\")
1366   table.insert(buf, (" "):rep(indent + 2) .. "$options \\")
1367
1368   if self._is_help_command then
1369      table.insert(buf, (" "):rep(indent + 2) .. '": :(' .. self._parent:_get_commands() .. ')" \\')
1370   else
1371      for _, argument in ipairs(self._arguments) do
1372         local spec
1373         if argument._choices then
1374            spec = ": :(" .. table.concat(argument._choices, " ") .. ")"
1375         else
1376            spec = ": :_files"
1377         end
1378         if argument._maxargs == math.huge then
1379            table.insert(buf, (" "):rep(indent + 2) .. '"*' .. spec .. '" \\')
1380            break
1381         end
1382         for _ = 1, argument._maxargs do
1383            table.insert(buf, (" "):rep(indent + 2) .. '"' .. spec .. '" \\')
1384         end
1385      end
1386
1387      if #self._commands > 0 then
1388         table.insert(buf, (" "):rep(indent + 2) .. '": :_' .. cmd_name .. '_cmds" \\')
1389         table.insert(buf, (" "):rep(indent + 2) .. '"*:: :->args" \\')
1390      end
1391   end
1392
1393   table.insert(buf, (" "):rep(indent + 2) .. "&& return 0")
1394end
1395
1396function Parser:_zsh_cmds(buf, cmd_name)
1397   table.insert(buf, "\n_" .. cmd_name .. "_cmds() {")
1398   table.insert(buf, "  local -a commands=(")
1399
1400   for _, command in ipairs(self._commands) do
1401      local line = {}
1402      if #command._aliases > 1 then
1403         table.insert(line, "{" .. table.concat(command._aliases, ",") .. '}"')
1404      else
1405         table.insert(line, '"' .. command._name)
1406      end
1407      if command._description then
1408         table.insert(line, ":" .. get_short_description(command):gsub('["`$]', "\\%0"))
1409      end
1410      table.insert(buf, "    " .. table.concat(line) .. '"')
1411   end
1412
1413   table.insert(buf, '  )\n  _describe "command" commands\n}')
1414end
1415
1416function Parser:_zsh_complete_help(buf, cmds_buf, cmd_name, indent)
1417   if #self._commands == 0 then
1418      return
1419   end
1420
1421   self:_zsh_cmds(cmds_buf, cmd_name)
1422   table.insert(buf, "\n" .. (" "):rep(indent) .. "case $words[1] in")
1423
1424   for _, command in ipairs(self._commands) do
1425      local name = cmd_name .. "_" .. command._name
1426      table.insert(buf, (" "):rep(indent + 2) .. table.concat(command._aliases, "|") .. ")")
1427      command:_zsh_arguments(buf, name, indent + 4)
1428      command:_zsh_complete_help(buf, cmds_buf, name, indent + 4)
1429      table.insert(buf, (" "):rep(indent + 4) .. ";;\n")
1430   end
1431
1432   table.insert(buf, (" "):rep(indent) .. "esac")
1433end
1434
1435function Parser:get_zsh_complete()
1436   self._basename = base_name(self._name)
1437   assert(self:_is_shell_safe())
1438   local buf = {("#compdef %s\n"):format(self._basename)}
1439   local cmds_buf = {}
1440   table.insert(buf, "_" .. self._basename .. "() {")
1441   if #self._commands > 0 then
1442      table.insert(buf, "  local context state state_descr line")
1443      table.insert(buf, "  typeset -A opt_args\n")
1444   end
1445   self:_zsh_arguments(buf, self._basename, 2)
1446   self:_zsh_complete_help(buf, cmds_buf, self._basename, 2)
1447   table.insert(buf, "\n  return 1")
1448   table.insert(buf, "}")
1449
1450   local result = table.concat(buf, "\n")
1451   if #cmds_buf > 0 then
1452      result = result .. "\n" .. table.concat(cmds_buf, "\n")
1453   end
1454   return result .. "\n\n_" .. self._basename .. "\n"
1455end
1456
1457local function fish_escape(string)
1458   return string:gsub("[\\']", "\\%0")
1459end
1460
1461function Parser:_fish_get_cmd(buf, indent)
1462   if #self._commands == 0 then
1463      return
1464   end
1465
1466   table.insert(buf, (" "):rep(indent) .. "set -e cmdline[1]")
1467   table.insert(buf, (" "):rep(indent) .. "for arg in $cmdline")
1468   table.insert(buf, (" "):rep(indent + 4) .. "switch $arg")
1469
1470   for _, command in ipairs(self._commands) do
1471      table.insert(buf, (" "):rep(indent + 8) .. "case " .. table.concat(command._aliases, " "))
1472      table.insert(buf, (" "):rep(indent + 12) .. "set cmd $cmd " .. command._name)
1473      command:_fish_get_cmd(buf, indent + 12)
1474      table.insert(buf, (" "):rep(indent + 12) .. "break")
1475   end
1476
1477   table.insert(buf, (" "):rep(indent + 4) .. "end")
1478   table.insert(buf, (" "):rep(indent) .. "end")
1479end
1480
1481function Parser:_fish_complete_help(buf, basename)
1482   local prefix = "complete -c " .. basename
1483   table.insert(buf, "")
1484
1485   for _, command in ipairs(self._commands) do
1486      local aliases = table.concat(command._aliases, " ")
1487      local line
1488      if self._parent then
1489         line = ("%s -n '__fish_%s_using_command %s' -xa '%s'")
1490            :format(prefix, basename, self:_get_fullname(true), aliases)
1491      else
1492         line = ("%s -n '__fish_%s_using_command' -xa '%s'"):format(prefix, basename, aliases)
1493      end
1494      if command._description then
1495         line = ("%s -d '%s'"):format(line, fish_escape(get_short_description(command)))
1496      end
1497      table.insert(buf, line)
1498   end
1499
1500   if self._is_help_command then
1501      local line = ("%s -n '__fish_%s_using_command %s' -xa '%s'")
1502         :format(prefix, basename, self:_get_fullname(true), self._parent:_get_commands())
1503      table.insert(buf, line)
1504   end
1505
1506   for _, option in ipairs(self._options) do
1507      local parts = {prefix}
1508
1509      if self._parent then
1510         table.insert(parts, "-n '__fish_" .. basename .. "_seen_command " .. self:_get_fullname(true) .. "'")
1511      end
1512
1513      for _, alias in ipairs(option._aliases) do
1514         if alias:match("^%-.$") then
1515            table.insert(parts, "-s " .. alias:sub(2))
1516         elseif alias:match("^%-%-.+") then
1517            table.insert(parts, "-l " .. alias:sub(3))
1518         end
1519      end
1520
1521      if option._choices then
1522         table.insert(parts, "-xa '" .. table.concat(option._choices, " ") .. "'")
1523      elseif option._minargs > 0 then
1524         table.insert(parts, "-r")
1525      end
1526
1527      if option._description then
1528         table.insert(parts, "-d '" .. fish_escape(get_short_description(option)) .. "'")
1529      end
1530
1531      table.insert(buf, table.concat(parts, " "))
1532   end
1533
1534   for _, command in ipairs(self._commands) do
1535      command:_fish_complete_help(buf, basename)
1536   end
1537end
1538
1539function Parser:get_fish_complete()
1540   self._basename = base_name(self._name)
1541   assert(self:_is_shell_safe())
1542   local buf = {}
1543
1544   if #self._commands > 0 then
1545      table.insert(buf, ([[
1546function __fish_%s_print_command
1547    set -l cmdline (commandline -poc)
1548    set -l cmd]]):format(self._basename))
1549      self:_fish_get_cmd(buf, 4)
1550      table.insert(buf, ([[
1551    echo "$cmd"
1552end
1553
1554function __fish_%s_using_command
1555    test (__fish_%s_print_command) = "$argv"
1556    and return 0
1557    or return 1
1558end
1559
1560function __fish_%s_seen_command
1561    string match -q "$argv*" (__fish_%s_print_command)
1562    and return 0
1563    or return 1
1564end]]):format(self._basename, self._basename, self._basename, self._basename))
1565   end
1566
1567   self:_fish_complete_help(buf, self._basename)
1568   return table.concat(buf, "\n") .. "\n"
1569end
1570
1571local function get_tip(context, wrong_name)
1572   local context_pool = {}
1573   local possible_name
1574   local possible_names = {}
1575
1576   for name in pairs(context) do
1577      if type(name) == "string" then
1578         for i = 1, #name do
1579            possible_name = name:sub(1, i - 1) .. name:sub(i + 1)
1580
1581            if not context_pool[possible_name] then
1582               context_pool[possible_name] = {}
1583            end
1584
1585            table.insert(context_pool[possible_name], name)
1586         end
1587      end
1588   end
1589
1590   for i = 1, #wrong_name + 1 do
1591      possible_name = wrong_name:sub(1, i - 1) .. wrong_name:sub(i + 1)
1592
1593      if context[possible_name] then
1594         possible_names[possible_name] = true
1595      elseif context_pool[possible_name] then
1596         for _, name in ipairs(context_pool[possible_name]) do
1597            possible_names[name] = true
1598         end
1599      end
1600   end
1601
1602   local first = next(possible_names)
1603
1604   if first then
1605      if next(possible_names, first) then
1606         local possible_names_arr = {}
1607
1608         for name in pairs(possible_names) do
1609            table.insert(possible_names_arr, "'" .. name .. "'")
1610         end
1611
1612         table.sort(possible_names_arr)
1613         return "\nDid you mean one of these: " .. table.concat(possible_names_arr, " ") .. "?"
1614      else
1615         return "\nDid you mean '" .. first .. "'?"
1616      end
1617   else
1618      return ""
1619   end
1620end
1621
1622local ElementState = class({
1623   invocations = 0
1624})
1625
1626function ElementState:__call(state, element)
1627   self.state = state
1628   self.result = state.result
1629   self.element = element
1630   self.target = element._target or element:_get_default_target()
1631   self.action, self.result[self.target] = element:_get_action()
1632   return self
1633end
1634
1635function ElementState:error(fmt, ...)
1636   self.state:error(fmt, ...)
1637end
1638
1639function ElementState:convert(argument, index)
1640   local converter = self.element._convert
1641
1642   if converter then
1643      local ok, err
1644
1645      if type(converter) == "function" then
1646         ok, err = converter(argument)
1647      elseif type(converter[index]) == "function" then
1648         ok, err = converter[index](argument)
1649      else
1650         ok = converter[argument]
1651      end
1652
1653      if ok == nil then
1654         self:error(err and "%s" or "malformed argument '%s'", err or argument)
1655      end
1656
1657      argument = ok
1658   end
1659
1660   return argument
1661end
1662
1663function ElementState:default(mode)
1664   return self.element._defmode:find(mode) and self.element._default
1665end
1666
1667local function bound(noun, min, max, is_max)
1668   local res = ""
1669
1670   if min ~= max then
1671      res = "at " .. (is_max and "most" or "least") .. " "
1672   end
1673
1674   local number = is_max and max or min
1675   return res .. tostring(number) .. " " .. noun ..  (number == 1 and "" or "s")
1676end
1677
1678function ElementState:set_name(alias)
1679   self.name = ("%s '%s'"):format(alias and "option" or "argument", alias or self.element._name)
1680end
1681
1682function ElementState:invoke()
1683   self.open = true
1684   self.overwrite = false
1685
1686   if self.invocations >= self.element._maxcount then
1687      if self.element._overwrite then
1688         self.overwrite = true
1689      else
1690         local num_times_repr = bound("time", self.element._mincount, self.element._maxcount, true)
1691         self:error("%s must be used %s", self.name, num_times_repr)
1692      end
1693   else
1694      self.invocations = self.invocations + 1
1695   end
1696
1697   self.args = {}
1698
1699   if self.element._maxargs <= 0 then
1700      self:close()
1701   end
1702
1703   return self.open
1704end
1705
1706function ElementState:check_choices(argument)
1707   if self.element._choices then
1708      for _, choice in ipairs(self.element._choices) do
1709         if argument == choice then
1710            return
1711         end
1712      end
1713      local choices_list = "'" .. table.concat(self.element._choices, "', '") .. "'"
1714      local is_option = getmetatable(self.element) == Option
1715      self:error("%s%s must be one of %s", is_option and "argument for " or "", self.name, choices_list)
1716   end
1717end
1718
1719function ElementState:pass(argument)
1720   self:check_choices(argument)
1721   argument = self:convert(argument, #self.args + 1)
1722   table.insert(self.args, argument)
1723
1724   if #self.args >= self.element._maxargs then
1725      self:close()
1726   end
1727
1728   return self.open
1729end
1730
1731function ElementState:complete_invocation()
1732   while #self.args < self.element._minargs do
1733      self:pass(self.element._default)
1734   end
1735end
1736
1737function ElementState:close()
1738   if self.open then
1739      self.open = false
1740
1741      if #self.args < self.element._minargs then
1742         if self:default("a") then
1743            self:complete_invocation()
1744         else
1745            if #self.args == 0 then
1746               if getmetatable(self.element) == Argument then
1747                  self:error("missing %s", self.name)
1748               elseif self.element._maxargs == 1 then
1749                  self:error("%s requires an argument", self.name)
1750               end
1751            end
1752
1753            self:error("%s requires %s", self.name, bound("argument", self.element._minargs, self.element._maxargs))
1754         end
1755      end
1756
1757      local args
1758
1759      if self.element._maxargs == 0 then
1760         args = self.args[1]
1761      elseif self.element._maxargs == 1 then
1762         if self.element._minargs == 0 and self.element._mincount ~= self.element._maxcount then
1763            args = self.args
1764         else
1765            args = self.args[1]
1766         end
1767      else
1768         args = self.args
1769      end
1770
1771      self.action(self.result, self.target, args, self.overwrite)
1772   end
1773end
1774
1775local ParseState = class({
1776   result = {},
1777   options = {},
1778   arguments = {},
1779   argument_i = 1,
1780   element_to_mutexes = {},
1781   mutex_to_element_state = {},
1782   command_actions = {}
1783})
1784
1785function ParseState:__call(parser, error_handler)
1786   self.parser = parser
1787   self.error_handler = error_handler
1788   self.charset = parser:_update_charset()
1789   self:switch(parser)
1790   return self
1791end
1792
1793function ParseState:error(fmt, ...)
1794   self.error_handler(self.parser, fmt:format(...))
1795end
1796
1797function ParseState:switch(parser)
1798   self.parser = parser
1799
1800   if parser._action then
1801      table.insert(self.command_actions, {action = parser._action, name = parser._name})
1802   end
1803
1804   for _, option in ipairs(parser._options) do
1805      option = ElementState(self, option)
1806      table.insert(self.options, option)
1807
1808      for _, alias in ipairs(option.element._aliases) do
1809         self.options[alias] = option
1810      end
1811   end
1812
1813   for _, mutex in ipairs(parser._mutexes) do
1814      for _, element in ipairs(mutex) do
1815         if not self.element_to_mutexes[element] then
1816            self.element_to_mutexes[element] = {}
1817         end
1818
1819         table.insert(self.element_to_mutexes[element], mutex)
1820      end
1821   end
1822
1823   for _, argument in ipairs(parser._arguments) do
1824      argument = ElementState(self, argument)
1825      table.insert(self.arguments, argument)
1826      argument:set_name()
1827      argument:invoke()
1828   end
1829
1830   self.handle_options = parser._handle_options
1831   self.argument = self.arguments[self.argument_i]
1832   self.commands = parser._commands
1833
1834   for _, command in ipairs(self.commands) do
1835      for _, alias in ipairs(command._aliases) do
1836         self.commands[alias] = command
1837      end
1838   end
1839end
1840
1841function ParseState:get_option(name)
1842   local option = self.options[name]
1843
1844   if not option then
1845      self:error("unknown option '%s'%s", name, get_tip(self.options, name))
1846   else
1847      return option
1848   end
1849end
1850
1851function ParseState:get_command(name)
1852   local command = self.commands[name]
1853
1854   if not command then
1855      if #self.commands > 0 then
1856         self:error("unknown command '%s'%s", name, get_tip(self.commands, name))
1857      else
1858         self:error("too many arguments")
1859      end
1860   else
1861      return command
1862   end
1863end
1864
1865function ParseState:check_mutexes(element_state)
1866   if self.element_to_mutexes[element_state.element] then
1867      for _, mutex in ipairs(self.element_to_mutexes[element_state.element]) do
1868         local used_element_state = self.mutex_to_element_state[mutex]
1869
1870         if used_element_state and used_element_state ~= element_state then
1871            self:error("%s can not be used together with %s", element_state.name, used_element_state.name)
1872         else
1873            self.mutex_to_element_state[mutex] = element_state
1874         end
1875      end
1876   end
1877end
1878
1879function ParseState:invoke(option, name)
1880   self:close()
1881   option:set_name(name)
1882   self:check_mutexes(option, name)
1883
1884   if option:invoke() then
1885      self.option = option
1886   end
1887end
1888
1889function ParseState:pass(arg)
1890   if self.option then
1891      if not self.option:pass(arg) then
1892         self.option = nil
1893      end
1894   elseif self.argument then
1895      self:check_mutexes(self.argument)
1896
1897      if not self.argument:pass(arg) then
1898         self.argument_i = self.argument_i + 1
1899         self.argument = self.arguments[self.argument_i]
1900      end
1901   else
1902      local command = self:get_command(arg)
1903      self.result[command._target or command._name] = true
1904
1905      if self.parser._command_target then
1906         self.result[self.parser._command_target] = command._name
1907      end
1908
1909      self:switch(command)
1910   end
1911end
1912
1913function ParseState:close()
1914   if self.option then
1915      self.option:close()
1916      self.option = nil
1917   end
1918end
1919
1920function ParseState:finalize()
1921   self:close()
1922
1923   for i = self.argument_i, #self.arguments do
1924      local argument = self.arguments[i]
1925      if #argument.args == 0 and argument:default("u") then
1926         argument:complete_invocation()
1927      else
1928         argument:close()
1929      end
1930   end
1931
1932   if self.parser._require_command and #self.commands > 0 then
1933      self:error("a command is required")
1934   end
1935
1936   for _, option in ipairs(self.options) do
1937      option.name = option.name or ("option '%s'"):format(option.element._name)
1938
1939      if option.invocations == 0 then
1940         if option:default("u") then
1941            option:invoke()
1942            option:complete_invocation()
1943            option:close()
1944         end
1945      end
1946
1947      local mincount = option.element._mincount
1948
1949      if option.invocations < mincount then
1950         if option:default("a") then
1951            while option.invocations < mincount do
1952               option:invoke()
1953               option:close()
1954            end
1955         elseif option.invocations == 0 then
1956            self:error("missing %s", option.name)
1957         else
1958            self:error("%s must be used %s", option.name, bound("time", mincount, option.element._maxcount))
1959         end
1960      end
1961   end
1962
1963   for i = #self.command_actions, 1, -1 do
1964      self.command_actions[i].action(self.result, self.command_actions[i].name)
1965   end
1966end
1967
1968function ParseState:parse(args)
1969   for _, arg in ipairs(args) do
1970      local plain = true
1971
1972      if self.handle_options then
1973         local first = arg:sub(1, 1)
1974
1975         if self.charset[first] then
1976            if #arg > 1 then
1977               plain = false
1978
1979               if arg:sub(2, 2) == first then
1980                  if #arg == 2 then
1981                     if self.options[arg] then
1982                        local option = self:get_option(arg)
1983                        self:invoke(option, arg)
1984                     else
1985                        self:close()
1986                     end
1987
1988                     self.handle_options = false
1989                  else
1990                     local equals = arg:find "="
1991                     if equals then
1992                        local name = arg:sub(1, equals - 1)
1993                        local option = self:get_option(name)
1994
1995                        if option.element._maxargs <= 0 then
1996                           self:error("option '%s' does not take arguments", name)
1997                        end
1998
1999                        self:invoke(option, name)
2000                        self:pass(arg:sub(equals + 1))
2001                     else
2002                        local option = self:get_option(arg)
2003                        self:invoke(option, arg)
2004                     end
2005                  end
2006               else
2007                  for i = 2, #arg do
2008                     local name = first .. arg:sub(i, i)
2009                     local option = self:get_option(name)
2010                     self:invoke(option, name)
2011
2012                     if i ~= #arg and option.element._maxargs > 0 then
2013                        self:pass(arg:sub(i + 1))
2014                        break
2015                     end
2016                  end
2017               end
2018            end
2019         end
2020      end
2021
2022      if plain then
2023         self:pass(arg)
2024      end
2025   end
2026
2027   self:finalize()
2028   return self.result
2029end
2030
2031function Parser:error(msg)
2032   io.stderr:write(("%s\n\nError: %s\n"):format(self:get_usage(), msg))
2033   os.exit(1)
2034end
2035
2036-- Compatibility with strict.lua and other checkers:
2037local default_cmdline = rawget(_G, "arg") or {}
2038
2039function Parser:_parse(args, error_handler)
2040   return ParseState(self, error_handler):parse(args or default_cmdline)
2041end
2042
2043function Parser:parse(args)
2044   return self:_parse(args, self.error)
2045end
2046
2047local function xpcall_error_handler(err)
2048   return tostring(err) .. "\noriginal " .. debug.traceback("", 2):sub(2)
2049end
2050
2051function Parser:pparse(args)
2052   local parse_error
2053
2054   local ok, result = xpcall(function()
2055      return self:_parse(args, function(_, err)
2056         parse_error = err
2057         error(err, 0)
2058      end)
2059   end, xpcall_error_handler)
2060
2061   if ok then
2062      return true, result
2063   elseif not parse_error then
2064      error(result, 0)
2065   else
2066      return false, parse_error
2067   end
2068end
2069
2070local argparse = {}
2071
2072argparse.version = "0.7.0"
2073
2074setmetatable(argparse, {__call = function(_, ...)
2075   return Parser(default_cmdline[0]):add_help(true)(...)
2076end})
2077
2078return argparse
2079