cligen/osUt

Search:
Group by:

This module is a grab bag of utility code that is often useful interfacing between CLIs and the OS. Example:

proc something*(file="", delim='\n', paths: seq[string]): int =
  ## Do example on paths which are the UNION of ``paths`` & optional
  ## ``delim``-delimited input ``file`` (stdin if "-"|if "" & stdin not
  ## a tty).  Eg., ``find -type f -print0|example -d\\x00 a b c``.
  for path in both(fileStrings(file, delim), paths)(): discard
dispatch(something)
This is a portability shim include file for when you want code to work without deprecation warnings under younger & older Nim compilers. Just include cligen/unsafeAdr at the global scope before using unsafeAddr.

Types

Ce = CatchableError
CPUSet {.importc: "cpu_set_t", header: "<sched.h>", final, pure.} = object
  nil

Lets

IOFBF = vIOFBF
IOLBF = vIOLBF
IONBF = vIONBF

Procs

proc autoClose() {....raises: [], tags: [], forbids: [].}
Close all files opened so far by autoOpen.
proc autoOpen(path: string; mode = fmWrite; bufSize = -1): File {.
    ...raises: [OSError, IOError], tags: [WriteDirEffect, ReadDirEffect],
    forbids: [].}
For callers in expressional situations to open files on-demand.
proc both[T](it: iterator (): T; s: seq[T]): iterator (): T
Return an iterator yielding both seq elements and the passed iterator.
proc both[T](s: seq[T]; it: iterator (): T): iterator (): T
Return an iterator yielding both seq elements and the passed iterator.
proc c_getdelim(p: ptr cstring; nA: ptr csize; dlm: cint; f: File): int {.
    importc: "getdelim", header: "<stdio.h>", ...raises: [], tags: [], forbids: [].}
proc c_setvbuf(f: File; buf: pointer; mode: cint; size: csize): cint {.
    importc: "setvbuf", header: "<stdio.h>", ...tags: [], raises: [], forbids: [].}
proc clearDir(dir: string) {....raises: [OSError],
                             tags: [ReadDirEffect, WriteDirEffect], forbids: [].}
Best effort removal of the contents of dir, but not dir itself.
proc cpu_set(cpu: cint; set: ptr CPUSet) {.importc: "CPU_SET",
    header: "<sched.h>", ...raises: [], tags: [], forbids: [].}
proc cpu_zero(set: ptr CPUSet) {.importc: "CPU_ZERO", header: "<sched.h>",
                                 ...raises: [], tags: [], forbids: [].}
proc eof(f: File): bool {.inline, ...raises: [], tags: [], forbids: [].}
proc errstr(): string {....raises: [], tags: [], forbids: [].}
proc ferr(f: File): cint {.importc: "ferror", header: "stdio.h", ...tags: [],
                           raises: [], forbids: [].}
expose ANSI C stdio error condition checking
proc fileNewerThan(a, b: string): bool {....raises: [], tags: [], forbids: [].}
True if both path a & b exist and a is newer
proc fileOlderThan(a, b: string): bool {....raises: [], tags: [], forbids: [].}
True if both path a & b exist and a is older
proc fileStrings(path: string; delim: char): auto {....raises: [], tags: [],
    forbids: [].}
Return an iterator yielding delim-delimited records in file path. Note path = "/" is equivalent to a Unix path = "/dev/null".
proc findPathPattern(pathPattern: string): string {....raises: [OSError],
    tags: [ReadDirEffect], forbids: [].}
Search directory containing pathPattern (or ".") for first matching name. Pattern matching is currently substring only.
proc getcpu(): cint {.importc: "sched_getcpu", header: "<sched.h>", ...raises: [],
                      tags: [], forbids: [].}
proc lgBold(f: File; s: string) {....raises: [IOError], tags: [WriteIOEffect],
                                  forbids: [].}
Log to a File, f with ANSI SGR bold.
proc lgInv(f: File; s: string) {....raises: [IOError], tags: [WriteIOEffect],
                                 forbids: [].}
Log to a File, f with ANSI SGR inverse.
proc loadSym(x: string): pointer {....raises: [IOError], tags: [WriteIOEffect],
                                   forbids: [].}
split x on ':' into library:symbol parts, dynlib.loadLib the library and then dynlib.symAddr the symbol. Returns the pointer or nil if either operation fails.
proc mkdirOpen(path: string; mode = fmRead; bufSize = -1): File {.
    ...raises: [OSError, IOError], tags: [WriteDirEffect, ReadDirEffect],
    forbids: [].}
