-
-
Notifications
You must be signed in to change notification settings - Fork 39
/
files.el
5842 lines (5413 loc) · 232 KB
/
files.el
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
;;; files.el --- file input and output commands for Emacs
;; Copyright (C) 1985, 1986, 1987, 1992, 1993, 1994, 1995, 1996,
;; 1997, 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005,
;; 2006, 2007, 2008 Free Software Foundation, Inc.
;; Maintainer: FSF
;; This file is part of GNU Emacs.
;; GNU Emacs is free software: you can redistribute it and/or modify
;; it under the terms of the GNU General Public License as published by
;; the Free Software Foundation, either version 3 of the License, or
;; (at your option) any later version.
;; GNU Emacs is distributed in the hope that it will be useful,
;; but WITHOUT ANY WARRANTY; without even the implied warranty of
;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
;; GNU General Public License for more details.
;; You should have received a copy of the GNU General Public License
;; along with GNU Emacs. If not, see <http://www.gnu.org/licenses/>.
;;; Commentary:
;; Defines most of Emacs's file- and directory-handling functions,
;; including basic file visiting, backup generation, link handling,
;; ITS-id version control, load- and write-hook handling, and the like.
;;; Code:
(defvar font-lock-keywords)
(defgroup backup nil
"Backups of edited data files."
:group 'files)
(defgroup find-file nil
"Finding files."
:group 'files)
(defcustom delete-auto-save-files t
"Non-nil means delete auto-save file when a buffer is saved or killed.
Note that the auto-save file will not be deleted if the buffer is killed
when it has unsaved changes."
:type 'boolean
:group 'auto-save)
(defcustom directory-abbrev-alist
nil
"Alist of abbreviations for file directories.
A list of elements of the form (FROM . TO), each meaning to replace
FROM with TO when it appears in a directory name. This replacement is
done when setting up the default directory of a newly visited file.
*Every* FROM string should start with `^'.
FROM and TO should be equivalent names, which refer to the
same directory. Do not use `~' in the TO strings;
they should be ordinary absolute directory names.
Use this feature when you have directories which you normally refer to
via absolute symbolic links. Make TO the name of the link, and FROM
the name it is linked to."
:type '(repeat (cons :format "%v"
:value ("" . "")
(regexp :tag "From")
(regexp :tag "To")))
:group 'abbrev
:group 'find-file)
(defcustom make-backup-files t
"Non-nil means make a backup of a file the first time it is saved.
This can be done by renaming the file or by copying.
Renaming means that Emacs renames the existing file so that it is a
backup file, then writes the buffer into a new file. Any other names
that the old file had will now refer to the backup file. The new file
is owned by you and its group is defaulted.
Copying means that Emacs copies the existing file into the backup
file, then writes the buffer on top of the existing file. Any other
names that the old file had will now refer to the new (edited) file.
The file's owner and group are unchanged.
The choice of renaming or copying is controlled by the variables
`backup-by-copying', `backup-by-copying-when-linked',
`backup-by-copying-when-mismatch' and
`backup-by-copying-when-privileged-mismatch'. See also `backup-inhibited'."
:type 'boolean
:group 'backup)
;; Do this so that local variables based on the file name
;; are not overridden by the major mode.
(defvar backup-inhibited nil
"Non-nil means don't make a backup, regardless of the other parameters.
This variable is intended for use by making it local to a buffer.
But it is local only if you make it local.")
(put 'backup-inhibited 'permanent-local t)
(defcustom backup-by-copying nil
"Non-nil means always use copying to create backup files.
See documentation of variable `make-backup-files'."
:type 'boolean
:group 'backup)
(defcustom backup-by-copying-when-linked nil
"Non-nil means use copying to create backups for files with multiple names.
This causes the alternate names to refer to the latest version as edited.
This variable is relevant only if `backup-by-copying' is nil."
:type 'boolean
:group 'backup)
(defcustom backup-by-copying-when-mismatch nil
"Non-nil means create backups by copying if this preserves owner or group.
Renaming may still be used (subject to control of other variables)
when it would not result in changing the owner or group of the file;
that is, for files which are owned by you and whose group matches
the default for a new file created there by you.
This variable is relevant only if `backup-by-copying' is nil."
:type 'boolean
:group 'backup)
(defcustom backup-by-copying-when-privileged-mismatch 200
"Non-nil means create backups by copying to preserve a privileged owner.
Renaming may still be used (subject to control of other variables)
when it would not result in changing the owner of the file or if the owner
has a user id greater than the value of this variable. This is useful
when low-numbered uid's are used for special system users (such as root)
that must maintain ownership of certain files.
This variable is relevant only if `backup-by-copying' and
`backup-by-copying-when-mismatch' are nil."
:type '(choice (const nil) integer)
:group 'backup)
(defvar backup-enable-predicate 'normal-backup-enable-predicate
"Predicate that looks at a file name and decides whether to make backups.
Called with an absolute file name as argument, it returns t to enable backup.")
(defcustom buffer-offer-save nil
"Non-nil in a buffer means always offer to save buffer on exit.
Do so even if the buffer is not visiting a file.
Automatically local in all buffers."
:type 'boolean
:group 'backup)
(make-variable-buffer-local 'buffer-offer-save)
(defcustom find-file-existing-other-name t
"Non-nil means find a file under alternative names, in existing buffers.
This means if any existing buffer is visiting the file you want
under another name, you get the existing buffer instead of a new buffer."
:type 'boolean
:group 'find-file)
(defcustom find-file-visit-truename nil
"*Non-nil means visit a file under its truename.
The truename of a file is found by chasing all links
both at the file level and at the levels of the containing directories."
:type 'boolean
:group 'find-file)
(put 'find-file-visit-truename 'safe-local-variable 'booleanp)
(defcustom revert-without-query nil
"Specify which files should be reverted without query.
The value is a list of regular expressions.
If the file name matches one of these regular expressions,
then `revert-buffer' reverts the file without querying
if the file has changed on disk and you have not edited the buffer."
:type '(repeat regexp)
:group 'find-file)
(defvar buffer-file-number nil
"The device number and file number of the file visited in the current buffer.
The value is a list of the form (FILENUM DEVNUM).
This pair of numbers uniquely identifies the file.
If the buffer is visiting a new file, the value is nil.")
(make-variable-buffer-local 'buffer-file-number)
(put 'buffer-file-number 'permanent-local t)
(defvar buffer-file-numbers-unique (not (memq system-type '(windows-nt)))
"Non-nil means that `buffer-file-number' uniquely identifies files.")
(defvar buffer-file-read-only nil
"Non-nil if visited file was read-only when visited.")
(make-variable-buffer-local 'buffer-file-read-only)
(defcustom temporary-file-directory
(file-name-as-directory
(cond ((memq system-type '(ms-dos windows-nt))
(or (getenv "TEMP") (getenv "TMPDIR") (getenv "TMP") "c:/temp"))
(t
(or (getenv "TMPDIR") (getenv "TMP") (getenv "TEMP") "/tmp"))))
"The directory for writing temporary files."
:group 'files
:type 'directory)
(defcustom small-temporary-file-directory
(if (eq system-type 'ms-dos) (getenv "TMPDIR"))
"The directory for writing small temporary files.
If non-nil, this directory is used instead of `temporary-file-directory'
by programs that create small temporary files. This is for systems that
have fast storage with limited space, such as a RAM disk."
:group 'files
:type '(choice (const nil) directory))
;; The system null device. (Should reference NULL_DEVICE from C.)
(defvar null-device "/dev/null" "The system null device.")
(declare-function msdos-long-file-names "msdos.c")
(declare-function w32-long-file-name "w32proc.c")
(declare-function dired-get-filename "dired" (&optional localp no-error-if-not-filep))
(declare-function dired-unmark "dired" (arg))
(declare-function dired-do-flagged-delete "dired" (&optional nomessage))
(declare-function dos-8+3-filename "dos-fns" (filename))
(declare-function view-mode-disable "view" ())
(defvar file-name-invalid-regexp
(cond ((and (eq system-type 'ms-dos) (not (msdos-long-file-names)))
(concat "^\\([^A-Z[-`a-z]\\|..+\\)?:\\|" ; colon except after drive
"[+, ;=|<>\"?*]\\|\\[\\|\\]\\|" ; invalid characters
"[\000-\037]\\|" ; control characters
"\\(/\\.\\.?[^/]\\)\\|" ; leading dots
"\\(/[^/.]+\\.[^/.]*\\.\\)")) ; more than a single dot
((memq system-type '(ms-dos windows-nt cygwin))
(concat "^\\([^A-Z[-`a-z]\\|..+\\)?:\\|" ; colon except after drive
"[|<>\"?*\000-\037]")) ; invalid characters
(t "[\000]"))
"Regexp recognizing file names which aren't allowed by the filesystem.")
(defcustom file-precious-flag nil
"Non-nil means protect against I/O errors while saving files.
Some modes set this non-nil in particular buffers.
This feature works by writing the new contents into a temporary file
and then renaming the temporary file to replace the original.
In this way, any I/O error in writing leaves the original untouched,
and there is never any instant where the file is nonexistent.
Note that this feature forces backups to be made by copying.
Yet, at the same time, saving a precious file
breaks any hard links between it and other files."
:type 'boolean
:group 'backup)
(defcustom version-control nil
"Control use of version numbers for backup files.
When t, make numeric backup versions unconditionally.
When nil, make them for files that have some already.
The value `never' means do not make them."
:type '(choice (const :tag "Never" never)
(const :tag "If existing" nil)
(other :tag "Always" t))
:group 'backup
:group 'vc)
(put 'version-control 'safe-local-variable
'(lambda (x) (or (booleanp x) (equal x 'never))))
(defcustom dired-kept-versions 2
"When cleaning directory, number of versions to keep."
:type 'integer
:group 'backup
:group 'dired)
(defcustom delete-old-versions nil
"If t, delete excess backup versions silently.
If nil, ask confirmation. Any other value prevents any trimming."
:type '(choice (const :tag "Delete" t)
(const :tag "Ask" nil)
(other :tag "Leave" other))
:group 'backup)
(defcustom kept-old-versions 2
"Number of oldest versions to keep when a new numbered backup is made."
:type 'integer
:group 'backup)
(put 'kept-old-versions 'safe-local-variable 'integerp)
(defcustom kept-new-versions 2
"Number of newest versions to keep when a new numbered backup is made.
Includes the new backup. Must be > 0"
:type 'integer
:group 'backup)
(put 'kept-new-versions 'safe-local-variable 'integerp)
(defcustom require-final-newline nil
"Whether to add a newline automatically at the end of the file.
A value of t means do this only when the file is about to be saved.
A value of `visit' means do this right after the file is visited.
A value of `visit-save' means do it at both of those times.
Any other non-nil value means ask user whether to add a newline, when saving.
A value of nil means don't add newlines.
Certain major modes set this locally to the value obtained
from `mode-require-final-newline'."
:type '(choice (const :tag "When visiting" visit)
(const :tag "When saving" t)
(const :tag "When visiting or saving" visit-save)
(const :tag "Don't add newlines" nil)
(other :tag "Ask each time" ask))
:group 'editing-basics)
(defcustom mode-require-final-newline t
"Whether to add a newline at end of file, in certain major modes.
Those modes set `require-final-newline' to this value when you enable them.
They do so because they are often used for files that are supposed
to end in newlines, and the question is how to arrange that.
A value of t means do this only when the file is about to be saved.
A value of `visit' means do this right after the file is visited.
A value of `visit-save' means do it at both of those times.
Any other non-nil value means ask user whether to add a newline, when saving.
A value of nil means do not add newlines. That is a risky choice in this
variable since this value is used for modes for files that ought to have
final newlines. So if you set this to nil, you must explicitly check and
add a final newline, whenever you save a file that really needs one."
:type '(choice (const :tag "When visiting" visit)
(const :tag "When saving" t)
(const :tag "When visiting or saving" visit-save)
(const :tag "Don't add newlines" nil)
(other :tag "Ask each time" ask))
:group 'editing-basics
:version "22.1")
(defcustom auto-save-default t
"Non-nil says by default do auto-saving of every file-visiting buffer."
:type 'boolean
:group 'auto-save)
(defcustom auto-save-visited-file-name nil
"Non-nil says auto-save a buffer in the file it is visiting, when practical.
Normally auto-save files are written under other names."
:type 'boolean
:group 'auto-save)
(defcustom auto-save-file-name-transforms
`(("\\`/[^/]*:\\([^/]*/\\)*\\([^/]*\\)\\'"
;; Don't put "\\2" inside expand-file-name, since it will be
;; transformed to "/2" on DOS/Windows.
,(concat temporary-file-directory "\\2") t))
"Transforms to apply to buffer file name before making auto-save file name.
Each transform is a list (REGEXP REPLACEMENT UNIQUIFY):
REGEXP is a regular expression to match against the file name.
If it matches, `replace-match' is used to replace the
matching part with REPLACEMENT.
If the optional element UNIQUIFY is non-nil, the auto-save file name is
constructed by taking the directory part of the replaced file-name,
concatenated with the buffer file name with all directory separators
changed to `!' to prevent clashes. This will not work
correctly if your filesystem truncates the resulting name.
All the transforms in the list are tried, in the order they are listed.
When one transform applies, its result is final;
no further transforms are tried.
The default value is set up to put the auto-save file into the
temporary directory (see the variable `temporary-file-directory') for
editing a remote file.
On MS-DOS filesystems without long names this variable is always
ignored."
:group 'auto-save
:type '(repeat (list (string :tag "Regexp") (string :tag "Replacement")
(boolean :tag "Uniquify")))
:version "21.1")
(defcustom save-abbrevs t
"Non-nil means save word abbrevs too when files are saved.
If `silently', don't ask the user before saving."
:type '(choice (const t) (const nil) (const silently))
:group 'abbrev)
(defcustom find-file-run-dired t
"Non-nil means allow `find-file' to visit directories.
To visit the directory, `find-file' runs `find-directory-functions'."
:type 'boolean
:group 'find-file)
(defcustom find-directory-functions '(cvs-dired-noselect dired-noselect)
"List of functions to try in sequence to visit a directory.
Each function is called with the directory name as the sole argument
and should return either a buffer or nil."
:type '(hook :options (cvs-dired-noselect dired-noselect))
:group 'find-file)
;;;It is not useful to make this a local variable.
;;;(put 'find-file-not-found-hooks 'permanent-local t)
(defvar find-file-not-found-functions nil
"List of functions to be called for `find-file' on nonexistent file.
These functions are called as soon as the error is detected.
Variable `buffer-file-name' is already set up.
The functions are called in the order given until one of them returns non-nil.")
(define-obsolete-variable-alias 'find-file-not-found-hooks
'find-file-not-found-functions "22.1")
;;;It is not useful to make this a local variable.
;;;(put 'find-file-hooks 'permanent-local t)
(define-obsolete-variable-alias 'find-file-hooks 'find-file-hook "22.1")
(defcustom find-file-hook nil
"List of functions to be called after a buffer is loaded from a file.
The buffer's local variables (if any) will have been processed before the
functions are called."
:group 'find-file
:type 'hook
:options '(auto-insert)
:version "22.1")
(defvar write-file-functions nil
"List of functions to be called before writing out a buffer to a file.
If one of them returns non-nil, the file is considered already written
and the rest are not called.
These hooks are considered to pertain to the visited file.
So any buffer-local binding of this variable is discarded if you change
the visited file name with \\[set-visited-file-name], but not when you
change the major mode.
This hook is not run if any of the functions in
`write-contents-functions' returns non-nil. Both hooks pertain
to how to save a buffer to file, for instance, choosing a suitable
coding system and setting mode bits. (See Info
node `(elisp)Saving Buffers'.) To perform various checks or
updates before the buffer is saved, use `before-save-hook'.")
(put 'write-file-functions 'permanent-local t)
(define-obsolete-variable-alias 'write-file-hooks 'write-file-functions "22.1")
(defvar local-write-file-hooks nil)
(make-variable-buffer-local 'local-write-file-hooks)
(put 'local-write-file-hooks 'permanent-local t)
(make-obsolete-variable 'local-write-file-hooks 'write-file-functions "22.1")
(defvar write-contents-functions nil
"List of functions to be called before writing out a buffer to a file.
If one of them returns non-nil, the file is considered already written
and the rest are not called and neither are the functions in
`write-file-functions'.
This variable is meant to be used for hooks that pertain to the
buffer's contents, not to the particular visited file; thus,
`set-visited-file-name' does not clear this variable; but changing the
major mode does clear it.
For hooks that _do_ pertain to the particular visited file, use
`write-file-functions'. Both this variable and
`write-file-functions' relate to how a buffer is saved to file.
To perform various checks or updates before the buffer is saved,
use `before-save-hook'.")
(make-variable-buffer-local 'write-contents-functions)
(define-obsolete-variable-alias 'write-contents-hooks
'write-contents-functions "22.1")
(defcustom enable-local-variables t
"Control use of local variables in files you visit.
The value can be t, nil, :safe, :all, or something else.
A value of t means file local variables specifications are obeyed
if all the specified variable values are safe; if any values are
not safe, Emacs queries you, once, whether to set them all.
\(When you say yes to certain values, they are remembered as safe.)
:safe means set the safe variables, and ignore the rest.
:all means set all variables, whether safe or not.
(Don't set it permanently to :all.)
A value of nil means always ignore the file local variables.
Any other value means always query you once whether to set them all.
\(When you say yes to certain values, they are remembered as safe, but
this has no effect when `enable-local-variables' is \"something else\".)
This variable also controls use of major modes specified in
a -*- line.
The command \\[normal-mode], when used interactively,
always obeys file local variable specifications and the -*- line,
and ignores this variable."
:type '(choice (const :tag "Query Unsafe" t)
(const :tag "Safe Only" :safe)
(const :tag "Do all" :all)
(const :tag "Ignore" nil)
(other :tag "Query" other))
:group 'find-file)
(defvar local-enable-local-variables t
"Like `enable-local-variables' but meant for buffer-local bindings.
The meaningful values are nil and non-nil. The default is non-nil.
If a major mode sets this to nil, buffer-locally, then any local
variables list in the file will be ignored.
This variable does not affect the use of major modes
specified in a -*- line.")
(defcustom enable-local-eval 'maybe
"Control processing of the \"variable\" `eval' in a file's local variables.
The value can be t, nil or something else.
A value of t means obey `eval' variables;
A value of nil means ignore them; anything else means query."
:type '(choice (const :tag "Obey" t)
(const :tag "Ignore" nil)
(other :tag "Query" other))
:group 'find-file)
;; Avoid losing in versions where CLASH_DETECTION is disabled.
(or (fboundp 'lock-buffer)
(defalias 'lock-buffer 'ignore))
(or (fboundp 'unlock-buffer)
(defalias 'unlock-buffer 'ignore))
(or (fboundp 'file-locked-p)
(defalias 'file-locked-p 'ignore))
(defcustom view-read-only nil
"Non-nil means buffers visiting files read-only do so in view mode.
In fact, this means that all read-only buffers normally have
View mode enabled, including buffers that are read-only because
you visit a file you cannot alter, and buffers you make read-only
using \\[toggle-read-only]."
:type 'boolean
:group 'view)
(defvar file-name-history nil
"History list of file names entered in the minibuffer.
Maximum length of the history list is determined by the value
of `history-length', which see.")
(put 'ange-ftp-completion-hook-function 'safe-magic t)
(defun ange-ftp-completion-hook-function (op &rest args)
"Provides support for ange-ftp host name completion.
Runs the usual ange-ftp hook, but only for completion operations."
;; Having this here avoids the need to load ange-ftp when it's not
;; really in use.
(if (memq op '(file-name-completion file-name-all-completions))
(apply 'ange-ftp-hook-function op args)
(let ((inhibit-file-name-handlers
(cons 'ange-ftp-completion-hook-function
(and (eq inhibit-file-name-operation op)
inhibit-file-name-handlers)))
(inhibit-file-name-operation op))
(apply op args))))
(defun convert-standard-filename (filename)
"Convert a standard file's name to something suitable for the OS.
This means to guarantee valid names and perhaps to canonicalize
certain patterns.
FILENAME should be an absolute file name since the conversion rules
sometimes vary depending on the position in the file name. E.g. c:/foo
is a valid DOS file name, but c:/bar/c:/foo is not.
This function's standard definition is trivial; it just returns
the argument. However, on Windows and DOS, replace invalid
characters. On DOS, make sure to obey the 8.3 limitations.
In the native Windows build, turn Cygwin names into native names,
and also turn slashes into backslashes if the shell requires it (see
`w32-shell-dos-semantics').
See Info node `(elisp)Standard File Names' for more details."
(if (eq system-type 'cygwin)
(let ((name (copy-sequence filename))
(start 0))
;; Replace invalid filename characters with !
(while (string-match "[?*:<>|\"\000-\037]" name start)
(aset name (match-beginning 0) ?!)
(setq start (match-end 0)))
name)
filename))
(defun read-directory-name (prompt &optional dir default-dirname mustmatch initial)
"Read directory name, prompting with PROMPT and completing in directory DIR.
Value is not expanded---you must call `expand-file-name' yourself.
Default name to DEFAULT-DIRNAME if user exits with the same
non-empty string that was inserted by this function.
(If DEFAULT-DIRNAME is omitted, DIR combined with INITIAL is used,
or just DIR if INITIAL is nil.)
If the user exits with an empty minibuffer, this function returns
an empty string. (This can only happen if the user erased the
pre-inserted contents or if `insert-default-directory' is nil.)
Fourth arg MUSTMATCH non-nil means require existing directory's name.
Non-nil and non-t means also require confirmation after completion.
Fifth arg INITIAL specifies text to start with.
DIR should be an absolute directory name. It defaults to
the value of `default-directory'."
(unless dir
(setq dir default-directory))
(read-file-name prompt dir (or default-dirname
(if initial (expand-file-name initial dir)
dir))
mustmatch initial
'file-directory-p))
(defun pwd ()
"Show the current default directory."
(interactive nil)
(message "Directory %s" default-directory))
(defvar cd-path nil
"Value of the CDPATH environment variable, as a list.
Not actually set up until the first time you use it.")
(defun parse-colon-path (cd-path)
"Explode a search path into a list of directory names.
Directories are separated by occurrences of `path-separator'
\(which is colon in GNU and GNU-like systems)."
;; We could use split-string here.
(and cd-path
(let (cd-list (cd-start 0) cd-colon)
(setq cd-path (concat cd-path path-separator))
(while (setq cd-colon (string-match path-separator cd-path cd-start))
(setq cd-list
(nconc cd-list
(list (if (= cd-start cd-colon)
nil
(substitute-in-file-name
(file-name-as-directory
(substring cd-path cd-start cd-colon)))))))
(setq cd-start (+ cd-colon 1)))
cd-list)))
(defun cd-absolute (dir)
"Change current directory to given absolute file name DIR."
;; Put the name into directory syntax now,
;; because otherwise expand-file-name may give some bad results.
(setq dir (file-name-as-directory dir))
(setq dir (abbreviate-file-name (expand-file-name dir)))
(if (not (file-directory-p dir))
(if (file-exists-p dir)
(error "%s is not a directory" dir)
(error "%s: no such directory" dir))
(unless (file-executable-p dir)
(error "Cannot cd to %s: Permission denied" dir))
(setq default-directory dir)
(set (make-local-variable 'list-buffers-directory) dir)))
(defun cd (dir)
"Make DIR become the current buffer's default directory.
If your environment includes a `CDPATH' variable, try each one of
that list of directories (separated by occurrences of
`path-separator') when resolving a relative directory name.
The path separator is colon in GNU and GNU-like systems."
(interactive
(list (read-directory-name "Change default directory: "
default-directory default-directory
(and (member cd-path '(nil ("./")))
(null (getenv "CDPATH"))))))
(if (file-name-absolute-p dir)
(cd-absolute (expand-file-name dir))
(if (null cd-path)
(let ((trypath (parse-colon-path (getenv "CDPATH"))))
(setq cd-path (or trypath (list "./")))))
(if (not (catch 'found
(mapc
(function (lambda (x)
(let ((f (expand-file-name (concat x dir))))
(if (file-directory-p f)
(progn
(cd-absolute f)
(throw 'found t))))))
cd-path)
nil))
(error "No such directory found via CDPATH environment variable"))))
(defun load-file (file)
"Load the Lisp file named FILE."
;; This is a case where .elc makes a lot of sense.
(interactive (list (let ((completion-ignored-extensions
(remove ".elc" completion-ignored-extensions)))
(read-file-name "Load file: "))))
(load (expand-file-name file) nil nil t))
(defun locate-file (filename path &optional suffixes predicate)
"Search for FILENAME through PATH.
If found, return the absolute file name of FILENAME, with its suffixes;
otherwise return nil.
PATH should be a list of directories to look in, like the lists in
`exec-path' or `load-path'.
If SUFFIXES is non-nil, it should be a list of suffixes to append to
file name when searching. If SUFFIXES is nil, it is equivalent to '(\"\").
Use '(\"/\") to disable PATH search, but still try the suffixes in SUFFIXES.
If non-nil, PREDICATE is used instead of `file-readable-p'.
PREDICATE can also be an integer to pass to the `access' system call,
in which case file-name handlers are ignored. This usage is deprecated.
For compatibility, PREDICATE can also be one of the symbols
`executable', `readable', `writable', or `exists', or a list of
one or more of those symbols."
(if (and predicate (symbolp predicate) (not (functionp predicate)))
(setq predicate (list predicate)))
(when (and (consp predicate) (not (functionp predicate)))
(setq predicate
(logior (if (memq 'executable predicate) 1 0)
(if (memq 'writable predicate) 2 0)
(if (memq 'readable predicate) 4 0))))
(locate-file-internal filename path suffixes predicate))
(defun locate-file-completion-table (dirs suffixes string pred action)
"Do completion for file names passed to `locate-file'."
(if (file-name-absolute-p string)
(let ((read-file-name-predicate pred))
(read-file-name-internal string nil action))
(let ((names nil)
(suffix (concat (regexp-opt suffixes t) "\\'"))
(string-dir (file-name-directory string)))
(dolist (dir dirs)
(unless dir
(setq dir default-directory))
(if string-dir (setq dir (expand-file-name string-dir dir)))
(when (file-directory-p dir)
(dolist (file (file-name-all-completions
(file-name-nondirectory string) dir))
(add-to-list 'names (if string-dir (concat string-dir file) file))
(when (string-match suffix file)
(setq file (substring file 0 (match-beginning 0)))
(push (if string-dir (concat string-dir file) file) names)))))
(complete-with-action action names string pred))))
(defun locate-file-completion (string path-and-suffixes action)
"Do completion for file names passed to `locate-file'.
PATH-AND-SUFFIXES is a pair of lists, (DIRECTORIES . SUFFIXES)."
(locate-file-completion-table (car path-and-suffixes)
(cdr path-and-suffixes)
string nil action))
(make-obsolete 'locate-file-completion 'locate-file-completion-table "23.1")
(defun locate-dominating-file (file regexp)
"Look up the directory hierarchy from FILE for a file matching REGEXP."
(catch 'found
;; `user' is not initialized yet because `file' may not exist, so we may
;; have to walk up part of the hierarchy before we find the "initial UID".
(let ((user nil)
;; Abbreviate, so as to stop when we cross ~/.
(dir (abbreviate-file-name (file-name-as-directory file)))
files)
(while (and dir
;; As a heuristic, we stop looking up the hierarchy of
;; directories as soon as we find a directory belonging to
;; another user. This should save us from looking in
;; things like /net and /afs. This assumes that all the
;; files inside a project belong to the same user.
(let ((prev-user user))
(setq user (nth 2 (file-attributes file)))
(or (null prev-user) (equal user prev-user))))
(if (setq files (and (file-directory-p dir)
(directory-files dir 'full regexp)))
(throw 'found (car files))
(if (equal dir
(setq dir (file-name-directory
(directory-file-name dir))))
(setq dir nil))))
nil)))
(defun executable-find (command)
"Search for COMMAND in `exec-path' and return the absolute file name.
Return nil if COMMAND is not found anywhere in `exec-path'."
;; Use 1 rather than file-executable-p to better match the behavior of
;; call-process.
(locate-file command exec-path exec-suffixes 1))
(defun load-library (library)
"Load the library named LIBRARY.
This is an interface to the function `load'."
(interactive
(list (completing-read "Load library: "
(apply-partially 'locate-file-completion-table
load-path
(get-load-suffixes)))))
(load library))
(defun file-remote-p (file &optional identification connected)
"Test whether FILE specifies a location on a remote system.
Returns nil or a string identifying the remote connection (ideally
a prefix of FILE). For example, the remote identification for filename
\"/user@host:/foo\" could be \"/user@host:\".
A file is considered \"remote\" if accessing it is likely to be slower or
less reliable than accessing local files.
Furthermore, relative file names do not work across remote connections.
IDENTIFICATION specifies which part of the identification shall
be returned as string. IDENTIFICATION can be the symbol
`method', `user' or `host'; any other value is handled like nil
and means to return the complete identification string.
If CONNECTED is non-nil, the function returns an identification only
if FILE is located on a remote system, and a connection is established
to that remote system.
`file-remote-p' will never open a connection on its own."
(let ((handler (find-file-name-handler file 'file-remote-p)))
(if handler
(funcall handler 'file-remote-p file identification connected)
nil)))
(defun file-local-copy (file)
"Copy the file FILE into a temporary file on this machine.
Returns the name of the local copy, or nil, if FILE is directly
accessible."
;; This formerly had an optional BUFFER argument that wasn't used by
;; anything.
(let ((handler (find-file-name-handler file 'file-local-copy)))
(if handler
(funcall handler 'file-local-copy file)
nil)))
(defun file-truename (filename &optional counter prev-dirs)
"Return the truename of FILENAME, which should be absolute.
The truename of a file name is found by chasing symbolic links
both at the level of the file and at the level of the directories
containing it, until no links are left at any level.
\(fn FILENAME)" ;; Don't document the optional arguments.
;; COUNTER and PREV-DIRS are only used in recursive calls.
;; COUNTER can be a cons cell whose car is the count of how many
;; more links to chase before getting an error.
;; PREV-DIRS can be a cons cell whose car is an alist
;; of truenames we've just recently computed.
(cond ((or (string= filename "") (string= filename "~"))
(setq filename (expand-file-name filename))
(if (string= filename "")
(setq filename "/")))
((and (string= (substring filename 0 1) "~")
(string-match "~[^/]*/?" filename))
(let ((first-part
(substring filename 0 (match-end 0)))
(rest (substring filename (match-end 0))))
(setq filename (concat (expand-file-name first-part) rest)))))
(or counter (setq counter (list 100)))
(let (done
;; For speed, remove the ange-ftp completion handler from the list.
;; We know it's not needed here.
;; For even more speed, do this only on the outermost call.
(file-name-handler-alist
(if prev-dirs file-name-handler-alist
(let ((tem (copy-sequence file-name-handler-alist)))
(delq (rassq 'ange-ftp-completion-hook-function tem) tem)))))
(or prev-dirs (setq prev-dirs (list nil)))
;; andrewi@harlequin.co.uk - none of the following code (except for
;; invoking the file-name handler) currently applies on Windows
;; (ie. there are no native symlinks), but there is an issue with
;; case differences being ignored by the OS, and short "8.3 DOS"
;; name aliases existing for all files. (The short names are not
;; reported by directory-files, but can be used to refer to files.)
;; It seems appropriate for file-truename to resolve these issues in
;; the most natural way, which on Windows is to call the function
;; `w32-long-file-name' - this returns the exact name of a file as
;; it is stored on disk (expanding short name aliases with the full
;; name in the process).
(if (eq system-type 'windows-nt)
(let ((handler (find-file-name-handler filename 'file-truename)))
;; For file name that has a special handler, call handler.
;; This is so that ange-ftp can save time by doing a no-op.
(if handler
(setq filename (funcall handler 'file-truename filename))
;; If filename contains a wildcard, newname will be the old name.
(unless (string-match "[[*?]" filename)
;; If filename exists, use the long name. If it doesn't exist,
;; drill down until we find a directory that exists, and use
;; the long name of that, with the extra non-existent path
;; components concatenated.
(let ((longname (w32-long-file-name filename))
missing rest)
(if longname
(setq filename longname)
;; include the preceding directory separator in the missing
;; part so subsequent recursion on the rest works.
(setq missing (concat "/" (file-name-nondirectory filename)))
(setq rest (substring filename 0 (* -1 (length missing))))
(setq filename (concat (file-truename rest) missing))))))
(setq done t)))
;; If this file directly leads to a link, process that iteratively
;; so that we don't use lots of stack.
(while (not done)
(setcar counter (1- (car counter)))
(if (< (car counter) 0)
(error "Apparent cycle of symbolic links for %s" filename))
(let ((handler (find-file-name-handler filename 'file-truename)))
;; For file name that has a special handler, call handler.
;; This is so that ange-ftp can save time by doing a no-op.
(if handler
(setq filename (funcall handler 'file-truename filename)
done t)
(let ((dir (or (file-name-directory filename) default-directory))
target dirfile)
;; Get the truename of the directory.
(setq dirfile (directory-file-name dir))
;; If these are equal, we have the (or a) root directory.
(or (string= dir dirfile)
;; If this is the same dir we last got the truename for,
;; save time--don't recalculate.
(if (assoc dir (car prev-dirs))
(setq dir (cdr (assoc dir (car prev-dirs))))
(let ((old dir)
(new (file-name-as-directory (file-truename dirfile counter prev-dirs))))
(setcar prev-dirs (cons (cons old new) (car prev-dirs)))
(setq dir new))))
(if (equal ".." (file-name-nondirectory filename))
(setq filename
(directory-file-name (file-name-directory (directory-file-name dir)))
done t)
(if (equal "." (file-name-nondirectory filename))
(setq filename (directory-file-name dir)
done t)
;; Put it back on the file name.
(setq filename (concat dir (file-name-nondirectory filename)))
;; Is the file name the name of a link?
(setq target (file-symlink-p filename))
(if target
;; Yes => chase that link, then start all over
;; since the link may point to a directory name that uses links.
;; We can't safely use expand-file-name here
;; since target might look like foo/../bar where foo
;; is itself a link. Instead, we handle . and .. above.
(setq filename
(if (file-name-absolute-p target)
target
(concat dir target))
done nil)
;; No, we are done!
(setq done t))))))))
filename))
(defun file-chase-links (filename &optional limit)
"Chase links in FILENAME until a name that is not a link.
Unlike `file-truename', this does not check whether a parent
directory name is a symbolic link.
If the optional argument LIMIT is a number,
it means chase no more than that many links and then stop."
(let (tem (newname filename)
(count 0))
(while (and (or (null limit) (< count limit))
(setq tem (file-symlink-p newname)))
(save-match-data
(if (and (null limit) (= count 100))
(error "Apparent cycle of symbolic links for %s" filename))
;; In the context of a link, `//' doesn't mean what Emacs thinks.
(while (string-match "//+" tem)
(setq tem (replace-match "/" nil nil tem)))
;; Handle `..' by hand, since it needs to work in the
;; target of any directory symlink.
;; This code is not quite complete; it does not handle
;; embedded .. in some cases such as ./../foo and foo/bar/../../../lose.
(while (string-match "\\`\\.\\./" tem)
(setq tem (substring tem 3))
(setq newname (expand-file-name newname))
;; Chase links in the default dir of the symlink.
(setq newname
(file-chase-links
(directory-file-name (file-name-directory newname))))
;; Now find the parent of that dir.
(setq newname (file-name-directory newname)))
(setq newname (expand-file-name tem (file-name-directory newname)))
(setq count (1+ count))))
newname))
(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 to
;; loosen 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))))
(defun recode-file-name (file coding new-coding &optional ok-if-already-exists)
"Change the encoding of FILE's name from CODING to NEW-CODING.
The value is a new name of FILE.
Signals a `file-already-exists' error if a file of the new name
already exists unless optional fourth argument OK-IF-ALREADY-EXISTS
is non-nil. A number as fourth arg means request confirmation if
the new name already exists. This is what happens in interactive