;;;; main.lisp -- oneliners.cli entrypoint (defpackage oneliners.cli (:use :cl)) (in-package :oneliners.cli) ;;; CLI OPTIONS ;; (opts:define-opts ;; (:name )) ;;; UTILITIES (defun parent-process-name () "Prints the name of the parent process of the current process." (let ((ppidfile (format nil "/proc/~a/status" (osicat-posix:getppid)))) (first (last (ppcre:split "\\s" (with-open-file (input ppidfile) (read-line input))))))) (defmacro wait-until ((&key (timeout 1) (poll-every 0.01)) &body check) "Run CHECK every POLL-EVERY seconds until either TIMEOUT seconds have passed or CHECK returns non-nil." (let ((clockvar (gensym)) (var (gensym))) `(loop for ,clockvar from 0 by ,poll-every to ,timeout for ,var = (progn ,@check) when ,var return ,var do (sleep ,poll-every)))) (defun run-with-shell (command &key (shell-name (parent-process-name)) (await-output-p 0.8) (output-stream *standard-output*)) "run COMMAND, a string, in a fresh shell environment, initialized with SHELL-NAME. The output from the command read line by line and is printed to OUTPUT-STREAM. " (let ((shell (uiop:launch-program shell-name :input :stream :output :stream))) (symbol-macrolet ((shell-input (uiop:process-info-input shell)) (shell-output (uiop:process-info-output shell))) (write-line command shell-input) (finish-output shell-input) (when await-output-p (wait-until (:timeout await-output-p :poll-every 0.005) (listen shell-output)) (loop while (listen shell-output) do (princ (read-line shell-output) output-stream) (terpri output-stream))))))