cligen/posixUt

Search:
Group by:

Types

FileKind = enum             ## Helper enum for `match` st_mode/stx_mode against file kinds
  fkFile = "file", fkDir = "dir", fkLink = "link", fkBlk = "blk", fkChr = "chr",
  fkPipe = "pipe", fkSock = "sock"
Helper enum for match st_mode/stx_mode against file kinds
PathId = tuple[dev: Dev, ino: Ino]

Lets

AT_EACCESS {.inject.} = locAT_EACCESS
AT_FDCWD {.inject.} = locAT_FDCWD
AT_REMOVEDIR {.inject.} = locAT_REMOVEDIR
MADV_DONTNEED {.inject.} = locPOSIX_MADV_DONTNEED
MADV_NORMAL {.inject.} = locPOSIX_MADV_NORMAL
MADV_RANDOM {.inject.} = locPOSIX_MADV_RANDOM
MADV_SEQUENTIAL {.inject.} = locPOSIX_MADV_SEQUENTIAL
MADV_WILLNEED {.inject.} = locPOSIX_MADV_WILLNEED
strftimeCodes = {'a', 'A', 'b', 'B', 'c', 'C', 'd', 'D', 'e', 'E', 'F', 'G',
                 'g', 'h', 'H', 'I', 'j', 'k', 'l', 'm', 'M', 'n', 'O', 'p',
                 'P', 'r', 'R', 's', 'S', 't', 'T', 'u', 'U', 'V', 'w', 'W',
                 'x', 'X', 'y', 'Y', 'z', 'Z', '+', '1', '2', '3', '4', '5',
                 '6', '7', '8', '9'}
UTIME_NOW {.inject.} = locUTIME_NOW
UTIME_OMIT {.inject.} = locUTIME_OMIT

Consts

AT_EMPTY_PATH = 0x00001000
Allow empty relative pathname
AT_NO_AUTOMOUNT = 0x00000800
Suppress terminal automount traversal

Procs

proc `$`(st: Stat): string {....raises: [], tags: [], forbids: [].}
stdlib automatic $(Stat) broken due to pad0 junk.
proc `$`(x: Timespec): string {....raises: [], tags: [], forbids: [].}
proc `-`(a, b: Timespec): int {....raises: [], tags: [], forbids: [].}
proc `<`(a, b: Timespec): bool {....raises: [], tags: [], forbids: [].}
proc `<=`(a, b: Timespec): bool {....raises: [], tags: [], forbids: [].}
proc `==`(a, b: PathId): bool {.inline, ...raises: [], tags: [], forbids: [].}
proc argHelp(dfl: Timespec; a: var ArgcvtParams): seq[string] {.
    ...raises: [ValueError], tags: [], forbids: [].}
proc argParse(dst: var Timespec; dfl: Timespec; a: var ArgcvtParams): bool {.
    ...raises: [ValueError], tags: [], forbids: [].}
proc cmp(a, b: Timespec): int {....raises: [], tags: [], forbids: [].}
proc dropPrivilegeTo(newUser, newGroup: string; err = stderr): bool {.
    ...raises: [IOError, IOError], tags: [WriteIOEffect], forbids: [].}
Change from super-user/root to a less privileged account taking care to also change gid and re-initialize supplementary groups to what /etc/group says. I.e., like su, but in-process. (Test this works on your system by compiling this module with -d:testDropPriv.)
proc dtNs(t0: int; clockId = CLOCK_REALTIME): int {....raises: [], tags: [],
    forbids: [].}
Eg.: let t0 = getTmNs(); echo dtNs(t0). Worry of NTP/etc. fiddling with the system clock is addressed by CLOCK_MONOTONIC.getTmNs.
proc faccessat(dirfd: cint; path: cstring; mode: cint; flags: cint): cint {.
    importc, header: "<unistd.h>", sideEffect, ...raises: [], tags: [], forbids: [].}
proc fchmodat(dirfd: cint; path: cstring; mode: Mode; flags: cint): cint {.
    importc, header: "<unistd.h>", sideEffect, ...raises: [], tags: [], forbids: [].}
proc fchownat(dirfd: cint; path: cstring; owner: Uid; group: Gid; flags: cint): cint {.
    importc, header: "<unistd.h>", sideEffect, ...raises: [], tags: [], forbids: [].}
proc fileTime(st: Stat; code: string): int {....raises: [], tags: [], forbids: [].}
file time useful in sorting by [+-][amcv]-time.
proc fileTime(st: Stat; tim: char; dir: int): int {....raises: [], tags: [],
    forbids: [].}
