make-temp-file

Yuuichi Teranishi teranisi @ gohome.org
2003年 5月 14日 (水) 20:36:30 JST


wl, emacs-mime-ja あたりでの最近の議論ですが、APEL に移動します。
(Cc: emacs-mime-ja)

APEL で関数 make-temp-file を扱いたい、というはなしです。
詳しい経緯は、
http://lists.airs.net/wl/archive/200305/
あたりをご覧ください。まとめると、以下のようなかんじです。

(1) make-temp-name を使って一時ファイルを生成する方法は、
    一時ファイルを作る場所が /tmp 等の public な場所である場合、
    安全ではない。
(2) Emacs 21 には、この問題を回避するための関数 make-temp-file がある。
(3) 一方、Emacs21.[123] の make-temp-file の定義と CVS 最新版の定義は異なり、
    * 生成ファイルのモードが設定される (2002-09-27 の変更)
    * optional 引数 SUFFIX がある (2002-04-28 の変更)
    という違いがある。
 -> make-temp-file の有無、および API の違いを、APEL で吸収できると楽。

まず、write-region に第 7 引数がない環境でどうやって安全に(排他的に)
一時ファイルを作るかという問題がありましたが、それについては、
http://lists.airs.net/wl/archive/200305/msg00051.html
に示されたコードでよいのではないかという認識になっています。

いまのところ問題点は、上記 (3) の解決のために、21.[123] の定義を上書
きすることになってしまう点なのですが、基本的に CVS 最新版の API は上位互換
であり、APEL の従来の方針(邪悪さ?)の範囲なのではなかろうかと個人的には
思っています。

…ということで、末尾に poe.el へ追加するコードの案を添付します。
上記リンク先に示されたコードをもとに以下の変更をしてます。
* XEmacs で動くよう変更 ( ?\700 を 448 にしただけ )
* Windows への対応 (むかしの経験だと add-name-to-file が動かなかったと
  	  	    思うので copy-file に変更。
  	  	    手元にまともなテスト環境ないので検証できず(すみません))
* Emacs 21.[123] 向け定義 (CVS 版とおなじ)
 ;; このままだと 2002-04-28 から 2002-09-27 の間の Emacs CVS 版で file-modes が
 ;; 設定されませんが、そこはとりあえず don't care。


ご意見などあればください。

-------------- next part --------------
;; 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)))))))
  (error)) ; found our definition or no definition at compile-time.

;; For the Emacsen which don't have make-temp-file.
(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 ((orig-mode (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 dir)
                             ;; `make-directory' returns nil,
                             ;; but we return nil explicitly.
                             nil)
                         (file-already-exists t))
                  ;; the dir was somehow created by someone else between
                  ;; `make-temp-name' and `make-directory', let's try again.
                  )
                dir)
            ;; Create a temporary file.
            ;; First, create a temporary directory.
            (let (tempdir)
              (unwind-protect
                  (let ((tempdir-prefix (concat
                                         (file-name-directory prefix)
                                         "DIR")))
                    (while (condition-case ()
                               (progn
                                 (setq tempdir (make-temp-name tempdir-prefix))
                                 (make-directory tempdir)
                                 ;; `make-directory' returns nil,
                                 ;; but we return nil explicitly.
                                 nil)
                             (file-already-exists t))
                      ;; the tempdir was somehow created by someone else
                      ;; between `make-temp-name' and `make-directory',
                      ;; let's try again.
                      )
                    ;; Second, create a temporary file in the tempdir.
                    (let (tempfile)
                      (unwind-protect
                          (let (file)
                            (setq tempfile (make-temp-name
                                            (concat tempdir "/EMU")))
                            ;; There *is* a race condition between
                            ;; `make-temp-file' and `write-region',
                            ;; but we don't care it since we are in
                            ;; a private directory now.
                            (write-region "" nil tempfile nil 'silent nil)
                            ;; 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)))
					 (static-if (memq system-type
							  '(windows-nt ms-dos))
					     (copy-file tempfile file)
					   (add-name-to-file tempfile file))
                                         ;; `add-name-to-file' returns nil,
                                         ;; but we return nil explicitly.
                                         nil)
                                     (file-already-exists t))
                              ;; the file was somehow created by someone else
                              ;; between `make-temp-name' and
                              ;; `add-name-to-file', let's try again.
                              )
                            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 original file mode.
      (set-default-file-modes orig-mode))))
-------------- next part --------------
--
Yuuichi Teranishi (寺西裕一) <teranisi @ gohome.org>
GPG Public Key: http://www.gohome.org/gpg/teranisi.asc
"For tomorrow may rain, so I'll follow the sun."


More information about the APEL-ja mailing list