forked from CeleritasCelery/rune
-
Notifications
You must be signed in to change notification settings - Fork 0
/
files.el
8782 lines (8089 loc) · 370 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 -*- lexical-binding:t -*-
;; Copyright (C) 1985-1987, 1992-2023 Free Software Foundation, Inc.
;; Maintainer: emacs-devel@gnu.org
;; Package: emacs
;; 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 <https://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:
(eval-when-compile
(require 'pcase)
(require 'easy-mmode)) ; For `define-minor-mode'.
(defvar font-lock-keywords)
(defgroup backup nil
"Backups of edited data files."
:group 'files)
(defgroup find-file nil
"Finding files."
:group 'files)
(defcustom directory-abbrev-alist
nil
"Alist of abbreviations for file directories.
A list of elements of the form (FROM . TO), each meaning to replace
a match for FROM with TO when a directory name matches FROM. This
replacement is done when setting up the default directory of a
newly visited file buffer.
FROM is a regexp that is matched against directory names anchored at
the first character, so it should start with a \"\\\\\\=`\", or, if
directory names cannot have embedded newlines, with a \"^\".
FROM and TO should be equivalent names, which refer to the
same directory. TO should be an absolute directory name.
Do not use `~' in the TO strings.
Use this feature when you have directories that you normally refer to
via absolute symbolic links. Make TO the name of the link, and FROM
a regexp matching the name it is linked to."
:type '(repeat (cons :format "%v"
:value ("\\`" . "")
(regexp :tag "From")
(string :tag "To")))
:group 'abbrev
:group 'find-file)
(defun directory-abbrev-make-regexp (directory)
"Create a regexp to match DIRECTORY for `directory-abbrev-alist'."
(let ((regexp
;; We include a slash at the end, to avoid spurious
;; matches such as `/usr/foobar' when the home dir is
;; `/usr/foo'.
(concat "\\`" (regexp-quote directory) "\\(/\\|\\'\\)")))
;; The value of regexp could be multibyte or unibyte. In the
;; latter case, we need to decode it.
(if (multibyte-string-p regexp)
regexp
(decode-coding-string regexp
(if (eq system-type 'windows-nt)
'utf-8
locale-coding-system)))))
(defun directory-abbrev-apply (filename)
"Apply the abbreviations in `directory-abbrev-alist' to FILENAME.
Note that when calling this, you should set `case-fold-search' as
appropriate for the filesystem used for FILENAME."
(dolist (dir-abbrev directory-abbrev-alist filename)
(when (string-match (car dir-abbrev) filename)
(setq filename (concat (cdr dir-abbrev)
(substring filename (match-end 0)))))))
(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
"If non-nil, backups will be inhibited.
This variable is intended for use by making it local to a buffer,
but it is not an automatically buffer-local variable.")
(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 t
"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 that 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."
:version "24.1"
:type 'boolean
:group 'backup)
(put 'backup-by-copying-when-mismatch 'permanent-local t)
(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
user id and group id of the file are both greater than the value of
this variable. This is useful when low-numbered uid's and gid'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 exiting Emacs.
Do so even if the buffer is not visiting a file.
Automatically local in all buffers.
Set to the symbol `always' to offer to save buffer whenever
`save-some-buffers' is called.
Note that this option has no effect on `kill-buffer';
if you want to control what happens when a buffer is killed,
use `kill-buffer-query-functions'."
:type '(choice (const :tag "Never" nil)
(const :tag "On Emacs exit" t)
(const :tag "Whenever save-some-buffers is called" always))
:group 'backup)
(make-variable-buffer-local 'buffer-offer-save)
(put 'buffer-offer-save 'permanent-local t)
(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 visiting a file uses its truename as the visited-file name.
That is, the buffer visiting the file has the truename as the
value of `buffer-file-name'. 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)
(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-local buffer-file-number nil
"The inode number and the device of the file visited in the current buffer.
The value is a list of the form (INODENUM DEVICE), where DEVICE can be
either a single number or a cons cell of two numbers.
This tuple of numbers uniquely identifies the file.
If the buffer is visiting a new file, the value is nil.")
(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-local buffer-file-read-only nil
"Non-nil if visited file was read-only when visited.")
(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
:initialize 'custom-initialize-delay
:type '(choice (const nil) directory))
;; The system null device. (Should reference NULL_DEVICE from C.)
(defvar null-device (purecopy "/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 &optional interactive))
(declare-function dired-do-flagged-delete "dired" (&optional nomessage))
(declare-function dos-8+3-filename "dos-fns" (filename))
(declare-function dosified-file-name "dos-fns" (file-name))
(defvar file-name-invalid-regexp
(cond ((and (eq system-type 'ms-dos) (not (msdos-long-file-names)))
(purecopy
(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))
(purecopy
(concat "^\\([^A-Z[-`a-z]\\|..+\\)?:\\|" ; colon except after drive
"[|<>\"?*\000-\037]"))) ; invalid characters
(t (purecopy "[\000]")))
"Regexp recognizing file names that 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.
This feature is advisory: for example, if the directory in which the
file is being saved is not writable, Emacs may ignore a non-nil value
of `file-precious-flag' and write directly into the file.
See also: `break-hardlink-on-save' and `file-preserve-symlinks-on-save'."
:type 'boolean
:group 'backup)
(defcustom break-hardlink-on-save nil
"Whether to allow breaking hardlinks when saving files.
If non-nil, then when saving a file that exists under several
names \(i.e., has multiple hardlinks), break the hardlink
associated with `buffer-file-name' and write to a new file, so
that the other instances of the file are not affected by the
save.
If `buffer-file-name' refers to a symlink, do not break the symlink.
Unlike `file-precious-flag', `break-hardlink-on-save' is not advisory.
For example, if the directory in which a file is being saved is not
itself writable, then error instead of saving in some
hardlink-nonbreaking way.
See also `backup-by-copying' and `backup-by-copying-when-linked'."
:type 'boolean
:group 'files
:version "23.1")
(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))
:safe #'version-control-safe-local-p
:group 'backup)
(defun version-control-safe-local-p (x)
"Return whether X is safe as local value for `version-control'."
(or (booleanp x) (equal x 'never)))
(defcustom dired-kept-versions 2
"When cleaning directory, number of versions to keep."
:type 'natnum
: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 'natnum
:safe #'natnump
:group 'backup)
(defcustom kept-new-versions 2
"Number of newest versions to keep when a new numbered backup is made.
Includes the new backup. Must be greater than 0."
:type 'natnum
:safe #'natnump
:group 'backup)
(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'.
This variable is heeded only when visiting files (or saving
buffers into files they visit). Writing data to the file system
with `write-region' and the like is not influenced by this variable."
:safe #'symbolp
: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-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 nil, Emacs does not check for
file name clashes, so using that is not recommended. If UNIQUIFY
is one of the members of `secure-hash-algorithms', Emacs
constructs the nondirectory part of the auto-save file name by
applying that `secure-hash' to the buffer file name. This avoids
any risk of excessively long file names. Finally, if UNIQUIFY is
any other value 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 (regexp :tag "Regexp")
(string :tag "Replacement")
(choice
(const :tag "Uniquify" t)
,@(mapcar (lambda (algo)
(list 'const algo))
(secure-hash-algorithms)))))
:initialize 'custom-initialize-delay
:version "21.1")
(defvar auto-save--timer nil "Timer for `auto-save-visited-mode'.")
(defcustom auto-save-visited-interval 5
"Interval in seconds for `auto-save-visited-mode'.
If `auto-save-visited-mode' is enabled, Emacs will save all
buffers visiting a file to the visited file after it has been
idle for `auto-save-visited-interval' seconds."
:group 'auto-save
:type 'number
:version "26.1"
:set (lambda (symbol value)
(set-default symbol value)
(when auto-save--timer
(timer-set-idle-time auto-save--timer value :repeat))))
(defcustom auto-save-visited-predicate nil
"Predicate function for `auto-save-visited-mode'.
If non-nil, the value should be a function of no arguments; it
will be called once in each file-visiting buffer when the time
comes to auto-save. A buffer will be saved only if the predicate
function returns a non-nil value.
For example, you could add this to your Init file to only save
files that are both in Org mode and in a particular directory:
(setq auto-save-visited-predicate
(lambda () (and (eq major-mode \\='org-mode)
(string-match \"^/home/skangas/org/\"
buffer-file-name))))
If the value of this variable is not a function, it is ignored.
This is the same as having a predicate that always returns
non-nil."
:group 'auto-save
:type '(choice :tag "Function:"
(const :tag "No extra predicate" :value nil)
(function :tag "Predicate function" :value always))
:risky t
:version "29.1")
(defcustom remote-file-name-inhibit-auto-save-visited nil
"When nil, `auto-save-visited-mode' will auto-save remote files.
Any other value means that it will not."
:group 'auto-save
:type 'boolean
:version "29.1")
;; RUNE-BOOTSTRAP
;; (define-minor-mode auto-save-visited-mode
;; "Toggle automatic saving of file-visiting buffers to their files.
;; When this mode is enabled, file-visiting buffers are automatically
;; saved to their files. This is in contrast to `auto-save-mode', which
;; auto-saves those buffers to a separate file, leaving the original
;; file intact. See Info node `Saving' for details of the save process.
;; The user option `auto-save-visited-interval' controls how often to
;; auto-save a buffer into its visited file.
;; You can use `auto-save-visited-predicate' to control which
;; buffers are saved.
;; You can also set the buffer-local value of the variable
;; `auto-save-visited-mode' to nil. A buffer where the buffer-local
;; value of this variable is nil is ignored for the purpose of
;; `auto-save-visited-mode', even if `auto-save-visited-mode' is
;; enabled.
;; For more details, see Info node `(emacs) Auto Save Files'."
;; :group 'auto-save
;; :global t
;; (when auto-save--timer (cancel-timer auto-save--timer))
;; (setq auto-save--timer
;; (when auto-save-visited-mode
;; (run-with-idle-timer
;; auto-save-visited-interval :repeat
;; #'save-some-buffers :no-prompt
;; (lambda ()
;; (and buffer-file-name
;; auto-save-visited-mode
;; (not (and buffer-auto-save-file-name
;; auto-save-visited-file-name))
;; (or (not (file-remote-p buffer-file-name))
;; (not remote-file-name-inhibit-auto-save-visited))
;; (or (not (functionp auto-save-visited-predicate))
;; (funcall auto-save-visited-predicate))))))))
;; The 'set' part is so we don't get a warning for using this variable
;; above, while still catching code that _sets_ the variable to get
;; the same effect as the new auto-save-visited-mode.
(make-obsolete-variable 'auto-save-visited-file-name 'auto-save-visited-mode
"Emacs 26.1" 'set)
(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 lock-file-name-transforms nil
"Transforms to apply to buffer file name before making a lock file name.
This has the same syntax as `auto-save-file-name-transforms',
but applies to lock file names instead of auto-save file names.
By default, Emacs puts each lock file into the same directory as the
file it locks, prepending \".#\" to the base file name.
Note that changing this could break lock file functionality, e.g.:
if different users access the same file, using different lock file settings;
if accessing files on a shared file system from different hosts,
using a transform that puts the lock files on a local file system."
:group 'files
:type '(repeat (list (regexp :tag "Regexp")
(string :tag "Replacement")
(boolean :tag "Uniquify")))
:version "28.1")
(defcustom remote-file-name-inhibit-locks nil
"Whether to use file locks for remote files."
:group 'files
:version "28.1"
:type 'boolean)
;; RUNE-BOOTSTRAP
;; (define-minor-mode lock-file-mode
;; "Toggle file locking in the current buffer (Lock File mode)."
;; :version "28.1"
;; (setq-local create-lockfiles (and lock-file-mode t)))
(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)
;; FIXME: also add a hook for `(thing-at-point 'filename)'
(defcustom file-name-at-point-functions '(ffap-guess-file-name-at-point)
"List of functions to try in sequence to get a file name at point.
Each function should return either nil or a file name found at the
location of point in the current buffer."
:type '(hook :options (ffap-guess-file-name-at-point))
:group 'find-file)
;;;It is not useful to make this a local variable.
;;;(put 'find-file-not-found-functions '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.")
;;;It is not useful to make this a local variable.
;;;(put 'find-file-hook 'permanent-local t)
;; I found some external files still using the obsolete form in 2018.
(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. This includes directory-local variables, if any,
for the file's directory."
:group 'find-file
:type 'hook
:options '(auto-insert)
:version "22.1")
;; I found some external files still using the obsolete form in 2018.
(define-obsolete-variable-alias 'write-file-hooks 'write-file-functions "22.1")
(defvar write-file-functions nil
"List of functions to be called before saving a buffer to a file.
Used only by `save-buffer'.
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)
;; I found some files still using the obsolete form in 2018.
(defvar-local local-write-file-hooks nil)
(put 'local-write-file-hooks 'permanent-local t)
(make-obsolete-variable 'local-write-file-hooks 'write-file-functions "22.1")
;; I found some files still using the obsolete form in 2018.
(define-obsolete-variable-alias 'write-contents-hooks
'write-contents-functions "22.1")
(defvar-local write-contents-functions nil
"List of functions to be called before writing out a buffer to a file.
Used only by `save-buffer'. 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
hook can thus be used to create save behavior for buffers that
are not visiting a file at all.
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'.")
(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.
Also see the `permanently-enabled-local-variables' variable."
:risky t
: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 enable-dir-local-variables t
"Non-nil means enable use of directory-local variables.
Some modes may wish to set this to nil to prevent directory-local
settings being applied, but still respect file-local ones.")
;; This is an odd variable IMO.
;; You might wonder why it is needed, when we could just do:
;; (setq-local enable-local-variables nil)
;; These two are not precisely the same.
;; Setting this variable does not cause -*- mode settings to be
;; ignored, whereas setting enable-local-variables does.
;; Only three places in Emacs use this variable: tar and arc modes,
;; and rmail. The first two don't need it. They already use
;; inhibit-local-variables-regexps, which is probably enough, and
;; could also just set enable-local-variables locally to nil.
;; Them setting it has the side-effect that dir-locals cannot apply to
;; eg tar files (?). FIXME Is this appropriate?
;; AFAICS, rmail is the only thing that needs this, and the only
;; reason it uses it is for BABYL files (which are obsolete).
;; These contain "-*- rmail -*-" in the first line, which rmail wants
;; to respect, so that find-file on a BABYL file will switch to
;; rmail-mode automatically (this is nice, but hardly essential,
;; since most people are used to explicitly running a command to
;; access their mail; M-x gnus etc). Rmail files may happen to
;; contain Local Variables sections in messages, which Rmail wants to
;; ignore. So AFAICS the only reason this variable exists is for a
;; minor convenience feature for handling of an obsolete Rmail file format.
(defvar local-enable-local-variables t
"Like `enable-local-variables', except for major mode in a -*- line.
The meaningful values are nil and non-nil. The default is non-nil.
It should be set in a buffer-local fashion.
Setting this to nil has the same effect as setting `enable-local-variables'
to nil, except that it does not ignore any mode: setting in a -*- line.
Unless this difference matters to you, you should set `enable-local-variables'
instead of this variable.")
(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."
:risky t
:type '(choice (const :tag "Obey" t)
(const :tag "Ignore" nil)
(other :tag "Query" other))
:group 'find-file)
(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 \\[read-only-mode]."
: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.")
(defvar save-silently nil
"If non-nil, avoid messages when saving files.
Error-related messages will still be printed, but all other
messages will not.")
(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))))
(declare-function dos-convert-standard-filename "dos-fns.el" (filename))
(declare-function w32-convert-standard-filename "w32-fns.el" (filename))
(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.
See Info node `(elisp)Standard File Names' for more details."
(cond
((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))
((eq system-type 'windows-nt)
(w32-convert-standard-filename filename))
((eq system-type 'ms-dos)
(dos-convert-standard-filename filename))
(t 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 happen only 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 (&optional insert)
"Show the current default directory.
With prefix argument INSERT, insert the current default directory
at point instead."
(interactive "P")
(if insert
(insert default-directory)
(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 (search-path)
"Explode a search path into a list of directory names.
Directories are separated by `path-separator' (which is colon in
GNU and Unix systems). Substitute environment variables into the
resulting list of directory names. For an empty path element (i.e.,
a leading or trailing separator, or two adjacent separators), return
nil (meaning `default-directory') as the associated list element."
(when (stringp search-path)
(let ((spath (substitute-env-vars search-path))
(double-slash-special-p
(memq system-type '(windows-nt cygwin ms-dos))))
(mapcar (lambda (f)
(if (equal "" f) nil
(let ((dir (file-name-as-directory f)))
;; Previous implementation used `substitute-in-file-name'
;; which collapses multiple "/" in front, while
;; preserving double slash where it matters. Do
;; the same for backward compatibility.
(if (string-match "\\`//+" dir)
(substring dir (- (match-end 0)
(if double-slash-special-p 2 1)))
dir))))
(split-string spath path-separator)))))
(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))
;; We used to additionally call abbreviate-file-name here, for an
;; unknown reason. Problem is that most buffers are setup
;; without going through cd-absolute and don't call
;; abbreviate-file-name on their default-directory, so the few that
;; do end up using a superficially different directory.
(setq dir (expand-file-name dir))
(if (not (file-directory-p dir))
(error (if (file-exists-p dir)
"%s is not a directory"
"%s: no such directory")
dir)
(unless (file-accessible-directory-p dir)
(error "Cannot cd to %s: Permission denied" dir))
(setq default-directory dir)
(setq 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
;; FIXME: There's a subtle bug in the completion below. Seems linked
;; to a fundamental difficulty of implementing `predicate' correctly.
;; The manifestation is that TAB may list non-directories in the case where
;; those files also correspond to valid directories (if your cd-path is (A/
;; B/) and you have A/a a file and B/a a directory, then both `a' and `a/'
;; will be listed as valid completions).
;; This is because `a' (listed because of A/a) is indeed a valid choice
;; (which will lead to the use of B/a).
(minibuffer-with-setup-hook
(lambda ()
(setq-local minibuffer-completion-table
(apply-partially #'locate-file-completion-table
cd-path nil))
(setq-local minibuffer-completion-predicate
(lambda (dir)
(locate-file dir cd-path nil
(lambda (f) (and (file-directory-p f) 'dir-ok))))))
(unless cd-path
(setq cd-path (or (parse-colon-path (getenv "CDPATH"))
(list "./"))))
(read-directory-name "Change default directory: "
default-directory default-directory
t))))
(unless cd-path
(setq cd-path (or (parse-colon-path (getenv "CDPATH"))
(list "./"))))
(cd-absolute
(or
;; locate-file doesn't support remote file names, so detect them
;; and support them here by hand.
(and (file-remote-p (expand-file-name dir))
(file-accessible-directory-p (expand-file-name dir))
(expand-file-name dir))
(locate-file dir cd-path nil
(lambda (f) (and (file-directory-p f) 'dir-ok)))
(if (getenv "CDPATH")
(error "No such directory found via CDPATH environment variable: %s" dir)
(error "No such directory: %s" dir)))))
(defun directory-files-recursively (dir regexp
&optional include-directories predicate
follow-symlinks)
"Return list of all files under directory DIR whose names match REGEXP.
This function works recursively. Files are returned in \"depth
first\" order, and files from each directory are sorted in
alphabetical order. Each file name appears in the returned list
in its absolute form.
By default, the returned list excludes directories, but if
optional argument INCLUDE-DIRECTORIES is non-nil, they are
included.
PREDICATE can be either nil (which means that all subdirectories
of DIR are descended into), t (which means that subdirectories that
can't be read are ignored), or a function (which is called with
the name of each subdirectory, and should return non-nil if the
subdirectory is to be descended into).
If FOLLOW-SYMLINKS is non-nil, symbolic links that point to
directories are followed. Note that this can lead to infinite
recursion."
(let* ((result nil)
(files nil)
(dir (directory-file-name dir))
;; When DIR is "/", remote file names like "/method:" could
;; also be offered. We shall suppress them.
(tramp-mode (and tramp-mode (file-remote-p (expand-file-name dir)))))
(dolist (file (sort (file-name-all-completions "" dir)
'string<))
(unless (member file '("./" "../"))
(if (directory-name-p file)
(let* ((leaf (substring file 0 (1- (length file))))
(full-file (concat dir "/" leaf)))
;; Don't follow symlinks to other directories.
(when (and (or (not (file-symlink-p full-file))
(and (file-symlink-p full-file)
follow-symlinks))
;; Allow filtering subdirectories.
(or (eq predicate nil)
(eq predicate t)
(funcall predicate full-file)))
(let ((sub-files
(if (eq predicate t)
(ignore-error file-error
(directory-files-recursively
full-file regexp include-directories
predicate follow-symlinks))
(directory-files-recursively
full-file regexp include-directories
predicate follow-symlinks))))
(setq result (nconc result sub-files))))
(when (and include-directories
(string-match regexp leaf))
(setq result (nconc result (list full-file)))))
(when (string-match regexp file)
(push (concat dir "/" file) files)))))
(nconc result (nreverse files))))
(defun directory-empty-p (dir)