file time useful in sorting by [+-][amcv]-time; pre-parsed code.
proc fileTimeParse(code: string): tuple[tim: char, dir: int] {....raises: [],
    tags: [], forbids: [].}
Parse [+-][amcvAMCV]* into file time order specification. In case default increasing order is non-intuitive, this provides two ways to specify reverse order: leading '-' or upper-casing. Such reversals compose: "-A" === "a".
proc findAssociated(av: cstringArray; parentBase = "etc/foo"): string {.
    ...raises: [], tags: [ReadIOEffect], forbids: [].}
First existing of selfDir/../parentBase, selfDir/../../parentBase, selfDir/../../../parentBase, or if none exist /parentBase.
proc fstatat(dirfd: cint; path: cstring; stx: var Stat; flags: cint): cint {.
    importc, header: "<unistd.h>", sideEffect, ...raises: [], tags: [], forbids: [].}
proc futimens(fd: cint; times: array[2, Timespec]): cint {.importc,
    header: "<unistd.h>", sideEffect, ...raises: [], tags: [], forbids: [].}
proc futimesat(dirfd: cint; path: cstring; times: array[2, Timeval]): cint {.
    importc, header: "<unistd.h>", sideEffect, ...raises: [], tags: [], forbids: [].}
proc getCreationTimeNs(path`gensym85: string): int {....raises: [], tags: [],
    forbids: [].}
proc getCreationTimeNs(st`gensym85: Stat): int {....raises: [], tags: [],
    forbids: [].}
proc getDents(fd: cint; st: Stat; dts: ptr seq[int8] = nil;
              inos: ptr seq[Ino] = nil; avgLen = 24): seq[string] {....raises: [],
    tags: [], forbids: [].}
Read open dir fd. If provided, also give d_type and/or d_ino in dts & inos pairing with result strings. ALWAYS skips ".", "..".
proc getgroups(): HashSet[Gid] {....raises: [], tags: [], forbids: [].}
proc getgroups(gids: var HashSet[Gid]) {....raises: [], tags: [], forbids: [].}
Get all gids active for current process
proc getLastAccTimeNs(path`gensym83: string): int {....raises: [], tags: [],
    forbids: [].}
proc getLastAccTimeNs(st`gensym83: Stat): int {....raises: [], tags: [],
    forbids: [].}
proc getLastModTimeNs(path`gensym84: string): int {....raises: [], tags: [],
    forbids: [].}
proc getLastModTimeNs(st`gensym84: Stat): int {....raises: [], tags: [],
    forbids: [].}
proc getTime(clockId = CLOCK_REALTIME): Timespec {....raises: [], tags: [],
    forbids: [].}
Placeholder to avoid times module
proc getTmNs(clockId = CLOCK_REALTIME): int {....raises: [], tags: [], forbids: [].}
Eg.: let t0 = getTmNs(); let dt = getTmNs() - t0. Worry of NTP/etc. fiddling with the system clock is addressed by CLOCK_MONOTONIC.getTmNs.
proc groupIds(): Table[string, Gid] {....raises: [], tags: [], forbids: [].}
Populate Table[Id, string] with data from system account files
proc groups(): Table[Gid, string] {....raises: [], tags: [], forbids: [].}
Populate Table[Id, string] with data from system account files
proc hash(x: PathId): int {.inline, ...raises: [], tags: [], forbids: [].}
proc initGroups(user: cstring; group: Gid): cint {.importc: "initgroups",
    header: "grp.h", ...raises: [], tags: [], forbids: [].}
proc linkat(olddirfd: cint; oldpath: cstring; newdirfd: cint; newpath: cstring;
            flags: cint): cint {.importc, header: "<unistd.h>", sideEffect,
                                 ...raises: [], tags: [], forbids: [].}
proc log(f: File; s: string) {.inline, ...raises: [IOError], tags: [WriteIOEffect],
                               forbids: [].}
This does nothing if f is nil, but otherwise calls write.
proc lstatOk(path: string; st: ptr Stat = nil; err = stderr): bool {.inline,
    ...raises: [IOError], tags: [WriteIOEffect], forbids: [].}
lstat path optionally populating st if non-nil and writing any OS error message to err if non-nil.
proc madvise(mem: pointer; len: int; advice: cint): int {....raises: [], tags: [],
    forbids: [].}
