123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311 |
- #
- #
- # Nim's Runtime Library
- # (c) Copyright 2022 Andreas Rumpf
- #
- # See the file "copying.txt", included in this
- # distribution, for details about the copyright.
- #
- ## This module contains system facilities for reading command
- ## line parameters.
- ## **See also:**
- ## * `parseopt module <parseopt.html>`_ for command-line parser beyond
- ## `parseCmdLine proc`_
- include system/inclrtl
- when defined(nimPreviewSlimSystem):
- import std/widestrs
-
- when defined(nodejs):
- from std/private/oscommon import ReadDirEffect
- const weirdTarget = defined(nimscript) or defined(js)
- when weirdTarget:
- discard
- elif defined(windows):
- import winlean
- elif defined(posix):
- import posix
- else:
- {.error: "The cmdline module has not been implemented for the target platform.".}
- # Needed by windows in order to obtain the command line for targets
- # other than command line targets
- when defined(windows) and not weirdTarget:
- template getCommandLine*(): untyped = getCommandLineW()
- proc parseCmdLine*(c: string): seq[string] {.
- noSideEffect, rtl, extern: "nos$1".} =
- ## Splits a `command line`:idx: into several components.
- ##
- ## **Note**: This proc is only occasionally useful, better use the
- ## `parseopt module <parseopt.html>`_.
- ##
- ## On Windows, it uses the `following parsing rules
- ## <http://msdn.microsoft.com/en-us/library/17w5ykft.aspx>`_:
- ##
- ## * Arguments are delimited by white space, which is either a space or a tab.
- ## * The caret character (^) is not recognized as an escape character or
- ## delimiter. The character is handled completely by the command-line parser
- ## in the operating system before being passed to the argv array in the
- ## program.
- ## * A string surrounded by double quotation marks ("string") is interpreted
- ## as a single argument, regardless of white space contained within. A
- ## quoted string can be embedded in an argument.
- ## * A double quotation mark preceded by a backslash (\") is interpreted as a
- ## literal double quotation mark character (").
- ## * Backslashes are interpreted literally, unless they immediately precede
- ## a double quotation mark.
- ## * If an even number of backslashes is followed by a double quotation mark,
- ## one backslash is placed in the argv array for every pair of backslashes,
- ## and the double quotation mark is interpreted as a string delimiter.
- ## * If an odd number of backslashes is followed by a double quotation mark,
- ## one backslash is placed in the argv array for every pair of backslashes,
- ## and the double quotation mark is "escaped" by the remaining backslash,
- ## causing a literal double quotation mark (") to be placed in argv.
- ##
- ## On Posix systems, it uses the following parsing rules:
- ## Components are separated by whitespace unless the whitespace
- ## occurs within ``"`` or ``'`` quotes.
- ##
- ## See also:
- ## * `parseopt module <parseopt.html>`_
- ## * `paramCount proc`_
- ## * `paramStr proc`_
- ## * `commandLineParams proc`_
- result = @[]
- var i = 0
- var a = ""
- while true:
- setLen(a, 0)
- # eat all delimiting whitespace
- while i < c.len and c[i] in {' ', '\t', '\l', '\r'}: inc(i)
- if i >= c.len: break
- when defined(windows):
- # parse a single argument according to the above rules:
- var inQuote = false
- while i < c.len:
- case c[i]
- of '\\':
- var j = i
- while j < c.len and c[j] == '\\': inc(j)
- if j < c.len and c[j] == '"':
- for k in 1..(j-i) div 2: a.add('\\')
- if (j-i) mod 2 == 0:
- i = j
- else:
- a.add('"')
- i = j+1
- else:
- a.add(c[i])
- inc(i)
- of '"':
- inc(i)
- if not inQuote: inQuote = true
- elif i < c.len and c[i] == '"':
- a.add(c[i])
- inc(i)
- else:
- inQuote = false
- break
- of ' ', '\t':
- if not inQuote: break
- a.add(c[i])
- inc(i)
- else:
- a.add(c[i])
- inc(i)
- else:
- case c[i]
- of '\'', '\"':
- var delim = c[i]
- inc(i) # skip ' or "
- while i < c.len and c[i] != delim:
- add a, c[i]
- inc(i)
- if i < c.len: inc(i)
- else:
- while i < c.len and c[i] > ' ':
- add(a, c[i])
- inc(i)
- add(result, a)
- when defined(nimdoc):
- # Common forward declaration docstring block for parameter retrieval procs.
- proc paramCount*(): int {.tags: [ReadIOEffect].} =
- ## Returns the number of `command line arguments`:idx: given to the
- ## application.
- ##
- ## Unlike `argc`:idx: in C, if your binary was called without parameters this
- ## will return zero.
- ## You can query each individual parameter with `paramStr proc`_
- ## or retrieve all of them in one go with `commandLineParams proc`_.
- ##
- ## **Availability**: When generating a dynamic library (see `--app:lib`) on
- ## Posix this proc is not defined.
- ## Test for availability using `declared() <system.html#declared,untyped>`_.
- ##
- ## See also:
- ## * `parseopt module <parseopt.html>`_
- ## * `parseCmdLine proc`_
- ## * `paramStr proc`_
- ## * `commandLineParams proc`_
- ##
- ## **Examples:**
- ##
- ## .. code-block:: nim
- ## when declared(paramCount):
- ## # Use paramCount() here
- ## else:
- ## # Do something else!
- proc paramStr*(i: int): string {.tags: [ReadIOEffect].} =
- ## Returns the `i`-th `command line argument`:idx: given to the application.
- ##
- ## `i` should be in the range `1..paramCount()`, the `IndexDefect`
- ## exception will be raised for invalid values. Instead of iterating
- ## over `paramCount()`_ with this proc you can
- ## call the convenience `commandLineParams()`_.
- ##
- ## Similarly to `argv`:idx: in C,
- ## it is possible to call `paramStr(0)` but this will return OS specific
- ## contents (usually the name of the invoked executable). You should avoid
- ## this and call `getAppFilename()`_ instead.
- ##
- ## **Availability**: When generating a dynamic library (see `--app:lib`) on
- ## Posix this proc is not defined.
- ## Test for availability using `declared() <system.html#declared,untyped>`_.
- ##
- ## See also:
- ## * `parseopt module <parseopt.html>`_
- ## * `parseCmdLine proc`_
- ## * `paramCount proc`_
- ## * `commandLineParams proc`_
- ## * `getAppFilename proc`_
- ##
- ## **Examples:**
- ##
- ## .. code-block:: nim
- ## when declared(paramStr):
- ## # Use paramStr() here
- ## else:
- ## # Do something else!
- elif defined(nimscript): discard
- elif defined(nodejs):
- type Argv = object of JsRoot
- let argv {.importjs: "process.argv".} : Argv
- proc len(argv: Argv): int {.importjs: "#.length".}
- proc `[]`(argv: Argv, i: int): cstring {.importjs: "#[#]".}
- proc paramCount*(): int {.tags: [ReadDirEffect].} =
- result = argv.len - 2
- proc paramStr*(i: int): string {.tags: [ReadIOEffect].} =
- let i = i + 1
- if i < argv.len and i >= 0:
- result = $argv[i]
- else:
- raise newException(IndexDefect, formatErrorIndexBound(i - 1, argv.len - 2))
- elif defined(windows):
- # Since we support GUI applications with Nim, we sometimes generate
- # a WinMain entry proc. But a WinMain proc has no access to the parsed
- # command line arguments. The way to get them differs. Thus we parse them
- # ourselves. This has the additional benefit that the program's behaviour
- # is always the same -- independent of the used C compiler.
- var
- ownArgv {.threadvar.}: seq[string]
- ownParsedArgv {.threadvar.}: bool
- proc paramCount*(): int {.rtl, extern: "nos$1", tags: [ReadIOEffect].} =
- # Docstring in nimdoc block.
- if not ownParsedArgv:
- ownArgv = parseCmdLine($getCommandLine())
- ownParsedArgv = true
- result = ownArgv.len-1
- proc paramStr*(i: int): string {.rtl, extern: "nos$1",
- tags: [ReadIOEffect].} =
- # Docstring in nimdoc block.
- if not ownParsedArgv:
- ownArgv = parseCmdLine($getCommandLine())
- ownParsedArgv = true
- if i < ownArgv.len and i >= 0:
- result = ownArgv[i]
- else:
- raise newException(IndexDefect, formatErrorIndexBound(i, ownArgv.len-1))
- elif defined(genode):
- proc paramStr*(i: int): string =
- raise newException(OSError, "paramStr is not implemented on Genode")
- proc paramCount*(): int =
- raise newException(OSError, "paramCount is not implemented on Genode")
- elif weirdTarget or (defined(posix) and appType == "lib"):
- proc paramStr*(i: int): string {.tags: [ReadIOEffect].} =
- raise newException(OSError, "paramStr is not implemented on current platform")
- proc paramCount*(): int {.tags: [ReadIOEffect].} =
- raise newException(OSError, "paramCount is not implemented on current platform")
- elif not defined(createNimRtl) and
- not(defined(posix) and appType == "lib"):
- # On Posix, there is no portable way to get the command line from a DLL.
- var
- cmdCount {.importc: "cmdCount".}: cint
- cmdLine {.importc: "cmdLine".}: cstringArray
- proc paramStr*(i: int): string {.tags: [ReadIOEffect].} =
- # Docstring in nimdoc block.
- if i < cmdCount and i >= 0:
- result = $cmdLine[i]
- else:
- raise newException(IndexDefect, formatErrorIndexBound(i, cmdCount-1))
- proc paramCount*(): int {.tags: [ReadIOEffect].} =
- # Docstring in nimdoc block.
- result = cmdCount-1
- when declared(paramCount) or defined(nimdoc):
- proc commandLineParams*(): seq[string] =
- ## Convenience proc which returns the command line parameters.
- ##
- ## This returns **only** the parameters. If you want to get the application
- ## executable filename, call `getAppFilename()`_.
- ##
- ## **Availability**: On Posix there is no portable way to get the command
- ## line from a DLL and thus the proc isn't defined in this environment. You
- ## can test for its availability with `declared()
- ## <system.html#declared,untyped>`_.
- ##
- ## See also:
- ## * `parseopt module <parseopt.html>`_
- ## * `parseCmdLine proc`_
- ## * `paramCount proc`_
- ## * `paramStr proc`_
- ## * `getAppFilename proc`_
- ##
- ## **Examples:**
- ##
- ## .. code-block:: nim
- ## when declared(commandLineParams):
- ## # Use commandLineParams() here
- ## else:
- ## # Do something else!
- result = @[]
- for i in 1..paramCount():
- result.add(paramStr(i))
- else:
- proc commandLineParams*(): seq[string] {.error:
- "commandLineParams() unsupported by dynamic libraries".} =
- discard
|