* poe.el (make-temp-file) [no make-temp-file, single-user system]:
[elisp/apel.git] / poe.el
diff --git a/poe.el b/poe.el
index 8d25dfd..8231606 100644 (file)
--- a/poe.el
+++ b/poe.el
@@ -267,7 +267,7 @@ HIST, if non-nil, specifies a history list
 DEF, if non-nil, is the default value.
 
 Completion ignores case if the ambient value of
-  `completion-ignore-case' is non-nil."  
+  `completion-ignore-case' is non-nil."
        (let ((string (si:completing-read prompt table predicate
                                          require-match init hist)))
          (if (and (string= string "") def)
@@ -1160,8 +1160,7 @@ which is made by replacing the part of STRING that was matched."
                    (buffer-string)))
               (si:replace-match newtext fixedcase literal)))))))))
 
-;; Emacs 20: (format-time-string)
-;; The the third optional argument universal is yet to be implemented.
+;; Emacs 20: (format-time-string FORMAT &optional TIME UNIVERSAL)
 ;; Those format constructs are yet to be implemented.
 ;;   %c, %C, %j, %U, %W, %x, %X
 ;; Not fully compatible especially when invalid format is specified.
@@ -1235,7 +1234,6 @@ For example, to produce full ISO 8601 format, use \"%Y-%m-%dT%T%z\".
 
 Compatibility Note.
 
-The the third optional argument universal is yet to be implemented.
 Those format constructs are yet to be implemented.
   %c, %C, %j, %U, %W, %x, %X
 Not fully compatible especially when invalid format is specified."
@@ -1250,9 +1248,24 @@ Not fully compatible especially when invalid format is specified."
          field-result
          pad-left change-case
          (paren-level 0)
-         hour
-         (time-string (current-time-string time)))
-      (setq hour (string-to-int (substring time-string 11 13)))
+         hour ms ls
+         (tz (car (current-time-zone)))
+         time-string)
+      (if universal
+         (progn
+           (or time
+               (setq time (current-time)))
+           (setq ms (car time)
+                 ls (- (nth 1 time) tz))
+           (cond ((< ls 0)
+                  (setq ms (1- ms)
+                        ls (+ ls 65536)))
+                 ((>= ls 65536)
+                  (setq ms (1+ ms)
+                        ls (- ls 65536))))
+           (setq time (append (list ms ls) (nth 2 time)))))
+      (setq time-string (current-time-string time)
+           hour (string-to-int (substring time-string 11 13)))
       (while (< ind fmt-len)
        (setq cur-char (aref format ind))
        (setq
@@ -1301,7 +1314,7 @@ Not fully compatible especially when invalid format is specified."
                (cond
                 ((eq cur-char ?%)
                  "%")
-                ;; the abbreviated name of the day of week.             
+                ;; the abbreviated name of the day of week.
                 ((eq cur-char ?a)
                  (substring time-string 0 3))
                 ;; the full name of the day of week
@@ -1322,7 +1335,7 @@ Not fully compatible especially when invalid format is specified."
                 ((eq cur-char ?C)
                  "")
                 ;; the day of month, zero-padded
-                ((eq cur-char ?d)      
+                ((eq cur-char ?d)
                  (format "%02d" (string-to-int (substring time-string 8 10))))
                 ;; a synonym for `%m/%d/%y'
                 ((eq cur-char ?D)
@@ -1390,7 +1403,7 @@ Not fully compatible especially when invalid format is specified."
                          (substring time-string 11 13)
                          (substring time-string 14 16)
                          (substring time-string 17 19)))
-                ;; the week of the year (01-52), assuming that weeks 
+                ;; the week of the year (01-52), assuming that weeks
                 ;; start on Sunday (yet to come)
                 ((eq cur-char ?U)
                  "")
@@ -1416,13 +1429,18 @@ Not fully compatible especially when invalid format is specified."
                  (substring time-string -4))
                 ;; the time zone abbreviation
                 ((eq cur-char ?Z)
-                 (setq change-case (not change-case))
-                 (downcase (cadr (current-time-zone))))
+                 (if universal
+                     "UTC"
+                   (setq change-case (not change-case))
+                   (downcase (cadr (current-time-zone)))))
                 ((eq cur-char ?z)
-                 (let ((tz (car (current-time-zone))))
+                 (if universal
+                     "+0000"
                    (if (< tz 0)
-                       (format "-%02d%02d" (/ (- tz) 3600) (/ (% (- tz) 3600) 60))
-                     (format "+%02d%02d" (/ tz 3600) (/ (% tz 3600) 60)))))
+                       (format "-%02d%02d"
+                               (/ (- tz) 3600) (/ (% (- tz) 3600) 60))
+                     (format "+%02d%02d"
+                             (/ tz 3600) (/ (% tz 3600) 60)))))
                 (t
                  (concat
                   "%"
@@ -1577,6 +1595,168 @@ See `walk-windows' for the meaning of MINIBUF and FRAME."
          (or (getenv "TMPDIR") (getenv "TMP") (getenv "TEMP") "/tmp"))))
   "The directory for writing temporary files.")
 