Define briefer/old school madvise in terms of the more portable (posix|POSIX)_ constructs.
proc match(m: Mode | uint16; kinds: set[FileKind]): bool {.inline.}
true if file with st_mode or stx_mode m is one of kinds.
proc mkdirat(dirfd: cint; path: cstring; mode: Mode): cint {.importc,
    header: "<unistd.h>", sideEffect, ...raises: [], tags: [], forbids: [].}
proc mknodat(dirfd: cint; path: cstring; mode: Mode; dev: Dev): cint {.importc,
    header: "<unistd.h>", sideEffect, ...raises: [], tags: [], forbids: [].}
proc nanosleep(delay: Timespec) {....raises: [], tags: [], forbids: [].}
Carefully sleep by amount delay.
proc nice(pid: Pid; niceIncr: cint): int {....raises: [], tags: [], forbids: [].}
Increment nice value/scheduling priority bias of a process/thread.
proc ns(t: Timespec): int {....raises: [], tags: [], forbids: [].}
Signed nanoseconds since origin (usually epoch, but maybe not if t comes from CLOCK_MONOTONIC); Signed 64bit ints => +-292 years from 1970. Eg.: let t0 = path.getLastModificationTime.ns.
proc nsToTimeval(ns: int64): Timeval {....raises: [], tags: [], forbids: [].}
proc openat(dirfd: cint; path: cstring; flags: cint): cint {.varargs, importc,
    header: "<unistd.h>", sideEffect, ...raises: [], tags: [], forbids: [].}
proc pathId(path: string): PathId {....raises: [], tags: [], forbids: [].}
proc pathToSelf(av: cstringArray): string {....raises: [], tags: [ReadIOEffect],
    forbids: [].}
Return path to executable of the currently running program or "" if cannot. First this tries os.getAppFilename - often but not always reliable. If that fails & $0 starts with [./], return that (giving users sh wrapper | /path/binExec alias likely workarounds). For . it relies on no chdir between program start & here. If $0 starts elsewise, search $PATH for $0 like shells using access (inducing POSIX requirement).
proc readFile(path: string; buf: var string; st: ptr Stat = nil; perRead = 4096) {.
    ...raises: [], tags: [], forbids: [].}
Read whole file of unknown (& fstat-non-informative) size using re-usable IO buffer provided. If st is non-nil then fill it in via fstat.
proc readlinkat(dirfd: cint; path: cstring; buf: cstring; bufsiz: csize): clong {.
    importc, header: "<unistd.h>", sideEffect, ...raises: [], tags: [], forbids: [].}
proc reapAnyKids(signo: cint) {.noconv, ...raises: [], tags: [], forbids: [].}
Wait on any/only waitable kids; Useful to signal(SIGCHLD, reapAnyKids) to avoid zombies when treating all background children the same is ok.
proc recEntries(it: iterator (): string; st: ptr Stat = nil; dt: ptr int8 = nil;
                follow = false; maxDepth = 0; err = stderr): iterator (): string {.
    ...raises: [], tags: [], forbids: [].}
Return iterator yielding maxDepth|follow recursive closure of it.
proc rename(oldpath, newpath: cstring): cint {.importc, header: "<unistd.h>",
    sideEffect, ...raises: [], tags: [], forbids: [].}
proc renameat(olddirfd: cint; oldpath: cstring; newdirfd: cint; newpath: cstring): cint {.
    importc, header: "<unistd.h>", sideEffect, ...raises: [], tags: [], forbids: [].}
proc renameat2(olddirfd: cint; oldpath: cstring; newdirfd: cint;
               newpath: cstring; flags: cint): cint {.importc,
    header: "<unistd.h>", sideEffect, ...raises: [], tags: [], forbids: [].}
proc setGroups(size: csize; list: ptr Gid): cint {.importc: "setgroups",
    header: "grp.h", ...raises: [], tags: [], forbids: [].}
proc st_inode(path: string; err = stderr): Ino {....raises: [IOError],
    tags: [WriteIOEffect], forbids: [].}
Return just the Stat.st_inode field for a path.
proc stat2dtype(st_mode: Mode): int8 {.inline, ...raises: [], tags: [], forbids: [].}
Convert S_ISDIR(st_mode) style dirent types to DT_DIR style.
proc statOk(path: string; st: ptr Stat = nil; err = stderr): bool {.inline,
    ...raises: [IOError], tags: [WriteIOEffect], forbids: [].}
stat path optionally populating st if non-nil and writing any OS error message to err if non-nil.
proc strftime(fmt: string; ts: Timespec): string {....raises: [], tags: [],
    forbids: [].}
