diff options
author | IgorDeepakM <117689228+IgorDeepakM@users.noreply.github.com> | 2022-11-30 11:00:26 +0100 |
---|---|---|
committer | GitHub <noreply@github.com> | 2022-11-30 11:00:26 +0100 |
commit | 84ea62ea0d64ba454d0f53beb4f3f16d0582ab45 (patch) | |
tree | 872d4e371696ab3f4ea3693b48955a053472f9c6 | |
parent | 8f728ace353857b007c9c38d1d4de98bf431d5e2 (diff) | |
download | Nim-84ea62ea0d64ba454d0f53beb4f3f16d0582ab45.tar.gz |
Move command line parameter code (#20946)
Command line paramater code moved from os.nim to cmdparam.nim Co-authored-by: IgorDeepakM <IgorDeepak@noreply.com>
-rw-r--r-- | lib/pure/os.nim | 275 | ||||
-rw-r--r-- | lib/std/cmdline.nim | 313 | ||||
-rw-r--r-- | lib/std/private/oscommon.nim | 2 |
3 files changed, 318 insertions, 272 deletions
diff --git a/lib/pure/os.nim b/lib/pure/os.nim index 0fa594b9d..5f8f116d3 100644 --- a/lib/pure/os.nim +++ b/lib/pure/os.nim @@ -8,8 +8,8 @@ # ## This module contains basic operating system facilities like -## retrieving environment variables, reading command line arguments, -## working with directories, running shell commands, etc. +## retrieving environment variables, working with directories, +## running shell commands, etc. runnableExamples: let myFile = "/path/to/my/file.nim" @@ -22,8 +22,6 @@ runnableExamples: ## **See also:** ## * `osproc module <osproc.html>`_ for process communication beyond ## `execShellCmd proc`_ -## * `parseopt module <parseopt.html>`_ for command-line parser beyond -## `parseCmdLine proc`_ ## * `uri module <uri.html>`_ ## * `distros module <distros.html>`_ ## * `dynlib module <dynlib.html>`_ @@ -48,6 +46,9 @@ import std/private/oscommon include system/inclrtl import std/private/since +import std/cmdline +export cmdline + import strutils, pathnorm when defined(nimPreviewSlimSystem): @@ -510,272 +511,6 @@ proc exclFilePermissions*(filename: string, ## setFilePermissions(filename, getFilePermissions(filename)-permissions) setFilePermissions(filename, getFilePermissions(filename)-permissions) -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 - when not weirdTarget and (defined(freebsd) or defined(dragonfly) or defined(netbsd)): proc sysctl(name: ptr cint, namelen: cuint, oldp: pointer, oldplen: ptr csize_t, newp: pointer, newplen: csize_t): cint diff --git a/lib/std/cmdline.nim b/lib/std/cmdline.nim new file mode 100644 index 000000000..53e72a265 --- /dev/null +++ b/lib/std/cmdline.nim @@ -0,0 +1,313 @@ +# +# +# 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: "cmdparam module not ported to your operating system!".} + + +# Needed by windows in order to obtain the command line for targets +# other than command line targets +when defined(windows) and not weirdTarget: + when useWinUnicode: + template getCommandLine*(): untyped = getCommandLineW() + else: + template getCommandLine*(): untyped = getCommandLineA() + + +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 \ No newline at end of file diff --git a/lib/std/private/oscommon.nim b/lib/std/private/oscommon.nim index bab4856ca..5b4e1d7e3 100644 --- a/lib/std/private/oscommon.nim +++ b/lib/std/private/oscommon.nim @@ -54,14 +54,12 @@ when defined(windows) and not weirdTarget: proc findFirstFile*(a: string, b: var WIN32_FIND_DATA): Handle = result = findFirstFileW(newWideCString(a), b) template findNextFile*(a, b: untyped): untyped = findNextFileW(a, b) - template getCommandLine*(): untyped = getCommandLineW() template getFilename*(f: untyped): untyped = $cast[WideCString](addr(f.cFileName[0])) else: template findFirstFile*(a, b: untyped): untyped = findFirstFileA(a, b) template findNextFile*(a, b: untyped): untyped = findNextFileA(a, b) - template getCommandLine*(): untyped = getCommandLineA() template getFilename*(f: untyped): untyped = $cast[cstring](addr f.cFileName) |