(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 process 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 TIMES is given, 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 (third time-diff))
(total-seconds (+ (* (first time-diff) 65536) (second 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)
"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."
(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)))
(switch-to-buffer buffer)))
(switch-to-buffer buffer)
))
(provide 'multi-task)