5 Commits
0.2.1 ... 0.3.4

2 changed files with 52 additions and 41 deletions

View File

@ -1,5 +1,7 @@
import docopt, json, osproc, posix, nre, streams, strutils, strtabs import docopt, json, osproc, posix, nre, streams, strtabs, unicode
import os except sleep import os except sleep
import strutils except toUpper, toLower
type type
CombinedConfig* = object CombinedConfig* = object
@ -31,48 +33,15 @@ proc sendMsg*(h: HandleProcMsgCB, outMsg: TaintedString,
errMsg: TaintedString = nil, cmd: string = ""): void = errMsg: TaintedString = nil, cmd: string = ""): void =
if h != nil: h(outMsg, errMsg, cmd) if h != nil: h(outMsg, errMsg, cmd)
proc waitForWithOutput*(p: Process, msgCB: HandleProcMsgCB, proc makeProcMsgHandler*(outSink, errSink: File, prefixCmd: bool = true): HandleProcMsgCB =
procCmd: string = ""):
tuple[output: TaintedString, error: TaintedString, exitCode: int] =
var pout = outputStream(p)
var perr = errorStream(p)
result = (TaintedString"", TaintedString"", -1)
var line = newStringOfCap(120).TaintedString
while true:
if pout.readLine(line):
msgCB.sendMsg(line, nil, procCmd)
result[0].string.add(line.string)
result[0].string.add("\n")
elif perr.readLine(line):
msgCB.sendMsg(nil, line, procCmd)
result[1].string.add(line.string)
result[1].string.add("\n")
else:
result[2] = peekExitCode(p)
if result[2] != -1: break
close(p)
proc exec*(command: string, workingDir: string = "",
args: openArray[string] = [], env: StringTableRef = nil,
options: set[ProcessOption] = {poUsePath},
msgCB: HandleProcMsgCB = nil):
tuple[output: TaintedString, error: TaintedString, exitCode: int]
{.tags: [ExecIOEffect, ReadIOEffect], gcsafe.} =
var p = startProcess(command, workingDir, args, env, options)
result = waitForWithOutput(p, msgCb, command)
proc makeProcMsgHandler*(outSink, errSink: File): HandleProcMsgCB =
result = proc(outMsg, errMsg: TaintedString, cmd: string): void {.closure.} = result = proc(outMsg, errMsg: TaintedString, cmd: string): void {.closure.} =
let prefix = if cmd != nil: cmd & ": " else: "" let prefix = if cmd == nil or not prefixCmd: "" else: cmd & ": "
if outMsg != nil: outSink.writeLine(prefix & outMsg) if outMsg != nil: outSink.writeLine(prefix & outMsg)
if errMsg != nil: errSink.writeLine(prefix & errMsg) if errMsg != nil: errSink.writeLine(prefix & errMsg)
proc makeProcMsgHandler*(outSink, errSink: Stream): HandleProcMsgCB = proc makeProcMsgHandler*(outSink, errSink: Stream, prefixCmd: bool = true): HandleProcMsgCB =
result = proc(outMsg, errMsg: TaintedString, cmd: string): void {.closure.} = result = proc(outMsg, errMsg: TaintedString, cmd: string): void {.closure.} =
let prefix = if cmd != nil: cmd & ": " else: "" let prefix = if cmd == nil or not prefixCmd: "" else: cmd & ": "
if outMsg != nil: outSink.writeLine(prefix & outMsg) if outMsg != nil: outSink.writeLine(prefix & outMsg)
if errMsg != nil: errSink.writeLine(prefix & errMsg) if errMsg != nil: errSink.writeLine(prefix & errMsg)
@ -84,6 +53,48 @@ proc combineProcMsgHandlers*(a, b: HandleProcMsgCB): HandleProcMsgCB =
a(cmd, outMsg, errMsg) a(cmd, outMsg, errMsg)
b(cmd, outMsg, errMsg) b(cmd, outMsg, errMsg)
proc waitFor*(p: Process, msgCB: HandleProcMsgCB, procCmd: string = ""): int =
var pout = outputStream(p)
var perr = errorStream(p)
var line = newStringOfCap(120).TaintedString
while true:
if pout.readLine(line):
msgCB.sendMsg(line, nil, procCmd)
elif perr.readLine(line):
msgCB.sendMsg(nil, line, procCmd)
else:
result = peekExitCode(p)
if result != -1: break
close(p)
proc exec*(command: string, workingDir: string = "",
args: openArray[string] = [], env: StringTableRef = nil,
options: set[ProcessOption] = {poUsePath},
msgCB: HandleProcMsgCB = nil): int
{.tags: [ExecIOEffect, ReadIOEffect, RootEffect], gcsafe.} =
var p = startProcess(command, workingDir, args, env, options)
result = waitFor(p, msgCB, command)
proc execWithOutput*(command: string, workingDir:string = "",
args: openArray[string] = [], env: StringTableRef = nil,
options: set[ProcessOption] = {poUsePath},
msgCB: HandleProcMsgCB = nil):
tuple[output: TaintedString, error: TaintedString, exitCode: int] =
result = (TaintedString"", TaintedString"", -1)
var outSeq, errSeq: seq[TaintedString]
outSeq = @[]; errSeq = @[]
var outputCollector = combineProcMsgHandlers(msgCB,
proc(outMsg, errMsg: TaintedString, cmd: string): void {.closure.} =
if outMsg != nil: outSeq.add(outMsg)
if errMsg != nil: errSeq.add(errMsg))
result[2] = exec(command, workingDir, args, env, options, outputCollector)
result[0] = outSeq.join("\n")
result[1] = errSeq.join("\n")
## Daemonize ## Daemonize
@ -99,7 +110,7 @@ proc onStop(sig: cint) {.noconv.} =
quit(QuitSuccess) quit(QuitSuccess)
proc daemonize*(pidfile, si, so, se: string, daemonMain: proc(): void): int = proc daemonize*(pidfile, si, so, se: string, daemonMain: proc(): void): Pid =
if fileExists(pidfile): if fileExists(pidfile):
raise newException(IOError, "pidfile " & pidfile & " already exists, daemon already running?") raise newException(IOError, "pidfile " & pidfile & " already exists, daemon already running?")

View File

@ -1,11 +1,11 @@
# Package # Package
version = "0.2.1" version = "0.3.4"
author = "Jonathan Bernard" author = "Jonathan Bernard"
description = "Helper functions for writing command line interfaces." description = "Helper functions for writing command line interfaces."
license = "MIT" license = "MIT"
# Dependencies # Dependencies
requires @["nim >= 0.15.3", "docopt"] requires @["nim >= 0.18.0", "docopt"]