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