Wrapper around system.open that ensures leading directory prefix exists.
proc mkdirP(path: string) {....raises: [OSError, IOError],
                            tags: [WriteDirEffect, ReadDirEffect], forbids: [].}
Create all parent dirs and path itself like Unix mkdir -p foo/bar/baz.
proc mkdirTo(path: string) {....raises: [OSError, IOError],
                             tags: [WriteDirEffect, ReadDirEffect], forbids: [].}
Ensure leading directory prefix of path exists.
proc open(fd: cint; mode = fmRead; bufMode = IOFBF; bufSize = 4096): File {.
    ...raises: [IOError], tags: [], forbids: [].}
Make File from OS-level handle fd w/specific mode, buffer mode & size.
proc pclose(f: File; cmd: string): cint {....raises: [], tags: [], forbids: [].}
Clean-up for popen[rw]. Returns exit status of popen()d command.
proc perror(x: cstring; err = stderr) {....raises: [], tags: [WriteIOEffect],
                                        forbids: [].}
Clunky w/spartan msgs, but allows safe output from OpenMP || blocks.
proc perror(x: cstring; len: int; code: OSErrorCode; err = stderr) {....raises: [],
    tags: [WriteIOEffect], forbids: [].}
Clunky w/spartan msgs, but allows safe output from OpenMP || blocks.
proc perror(x: cstring; len: int; err = stderr) {....raises: [],
    tags: [WriteIOEffect], forbids: [].}
Clunky w/spartan msgs, but allows safe output from OpenMP || blocks.
proc popenr(cmd: string; path = ""; bufSize = -1): File {.
    ...raises: [ValueError, OSError, IOError], tags: [], forbids: [].}
If cmd.len==0 this is like regular open(mode=fmRead) except that "" or "/dev/stdin" are in-line translated to return stdin. It otherwise wraps popen(cmd % path, "rb"). So, $1 is how users place path in cmd.
proc popenw(cmd: string; path = ""; bufSize = -1): File {.
    ...raises: [ValueError, OSError, IOError], tags: [], forbids: [].}
If cmd.len==0 this is like regular open(mode=fmWrite) except that "" or "/dev/stdout" are in-line translated to return stdout. It otherwise wraps popen(cmd % path,"wb"). So, $1 is how users place path in cmd.
proc putchar(c: char): cint {.importc: "putchar_unlocked", header: "stdio.h",
                              discardable, ...raises: [], tags: [], forbids: [].}
proc rdRecs(fd: cint; buf: var string; eor = '\x00'; n = 16384): int {.
    ...raises: [], tags: [], forbids: [].}
Like read but loop if end!=eor (growing buf to multiples of n). In effect, this reads an integral number of recs - which must still be split!
proc replacingUrite(f: File; s: string; eor: char; subEor: string) {....raises: [],
    tags: [], forbids: [].}
Unlocked write s to f replacing any eor char with subEor.
proc run(cmd: openArray[string]; env: StringTableRef = nil;
         opts = {poEchoCmd, poUsePath}; dryRun = false; logFile = stderr;
         logWrite = lgBold): int {....raises: [IOError, OSError, ValueError], tags: [
    WriteIOEffect, ExecIOEffect, ReadEnvEffect, RootEffect], forbids: [].}
Wrap a sync(waitForExit) os.startProcess BUT only print (if dryRun), default to use PATH & echo & allow log destination/embellishment overrides.
proc sched_setaffinity(pid: Pid; sz: csize; mask: ptr CPUSet): cint {.
    importc: "sched_setaffinity", header: "<sched.h>", ...raises: [], tags: [],
    forbids: [].}
proc setAffinity(cpus: openArray[cint] = []) {....raises: [], tags: [], forbids: [].}
Pin current thread to CPUs; No arg is like setaffinity [getcpu()].
proc setFileSize(fh: FileHandle; currSize, newSize: int64): OSErrorCode {.
    ...raises: [], tags: [], forbids: [].}
Set the size of open file pointed to by fh to newSize if >= 0. Space is pre-allocated only when cheaper than writing. To support no exceptions code, this returns 0 on success, elsewise last OSErrorCode.
proc simplifyPath(path: string; collapseDotDot = false): string {....raises: [],
    tags: [], forbids: [].}
