# # # Nim's Runtime Library # (c) Copyright 2015 Andreas Rumpf # # See the file "copying.txt", included in this # distribution, for details about the copyright. # ## This module provides the standard Nim command line parser. ## It supports one convenience iterator over all command line options and some ## lower-level features. ## ## Supported syntax with default empty ``shortNoVal``/``longNoVal``: ## ## 1. short options - ``-abcd``, where a, b, c, d are names ## 2. long option - ``--foo:bar``, ``--foo=bar`` or ``--foo`` ## 3. argument - everything else ## ## When ``shortNoVal``/``longNoVal`` are non-empty then the ':' and '=' above ## are still accepted, but become optional. Note that these option key sets ## must be updated along with the set of option keys taking no value, but ## keys which do take values need no special updates as their set evolves. ## ## When option values begin with ':' or '=' they need to be doubled up (as in ## ``--delim::``) or alternated (as in ``--delim=:``). ## ## The common ``--`` non-option argument delimiter appears as an empty string ## long option key. ``OptParser.cmd``, ``OptParser.pos``, and ## ``os.parseCmdLine`` may be used to complete parsing in that case. {.push debugger: off.} include "system/inclrtl" import os, strutils type CmdLineKind* = enum ## the detected command line token cmdEnd, ## end of command line reached cmdArgument, ## argument detected cmdLongOption, ## a long option ``--option`` detected cmdShortOption ## a short option ``-c`` detected OptParser* = object of RootObj ## this object implements the command line parser cmd*: string # cmd,pos exported so caller can catch "--" as.. pos*: int # ..empty key or subcmd cmdArg & handle specially inShortState: bool shortNoVal: set[char] longNoVal: seq[string] kind*: CmdLineKind ## the dected command line token key*, val*: TaintedString ## key and value pair; ``key`` is the option ## or the argument, ``value`` is not "" if ## the option was given a value {.deprecated: [TCmdLineKind: CmdLineKind, TOptParser: OptParser].} proc parseWord(s: string, i: int, w: var string, delim: set[char] = {'\x09', ' ', '\0'}): int = result = i if s[result] == '\"': inc(result) while not (s[result] in {'\0', '\"'}): add(w, s[result]) inc(result) if s[result] == '\"': inc(result) else: while not (s[result] in delim): add(w, s[result]) inc(result) when declared(os.paramCount): proc quote(s: string): string = if find(s, {' ', '\t'}) >= 0 and s[0] != '"': if s[0] == '-': result = newStringOfCap(s.len) var i = parseWord(s, 0, result, {'\0', ' ', '\x09', ':', '='}) if s[i] in {':','='}: result.add s[i] inc i result.add '"' while i < s.len: result.add s[i] inc i result.add '"' else: result = '"' & s & '"' else: result = s # we cannot provide this for NimRtl creation on Posix, because we can't # access the command line arguments then! proc initOptParser*(cmdline = "", shortNoVal: set[char]={}, longNoVal: seq[string] = @[]): OptParser = ## inits the option parser. If ``cmdline == ""``, the real command line ## (as provided by the ``OS`` module) is taken. If ``shortNoVal`` is ## provided command users do not need to delimit short option keys and ## values with a ':' or '='. If ``longNoVal`` is provided command users do ## not need to delimit long option keys and values with a ':' or '=' ## (though they still need at least a space). In both cases, ':' or '=' ## may still be used if desired. They just become optional. result.pos = 0 result.inShortState = false result.shortNoVal = shortNoVal result.longNoVal = longNoVal if cmdline != "": result.cmd = cmdline else: result.cmd = "" for i in countup(1, paramCount()): result.cmd.add quote(paramStr(i).string) result.cmd.add ' ' result.kind = cmdEnd result.key = TaintedString"" result.val = TaintedString"" proc initOptParser*(cmdline: seq[TaintedString], shortNoVal: set[char]={}, longNoVal: seq[string] = @[]): OptParser = ## inits the option parser. If ``cmdline.len == 0``, the real command line ## (as provided by the ``OS`` module) is taken. ``shortNoVal`` and ## ``longNoVal`` behavior is the same as for ``initOptParser(string,...)``. result.pos = 0 result.inShortState = false result.shortNoVal = shortNoVal result.longNoVal = longNoVal result.cmd = "" if cmdline.len != 0: for i in 0..