(shell-mode-map): Inherit comint-mode-map, but copy the completion menu.
[bpt/emacs.git] / lisp / timer.el
index b938307..953b8f6 100644 (file)
@@ -1,12 +1,14 @@
 ;;; timer.el --- run a function with args at some time in future
 
-;; Copyright (C) 1990 Free Software Foundation, Inc.
+;; Copyright (C) 1990, 1993, 1994 Free Software Foundation, Inc.
+
+;; Maintainer: FSF
 
 ;; This file is part of GNU Emacs.
 
 ;; GNU Emacs is free software; you can redistribute it and/or modify
 ;; it under the terms of the GNU General Public License as published by
-;; the Free Software Foundation; either version 1, or (at your option)
+;; the Free Software Foundation; either version 2, or (at your option)
 ;; any later version.
 
 ;; GNU Emacs is distributed in the hope that it will be useful,
 ;; along with GNU Emacs; see the file COPYING.  If not, write to
 ;; the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
 
+;;; Commentary:
+
+;; This package gives you the capability to run Emacs Lisp commands at
+;; specified times in the future, either as one-shots or periodically.
+;; The single entry point is `run-at-time'.
+
+;;; Code:
+
+;;; The name of the program to run as the timer subprocess.  It should
+;;; be in exec-directory.
+(defconst timer-program "timer")
+
 (defvar timer-process nil)
 (defvar timer-alist ())
 (defvar timer-out "")
   ;; rescheduling or people who otherwise expect to use the process frequently
   "If non-nil, don't exit the timer process when no more events are pending.")
 
+;; This should not be necessary, but on some systems, we get
+;; unkillable processes without this.
+;; It may be a kernel bug, but that's not certain.
+(defun timer-kill-emacs-hook ()
+  (if timer-process
+      (progn
+       (set-process-sentinel timer-process nil)
+       (set-process-filter timer-process nil)
+       (delete-process timer-process))))
+(add-hook 'kill-emacs-hook 'timer-kill-emacs-hook)
+
 ;;;###autoload
 (defun run-at-time (time repeat function &rest args)
   "Run a function at a time, and optionally on a regular interval.
 Arguments are TIME, REPEAT, FUNCTION &rest ARGS.
-TIME, a string,  can be specified absolutely or relative to now.
+TIME, a string, can be specified absolutely or relative to now.
+TIME can also be an integer, a number of seconds.
 REPEAT, an integer number of seconds, is the interval on which to repeat
-the call to the function."
+the call to the function.  If REPEAT is nil or 0, call it just once.
+
+Absolute times may be specified in a wide variety of formats;
+Something of the form `HOUR:MIN:SEC TIMEZONE MONTH/DAY/YEAR', where
+all fields are numbers, works; the format used by the Unix `date'
+command works too.
+
+Relative times may be specified as a series of numbers followed by units:
+  1 min                denotes one minute from now.
+  min                  does too.
+  1 min 5 sec          denotes 65 seconds from now.
+  1 min 2 sec 3 hour 4 day 5 week 6 fortnight 7 month 8 year
+                       denotes the sum of all the given durations from now."
   (interactive "sRun at time: \nNRepeat interval: \naFunction: ")
+  (if (equal repeat 0)
+      (setq repeat nil))
+  ;; Make TIME a string.
+  (if (integerp time)
+      (setq time (format "%d sec" time)))
   (cond ((or (not timer-process) 
              (memq (process-status timer-process) '(exit signal nil)))
          (if timer-process (delete-process timer-process))
-         (setq timer-process (start-process "timer" nil "timer")
+         (setq timer-process
+              (let ((process-connection-type nil))
+                ;; Don't search the exec path for the timer program;
+                ;; we know exactly which one we want.
+                (start-process "timer" nil
+                               (expand-file-name timer-program
+                                                 exec-directory)))
                timer-alist nil)
          (set-process-filter   timer-process 'timer-process-filter)
          (set-process-sentinel timer-process 'timer-process-sentinel)
@@ -45,9 +94,19 @@ the call to the function."
         ((eq (process-status timer-process) 'stop)
          (continue-process timer-process)))
   ;; There should be a living, breathing timer process now
-  (let ((token (concat (current-time-string) "-" (length timer-alist))))
-    (send-string timer-process (concat time "\001" token "\n"))
-    (setq timer-alist (cons (list token repeat function args) timer-alist))))
+  (let* ((token (concat (current-time-string) "-" (length timer-alist)))
+        (elt (list token repeat function args)))
+    (process-send-string timer-process (concat time "@" token "\n"))
+    (setq timer-alist (cons elt timer-alist))
+    elt))
+
+(defun cancel-timer (elt)
+  "Cancel a timer previously made with `run-at-time'.
+The argument should be a value previously returned by `run-at-time'.
+Cancelling the timer means that nothing special 
+will happen at the specified time."
+  (setcar (cdr elt) nil)
+  (setcar (cdr (cdr elt)) 'ignore))
 
 (defun timer-process-filter (proc str)
   (setq timer-out (concat timer-out str))
@@ -57,11 +116,12 @@ the call to the function."
             do (assoc token timer-alist)
             timer-out (substring timer-out (match-end 0)))
       (cond
-       (do (apply (nth 2 do) (nth 3 do))   ; do it
-           (if (natnump (nth 1 do))        ; reschedule it
-               (send-string proc (concat (nth 1 do) " sec\001" (car do) "\n"))
-             (setq timer-alist (delq do timer-alist))))
-       ((string-match "timer: \\([^:]+\\): \\([^\001]*\\)\001\\(.*\\)$" token)
+       (do
+       (apply (nth 2 do) (nth 3 do))   ; do it
+       (if (natnump (nth 1 do))        ; reschedule it
+           (send-string proc (concat (nth 1 do) " sec@" (car do) "\n"))
+         (setq timer-alist (delq do timer-alist))))
+       ((string-match "timer: \\([^:]+\\): \\([^@]*\\)@\\(.*\\)$" token)
         (setq error (substring token (match-beginning 1) (match-end 1))
               do    (substring token (match-beginning 2) (match-end 2))
               token (assoc (substring token (match-beginning 3) (match-end 3))
@@ -79,11 +139,12 @@ the call to the function."
       (if (or (not timer-alist) (eq stat 'exit)) ()
         (ding 'no-terminate)
         (message "Timer exited abnormally.  All events cancelled."))
-      (setq timer-process nil timer-alist nil timer-scratch ""))))
+      ;; Used to set timer-scratch to "", but nothing uses that var.
+      (setq timer-process nil timer-alist nil))))
 
-(defun cancel-timer (function)
-  "Cancel all events scheduled by ``run-at-time'' which would run FUNCTION."
-  (interactive "aCancel function: ")
+(defun cancel-function-timers (function)
+  "Cancel all events scheduled by `run-at-time' which would run FUNCTION."
+  (interactive "aCancel timers of function: ")
   (let ((alist timer-alist))
     (while alist
       (if (eq (nth 2 (car alist)) function)