(defvar multi-task-status-buffer-name
"*Multi-Task-Status*"
"Buffer name to use for process status.")
(defun multi-task-start (commands)
"Start COMMANDS asynchronously and return a process list.
COMMANDS is a list of commands containing:
NAME is the name of the process.
PROGRAM is the program to run.
ARGS is a list of string arguments to pass to PROGRAM.
BUFFER is a buffer name to direct the output.
Returned list contains lists of processes and timestamps."
(let (processes)
(dolist (command commands)
(let* ((name (car command))
(program (cadr command))
(args (caddr command))
(buffer (cadddr command)))
(push (list (eval `(start-process name buffer program ,@args)) (current-time)) processes)))
(nreverse processes)))
(defun multi-task-status (processes &optional times)
"Print the status of PROCESSES and return non-nil if any are still running.
If optional parameter TIMES is non-nil, timing information will
be displayed and returned."
(let ((done t))
(when (and times (not (listp times)))
(setq times nil)
(dolist (lst processes)
(push (cons (process-name (car lst)) (list 0 0 0)) times)))
(insert "Process Status")
(newline)
(newline)
(insert "Process Command Status")
(when times
(insert " Time"))
(newline)
(insert "---------------------------- ---------------------------- ------")
(when times
(insert " ---------------"))
(newline)
(dolist (lst processes)
(let* ((process (car lst))
(time (cadr lst))
(name (process-name process))
(status (process-status process))
time-diff
command)
(when times
(setq time-diff (cdr (assoc name times))))
(dolist (x (process-command process))
(if command
(setq command (concat command " " x))
(setq command x)))
(when (eq status 'run)
(when times
(setq time-diff (time-subtract (current-time) time))
(setcdr (assoc name times) time-diff))
(setq done nil))
(when (> (length name) 28)
(setq name (substring name 0 28)))
(when (> (length command) 28)
(setq command (substring command 0 28)))
(if times
(let* ((microsecs (caddr time-diff))
(total-seconds (+ (* (car time-diff) 65536) (cadr time-diff)))
(hours (floor (/ total-seconds 3600)))
(mins (floor (/ (- total-seconds (* hours 3600)) 60)))
(secs (- total-seconds (* hours 3600) (* mins 60))))
(setq time-diff (concat
(if (> hours 0)
(format "%2d:%02d:%02d.%d" hours mins secs microsecs)
(if (> mins 0)
(format " %2d:%02d.%d" mins secs microsecs)
(format " %2d.%d" secs microsecs)))))
(insert (format "%-29s %-29s %-7S %s" process command status time-diff)))
(insert (format "%-29s %-29s %S" process command status)))
(newline)))
(if done
nil
(if times
times
t))))
(defun multi-task (commands &optional kill-buffers)
"Start COMMANDS asynchronously, report running statistics,
and return when commands have completed.
COMMANDS is a list of commands containing:
NAME is the name of the process.
PROGRAM is the program to run.
ARGS is a list of string arguments to pass to PROGRAM.
BUFFER is a buffer name to direct the output.
If optional parameter KILL-BUFFERS is non-nil (default) then the
command buffers are killed after they finish running."
(interactive)
(let (buffer processes (times t)) (setq processes (multi-task-start commands))
(setq buffer (generate-new-buffer multi-task-status-buffer-name))
(switch-to-buffer buffer)
(buffer-disable-undo buffer)
(let ((status t))
(while status
(setq buffer-read-only nil)
(erase-buffer)
(setq status (multi-task-status processes times))
(when status
(setq times status))
(setq buffer-read-only t)
(sit-for 0.1)))
(dolist (lst (reverse processes))
(let* ((process (car lst))
(buffer (process-buffer process)))
(if kill-buffers
(kill-buffer buffer)
(switch-to-buffer buffer))))
(switch-to-buffer buffer)))
(provide 'multi-task)