comparison lisp/mule/mule-files.el @ 70:131b0175ea99 r20-0b30

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