;;; FILE I/O
-(defun auto-file-coding-system (head-lines)
- "Return coding system for a file which has HEAD-LINES at the head.
-HEAD-LINES is a string of the first two lines of the file.
-This checks for a -*- coding tag in the buffers's text,
-and return the specified coding system.
+(defun set-auto-coding (string)
+ "Return coding system for a file which has STRING at the head and tail.
+STRING is a concatination of the first 1K-byte and
+ the last 3K-byte of the file.
+
+It checks for a -*- coding: tag in the first one or two lines of STRING.
+If there's no coding: tag in the head, it checks local variables spec
+in the tailing 3K-byte oof STRING.
+
+The return value is the specified coding system,
+or nil if nothing specified.
The variable `auto-file-coding-system' (which see) is set to this
function by default."
- (let ((limit (string-match "\n" head-lines))
- (coding-system nil))
- (if limit
- (when (string-match "^#!" head-lines)
- ;; If the file begins with "#!" (exec interpreter magic),
- ;; look for coding frobs in the first two lines. You cannot
- ;; necessarily put them in the first line of such a file
- ;; without screwing up the interpreter invocation.
- (setq limit (string-match "\n" head-lines limit))
- (or limit
- (setq limit (length head-lines))))
- (setq limit (length head-lines)))
- (when (and (string-match "-\\*-[ \t]*coding:[ \t]*\\([^ ;]+\\)" head-lines)
- (< (match-beginning 1) limit))
- (setq coding-system
- (intern (substring head-lines (match-beginning 1) (match-end 1))))
- (if (coding-system-p coding-system)
- coding-system))))
-
-(setq auto-file-coding-system-function 'auto-file-coding-system)
+ (condition-case nil
+ (let ((case-fold-search t)
+ (len (length string))
+ (limit (string-match "\n" string))
+ (coding-system nil))
+
+ ;; At first check the head.
+ (if limit
+ (when (string-match "^#!" string)
+ ;; If the file begins with "#!" (exec interpreter
+ ;; magic), look for coding frobs in the first two lines.
+ ;; You cannot necessarily put them in the first line of
+ ;; such a file without screwing up the interpreter
+ ;; invocation.
+ (setq limit (string-match "\n" string limit))
+ (or limit
+ (setq limit len)))
+ (setq limit len))
+ (when (and (string-match "-\\*-[ \t]*coding:[ \t]*\\([^ ;]+\\)" string)
+ (< (match-beginning 1) limit))
+ (setq coding-system
+ (intern (substring string (match-beginning 1) (match-end 1))))
+ (if (not (coding-system-p coding-system))
+ (setq coding-system nil)))
+
+ ;; If no coding system is specified in the head, check the tail.
+ (when (and (not coding-system)
+ (let ((idx (if (> len 3000) (- len 3000) 0))
+ start)
+ (while (setq start (string-match "\n\^L" string idx))
+ (setq idx (+ start 2)))
+ (string-match
+ "^\\(.*\\)[ \t]*Local Variables:[ \t]*\\(.*\\)$"
+ string idx)))
+ ;; The prefix is what comes before "local variables:" in its line.
+ ;; The suffix is what comes after "local variables:" in its line.
+ (let* ((idx (1+ (match-end 0)))
+ (prefix (regexp-quote
+ (substring string
+ (match-beginning 1) (match-end 1))))
+ (suffix (regexp-quote
+ (substring string
+ (match-beginning 2) (match-end 2))))
+ (re-coding (concat "^" prefix
+ "coding[ \t]*:[ \t]*\\([^ \t]+\\)[ \t]*"
+ suffix "$"))
+ (re-end (concat "^" prefix "end *:[ \t]*" suffix "$"))
+ (limit (or (string-match re-end string idx) len)))
+ (when (and (setq idx (string-match re-coding string idx))
+ (< idx limit))
+ (setq coding-system
+ (intern (substring string
+ (match-beginning 1) (match-end 1))))
+ (or (coding-system-p coding-system)
+ (setq coding-system nil)))))
+
+ coding-system)
+ (error nil)))
+
+(setq set-auto-coding-function 'set-auto-coding)
;; Set buffer-file-coding-system of the current buffer after some text
;; is inserted.