Nim wrap strftime & translate %[1..9] => '.' & that many tv_nsec digits.
proc symlinkat(target: cstring; newdirfd: cint; linkpath: cstring): cint {.
    importc, header: "<unistd.h>", sideEffect, ...raises: [], tags: [], forbids: [].}
proc system(csa: cstringArray; wait = true): cint {....raises: [], tags: [],
    forbids: [].}
Like system(3) but does fork & exec of an already set up cstringArray. If wait==true, returns status for WEXITSTATUS(); else returns kid pid.
proc toGidSet(strs: seq[string]): HashSet[Gid] {....raises: [ValueError], tags: [],
    forbids: [].}
Just parse some ints into typed Gids
proc toUidSet(strs: seq[string]): HashSet[Uid] {....raises: [ValueError], tags: [],
    forbids: [].}
Just parse some ints into typed Uids
proc unlinkat(dirfd: cint; path: cstring; flags: cint): cint {.importc,
    header: "<unistd.h>", sideEffect, ...raises: [], tags: [], forbids: [].}
proc userIds(): Table[string, Uid] {....raises: [], tags: [], forbids: [].}
Populate Table[Id, string] with data from system account files
proc users(): Table[Uid, string] {....raises: [], tags: [], forbids: [].}
Populate Table[Id, string] with data from system account files
proc utimensat(dirfd: cint; path: cstring; times: array[2, Timespec];
               flags: cint): cint {.importc, header: "<unistd.h>", sideEffect,
                                    ...raises: [], tags: [], forbids: [].}

Iterators

iterator dirEntries(dir: string; st: ptr Stat = nil; canRec: ptr bool = nil;
                    dt: ptr int8 = nil; err = stderr; follow = false;
                    relative = false): string {....raises: [IOError],
    tags: [WriteIOEffect], forbids: [].}

This iterator wraps readdir, optionally filling st[] if it was necessary to read, whether an entry can be recursed upon, and the dirent d_type in dt[] (or if unavailable DT_UNKNOWN). OS error messages are sent to File err (which can be nil). Yielded paths are relative to dir iff. relative is true.

follow is for the mode where outer, recursive iterations want to chase symbolic links to dirs. st[] is filled only if needed to compute canRec. lstat is used only if not follow and dt[]==DT_UNKNOWN) else stat is used. If dt[]==DT_DIR then only st_ino is assigned. Callers can detect if st was filled by st_nlink > 0.

iterator paths(roots: seq[string]; maxDepth = 0; follow = false; file = "";
               delim = '\n'; err = stderr; st: ptr Stat = nil;
               dt: ptr int8 = nil): string {....raises: [Exception],
    tags: [RootEffect], forbids: [].}
iterator for maybe-following, maybe-recursive closure of the union of roots and optional delim-delimited input file (stdin if "-" | if "" & stdin not a tty). Usage is for p in paths(roots,...): echo p. This allows fully general path input if used in a command pipeline like find . -print0 | cmd -d\\0 (where -d sets delim).
iterator recEntries(dir: string; st: ptr Stat = nil; dt: ptr int8 = nil;
                    follow = false; maxDepth = 0; err = stderr): string {.
    ...raises: [IOError], tags: [WriteIOEffect], forbids: [].}
This recursively yields all paths in the FS tree up to maxDepth levels beneath dir or without bound for maxDepth==0. If follow then recursion follows symbolic links to dirs. If err!=nil then OS error messages are written there. Unlike the stdlib walkDirRec, in addition to a maxDepth limit, following here avoids infinite symLink loops. If provided pointers are non-nil then they are filled like dirEntries. Example:
var st: Stat; var sum = 0      #`du` 1st & 2nd level under "." only
for path in recEntries(".", st.addr, follow=true, recurse=2):
  if st.st_nlink == 0 and not statOk(path, st): stderr.write "err\n"
  sum += st_blocks * 512

Templates

template impCint(path: string; name: untyped): untyped {.dirty.}
template impConst(T: untyped; path: string; name: untyped): untyped {.dirty.}
template impConstAs(T: untyped; path: string; name, nimName): untyped {.dirty.}
template localAlloc(param; typ: typedesc) {.dirty.}
One often wants to allow auxiliary data that may or may not be discovered as part of an operation to be returned optionally. One convenient pattern for this in Nim is accepting a ptr T which can be nil when the caller does not want the auxiliary data. Routines using this pattern define local space but only use its addr if callers provide none, as in var loc; let par = if par == nil: loc.addr else par. This template abstracts that away to simply localAlloc(par, parTypeLessPtr).