d74a11ca15b9f2b59899f2f6cb1c7302f6584ee4
[chise/xemacs-chise.git.1] / lisp / code-files.el
1 ;;; code-files.el --- File I/O functions for XEmacs.
2
3 ;; Copyright (C) 1992,93,94,95 Free Software Foundation, Inc.
4 ;; Copyright (C) 1995 Amdahl Corporation.
5 ;; Copyright (C) 1995 Sun Microsystems.
6
7 ;; This file is part of XEmacs.
8
9 ;; This file is very similar to mule-files.el
10
11 ;; XEmacs is free software; you can redistribute it and/or modify it
12 ;; under the terms of the GNU General Public License as published by
13 ;; the Free Software Foundation; either version 2, or (at your option)
14 ;; any later version.
15
16 ;; XEmacs is distributed in the hope that it will be useful, but
17 ;; WITHOUT ANY WARRANTY; without even the implied warranty of
18 ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
19 ;; General Public License for more details.
20
21 ;; You should have received a copy of the GNU General Public License
22 ;; along with XEmacs; see the file COPYING.  If not, write to the 
23 ;; Free Software Foundation, Inc., 59 Temple Place - Suite 330,
24 ;; Boston, MA 02111-1307, USA.
25
26 ;;; Commentary:
27
28 ;;; Derived from mule.el in the original Mule but heavily modified
29 ;;; by Ben Wing.
30
31 ;; 1997/3/11 modified by MORIOKA Tomohiko to sync with Emacs 20 API.
32
33 ;;; Code:
34
35 (setq-default buffer-file-coding-system 'no-conversion)
36 (put 'buffer-file-coding-system 'permanent-local t)
37
38 (define-obsolete-variable-alias
39   'file-coding-system
40   'buffer-file-coding-system)
41
42 (define-obsolete-variable-alias
43   'overriding-file-coding-system
44   'coding-system-for-read)
45
46 (defvar buffer-file-coding-system-for-read 'undecided
47   "Coding system used when reading a file.
48 This provides coarse-grained control; for finer-grained control, use
49 `file-coding-system-alist'.  From a Lisp program, if you wish to
50 unilaterally specify the coding system used for one particular
51 operation, you should bind the variable `coding-system-for-read'
52 rather than setting this variable, which is intended to be used for
53 global environment specification.")
54
55 (define-obsolete-variable-alias
56   'file-coding-system-for-read
57   'buffer-file-coding-system-for-read)
58
59 (defvar file-coding-system-alist
60   `(
61 ;; This must not be necessary, slb suggests -kkm
62 ;;  ("loaddefs.el$" . (binary . binary))
63     ,@(mapcar
64        #'(lambda (regexp) (cons regexp 'binary)) binary-file-regexps)
65     ("TUTORIAL\\.\\(?:hr\\|pl\\|ro\\)\\'" . iso-8859-2)
66     ;; ("\\.\\(el\\|emacs\\|info\\(-[0-9]+\\)?\\|texi\\)$" . iso-2022-8)
67     ;; ("\\(ChangeLog\\|CHANGES-beta\\)$" . iso-2022-8)
68     ("/spool/mail/.*$" . convert-mbox-coding-system))
69   "Alist to decide a coding system to use for a file I/O operation.
70 The format is ((PATTERN . VAL) ...),
71 where PATTERN is a regular expression matching a file name,
72 VAL is a coding system, a cons of coding systems, or a function symbol.
73 If VAL is a coding system, it is used for both decoding and encoding
74 the file contents.
75 If VAL is a cons of coding systems, the car part is used for decoding,
76 and the cdr part is used for encoding.
77 If VAL is a function symbol, the function must return a coding system
78 or a cons of coding systems which are used as above.
79
80 This overrides the more general specification in
81 `buffer-file-coding-system-for-read', but is overridden by
82 `coding-system-for-read'.")
83
84 (defun set-buffer-file-coding-system (coding-system &optional force)
85   "Set buffer-file-coding-system of the current buffer to CODING-SYSTEM.
86 If optional argument FORCE (interactively, the prefix argument) is not
87 given, attempt to match the EOL type of the new coding system to
88 the current value of `buffer-file-coding-system'."
89   (interactive "zFile coding system: \nP")
90   (get-coding-system coding-system) ;; correctness check
91   (if (not force)
92       (setq coding-system
93             (subsidiary-coding-system
94              coding-system
95              (coding-system-eol-type buffer-file-coding-system))))
96   (setq buffer-file-coding-system coding-system)
97   (redraw-modeline t))
98
99 (defun toggle-buffer-file-coding-system ()
100   "Set EOL type of buffer-file-coding-system of the current buffer to
101 something other than what it is at the moment."
102   (interactive)
103   (let ((eol-type
104          (coding-system-eol-type buffer-file-coding-system)))
105     (setq buffer-file-coding-system
106           (subsidiary-coding-system
107            (coding-system-base buffer-file-coding-system)
108            (cond ((eq eol-type 'lf) 'crlf)
109                  ((eq eol-type 'crlf) 'lf)
110                  ((eq eol-type 'cr) 'lf))))))
111
112 (define-obsolete-function-alias
113   'set-file-coding-system
114   'set-buffer-file-coding-system)
115
116 (defun set-buffer-file-coding-system-for-read (coding-system)
117   "Set the coding system used when reading in a file.
118 This is equivalent to setting the variable
119 `buffer-file-coding-system-for-read'.  You can also use
120 `file-coding-system-alist' to specify the coding system for
121 particular files."
122   (interactive "zFile coding system for read: ")
123   (get-coding-system coding-system) ;; correctness check
124   (setq buffer-file-coding-system-for-read coding-system))
125
126 (define-obsolete-function-alias
127   'set-file-coding-system-for-read
128   'set-buffer-file-coding-system-for-read)
129
130 (defun set-default-buffer-file-coding-system (coding-system)
131   "Set the default value of `buffer-file-coding-system' to CODING-SYSTEM.
132 The default value is used both for buffers without associated files
133 and for files with no apparent coding system (i.e. primarily ASCII).
134 See `buffer-file-coding-system' for more information."
135   (interactive "zDefault file coding system: ")
136   (setq-default buffer-file-coding-system coding-system)
137   (redraw-modeline t))
138
139 (define-obsolete-function-alias
140   'set-default-file-coding-system
141   'set-default-buffer-file-coding-system)
142
143 (defun find-file-coding-system-for-read-from-filename (filename)
144   "Look up coding system to read a file in `file-coding-system-alist'.
145 The return value will be nil (no applicable entry) or a coding system
146 object (the entry specified a coding system)."
147   (let ((alist file-coding-system-alist)
148         (found nil)
149         (codesys nil))
150     (let ((case-fold-search nil))
151       (setq filename (file-name-sans-versions filename))
152       (while (and (not found) alist)
153         (if (string-match (car (car alist)) filename)
154             (setq codesys (cdr (car alist))
155                   found t))
156         (setq alist (cdr alist))))
157     (when codesys
158       (if (functionp codesys)
159           (setq codesys (funcall codesys 'insert-file-contents filename))
160         )
161       (cond ((consp codesys) (find-coding-system (car codesys)))
162             ((find-coding-system codesys))
163             ))))
164
165 (define-obsolete-function-alias
166   'find-file-coding-system-from-filename
167   'find-file-coding-system-for-read-from-filename)
168
169 (defun find-file-coding-system-for-write-from-filename (filename)
170   "Look up coding system to write a file in `file-coding-system-alist'.
171 The return value will be nil (no applicable entry) or a coding system
172 object (the entry specified a coding system)."
173   (let ((alist file-coding-system-alist)
174         (found nil)
175         (codesys nil))
176     (let ((case-fold-search nil))
177       (setq filename (file-name-sans-versions filename))
178       (while (and (not found) alist)
179         (if (string-match (car (car alist)) filename)
180             (setq codesys (cdr (car alist))
181                   found t))
182         (setq alist (cdr alist))))
183     (when codesys
184       (if (functionp codesys)
185           (setq codesys (funcall codesys 'write-region filename))
186         )
187       (cond ((consp codesys) (find-coding-system (cdr codesys)))
188             ((find-coding-system codesys))
189             ))))
190
191 (defun convert-mbox-coding-system (filename visit start end)
192   "Decoding function for Unix mailboxes.
193 Does separate detection and decoding on each message, since each
194 message might be in a different encoding."
195   (let ((buffer-read-only nil))
196     (save-restriction
197       (narrow-to-region start end)
198       (goto-char (point-min))
199       (while (not (eobp))
200         (let ((start (point))
201               end)
202           (forward-char 1)
203           (if (re-search-forward "^From" nil 'move)
204               (beginning-of-line))
205           (setq end (point))
206           (decode-coding-region start end 'undecided))))))
207
208 (defun find-coding-system-magic-cookie ()
209   "Look for the coding-system magic cookie in the current buffer.\n"
210 "The coding-system magic cookie is the exact string\n"
211 "\";;;###coding system: \" followed by a valid coding system symbol,\n"
212 "somewhere within the first 3000 characters of the file.  If found,\n"
213 "the coding system symbol is returned; otherwise nil is returned.\n"
214 "Note that it is extremely unlikely that such a string would occur\n"
215 "coincidentally as the result of encoding some characters in a non-ASCII\n"
216 "charset, and that the spaces make it even less likely since the space\n"
217 "character is not a valid octet in any ISO 2022 encoding of most non-ASCII\n"
218 "charsets."
219   (save-excursion
220     (goto-char (point-min))
221     (or (and (looking-at
222               "^[^\n]*-\\*-[^\n]*coding: \\([^ \t\n;]+\\)[^\n]*-\\*-")
223              (let ((codesys (intern (buffer-substring
224                                      (match-beginning 1)(match-end 1)))))
225                (if (find-coding-system codesys) codesys)))
226         ;; (save-excursion
227         ;;   (let (start end)
228         ;;     (and (re-search-forward "^;+[ \t]*Local Variables:" nil t)
229         ;;          (setq start (match-end 0))
230         ;;          (re-search-forward "\n;+[ \t]*End:")
231         ;;          (setq end (match-beginning 0))
232         ;;          (save-restriction
233         ;;            (narrow-to-region start end)
234         ;;            (goto-char start)
235         ;;            (re-search-forward "^;;; coding: \\([^\n]+\\)$" nil t)
236         ;;            )
237         ;;          (let ((codesys
238         ;;                 (intern (buffer-substring
239         ;;                          (match-beginning 1)(match-end 1)))))
240         ;;            (if (find-coding-system codesys) codesys))
241         ;;          )))
242         (let ((case-fold-search nil))
243           (if (search-forward
244                ";;;###coding system: " (+ (point-min) 3000) t)
245               (let ((start (point))
246                     (end (progn
247                            (skip-chars-forward "^ \t\n\r")
248                            (point))))
249                 (if (> end start)
250                     (let ((codesys (intern (buffer-substring start end))))
251                       (if (find-coding-system codesys) codesys)))
252                 )))
253         )))
254
255 (defun load (file &optional noerror nomessage nosuffix)
256   "Execute a file of Lisp code named FILE.
257 First tries FILE with .elc appended, then tries with .el,
258  then tries FILE unmodified.  Searches directories in load-path.
259 If optional second arg NOERROR is non-nil,
260  report no error if FILE doesn't exist.
261 Print messages at start and end of loading unless
262  optional third arg NOMESSAGE is non-nil.
263 If optional fourth arg NOSUFFIX is non-nil, don't try adding
264  suffixes .elc or .el to the specified name FILE.
265 Return t if file exists."
266   (let* ((filename (substitute-in-file-name file))
267          (handler (find-file-name-handler filename 'load))
268          (path nil))
269     (if handler
270         (funcall handler 'load filename noerror nomessage nosuffix)
271       (if (or (<= (length filename) 0)
272               (null (setq path
273                           (locate-file filename load-path
274                                        (and (not nosuffix) '(".elc" ".el" ""))))))
275           (and (null noerror)
276                (signal 'file-error (list "Cannot open load file" filename)))
277         ;; now use the internal load to actually load the file.
278         (load-internal
279          file noerror nomessage nosuffix
280          (let ((elc ; use string= instead of string-match to keep match-data.
281                 (string= ".elc" (downcase (substring path -4)))))
282            (or (and (not elc) coding-system-for-read) ; prefer for source file
283                ;; find magic-cookie
284                (save-excursion
285                  (set-buffer (get-buffer-create " *load*"))
286                  (erase-buffer)
287                  (let ((coding-system-for-read 'no-conversion))
288                    (insert-file-contents path nil 1 3001))
289                  (find-coding-system-magic-cookie))
290                (if elc
291                    ;; if reading a byte-compiled file and we didn't find
292                    ;; a coding-system magic cookie, then use `binary'.
293                    ;; We need to guarantee that we never do autodetection
294                    ;; on byte-compiled files because confusion here would
295                    ;; be a very bad thing.  Pre-existing byte-compiled
296                    ;; files are always in the `binary' coding system.
297                    ;; Also, byte-compiled files always use `lf' to terminate
298                    ;; a line; don't risk confusion here either.
299                    'binary
300                  (or (find-file-coding-system-for-read-from-filename path)
301                      ;; looking up in `file-coding-system-alist'.
302                      ;; otherwise use `buffer-file-coding-system-for-read',
303                      ;; as normal
304                      buffer-file-coding-system-for-read)
305                  )))
306          )))))
307
308 (defvar insert-file-contents-access-hook nil
309   "A hook to make a file accessible before reading it.
310 `insert-file-contents' calls this hook before doing anything else.
311 Called with two arguments: FILENAME and VISIT, the same as the
312 corresponding arguments in the call to `insert-file-contents'.")
313
314 (defvar insert-file-contents-pre-hook nil
315   "A special hook to decide the coding system used for reading in a file.
316
317 Before reading a file, `insert-file-contents' calls the functions on
318 this hook with arguments FILENAME and VISIT, the same as the
319 corresponding arguments in the call to `insert-file-contents'.  In
320 these functions, you may refer to the global variable
321 `buffer-file-coding-system-for-read'.
322
323 The return value of the functions should be either
324
325 -- nil
326 -- A coding system or a symbol denoting it, indicating the coding system
327    to be used for reading the file
328 -- A list of two elements (absolute pathname and length of data inserted),
329    which is used as the return value to `insert-file-contents'.  In this
330    case, `insert-file-contents' assumes that the function has inserted
331    the file for itself and suppresses further reading.
332
333 If any function returns non-nil, the remaining functions are not called.")
334
335 (defvar insert-file-contents-error-hook nil
336   "A hook to set `buffer-file-coding-system' when a read error has occurred.
337
338 When a file error (e.g. nonexistent file) occurs while read a file,
339 `insert-file-contents' calls the functions on this hook with three
340 arguments: FILENAME and VISIT (the same as the corresponding arguments
341 in the call to `insert-file-contents') and a cons (SIGNALED-CONDITIONS
342 . SIGNAL-DATA).
343
344 After calling this hook, the error is signalled for real and
345 propagates to the caller of `insert-file-contents'.")
346
347 (defvar insert-file-contents-post-hook nil
348   "A hook to set `buffer-file-coding-system' for the current buffer.
349
350 After successful reading, `insert-file-contents' calls the functions
351 on this hook with four arguments: FILENAME and VISIT (the same as the
352 corresponding arguments in the call to `insert-file-contents'),
353 CODING-SYSTEM (the actual coding system used to decode the file), and
354 a cons of absolute pathname and length of data inserted (the same
355 thing as will be returned from `insert-file-contents').")
356
357 (defun insert-file-contents (filename &optional visit beg end replace)
358   "Insert contents of file FILENAME after point.
359 Returns list of absolute file name and length of data inserted.
360 If second argument VISIT is non-nil, the buffer's visited filename
361 and last save file modtime are set, and it is marked unmodified.
362 If visiting and the file does not exist, visiting is completed
363 before the error is signaled.
364
365 The optional third and fourth arguments BEG and END
366 specify what portion of the file to insert.
367 If VISIT is non-nil, BEG and END must be nil.
368 If optional fifth argument REPLACE is non-nil,
369 it means replace the current buffer contents (in the accessible portion)
370 with the file contents.  This is better than simply deleting and inserting
371 the whole thing because (1) it preserves some marker positions
372 and (2) it puts less data in the undo list.
373
374 NOTE: When Mule support is enabled, the REPLACE argument is
375 currently ignored.
376
377 The coding system used for decoding the file is determined as follows:
378
379 1. `coding-system-for-read', if non-nil.
380 2. The result of `insert-file-contents-pre-hook', if non-nil.
381 3. The matching value for this filename from
382    `file-coding-system-alist', if any.
383 4. `buffer-file-coding-system-for-read', if non-nil.
384 5. The coding system 'no-conversion.
385
386 If a local value for `buffer-file-coding-system' in the current buffer
387 does not exist, it is set to the coding system which was actually used
388 for reading.
389
390 See also `insert-file-contents-access-hook',
391 `insert-file-contents-pre-hook', `insert-file-contents-error-hook',
392 and `insert-file-contents-post-hook'."
393   (let (return-val coding-system used-codesys)
394     ;; OK, first load the file.
395     (condition-case err
396         (progn
397           (run-hook-with-args 'insert-file-contents-access-hook
398                               filename visit)
399           ;; determine the coding system to use, as described above.
400           (setq coding-system
401                 (or
402                  ;; #1.
403                  coding-system-for-read
404                  ;; #2.
405                  (run-hook-with-args-until-success
406                   'insert-file-contents-pre-hook
407                   filename visit)
408                  ;; #3.
409                  (find-file-coding-system-for-read-from-filename filename)
410                  ;; #4.
411                  buffer-file-coding-system-for-read
412                  ;; #5.
413                  'no-conversion))
414           (if (consp coding-system)
415               (setq return-val coding-system)
416             (if (null (find-coding-system coding-system))
417                 (progn
418                   (message
419                    "Invalid coding-system (%s), using 'undecided"
420                    coding-system)
421                   (setq coding-system 'undecided)))
422             (setq return-val
423                   (insert-file-contents-internal filename visit beg end
424                                                  replace coding-system
425                                                  ;; store here!
426                                                  'used-codesys))
427             ))
428       (file-error
429        (run-hook-with-args 'insert-file-contents-error-hook
430                            filename visit err)
431        (signal (car err) (cdr err))))
432     (setq coding-system used-codesys)
433     ;; call any `post-read-conversion' for the coding system that
434     ;; was used ...
435     (let ((func
436            (coding-system-property coding-system 'post-read-conversion))
437           (endmark (make-marker)))
438       (set-marker endmark (+ (point) (nth 1 return-val)))
439       (if func
440           (unwind-protect
441               (save-excursion
442                 (let (buffer-read-only)
443                   (funcall func (point) (marker-position endmark))))
444             (if visit
445                 (progn
446                   (set-buffer-auto-saved)
447                   (set-buffer-modified-p nil)))))
448       (setcar (cdr return-val) (- (marker-position endmark) (point))))
449     ;; now finally set the buffer's `buffer-file-coding-system'.
450     (if (run-hook-with-args-until-success 'insert-file-contents-post-hook
451                                           filename visit return-val)
452         nil
453       (if (local-variable-p 'buffer-file-coding-system (current-buffer))
454           ;; if buffer-file-coding-system is already local, just
455           ;; set its eol type to what was found, if it wasn't
456           ;; set already.
457           (set-buffer-file-coding-system
458            (subsidiary-coding-system buffer-file-coding-system
459                                      (coding-system-eol-type coding-system)))
460         ;; otherwise actually set buffer-file-coding-system.
461         (set-buffer-file-coding-system coding-system)))
462     return-val))
463
464 (defvar write-region-pre-hook nil
465   "A special hook to decide the coding system used for writing out a file.
466
467 Before writing a file, `write-region' calls the functions on this hook
468 with arguments START, END, FILENAME, APPEND, VISIT, and CODING-SYSTEM,
469 the same as the corresponding arguments in the call to
470 `write-region'.
471
472 The return value of the functions should be either
473
474 -- nil
475 -- A coding system or a symbol denoting it, indicating the coding system
476    to be used for reading the file
477 -- A list of two elements (absolute pathname and length of data written),
478    which is used as the return value to `write-region'.  In this
479    case, `write-region' assumes that the function has written
480    the file for itself and suppresses further writing.
481
482 If any function returns non-nil, the remaining functions are not called.")
483
484 (defvar write-region-post-hook nil
485   "A hook called by `write-region' after a file has been written out.
486
487 The functions on this hook are called with arguments START, END,
488 FILENAME, APPEND, VISIT, and CODING-SYSTEM, the same as the
489 corresponding arguments in the call to `write-region'.")
490
491 (defun write-region (start end filename &optional append visit lockname coding-system)
492   "Write current region into specified file.
493 By default the file's existing contents are replaced by the specified region.
494 When called from a program, takes three arguments:
495 START, END and FILENAME.  START and END are buffer positions.
496 Optional fourth argument APPEND if non-nil means
497   append to existing file contents (if any).
498 Optional fifth argument VISIT if t means
499   set last-save-file-modtime of buffer to this file's modtime
500   and mark buffer not modified.
501 If VISIT is a string, it is a second file name;
502   the output goes to FILENAME, but the buffer is marked as visiting VISIT.
503   VISIT is also the file name to lock and unlock for clash detection.
504 If VISIT is neither t nor nil nor a string,
505   that means do not print the \"Wrote file\" message.
506 The optional sixth arg LOCKNAME, if non-nil, specifies the name to
507   use for locking and unlocking, overriding FILENAME and VISIT.
508 Kludgy feature: if START is a string, then that string is written
509 to the file, instead of any buffer contents, and END is ignored.
510 Optional seventh argument CODING-SYSTEM specifies the coding system
511   used to encode the text when it is written out, and defaults to
512   the value of `buffer-file-coding-system' in the current buffer.
513   Interactively, with a prefix arg, you will be prompted for the
514   coding system.
515 See also `write-region-pre-hook' and `write-region-post-hook'."
516   (interactive "r\nFWrite region to file: \ni\ni\ni\nZCoding-system: ")
517   (setq coding-system
518         (or coding-system-for-write
519             (run-hook-with-args-until-success
520              'write-region-pre-hook start end filename append visit lockname)
521             coding-system
522             buffer-file-coding-system
523             (find-file-coding-system-for-write-from-filename filename)
524             ))
525   (if (consp coding-system)
526       coding-system
527     (let ((func
528            (coding-system-property coding-system 'pre-write-conversion)))
529       (if func
530           (let ((curbuf (current-buffer))
531                 (tempbuf (generate-new-buffer " *temp-write-buffer*"))
532                 (modif (buffer-modified-p)))
533             (unwind-protect
534                 (save-excursion
535                   (set-buffer tempbuf)
536                   (erase-buffer)
537                   (insert-buffer-substring curbuf start end)
538                   (funcall func (point-min) (point-max))
539                   (write-region-internal (point-min) (point-max) filename
540                                          append
541                                          (if (eq visit t) nil visit)
542                                          lockname
543                                          coding-system))
544               ;; leaving a buffer associated with file will cause problems
545               ;; when next visiting.
546               (kill-buffer tempbuf)
547               (if (or visit (null modif))
548                   (progn
549                     (set-buffer-auto-saved)
550                     (set-buffer-modified-p nil)
551                     (if (buffer-file-name) (set-visited-file-modtime))))))
552         (write-region-internal start end filename append visit lockname
553                                coding-system)))
554     (run-hook-with-args 'write-region-post-hook
555                         start end filename append visit lockname
556                         coding-system)))
557
558 ;;; mule-files.el ends here