+;; Emacs 21: Create a temporary file. (lisp/subr.el)
+;; Emacs 21.1-21.3
+;;  (make-temp-file PREFIX &optional DIR-FLAG)
+;; Emacs 21.3.x(?) and later
+;;  (make-temp-file PREFIX &optional DIR-FLAG SUFFIX)
+(static-condition-case nil
+    ;; compile-time check
+    (progn
+      (delete-file (make-temp-file "EMU" nil ".txt"))
+      (if (get 'make-temp-file 'defun-maybe)
+         (error "`make-temp-file' is already defined")))
+  (wrong-number-of-arguments ; Emacs 21.1-21.3
+   ;; load-time check.
+   ;; Replace original definition.
+   (or (fboundp 'si:make-temp-file)
+       (progn
+        (fset 'si:make-temp-file (symbol-function 'make-temp-file))
+        (put 'make-temp-file 'defun-maybe t)
+        (defun make-temp-file (prefix &optional dir-flag suffix)
+          "\
+Create a temporary file.
+The returned file name (created by appending some random characters at the end
+of PREFIX, and expanding against `temporary-file-directory' if necessary),
+is guaranteed to point to a newly created empty file.
+You can then use `write-region' to write new data into the file.
+
+If DIR-FLAG is non-nil, create a new empty directory instead of a file.
+
+If SUFFIX is non-nil, add that at the end of the file name."
+          (let ((umask (default-file-modes))
+                file)
+            (unwind-protect
+                (progn
+                  ;; Create temp files with strict access rights.  
+                  ;; It's easy toloosen them later, whereas it's impossible
+                  ;;  to close the time-window of loose permissions otherwise.
+                  (set-default-file-modes ?\700)
+                  (while (condition-case ()
+                             (progn
+                               (setq file
+                                     (make-temp-name
+                                      (expand-file-name
+                                       prefix temporary-file-directory)))
+                               (if suffix
+                                   (setq file (concat file suffix)))
+                               (if dir-flag
+                                   (make-directory file)
+                                 (write-region "" nil file nil
+                                               'silent nil 'excl))
+                               nil)
+                           (file-already-exists t))
+                    ;; the file was somehow created by someone else between
+                    ;; `make-temp-name' and `write-region', let's try again.
+                    nil)
+                  file)
+              ;; Reset the umask.
+              (set-default-file-modes umask))))
+        ;; for `load-history'.
+        (setq current-load-list (cons 'make-temp-file current-load-list)))))
+  (error)) ; found our definition or no definition at compile-time.
+
+;; For the Emacsen which don't have make-temp-file.
+(cond
+ ;; must be load-time check to share .elc between different systems.
+ ((fboundp 'make-temp-file))
+ ((memq system-type '(windows-nt ms-dos OS/2 emx))
+  ;; For single-user systems:
+  (defun-maybe make-temp-file (prefix &optional dir-flag suffix)
+    "Create a temporary file.
+The returned file name (created by appending some random characters at the end
+of PREFIX, and expanding against `temporary-file-directory' if necessary),
+is guaranteed to point to a newly created empty file.
+You can then use `write-region' to write new data into the file.
+
+If DIR-FLAG is non-nil, create a new empty directory instead of a file.
+
+If SUFFIX is non-nil, add that at the end of the file name."
+    (let ((file (make-temp-name (expand-file-name prefix
+                                                 temporary-file-directory))))
+      (if suffix
+         (setq file (concat file suffix)))
+      (if dir-flag
+         (make-directory file)
+       (write-region "" nil file nil 'silent))
+      file)))
+ (t
+  (defun-maybe make-temp-file (prefix &optional dir-flag suffix)
+    "Create a temporary file.
+The returned file name (created by appending some random characters at the end
+of PREFIX, and expanding against `temporary-file-directory' if necessary),
+is guaranteed to point to a newly created empty file.
+You can then use `write-region' to write new data into the file.
+
+If DIR-FLAG is non-nil, create a new empty directory instead of a file.
+
+If SUFFIX is non-nil, add that at the end of the file name."
+    (let ((umask (default-file-modes)))
+      (unwind-protect
+          (let ((prefix (expand-file-name prefix temporary-file-directory)))
+            ;; Create temp files with strict access rights.  It's easy to
+            ;; loosen them later, whereas it's impossible to close the
+            ;; time-window of loose permissions otherwise.
+            (set-default-file-modes 448)
+            (if dir-flag
+                ;; Create a new empty directory.
+                (let (dir)
+                  (while (condition-case ()
+                             (progn
+                               (setq dir (make-temp-name prefix))
+                               (if suffix
+                                   (setq dir (concat dir suffix)))
+                               ;; `make-directory' returns nil for success,
+                               ;; otherwise signals an error.
+                               (make-directory dir))
+                           ;; the dir was somehow created by someone else
+                           ;; between `make-temp-name' and `make-directory',
+                           ;; let's try again.
+                           (file-already-exists t)))
+                  dir)
+              ;; Create a new empty file.
+              (let (tempdir tempfile)
+                (unwind-protect
+                    (let (file)
+                      ;; First, create a temporary directory.
+                      (while (condition-case ()
+                                 (progn
+                                   (setq tempdir (make-temp-name
+                                                  (concat
+                                                   (file-name-directory prefix)
+                                                   "DIR")))
+                                   ;; return nil or signal an error.
+                                   (make-directory tempdir))
+                               ;; let's try again.
+                               (file-already-exists t)))
+                      ;; Second, create a temporary file in the tempdir.
+                      ;; There *is* a race condition between `make-temp-name'
+                      ;; and `write-region', but we don't care it since we are
+                      ;; in a private directory now.
+                      (setq tempfile (make-temp-name (concat tempdir "/EMU")))
+                      (write-region "" nil tempfile nil 'silent)
+                      ;; Finally, make a hard-link from the tempfile.
+                      (while (condition-case ()
+                                 (progn
+                                   (setq file (make-temp-name prefix))
+                                   (if suffix
+                                       (setq file (concat file suffix)))
+                                   ;; return nil or signal an error.
+                                   (add-name-to-file tempfile file))
+                               ;; let's try again.
+                               (file-already-exists t)))
+                      file)
+                  ;; Cleanup the tempfile.
+                  (and tempfile
+                       (file-exists-p tempfile)
+                       (delete-file tempfile))
+                  ;; Cleanup the tempdir.
+                  (and tempdir
+                       (file-directory-p tempdir)
+                       (delete-directory tempdir))))))
+        ;; Reset the umask.
+        (set-default-file-modes umask))))))
+
 ;; Actually, `path-separator' is defined in src/emacs.c and overrided
 ;; in dos-w32.el.
 (defvar-maybe path-separator ":"
@@ -1762,10 +1942,10 @@ the echo area while this function is waiting for an event."
   ((and (fboundp 'read-event)
        (subrp (symbol-function 'read-event)))
    ;; Emacs 19, 20.1 and 20.2.
-   (if prompt (message prompt))
+   (if prompt (message "%s" prompt))
    (read-event))
   (t
-   (if prompt (message prompt))
+   (if prompt (message "%s" prompt))
    (read-char)))
 \f