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