"././hey///ho/./there/" => "hey/ho/there/". Result always ends with '/' as source does (it's an easy client check & setLen to remove it). If collapseDotDot then also delete foo/.. pairs which can alter the behavior of paths in the presence of symbolic links to directories.
proc splitPathName(path: string; shortestExt = false): tuple[
    dir, name, ext: string] {....raises: [], tags: [], forbids: [].}
Like os.splitFile but return longest extensions not shortest (unless shortestExt). E.g. "/a/b/c.tar.gz" => ("/a/b", "c", ".tar.gz"), but with shortest would be ("/a/b", "c.tar", ".gz").
proc touch(path: string) {....raises: [IOError, IOError],
                           tags: [TimeEffect, ReadDirEffect], forbids: [].}
Create path or update its timestamp to the present - if possible.
proc uRd[T](f: File; ob: var T): bool
Unlocked read flat object ob from File.
proc ureadBuffer(f: File; buffer: pointer; len: Natural): int {.inline,
    ...raises: [], tags: [], forbids: [].}
proc urite(f: File; a: varargs[string, `$`]) {.inline, ...raises: [IOError],
    tags: [], forbids: [].}
Unlocked (i.e. single threaded) libc write (maybe Linux-only).
proc urite(f: File; s: string) {.inline, ...raises: [IOError], tags: [],
                                 forbids: [].}
Unlocked (i.e. single threaded) libc write (maybe Linux-only).
proc urite[A, B](f: File; str: string; s: HSlice[A, B])
proc uriteBuffer(f: File; buffer: pointer; len: Natural): int {.inline,
    ...raises: [], tags: [], forbids: [].}
Unlocked (i.e. single threaded) libc writeBuffer (maybe Linux-only).
proc useStdin(path: string): bool {....raises: [], tags: [], forbids: [].}
Decide if path means stdin ("-" or "" and not isatty(stdin)).
proc uWr[T](f: File; ob: var T): bool
Unlocked write flat object ob to File.
proc walkPatSorted(pattern: string): seq[string] {....raises: [],
    tags: [ReadDirEffect], forbids: [].}
This is a glob/filename generation operation but returning a sorted seq the way most Unix shells would.
proc wr[T](fd: cint; ob: T): int {....deprecated: "use `wrOb`".}
Deprecated: use `wrOb`
proc wr0term(fd: cint; buf: string): int {....raises: [], tags: [], forbids: [].}
Write buf as a NUL-terminated string to fd.
proc write(fh: cint; s: string) {....raises: [IOError], tags: [], forbids: [].}
Write a Nim string to a file descriptor
proc writeNumberToFile(path: string; num: int) {....raises: [IOError],
    tags: [WriteIOEffect], forbids: [].}
Best effort attempt to write a single number to a file.
proc wrLenBuf(fd: cint; buf: string): int {....raises: [], tags: [], forbids: [].}
Write int length prefix & buf data atomically (writev on Linux).
proc wrLenSeq[T](fd: cint; s: seq[T]): int
Write int length prefixed data of a seq[T] atomically (writev on Linux), where T are either flat objects or tuples of flat objects (no indirections allowed).
proc wrLine(fd: cint; buf: string): int {....raises: [], tags: [], forbids: [].}
Write buf & then a single newline atomically (writev on Linux).
proc wrOb[T](fd: cint; ob: T): int
Write flat object ob to file handle/descriptor fd.

Iterators

iterator getDelim(f: File; dlm: char = '\n'): string {....raises: [], tags: [],
    forbids: [].}
Efficient file line/record iterator using POSIX getdelim
iterator getDelim(path: string; dlm: char = '\n'): string {....raises: [IOError],
    tags: [], forbids: [].}
Like getDelim but take a path instead of an open File.
iterator getDelims(f: File; dlm: char = '\n'): (cstring, int) {....raises: [],
    tags: [], forbids: [].}
Like getDelim but yield (ptr, len) not auto-built Nim string. Note that unlike lines or getDelim, len always includes dlm.
for (s, n) in stdin.getDelims: # or proc toNimStr(str: cstring, len: int)
  discard toOpenArray[char](cast[ptr UncheckedArray[char]](s), 0, n-1).len
iterator getDelims(path: string; dlm: char = '\n'): (cstring, int) {.
    ...raises: [IOError], tags: [], forbids: [].}
Like getDelims but take a path instead of an open File.
iterator getLenPfx[T: SomeNumber](f: File): string
Like getDelim but "parse" length-prefixed values where a native-endian format binary length prefix is SomeNumber. Caller is responsible for specifying the right numeric type, but format is simple & 8-bit clean.

Templates

template erru(a: varargs[string, `$`])
Like stderr.write but using fwrite_unlocked
template outu(a: varargs[string, `$`])
Like stdout.write but using fwrite_unlocked
template timeIt(output: untyped; label: string; unit = 0.000001; places = 3;
                sep = "\n"; reps = 1; body: untyped)
A simple benchmark harness. output should be something like echo or stdout.write depending on desired formatting. label comes before time(reps == 1)|time statistics(rep > 1), while sep comes after the numbers.