*** empty log message ***
[bpt/emacs.git] / src / fileio.c
CommitLineData
570d7624
JB
1/* File IO for GNU Emacs.
2 Copyright (C) 1985, 1986, 1987, 1988 Free Software Foundation, Inc.
3
4This file is part of GNU Emacs.
5
6GNU Emacs is free software; you can redistribute it and/or modify
7it under the terms of the GNU General Public License as published by
8the Free Software Foundation; either version 1, or (at your option)
9any later version.
10
11GNU Emacs is distributed in the hope that it will be useful,
12but WITHOUT ANY WARRANTY; without even the implied warranty of
13MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14GNU General Public License for more details.
15
16You should have received a copy of the GNU General Public License
17along with GNU Emacs; see the file COPYING. If not, write to
18the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
19
20
21#include <sys/types.h>
22#include <sys/stat.h>
23#include <pwd.h>
24#include <ctype.h>
25#include <sys/dir.h>
26#include <errno.h>
27
28#ifndef VMS
29extern int errno;
30extern char *sys_errlist[];
31extern int sys_nerr;
32#endif
33
34#define err_str(a) ((a) < sys_nerr ? sys_errlist[a] : "unknown error")
35
36#ifdef APOLLO
37#include <sys/time.h>
38#endif
39
40#ifdef NULL
41#undef NULL
42#endif
43#include "config.h"
44#include "lisp.h"
45#include "buffer.h"
46#include "window.h"
47
48#ifdef VMS
49#include <perror.h>
50#include <file.h>
51#include <rmsdef.h>
52#include <fab.h>
53#include <nam.h>
54#endif
55
56#ifdef NEED_TIME_H
57#include <time.h>
58#else /* not NEED_TIME_H */
59#ifdef HAVE_TIMEVAL
60#include <sys/time.h>
61#endif /* HAVE_TIMEVAL */
62#endif /* not NEED_TIME_H */
63
64#ifdef HPUX
65#include <netio.h>
9b7828a5 66#ifndef HPUX8
570d7624
JB
67#include <errnet.h>
68#endif
9b7828a5 69#endif
570d7624
JB
70
71#ifndef O_WRONLY
72#define O_WRONLY 1
73#endif
74
75#define min(a, b) ((a) < (b) ? (a) : (b))
76#define max(a, b) ((a) > (b) ? (a) : (b))
77
78/* Nonzero during writing of auto-save files */
79int auto_saving;
80
81/* Set by auto_save_1 to mode of original file so Fwrite_region will create
82 a new file with the same mode as the original */
83int auto_save_mode_bits;
84
85/* Nonzero means, when reading a filename in the minibuffer,
86 start out by inserting the default directory into the minibuffer. */
87int insert_default_directory;
88
89/* On VMS, nonzero means write new files with record format stmlf.
90 Zero means use var format. */
91int vms_stmlf_recfm;
92
93Lisp_Object Qfile_error, Qfile_already_exists;
94
95report_file_error (string, data)
96 char *string;
97 Lisp_Object data;
98{
99 Lisp_Object errstring;
100
101 if (errno >= 0 && errno < sys_nerr)
102 errstring = build_string (sys_errlist[errno]);
103 else
104 errstring = build_string ("undocumented error code");
105
106 /* System error messages are capitalized. Downcase the initial
107 unless it is followed by a slash. */
108 if (XSTRING (errstring)->data[1] != '/')
109 XSTRING (errstring)->data[0] = DOWNCASE (XSTRING (errstring)->data[0]);
110
111 while (1)
112 Fsignal (Qfile_error,
113 Fcons (build_string (string), Fcons (errstring, data)));
114}
115\f
116DEFUN ("file-name-directory", Ffile_name_directory, Sfile_name_directory,
117 1, 1, 0,
118 "Return the directory component in file name NAME.\n\
119Return nil if NAME does not include a directory.\n\
120Otherwise return a directory spec.\n\
121Given a Unix syntax file name, returns a string ending in slash;\n\
122on VMS, perhaps instead a string ending in `:', `]' or `>'.")
123 (file)
124 Lisp_Object file;
125{
126 register unsigned char *beg;
127 register unsigned char *p;
128
129 CHECK_STRING (file, 0);
130
131 beg = XSTRING (file)->data;
132 p = beg + XSTRING (file)->size;
133
134 while (p != beg && p[-1] != '/'
135#ifdef VMS
136 && p[-1] != ':' && p[-1] != ']' && p[-1] != '>'
137#endif /* VMS */
138 ) p--;
139
140 if (p == beg)
141 return Qnil;
142 return make_string (beg, p - beg);
143}
144
145DEFUN ("file-name-nondirectory", Ffile_name_nondirectory, Sfile_name_nondirectory,
146 1, 1, 0,
147 "Return file name NAME sans its directory.\n\
148For example, in a Unix-syntax file name,\n\
149this is everything after the last slash,\n\
150or the entire name if it contains no slash.")
151 (file)
152 Lisp_Object file;
153{
154 register unsigned char *beg, *p, *end;
155
156 CHECK_STRING (file, 0);
157
158 beg = XSTRING (file)->data;
159 end = p = beg + XSTRING (file)->size;
160
161 while (p != beg && p[-1] != '/'
162#ifdef VMS
163 && p[-1] != ':' && p[-1] != ']' && p[-1] != '>'
164#endif /* VMS */
165 ) p--;
166
167 return make_string (p, end - p);
168}
169\f
170char *
171file_name_as_directory (out, in)
172 char *out, *in;
173{
174 int size = strlen (in) - 1;
175
176 strcpy (out, in);
177
178#ifdef VMS
179 /* Is it already a directory string? */
180 if (in[size] == ':' || in[size] == ']' || in[size] == '>')
181 return out;
182 /* Is it a VMS directory file name? If so, hack VMS syntax. */
183 else if (! index (in, '/')
184 && ((size > 3 && ! strcmp (&in[size - 3], ".DIR"))
185 || (size > 3 && ! strcmp (&in[size - 3], ".dir"))
186 || (size > 5 && (! strncmp (&in[size - 5], ".DIR", 4)
187 || ! strncmp (&in[size - 5], ".dir", 4))
188 && (in[size - 1] == '.' || in[size - 1] == ';')
189 && in[size] == '1')))
190 {
191 register char *p, *dot;
192 char brack;
193
194 /* x.dir -> [.x]
195 dir:x.dir --> dir:[x]
196 dir:[x]y.dir --> dir:[x.y] */
197 p = in + size;
198 while (p != in && *p != ':' && *p != '>' && *p != ']') p--;
199 if (p != in)
200 {
201 strncpy (out, in, p - in);
202 out[p - in] = '\0';
203 if (*p == ':')
204 {
205 brack = ']';
206 strcat (out, ":[");
207 }
208 else
209 {
210 brack = *p;
211 strcat (out, ".");
212 }
213 p++;
214 }
215 else
216 {
217 brack = ']';
218 strcpy (out, "[.");
219 }
220 if (dot = index (p, '.'))
221 {
222 /* blindly remove any extension */
223 size = strlen (out) + (dot - p);
224 strncat (out, p, dot - p);
225 }
226 else
227 {
228 strcat (out, p);
229 size = strlen (out);
230 }
231 out[size++] = brack;
232 out[size] = '\0';
233 }
234#else /* not VMS */
235 /* For Unix syntax, Append a slash if necessary */
236 if (out[size] != '/')
237 strcat (out, "/");
238#endif /* not VMS */
239 return out;
240}
241
242DEFUN ("file-name-as-directory", Ffile_name_as_directory,
243 Sfile_name_as_directory, 1, 1, 0,
244 "Return a string representing file FILENAME interpreted as a directory.\n\
245This operation exists because a directory is also a file, but its name as\n\
246a directory is different from its name as a file.\n\
247The result can be used as the value of `default-directory'\n\
248or passed as second argument to `expand-file-name'.\n\
249For a Unix-syntax file name, just appends a slash.\n\
250On VMS, converts \"[X]FOO.DIR\" to \"[X.FOO]\", etc.")
251 (file)
252 Lisp_Object file;
253{
254 char *buf;
255
256 CHECK_STRING (file, 0);
257 if (NULL (file))
258 return Qnil;
259 buf = (char *) alloca (XSTRING (file)->size + 10);
260 return build_string (file_name_as_directory (buf, XSTRING (file)->data));
261}
262\f
263/*
264 * Convert from directory name to filename.
265 * On VMS:
266 * xyzzy:[mukesh.emacs] => xyzzy:[mukesh]emacs.dir.1
267 * xyzzy:[mukesh] => xyzzy:[000000]mukesh.dir.1
268 * On UNIX, it's simple: just make sure there is a terminating /
269
270 * Value is nonzero if the string output is different from the input.
271 */
272
273directory_file_name (src, dst)
274 char *src, *dst;
275{
276 long slen;
277#ifdef VMS
278 long rlen;
279 char * ptr, * rptr;
280 char bracket;
281 struct FAB fab = cc$rms_fab;
282 struct NAM nam = cc$rms_nam;
283 char esa[NAM$C_MAXRSS];
284#endif /* VMS */
285
286 slen = strlen (src);
287#ifdef VMS
288 if (! index (src, '/')
289 && (src[slen - 1] == ']'
290 || src[slen - 1] == ':'
291 || src[slen - 1] == '>'))
292 {
293 /* VMS style - convert [x.y.z] to [x.y]z, [x] to [000000]x */
294 fab.fab$l_fna = src;
295 fab.fab$b_fns = slen;
296 fab.fab$l_nam = &nam;
297 fab.fab$l_fop = FAB$M_NAM;
298
299 nam.nam$l_esa = esa;
300 nam.nam$b_ess = sizeof esa;
301 nam.nam$b_nop |= NAM$M_SYNCHK;
302
303 /* We call SYS$PARSE to handle such things as [--] for us. */
304 if (SYS$PARSE(&fab, 0, 0) == RMS$_NORMAL)
305 {
306 slen = nam.nam$b_esl;
307 if (esa[slen - 1] == ';' && esa[slen - 2] == '.')
308 slen -= 2;
309 esa[slen] = '\0';
310 src = esa;
311 }
312 if (src[slen - 1] != ']' && src[slen - 1] != '>')
313 {
314 /* what about when we have logical_name:???? */
315 if (src[slen - 1] == ':')
316 { /* Xlate logical name and see what we get */
317 ptr = strcpy (dst, src); /* upper case for getenv */
318 while (*ptr)
319 {
320 if ('a' <= *ptr && *ptr <= 'z')
321 *ptr -= 040;
322 ptr++;
323 }
324 dst[slen - 1] = 0; /* remove colon */
325 if (!(src = egetenv (dst)))
326 return 0;
327 /* should we jump to the beginning of this procedure?
328 Good points: allows us to use logical names that xlate
329 to Unix names,
330 Bad points: can be a problem if we just translated to a device
331 name...
332 For now, I'll punt and always expect VMS names, and hope for
333 the best! */
334 slen = strlen (src);
335 if (src[slen - 1] != ']' && src[slen - 1] != '>')
336 { /* no recursion here! */
337 strcpy (dst, src);
338 return 0;
339 }
340 }
341 else
342 { /* not a directory spec */
343 strcpy (dst, src);
344 return 0;
345 }
346 }
347 bracket = src[slen - 1];
348
349 /* If bracket is ']' or '>', bracket - 2 is the corresponding
350 opening bracket. */
351 if (!(ptr = index (src, bracket - 2)))
352 { /* no opening bracket */
353 strcpy (dst, src);
354 return 0;
355 }
356 if (!(rptr = rindex (src, '.')))
357 rptr = ptr;
358 slen = rptr - src;
359 strncpy (dst, src, slen);
360 dst[slen] = '\0';
361 if (*rptr == '.')
362 {
363 dst[slen++] = bracket;
364 dst[slen] = '\0';
365 }
366 else
367 {
368 /* If we have the top-level of a rooted directory (i.e. xx:[000000]),
369 then translate the device and recurse. */
370 if (dst[slen - 1] == ':'
371 && dst[slen - 2] != ':' /* skip decnet nodes */
372 && strcmp(src + slen, "[000000]") == 0)
373 {
374 dst[slen - 1] = '\0';
375 if ((ptr = egetenv (dst))
376 && (rlen = strlen (ptr) - 1) > 0
377 && (ptr[rlen] == ']' || ptr[rlen] == '>')
378 && ptr[rlen - 1] == '.')
379 {
380 ptr[rlen - 1] = ']';
381 ptr[rlen] = '\0';
382 return directory_file_name (ptr, dst);
383 }
384 else
385 dst[slen - 1] = ':';
386 }
387 strcat (dst, "[000000]");
388 slen += 8;
389 }
390 rptr++;
391 rlen = strlen (rptr) - 1;
392 strncat (dst, rptr, rlen);
393 dst[slen + rlen] = '\0';
394 strcat (dst, ".DIR.1");
395 return 1;
396 }
397#endif /* VMS */
398 /* Process as Unix format: just remove any final slash.
399 But leave "/" unchanged; do not change it to "". */
400 strcpy (dst, src);
401 if (dst[slen - 1] == '/' && slen > 1)
402 dst[slen - 1] = 0;
403 return 1;
404}
405
406DEFUN ("directory-file-name", Fdirectory_file_name, Sdirectory_file_name,
407 1, 1, 0,
408 "Returns the file name of the directory named DIR.\n\
409This is the name of the file that holds the data for the directory DIR.\n\
410This operation exists because a directory is also a file, but its name as\n\
411a directory is different from its name as a file.\n\
412In Unix-syntax, this function just removes the final slash.\n\
413On VMS, given a VMS-syntax directory name such as \"[X.Y]\",\n\
414it returns a file name such as \"[X]Y.DIR.1\".")
415 (directory)
416 Lisp_Object directory;
417{
418 char *buf;
419
420 CHECK_STRING (directory, 0);
421
422 if (NULL (directory))
423 return Qnil;
424#ifdef VMS
425 /* 20 extra chars is insufficient for VMS, since we might perform a
426 logical name translation. an equivalence string can be up to 255
427 chars long, so grab that much extra space... - sss */
428 buf = (char *) alloca (XSTRING (directory)->size + 20 + 255);
429#else
430 buf = (char *) alloca (XSTRING (directory)->size + 20);
431#endif
432 directory_file_name (XSTRING (directory)->data, buf);
433 return build_string (buf);
434}
435
436DEFUN ("make-temp-name", Fmake_temp_name, Smake_temp_name, 1, 1, 0,
437 "Generate temporary file name (string) starting with PREFIX (a string).\n\
438The Emacs process number forms part of the result,\n\
439so there is no danger of generating a name being used by another process.")
440 (prefix)
441 Lisp_Object prefix;
442{
443 Lisp_Object val;
444 val = concat2 (prefix, build_string ("XXXXXX"));
445 mktemp (XSTRING (val)->data);
446 return val;
447}
448\f
449DEFUN ("expand-file-name", Fexpand_file_name, Sexpand_file_name, 1, 2, 0,
450 "Convert FILENAME to absolute, and canonicalize it.\n\
451Second arg DEFAULT is directory to start with if FILENAME is relative\n\
452 (does not start with slash); if DEFAULT is nil or missing,\n\
453the current buffer's value of default-directory is used.\n\
454Filenames containing `.' or `..' as components are simplified;\n\
455initial `~/' expands to your home directory.\n\
456See also the function `substitute-in-file-name'.")
457 (name, defalt)
458 Lisp_Object name, defalt;
459{
460 unsigned char *nm;
461
462 register unsigned char *newdir, *p, *o;
463 int tlen;
464 unsigned char *target;
465 struct passwd *pw;
466 int lose;
467#ifdef VMS
468 unsigned char * colon = 0;
469 unsigned char * close = 0;
470 unsigned char * slash = 0;
471 unsigned char * brack = 0;
472 int lbrack = 0, rbrack = 0;
473 int dots = 0;
474#endif /* VMS */
475
476 CHECK_STRING (name, 0);
477
478#ifdef VMS
479 /* Filenames on VMS are always upper case. */
480 name = Fupcase (name);
481#endif
482
483 nm = XSTRING (name)->data;
484
485 /* If nm is absolute, flush ...// and detect /./ and /../.
486 If no /./ or /../ we can return right away. */
487 if (
488 nm[0] == '/'
489#ifdef VMS
490 || index (nm, ':')
491#endif /* VMS */
492 )
493 {
494 p = nm;
495 lose = 0;
496 while (*p)
497 {
498 if (p[0] == '/' && p[1] == '/'
499#ifdef APOLLO
500 /* // at start of filename is meaningful on Apollo system */
501 && nm != p
502#endif /* APOLLO */
503 )
504 nm = p + 1;
505 if (p[0] == '/' && p[1] == '~')
506 nm = p + 1, lose = 1;
507 if (p[0] == '/' && p[1] == '.'
508 && (p[2] == '/' || p[2] == 0
509 || (p[2] == '.' && (p[3] == '/' || p[3] == 0))))
510 lose = 1;
511#ifdef VMS
512 if (p[0] == '\\')
513 lose = 1;
514 if (p[0] == '/') {
515 /* if dev:[dir]/, move nm to / */
516 if (!slash && p > nm && (brack || colon)) {
517 nm = (brack ? brack + 1 : colon + 1);
518 lbrack = rbrack = 0;
519 brack = 0;
520 colon = 0;
521 }
522 slash = p;
523 }
524 if (p[0] == '-')
525#ifndef VMS4_4
526 /* VMS pre V4.4,convert '-'s in filenames. */
527 if (lbrack == rbrack)
528 {
529 if (dots < 2) /* this is to allow negative version numbers */
530 p[0] = '_';
531 }
532 else
533#endif /* VMS4_4 */
534 if (lbrack > rbrack &&
535 ((p[-1] == '.' || p[-1] == '[' || p[-1] == '<') &&
536 (p[1] == '.' || p[1] == ']' || p[1] == '>')))
537 lose = 1;
538#ifndef VMS4_4
539 else
540 p[0] = '_';
541#endif /* VMS4_4 */
542 /* count open brackets, reset close bracket pointer */
543 if (p[0] == '[' || p[0] == '<')
544 lbrack++, brack = 0;
545 /* count close brackets, set close bracket pointer */
546 if (p[0] == ']' || p[0] == '>')
547 rbrack++, brack = p;
548 /* detect ][ or >< */
549 if ((p[0] == ']' || p[0] == '>') && (p[1] == '[' || p[1] == '<'))
550 lose = 1;
551 if ((p[0] == ':' || p[0] == ']' || p[0] == '>') && p[1] == '~')
552 nm = p + 1, lose = 1;
553 if (p[0] == ':' && (colon || slash))
554 /* if dev1:[dir]dev2:, move nm to dev2: */
555 if (brack)
556 {
557 nm = brack + 1;
558 brack = 0;
559 }
560 /* if /pathname/dev:, move nm to dev: */
561 else if (slash)
562 nm = slash + 1;
563 /* if node::dev:, move colon following dev */
564 else if (colon && colon[-1] == ':')
565 colon = p;
566 /* if dev1:dev2:, move nm to dev2: */
567 else if (colon && colon[-1] != ':')
568 {
569 nm = colon + 1;
570 colon = 0;
571 }
572 if (p[0] == ':' && !colon)
573 {
574 if (p[1] == ':')
575 p++;
576 colon = p;
577 }
578 if (lbrack == rbrack)
579 if (p[0] == ';')
580 dots = 2;
581 else if (p[0] == '.')
582 dots++;
583#endif /* VMS */
584 p++;
585 }
586 if (!lose)
587 {
588#ifdef VMS
589 if (index (nm, '/'))
590 return build_string (sys_translate_unix (nm));
591#endif /* VMS */
592 if (nm == XSTRING (name)->data)
593 return name;
594 return build_string (nm);
595 }
596 }
597
598 /* Now determine directory to start with and put it in newdir */
599
600 newdir = 0;
601
602 if (nm[0] == '~') /* prefix ~ */
603 if (nm[1] == '/'
604#ifdef VMS
605 || nm[1] == ':'
606#endif /* VMS */
607 || nm[1] == 0)/* ~/filename */
608 {
609 if (!(newdir = (unsigned char *) egetenv ("HOME")))
610 newdir = (unsigned char *) "";
611 nm++;
612#ifdef VMS
613 nm++; /* Don't leave the slash in nm. */
614#endif /* VMS */
615 }
616 else /* ~user/filename */
617 {
618 for (p = nm; *p && (*p != '/'
619#ifdef VMS
620 && *p != ':'
621#endif /* VMS */
622 ); p++);
623 o = (unsigned char *) alloca (p - nm + 1);
624 bcopy ((char *) nm, o, p - nm);
625 o [p - nm] = 0;
626
627 pw = (struct passwd *) getpwnam (o + 1);
628 if (!pw)
629 error ("\"%s\" isn't a registered user", o + 1);
630
631#ifdef VMS
632 nm = p + 1; /* skip the terminator */
633#else
634 nm = p;
635#endif /* VMS */
636 newdir = (unsigned char *) pw -> pw_dir;
637 }
638
639 if (nm[0] != '/'
640#ifdef VMS
641 && !index (nm, ':')
642#endif /* not VMS */
643 && !newdir)
644 {
645 if (NULL (defalt))
646 defalt = current_buffer->directory;
647 CHECK_STRING (defalt, 1);
648 newdir = XSTRING (defalt)->data;
649 }
650
651 /* Now concatenate the directory and name to new space in the stack frame */
652
653 tlen = (newdir ? strlen (newdir) + 1 : 0) + strlen (nm) + 1;
654 target = (unsigned char *) alloca (tlen);
655 *target = 0;
656
657 if (newdir)
658 {
659#ifndef VMS
660 if (nm[0] == 0 || nm[0] == '/')
661 strcpy (target, newdir);
662 else
663#endif
664 file_name_as_directory (target, newdir);
665 }
666
667 strcat (target, nm);
668#ifdef VMS
669 if (index (target, '/'))
670 strcpy (target, sys_translate_unix (target));
671#endif /* VMS */
672
673 /* Now canonicalize by removing /. and /foo/.. if they appear */
674
675 p = target;
676 o = target;
677
678 while (*p)
679 {
680#ifdef VMS
681 if (*p != ']' && *p != '>' && *p != '-')
682 {
683 if (*p == '\\')
684 p++;
685 *o++ = *p++;
686 }
687 else if ((p[0] == ']' || p[0] == '>') && p[0] == p[1] + 2)
688 /* brackets are offset from each other by 2 */
689 {
690 p += 2;
691 if (*p != '.' && *p != '-' && o[-1] != '.')
692 /* convert [foo][bar] to [bar] */
693 while (o[-1] != '[' && o[-1] != '<')
694 o--;
695 else if (*p == '-' && *o != '.')
696 *--p = '.';
697 }
698 else if (p[0] == '-' && o[-1] == '.' &&
699 (p[1] == '.' || p[1] == ']' || p[1] == '>'))
700 /* flush .foo.- ; leave - if stopped by '[' or '<' */
701 {
702 do
703 o--;
704 while (o[-1] != '.' && o[-1] != '[' && o[-1] != '<');
705 if (p[1] == '.') /* foo.-.bar ==> bar*/
706 p += 2;
707 else if (o[-1] == '.') /* '.foo.-]' ==> ']' */
708 p++, o--;
709 /* else [foo.-] ==> [-] */
710 }
711 else
712 {
713#ifndef VMS4_4
714 if (*p == '-' &&
715 o[-1] != '[' && o[-1] != '<' && o[-1] != '.' &&
716 p[1] != ']' && p[1] != '>' && p[1] != '.')
717 *p = '_';
718#endif /* VMS4_4 */
719 *o++ = *p++;
720 }
721#else /* not VMS */
722 if (*p != '/')
723 {
724 *o++ = *p++;
725 }
726 else if (!strncmp (p, "//", 2)
727#ifdef APOLLO
728 /* // at start of filename is meaningful in Apollo system */
729 && o != target
730#endif /* APOLLO */
731 )
732 {
733 o = target;
734 p++;
735 }
736 else if (p[0] == '/' && p[1] == '.' &&
737 (p[2] == '/' || p[2] == 0))
738 p += 2;
739 else if (!strncmp (p, "/..", 3)
740 /* `/../' is the "superroot" on certain file systems. */
741 && o != target
742 && (p[3] == '/' || p[3] == 0))
743 {
744 while (o != target && *--o != '/')
745 ;
746#ifdef APOLLO
747 if (o == target + 1 && o[-1] == '/' && o[0] == '/')
748 ++o;
749 else
750#endif /* APOLLO */
751 if (o == target && *o == '/')
752 ++o;
753 p += 3;
754 }
755 else
756 {
757 *o++ = *p++;
758 }
759#endif /* not VMS */
760 }
761
762 return make_string (target, o - target);
763}
764#if 0
765DEFUN ("expand-file-name", Fexpand_file_name, Sexpand_file_name, 1, 2, 0,
766 "Convert FILENAME to absolute, and canonicalize it.\n\
767Second arg DEFAULT is directory to start with if FILENAME is relative\n\
768 (does not start with slash); if DEFAULT is nil or missing,\n\
769the current buffer's value of default-directory is used.\n\
770Filenames containing `.' or `..' as components are simplified;\n\
771initial `~/' expands to your home directory.\n\
772See also the function `substitute-in-file-name'.")
773 (name, defalt)
774 Lisp_Object name, defalt;
775{
776 unsigned char *nm;
777
778 register unsigned char *newdir, *p, *o;
779 int tlen;
780 unsigned char *target;
781 struct passwd *pw;
782 int lose;
783#ifdef VMS
784 unsigned char * colon = 0;
785 unsigned char * close = 0;
786 unsigned char * slash = 0;
787 unsigned char * brack = 0;
788 int lbrack = 0, rbrack = 0;
789 int dots = 0;
790#endif /* VMS */
791
792 CHECK_STRING (name, 0);
793
794#ifdef VMS
795 /* Filenames on VMS are always upper case. */
796 name = Fupcase (name);
797#endif
798
799 nm = XSTRING (name)->data;
800
801 /* If nm is absolute, flush ...// and detect /./ and /../.
802 If no /./ or /../ we can return right away. */
803 if (
804 nm[0] == '/'
805#ifdef VMS
806 || index (nm, ':')
807#endif /* VMS */
808 )
809 {
810 p = nm;
811 lose = 0;
812 while (*p)
813 {
814 if (p[0] == '/' && p[1] == '/'
815#ifdef APOLLO
816 /* // at start of filename is meaningful on Apollo system */
817 && nm != p
818#endif /* APOLLO */
819 )
820 nm = p + 1;
821 if (p[0] == '/' && p[1] == '~')
822 nm = p + 1, lose = 1;
823 if (p[0] == '/' && p[1] == '.'
824 && (p[2] == '/' || p[2] == 0
825 || (p[2] == '.' && (p[3] == '/' || p[3] == 0))))
826 lose = 1;
827#ifdef VMS
828 if (p[0] == '\\')
829 lose = 1;
830 if (p[0] == '/') {
831 /* if dev:[dir]/, move nm to / */
832 if (!slash && p > nm && (brack || colon)) {
833 nm = (brack ? brack + 1 : colon + 1);
834 lbrack = rbrack = 0;
835 brack = 0;
836 colon = 0;
837 }
838 slash = p;
839 }
840 if (p[0] == '-')
841#ifndef VMS4_4
842 /* VMS pre V4.4,convert '-'s in filenames. */
843 if (lbrack == rbrack)
844 {
845 if (dots < 2) /* this is to allow negative version numbers */
846 p[0] = '_';
847 }
848 else
849#endif /* VMS4_4 */
850 if (lbrack > rbrack &&
851 ((p[-1] == '.' || p[-1] == '[' || p[-1] == '<') &&
852 (p[1] == '.' || p[1] == ']' || p[1] == '>')))
853 lose = 1;
854#ifndef VMS4_4
855 else
856 p[0] = '_';
857#endif /* VMS4_4 */
858 /* count open brackets, reset close bracket pointer */
859 if (p[0] == '[' || p[0] == '<')
860 lbrack++, brack = 0;
861 /* count close brackets, set close bracket pointer */
862 if (p[0] == ']' || p[0] == '>')
863 rbrack++, brack = p;
864 /* detect ][ or >< */
865 if ((p[0] == ']' || p[0] == '>') && (p[1] == '[' || p[1] == '<'))
866 lose = 1;
867 if ((p[0] == ':' || p[0] == ']' || p[0] == '>') && p[1] == '~')
868 nm = p + 1, lose = 1;
869 if (p[0] == ':' && (colon || slash))
870 /* if dev1:[dir]dev2:, move nm to dev2: */
871 if (brack)
872 {
873 nm = brack + 1;
874 brack = 0;
875 }
876 /* if /pathname/dev:, move nm to dev: */
877 else if (slash)
878 nm = slash + 1;
879 /* if node::dev:, move colon following dev */
880 else if (colon && colon[-1] == ':')
881 colon = p;
882 /* if dev1:dev2:, move nm to dev2: */
883 else if (colon && colon[-1] != ':')
884 {
885 nm = colon + 1;
886 colon = 0;
887 }
888 if (p[0] == ':' && !colon)
889 {
890 if (p[1] == ':')
891 p++;
892 colon = p;
893 }
894 if (lbrack == rbrack)
895 if (p[0] == ';')
896 dots = 2;
897 else if (p[0] == '.')
898 dots++;
899#endif /* VMS */
900 p++;
901 }
902 if (!lose)
903 {
904#ifdef VMS
905 if (index (nm, '/'))
906 return build_string (sys_translate_unix (nm));
907#endif /* VMS */
908 if (nm == XSTRING (name)->data)
909 return name;
910 return build_string (nm);
911 }
912 }
913
914 /* Now determine directory to start with and put it in NEWDIR */
915
916 newdir = 0;
917
918 if (nm[0] == '~') /* prefix ~ */
919 if (nm[1] == '/'
920#ifdef VMS
921 || nm[1] == ':'
922#endif /* VMS */
923 || nm[1] == 0)/* ~/filename */
924 {
925 if (!(newdir = (unsigned char *) egetenv ("HOME")))
926 newdir = (unsigned char *) "";
927 nm++;
928#ifdef VMS
929 nm++; /* Don't leave the slash in nm. */
930#endif /* VMS */
931 }
932 else /* ~user/filename */
933 {
934 /* Get past ~ to user */
935 unsigned char *user = nm + 1;
936 /* Find end of name. */
937 unsigned char *ptr = (unsigned char *) index (user, '/');
938 int len = ptr ? ptr - user : strlen (user);
939#ifdef VMS
940 unsigned char *ptr1 = index (user, ':');
941 if (ptr1 != 0 && ptr1 - user < len)
942 len = ptr1 - user;
943#endif /* VMS */
944 /* Copy the user name into temp storage. */
945 o = (unsigned char *) alloca (len + 1);
946 bcopy ((char *) user, o, len);
947 o[len] = 0;
948
949 /* Look up the user name. */
950 pw = (struct passwd *) getpwnam (o + 1);
951 if (!pw)
952 error ("\"%s\" isn't a registered user", o + 1);
953
954 newdir = (unsigned char *) pw->pw_dir;
955
956 /* Discard the user name from NM. */
957 nm += len;
958 }
959
960 if (nm[0] != '/'
961#ifdef VMS
962 && !index (nm, ':')
963#endif /* not VMS */
964 && !newdir)
965 {
966 if (NULL (defalt))
967 defalt = current_buffer->directory;
968 CHECK_STRING (defalt, 1);
969 newdir = XSTRING (defalt)->data;
970 }
971
972 /* Now concatenate the directory and name to new space in the stack frame */
973
974 tlen = (newdir ? strlen (newdir) + 1 : 0) + strlen (nm) + 1;
975 target = (unsigned char *) alloca (tlen);
976 *target = 0;
977
978 if (newdir)
979 {
980#ifndef VMS
981 if (nm[0] == 0 || nm[0] == '/')
982 strcpy (target, newdir);
983 else
984#endif
985 file_name_as_directory (target, newdir);
986 }
987
988 strcat (target, nm);
989#ifdef VMS
990 if (index (target, '/'))
991 strcpy (target, sys_translate_unix (target));
992#endif /* VMS */
993
994 /* Now canonicalize by removing /. and /foo/.. if they appear */
995
996 p = target;
997 o = target;
998
999 while (*p)
1000 {
1001#ifdef VMS
1002 if (*p != ']' && *p != '>' && *p != '-')
1003 {
1004 if (*p == '\\')
1005 p++;
1006 *o++ = *p++;
1007 }
1008 else if ((p[0] == ']' || p[0] == '>') && p[0] == p[1] + 2)
1009 /* brackets are offset from each other by 2 */
1010 {
1011 p += 2;
1012 if (*p != '.' && *p != '-' && o[-1] != '.')
1013 /* convert [foo][bar] to [bar] */
1014 while (o[-1] != '[' && o[-1] != '<')
1015 o--;
1016 else if (*p == '-' && *o != '.')
1017 *--p = '.';
1018 }
1019 else if (p[0] == '-' && o[-1] == '.' &&
1020 (p[1] == '.' || p[1] == ']' || p[1] == '>'))
1021 /* flush .foo.- ; leave - if stopped by '[' or '<' */
1022 {
1023 do
1024 o--;
1025 while (o[-1] != '.' && o[-1] != '[' && o[-1] != '<');
1026 if (p[1] == '.') /* foo.-.bar ==> bar*/
1027 p += 2;
1028 else if (o[-1] == '.') /* '.foo.-]' ==> ']' */
1029 p++, o--;
1030 /* else [foo.-] ==> [-] */
1031 }
1032 else
1033 {
1034#ifndef VMS4_4
1035 if (*p == '-' &&
1036 o[-1] != '[' && o[-1] != '<' && o[-1] != '.' &&
1037 p[1] != ']' && p[1] != '>' && p[1] != '.')
1038 *p = '_';
1039#endif /* VMS4_4 */
1040 *o++ = *p++;
1041 }
1042#else /* not VMS */
1043 if (*p != '/')
1044 {
1045 *o++ = *p++;
1046 }
1047 else if (!strncmp (p, "//", 2)
1048#ifdef APOLLO
1049 /* // at start of filename is meaningful in Apollo system */
1050 && o != target
1051#endif /* APOLLO */
1052 )
1053 {
1054 o = target;
1055 p++;
1056 }
1057 else if (p[0] == '/' && p[1] == '.' &&
1058 (p[2] == '/' || p[2] == 0))
1059 p += 2;
1060 else if (!strncmp (p, "/..", 3)
1061 /* `/../' is the "superroot" on certain file systems. */
1062 && o != target
1063 && (p[3] == '/' || p[3] == 0))
1064 {
1065 while (o != target && *--o != '/')
1066 ;
1067#ifdef APOLLO
1068 if (o == target + 1 && o[-1] == '/' && o[0] == '/')
1069 ++o;
1070 else
1071#endif /* APOLLO */
1072 if (o == target && *o == '/')
1073 ++o;
1074 p += 3;
1075 }
1076 else
1077 {
1078 *o++ = *p++;
1079 }
1080#endif /* not VMS */
1081 }
1082
1083 return make_string (target, o - target);
1084}
1085#endif
1086\f
1087DEFUN ("substitute-in-file-name", Fsubstitute_in_file_name,
1088 Ssubstitute_in_file_name, 1, 1, 0,
1089 "Substitute environment variables referred to in FILENAME.\n\
1090`$FOO' where FOO is an environment variable name means to substitute\n\
1091the value of that variable. The variable name should be terminated\n\
1092with a character not a letter, digit or underscore; otherwise, enclose\n\
1093the entire variable name in braces.\n\
1094If `/~' appears, all of FILENAME through that `/' is discarded.\n\n\
1095On VMS, `$' substitution is not done; this function does little and only\n\
1096duplicates what `expand-file-name' does.")
1097 (string)
1098 Lisp_Object string;
1099{
1100 unsigned char *nm;
1101
1102 register unsigned char *s, *p, *o, *x, *endp;
1103 unsigned char *target;
1104 int total = 0;
1105 int substituted = 0;
1106 unsigned char *xnm;
1107
1108 CHECK_STRING (string, 0);
1109
1110 nm = XSTRING (string)->data;
1111 endp = nm + XSTRING (string)->size;
1112
1113 /* If /~ or // appears, discard everything through first slash. */
1114
1115 for (p = nm; p != endp; p++)
1116 {
1117 if ((p[0] == '~' ||
1118#ifdef APOLLO
1119 /* // at start of file name is meaningful in Apollo system */
1120 (p[0] == '/' && p - 1 != nm)
1121#else /* not APOLLO */
1122 p[0] == '/'
1123#endif /* not APOLLO */
1124 )
1125 && p != nm &&
1126#ifdef VMS
1127 (p[-1] == ':' || p[-1] == ']' || p[-1] == '>' ||
1128#endif /* VMS */
1129 p[-1] == '/')
1130#ifdef VMS
1131 )
1132#endif /* VMS */
1133 {
1134 nm = p;
1135 substituted = 1;
1136 }
1137 }
1138
1139#ifdef VMS
1140 return build_string (nm);
1141#else
1142
1143 /* See if any variables are substituted into the string
1144 and find the total length of their values in `total' */
1145
1146 for (p = nm; p != endp;)
1147 if (*p != '$')
1148 p++;
1149 else
1150 {
1151 p++;
1152 if (p == endp)
1153 goto badsubst;
1154 else if (*p == '$')
1155 {
1156 /* "$$" means a single "$" */
1157 p++;
1158 total -= 1;
1159 substituted = 1;
1160 continue;
1161 }
1162 else if (*p == '{')
1163 {
1164 o = ++p;
1165 while (p != endp && *p != '}') p++;
1166 if (*p != '}') goto missingclose;
1167 s = p;
1168 }
1169 else
1170 {
1171 o = p;
1172 while (p != endp && (isalnum (*p) || *p == '_')) p++;
1173 s = p;
1174 }
1175
1176 /* Copy out the variable name */
1177 target = (unsigned char *) alloca (s - o + 1);
1178 strncpy (target, o, s - o);
1179 target[s - o] = 0;
1180
1181 /* Get variable value */
1182 o = (unsigned char *) egetenv (target);
1183/* The presence of this code makes vax 5.0 crash, for reasons yet unknown */
1184#if 0
1185#ifdef USG
1186 if (!o && !strcmp (target, "USER"))
1187 o = egetenv ("LOGNAME");
1188#endif /* USG */
1189#endif /* 0 */
1190 if (!o) goto badvar;
1191 total += strlen (o);
1192 substituted = 1;
1193 }
1194
1195 if (!substituted)
1196 return string;
1197
1198 /* If substitution required, recopy the string and do it */
1199 /* Make space in stack frame for the new copy */
1200 xnm = (unsigned char *) alloca (XSTRING (string)->size + total + 1);
1201 x = xnm;
1202
1203 /* Copy the rest of the name through, replacing $ constructs with values */
1204 for (p = nm; *p;)
1205 if (*p != '$')
1206 *x++ = *p++;
1207 else
1208 {
1209 p++;
1210 if (p == endp)
1211 goto badsubst;
1212 else if (*p == '$')
1213 {
1214 *x++ = *p++;
1215 continue;
1216 }
1217 else if (*p == '{')
1218 {
1219 o = ++p;
1220 while (p != endp && *p != '}') p++;
1221 if (*p != '}') goto missingclose;
1222 s = p++;
1223 }
1224 else
1225 {
1226 o = p;
1227 while (p != endp && (isalnum (*p) || *p == '_')) p++;
1228 s = p;
1229 }
1230
1231 /* Copy out the variable name */
1232 target = (unsigned char *) alloca (s - o + 1);
1233 strncpy (target, o, s - o);
1234 target[s - o] = 0;
1235
1236 /* Get variable value */
1237 o = (unsigned char *) egetenv (target);
1238/* The presence of this code makes vax 5.0 crash, for reasons yet unknown */
1239#if 0
1240#ifdef USG
1241 if (!o && !strcmp (target, "USER"))
1242 o = egetenv ("LOGNAME");
1243#endif /* USG */
1244#endif /* 0 */
1245 if (!o)
1246 goto badvar;
1247
1248 strcpy (x, o);
1249 x += strlen (o);
1250 }
1251
1252 *x = 0;
1253
1254 /* If /~ or // appears, discard everything through first slash. */
1255
1256 for (p = xnm; p != x; p++)
1257 if ((p[0] == '~' ||
1258#ifdef APOLLO
1259 /* // at start of file name is meaningful in Apollo system */
1260 (p[0] == '/' && p - 1 != xnm)
1261#else /* not APOLLO */
1262 p[0] == '/'
1263#endif /* not APOLLO */
1264 )
1265 && p != nm && p[-1] == '/')
1266 xnm = p;
1267
1268 return make_string (xnm, x - xnm);
1269
1270 badsubst:
1271 error ("Bad format environment-variable substitution");
1272 missingclose:
1273 error ("Missing \"}\" in environment-variable substitution");
1274 badvar:
1275 error ("Substituting nonexistent environment variable \"%s\"", target);
1276
1277 /* NOTREACHED */
1278#endif /* not VMS */
1279}
1280\f
1281Lisp_Object
1282expand_and_dir_to_file (filename, defdir)
1283 Lisp_Object filename, defdir;
1284{
1285 register Lisp_Object abspath;
1286
1287 abspath = Fexpand_file_name (filename, defdir);
1288#ifdef VMS
1289 {
1290 register int c = XSTRING (abspath)->data[XSTRING (abspath)->size - 1];
1291 if (c == ':' || c == ']' || c == '>')
1292 abspath = Fdirectory_file_name (abspath);
1293 }
1294#else
1295 /* Remove final slash, if any (unless path is root).
1296 stat behaves differently depending! */
1297 if (XSTRING (abspath)->size > 1
1298 && XSTRING (abspath)->data[XSTRING (abspath)->size - 1] == '/')
1299 {
1300 if (EQ (abspath, filename))
1301 abspath = Fcopy_sequence (abspath);
1302 XSTRING (abspath)->data[XSTRING (abspath)->size - 1] = 0;
1303 }
1304#endif
1305 return abspath;
1306}
1307\f
1308barf_or_query_if_file_exists (absname, querystring, interactive)
1309 Lisp_Object absname;
1310 unsigned char *querystring;
1311 int interactive;
1312{
1313 register Lisp_Object tem;
1314 struct gcpro gcpro1;
1315
1316 if (access (XSTRING (absname)->data, 4) >= 0)
1317 {
1318 if (! interactive)
1319 Fsignal (Qfile_already_exists,
1320 Fcons (build_string ("File already exists"),
1321 Fcons (absname, Qnil)));
1322 GCPRO1 (absname);
1323 tem = do_yes_or_no_p (format1 ("File %s already exists; %s anyway? ",
1324 XSTRING (absname)->data, querystring));
1325 UNGCPRO;
1326 if (NULL (tem))
1327 Fsignal (Qfile_already_exists,
1328 Fcons (build_string ("File already exists"),
1329 Fcons (absname, Qnil)));
1330 }
1331 return;
1332}
1333
1334DEFUN ("copy-file", Fcopy_file, Scopy_file, 2, 4,
1335 "fCopy file: \nFCopy %s to file: \np",
1336 "Copy FILE to NEWNAME. Both args must be strings.\n\
1337Signals a `file-already-exists' error if file NEWNAME already exists,\n\
1338unless a third argument OK-IF-ALREADY-EXISTS is supplied and non-nil.\n\
1339A number as third arg means request confirmation if NEWNAME already exists.\n\
1340This is what happens in interactive use with M-x.\n\
1341Fourth arg non-nil means give the new file the same last-modified time\n\
1342that the old one has. (This works on only some systems.)")
1343 (filename, newname, ok_if_already_exists, keep_date)
1344 Lisp_Object filename, newname, ok_if_already_exists, keep_date;
1345{
1346 int ifd, ofd, n;
1347 char buf[16 * 1024];
1348 struct stat st;
1349 struct gcpro gcpro1, gcpro2;
1350
1351 GCPRO2 (filename, newname);
1352 CHECK_STRING (filename, 0);
1353 CHECK_STRING (newname, 1);
1354 filename = Fexpand_file_name (filename, Qnil);
1355 newname = Fexpand_file_name (newname, Qnil);
1356 if (NULL (ok_if_already_exists)
1357 || XTYPE (ok_if_already_exists) == Lisp_Int)
1358 barf_or_query_if_file_exists (newname, "copy to it",
1359 XTYPE (ok_if_already_exists) == Lisp_Int);
1360
1361 ifd = open (XSTRING (filename)->data, 0);
1362 if (ifd < 0)
1363 report_file_error ("Opening input file", Fcons (filename, Qnil));
1364
1365#ifdef VMS
1366 /* Create the copy file with the same record format as the input file */
1367 ofd = sys_creat (XSTRING (newname)->data, 0666, ifd);
1368#else
1369 ofd = creat (XSTRING (newname)->data, 0666);
1370#endif /* VMS */
1371 if (ofd < 0)
1372 {
1373 close (ifd);
1374 report_file_error ("Opening output file", Fcons (newname, Qnil));
1375 }
1376
1377 while ((n = read (ifd, buf, sizeof buf)) > 0)
1378 if (write (ofd, buf, n) != n)
1379 {
1380 close (ifd);
1381 close (ofd);
1382 report_file_error ("I/O error", Fcons (newname, Qnil));
1383 }
1384
1385 if (fstat (ifd, &st) >= 0)
1386 {
1387#ifdef HAVE_TIMEVAL
1388 if (!NULL (keep_date))
1389 {
1390#ifdef USE_UTIME
1391/* AIX has utimes() in compatibility package, but it dies. So use good old
1392 utime interface instead. */
1393 struct {
1394 time_t atime;
1395 time_t mtime;
1396 } tv;
1397 tv.atime = st.st_atime;
1398 tv.mtime = st.st_mtime;
1399 utime (XSTRING (newname)->data, &tv);
1400#else /* not USE_UTIME */
1401 struct timeval timevals[2];
1402 timevals[0].tv_sec = st.st_atime;
1403 timevals[1].tv_sec = st.st_mtime;
1404 timevals[0].tv_usec = timevals[1].tv_usec = 0;
1405 utimes (XSTRING (newname)->data, timevals);
1406#endif /* not USE_UTIME */
1407 }
1408#endif /* HAVE_TIMEVALS */
1409
1410#ifdef APOLLO
1411 if (!egetenv ("USE_DOMAIN_ACLS"))
1412#endif
1413 chmod (XSTRING (newname)->data, st.st_mode & 07777);
1414 }
1415
1416 close (ifd);
1417 if (close (ofd) < 0)
1418 report_file_error ("I/O error", Fcons (newname, Qnil));
1419
1420 UNGCPRO;
1421 return Qnil;
1422}
1423
1424DEFUN ("make-directory", Fmake_directory, Smake_directory, 1, 1, "FMake directory: ",
1425 "Create a directory. One argument, a file name string.")
1426 (dirname)
1427 Lisp_Object dirname;
1428{
1429 unsigned char *dir;
1430
1431 CHECK_STRING (dirname, 0);
1432 dirname = Fexpand_file_name (dirname, Qnil);
1433 dir = XSTRING (dirname)->data;
1434
1435 if (mkdir (dir, 0777) != 0)
1436 report_file_error ("Creating directory", Flist (1, &dirname));
1437
1438 return Qnil;
1439}
1440
1441DEFUN ("remove-directory", Fremove_directory, Sremove_directory, 1, 1, "FRemove directory: ",
1442 "Remove a directory. One argument, a file name string.")
1443 (dirname)
1444 Lisp_Object dirname;
1445{
1446 unsigned char *dir;
1447
1448 CHECK_STRING (dirname, 0);
1449 dirname = Fexpand_file_name (dirname, Qnil);
1450 dir = XSTRING (dirname)->data;
1451
1452 if (rmdir (dir) != 0)
1453 report_file_error ("Removing directory", Flist (1, &dirname));
1454
1455 return Qnil;
1456}
1457
1458DEFUN ("delete-file", Fdelete_file, Sdelete_file, 1, 1, "fDelete file: ",
1459 "Delete specified file. One argument, a file name string.\n\
1460If file has multiple names, it continues to exist with the other names.")
1461 (filename)
1462 Lisp_Object filename;
1463{
1464 CHECK_STRING (filename, 0);
1465 filename = Fexpand_file_name (filename, Qnil);
1466 if (0 > unlink (XSTRING (filename)->data))
1467 report_file_error ("Removing old name", Flist (1, &filename));
1468 return Qnil;
1469}
1470
1471DEFUN ("rename-file", Frename_file, Srename_file, 2, 3,
1472 "fRename file: \nFRename %s to file: \np",
1473 "Rename FILE as NEWNAME. Both args strings.\n\
1474If file has names other than FILE, it continues to have those names.\n\
1475Signals a `file-already-exists' error if a file NEWNAME already exists\n\
1476unless optional third argument OK-IF-ALREADY-EXISTS is non-nil.\n\
1477A number as third arg means request confirmation if NEWNAME already exists.\n\
1478This is what happens in interactive use with M-x.")
1479 (filename, newname, ok_if_already_exists)
1480 Lisp_Object filename, newname, ok_if_already_exists;
1481{
1482#ifdef NO_ARG_ARRAY
1483 Lisp_Object args[2];
1484#endif
1485 struct gcpro gcpro1, gcpro2;
1486
1487 GCPRO2 (filename, newname);
1488 CHECK_STRING (filename, 0);
1489 CHECK_STRING (newname, 1);
1490 filename = Fexpand_file_name (filename, Qnil);
1491 newname = Fexpand_file_name (newname, Qnil);
1492 if (NULL (ok_if_already_exists)
1493 || XTYPE (ok_if_already_exists) == Lisp_Int)
1494 barf_or_query_if_file_exists (newname, "rename to it",
1495 XTYPE (ok_if_already_exists) == Lisp_Int);
1496#ifndef BSD4_1
1497 if (0 > rename (XSTRING (filename)->data, XSTRING (newname)->data))
1498#else
1499 if (0 > link (XSTRING (filename)->data, XSTRING (newname)->data)
1500 || 0 > unlink (XSTRING (filename)->data))
1501#endif
1502 {
1503 if (errno == EXDEV)
1504 {
1505 Fcopy_file (filename, newname, ok_if_already_exists, Qt);
1506 Fdelete_file (filename);
1507 }
1508 else
1509#ifdef NO_ARG_ARRAY
1510 {
1511 args[0] = filename;
1512 args[1] = newname;
1513 report_file_error ("Renaming", Flist (2, args));
1514 }
1515#else
1516 report_file_error ("Renaming", Flist (2, &filename));
1517#endif
1518 }
1519 UNGCPRO;
1520 return Qnil;
1521}
1522
1523DEFUN ("add-name-to-file", Fadd_name_to_file, Sadd_name_to_file, 2, 3,
1524 "fAdd name to file: \nFName to add to %s: \np",
1525 "Give FILE additional name NEWNAME. Both args strings.\n\
1526Signals a `file-already-exists' error if a file NEWNAME already exists\n\
1527unless optional third argument OK-IF-ALREADY-EXISTS is non-nil.\n\
1528A number as third arg means request confirmation if NEWNAME already exists.\n\
1529This is what happens in interactive use with M-x.")
1530 (filename, newname, ok_if_already_exists)
1531 Lisp_Object filename, newname, ok_if_already_exists;
1532{
1533#ifdef NO_ARG_ARRAY
1534 Lisp_Object args[2];
1535#endif
1536 struct gcpro gcpro1, gcpro2;
1537
1538 GCPRO2 (filename, newname);
1539 CHECK_STRING (filename, 0);
1540 CHECK_STRING (newname, 1);
1541 filename = Fexpand_file_name (filename, Qnil);
1542 newname = Fexpand_file_name (newname, Qnil);
1543 if (NULL (ok_if_already_exists)
1544 || XTYPE (ok_if_already_exists) == Lisp_Int)
1545 barf_or_query_if_file_exists (newname, "make it a new name",
1546 XTYPE (ok_if_already_exists) == Lisp_Int);
1547 unlink (XSTRING (newname)->data);
1548 if (0 > link (XSTRING (filename)->data, XSTRING (newname)->data))
1549 {
1550#ifdef NO_ARG_ARRAY
1551 args[0] = filename;
1552 args[1] = newname;
1553 report_file_error ("Adding new name", Flist (2, args));
1554#else
1555 report_file_error ("Adding new name", Flist (2, &filename));
1556#endif
1557 }
1558
1559 UNGCPRO;
1560 return Qnil;
1561}
1562
1563#ifdef S_IFLNK
1564DEFUN ("make-symbolic-link", Fmake_symbolic_link, Smake_symbolic_link, 2, 3,
1565 "FMake symbolic link to file: \nFMake symbolic link to file %s: \np",
1566 "Make a symbolic link to FILENAME, named LINKNAME. Both args strings.\n\
1567Signals a `file-already-exists' error if a file NEWNAME already exists\n\
1568unless optional third argument OK-IF-ALREADY-EXISTS is non-nil.\n\
1569A number as third arg means request confirmation if NEWNAME already exists.\n\
1570This happens for interactive use with M-x.")
1571 (filename, newname, ok_if_already_exists)
1572 Lisp_Object filename, newname, ok_if_already_exists;
1573{
1574#ifdef NO_ARG_ARRAY
1575 Lisp_Object args[2];
1576#endif
1577 struct gcpro gcpro1, gcpro2;
1578
1579 GCPRO2 (filename, newname);
1580 CHECK_STRING (filename, 0);
1581 CHECK_STRING (newname, 1);
1582#if 0 /* This made it impossible to make a link to a relative name. */
1583 filename = Fexpand_file_name (filename, Qnil);
1584#endif
1585 newname = Fexpand_file_name (newname, Qnil);
1586 if (NULL (ok_if_already_exists)
1587 || XTYPE (ok_if_already_exists) == Lisp_Int)
1588 barf_or_query_if_file_exists (newname, "make it a link",
1589 XTYPE (ok_if_already_exists) == Lisp_Int);
1590 if (0 > symlink (XSTRING (filename)->data, XSTRING (newname)->data))
1591 {
1592 /* If we didn't complain already, silently delete existing file. */
1593 if (errno == EEXIST)
1594 {
1595 unlink (XSTRING (filename)->data);
1596 if (0 <= symlink (XSTRING (filename)->data, XSTRING (newname)->data))
1597 return Qnil;
1598 }
1599
1600#ifdef NO_ARG_ARRAY
1601 args[0] = filename;
1602 args[1] = newname;
1603 report_file_error ("Making symbolic link", Flist (2, args));
1604#else
1605 report_file_error ("Making symbolic link", Flist (2, &filename));
1606#endif
1607 }
1608 UNGCPRO;
1609 return Qnil;
1610}
1611#endif /* S_IFLNK */
1612
1613#ifdef VMS
1614
1615DEFUN ("define-logical-name", Fdefine_logical_name, Sdefine_logical_name,
1616 2, 2, "sDefine logical name: \nsDefine logical name %s as: ",
1617 "Define the job-wide logical name NAME to have the value STRING.\n\
1618If STRING is nil or a null string, the logical name NAME is deleted.")
1619 (varname, string)
1620 Lisp_Object varname;
1621 Lisp_Object string;
1622{
1623 CHECK_STRING (varname, 0);
1624 if (NULL (string))
1625 delete_logical_name (XSTRING (varname)->data);
1626 else
1627 {
1628 CHECK_STRING (string, 1);
1629
1630 if (XSTRING (string)->size == 0)
1631 delete_logical_name (XSTRING (varname)->data);
1632 else
1633 define_logical_name (XSTRING (varname)->data, XSTRING (string)->data);
1634 }
1635
1636 return string;
1637}
1638#endif /* VMS */
1639
1640#ifdef HPUX_NET
1641
1642DEFUN ("sysnetunam", Fsysnetunam, Ssysnetunam, 2, 2, 0,
1643 "Open a network connection to PATH using LOGIN as the login string.")
1644 (path, login)
1645 Lisp_Object path, login;
1646{
1647 int netresult;
1648
1649 CHECK_STRING (path, 0);
1650 CHECK_STRING (login, 0);
1651
1652 netresult = netunam (XSTRING (path)->data, XSTRING (login)->data);
1653
1654 if (netresult == -1)
1655 return Qnil;
1656 else
1657 return Qt;
1658}
1659#endif /* HPUX_NET */
1660\f
1661DEFUN ("file-name-absolute-p", Ffile_name_absolute_p, Sfile_name_absolute_p,
1662 1, 1, 0,
1663 "Return t if file FILENAME specifies an absolute path name.\n\
1664On Unix, this is a name starting with a `/' or a `~'.")
1665 (filename)
1666 Lisp_Object filename;
1667{
1668 unsigned char *ptr;
1669
1670 CHECK_STRING (filename, 0);
1671 ptr = XSTRING (filename)->data;
1672 if (*ptr == '/' || *ptr == '~'
1673#ifdef VMS
1674/* ??? This criterion is probably wrong for '<'. */
1675 || index (ptr, ':') || index (ptr, '<')
1676 || (*ptr == '[' && (ptr[1] != '-' || (ptr[2] != '.' && ptr[2] != ']'))
1677 && ptr[1] != '.')
1678#endif /* VMS */
1679 )
1680 return Qt;
1681 else
1682 return Qnil;
1683}
1684
1685DEFUN ("file-exists-p", Ffile_exists_p, Sfile_exists_p, 1, 1, 0,
1686 "Return t if file FILENAME exists. (This does not mean you can read it.)\n\
1687See also `file-readable-p' and `file-attributes'.")
1688 (filename)
1689 Lisp_Object filename;
1690{
1691 Lisp_Object abspath;
1692
1693 CHECK_STRING (filename, 0);
1694 abspath = Fexpand_file_name (filename, Qnil);
1695 return (access (XSTRING (abspath)->data, 0) >= 0) ? Qt : Qnil;
1696}
1697
1698DEFUN ("file-executable-p", Ffile_executable_p, Sfile_executable_p, 1, 1, 0,
1699 "Return t if FILENAME can be executed by you.\n\
1700For directories this means you can change to that directory.")
1701 (filename)
1702 Lisp_Object filename;
1703
1704{
1705 Lisp_Object abspath;
1706
1707 CHECK_STRING (filename, 0);
1708 abspath = Fexpand_file_name (filename, Qnil);
1709 return (access (XSTRING (abspath)->data, 1) >= 0) ? Qt : Qnil;
1710}
1711
1712DEFUN ("file-readable-p", Ffile_readable_p, Sfile_readable_p, 1, 1, 0,
1713 "Return t if file FILENAME exists and you can read it.\n\
1714See also `file-exists-p' and `file-attributes'.")
1715 (filename)
1716 Lisp_Object filename;
1717{
1718 Lisp_Object abspath;
1719
1720 CHECK_STRING (filename, 0);
1721 abspath = Fexpand_file_name (filename, Qnil);
1722 return (access (XSTRING (abspath)->data, 4) >= 0) ? Qt : Qnil;
1723}
1724
1725DEFUN ("file-symlink-p", Ffile_symlink_p, Sfile_symlink_p, 1, 1, 0,
1726 "If file FILENAME is the name of a symbolic link\n\
1727returns the name of the file to which it is linked.\n\
1728Otherwise returns NIL.")
1729 (filename)
1730 Lisp_Object filename;
1731{
1732#ifdef S_IFLNK
1733 char *buf;
1734 int bufsize;
1735 int valsize;
1736 Lisp_Object val;
1737
1738 CHECK_STRING (filename, 0);
1739 filename = Fexpand_file_name (filename, Qnil);
1740
1741 bufsize = 100;
1742 while (1)
1743 {
1744 buf = (char *) xmalloc (bufsize);
1745 bzero (buf, bufsize);
1746 valsize = readlink (XSTRING (filename)->data, buf, bufsize);
1747 if (valsize < bufsize) break;
1748 /* Buffer was not long enough */
1749 free (buf);
1750 bufsize *= 2;
1751 }
1752 if (valsize == -1)
1753 {
1754 free (buf);
1755 return Qnil;
1756 }
1757 val = make_string (buf, valsize);
1758 free (buf);
1759 return val;
1760#else /* not S_IFLNK */
1761 return Qnil;
1762#endif /* not S_IFLNK */
1763}
1764
1765/* Having this before file-symlink-p mysteriously caused it to be forgotten
1766 on the RT/PC. */
1767DEFUN ("file-writable-p", Ffile_writable_p, Sfile_writable_p, 1, 1, 0,
1768 "Return t if file FILENAME can be written or created by you.")
1769 (filename)
1770 Lisp_Object filename;
1771{
1772 Lisp_Object abspath, dir;
1773
1774 CHECK_STRING (filename, 0);
1775 abspath = Fexpand_file_name (filename, Qnil);
1776 if (access (XSTRING (abspath)->data, 0) >= 0)
1777 return (access (XSTRING (abspath)->data, 2) >= 0) ? Qt : Qnil;
1778 dir = Ffile_name_directory (abspath);
1779#ifdef VMS
1780 if (!NULL (dir))
1781 dir = Fdirectory_file_name (dir);
1782#endif /* VMS */
1783 return (access (!NULL (dir) ? (char *) XSTRING (dir)->data : "", 2) >= 0
1784 ? Qt : Qnil);
1785}
1786
1787DEFUN ("file-directory-p", Ffile_directory_p, Sfile_directory_p, 1, 1, 0,
1788 "Return t if file FILENAME is the name of a directory as a file.\n\
1789A directory name spec may be given instead; then the value is t\n\
1790if the directory so specified exists and really is a directory.")
1791 (filename)
1792 Lisp_Object filename;
1793{
1794 register Lisp_Object abspath;
1795 struct stat st;
1796
1797 abspath = expand_and_dir_to_file (filename, current_buffer->directory);
1798
1799 if (stat (XSTRING (abspath)->data, &st) < 0)
1800 return Qnil;
1801 return (st.st_mode & S_IFMT) == S_IFDIR ? Qt : Qnil;
1802}
1803
1804DEFUN ("file-modes", Ffile_modes, Sfile_modes, 1, 1, 0,
1805 "Return mode bits of FILE, as an integer.")
1806 (filename)
1807 Lisp_Object filename;
1808{
1809 Lisp_Object abspath;
1810 struct stat st;
1811
1812 abspath = expand_and_dir_to_file (filename, current_buffer->directory);
1813
1814 if (stat (XSTRING (abspath)->data, &st) < 0)
1815 return Qnil;
1816 return make_number (st.st_mode & 07777);
1817}
1818
1819DEFUN ("set-file-modes", Fset_file_modes, Sset_file_modes, 2, 2, 0,
1820 "Set mode bits of FILE to MODE (an integer).\n\
1821Only the 12 low bits of MODE are used.")
1822 (filename, mode)
1823 Lisp_Object filename, mode;
1824{
1825 Lisp_Object abspath;
1826
1827 abspath = Fexpand_file_name (filename, current_buffer->directory);
1828 CHECK_NUMBER (mode, 1);
1829
1830#ifndef APOLLO
1831 if (chmod (XSTRING (abspath)->data, XINT (mode)) < 0)
1832 report_file_error ("Doing chmod", Fcons (abspath, Qnil));
1833#else /* APOLLO */
1834 if (!egetenv ("USE_DOMAIN_ACLS"))
1835 {
1836 struct stat st;
1837 struct timeval tvp[2];
1838
1839 /* chmod on apollo also change the file's modtime; need to save the
1840 modtime and then restore it. */
1841 if (stat (XSTRING (abspath)->data, &st) < 0)
1842 {
1843 report_file_error ("Doing chmod", Fcons (abspath, Qnil));
1844 return (Qnil);
1845 }
1846
1847 if (chmod (XSTRING (abspath)->data, XINT (mode)) < 0)
1848 report_file_error ("Doing chmod", Fcons (abspath, Qnil));
1849
1850 /* reset the old accessed and modified times. */
1851 tvp[0].tv_sec = st.st_atime + 1; /* +1 due to an Apollo roundoff bug */
1852 tvp[0].tv_usec = 0;
1853 tvp[1].tv_sec = st.st_mtime + 1; /* +1 due to an Apollo roundoff bug */
1854 tvp[1].tv_usec = 0;
1855
1856 if (utimes (XSTRING (abspath)->data, tvp) < 0)
1857 report_file_error ("Doing utimes", Fcons (abspath, Qnil));
1858 }
1859#endif /* APOLLO */
1860
1861 return Qnil;
1862}
1863
1864DEFUN ("file-newer-than-file-p", Ffile_newer_than_file_p, Sfile_newer_than_file_p, 2, 2, 0,
1865 "Return t if file FILE1 is newer than file FILE2.\n\
1866If FILE1 does not exist, the answer is nil;\n\
1867otherwise, if FILE2 does not exist, the answer is t.")
1868 (file1, file2)
1869 Lisp_Object file1, file2;
1870{
1871 Lisp_Object abspath;
1872 struct stat st;
1873 int mtime1;
1874
1875 CHECK_STRING (file1, 0);
1876 CHECK_STRING (file2, 0);
1877
1878 abspath = expand_and_dir_to_file (file1, current_buffer->directory);
1879
1880 if (stat (XSTRING (abspath)->data, &st) < 0)
1881 return Qnil;
1882
1883 mtime1 = st.st_mtime;
1884
1885 abspath = expand_and_dir_to_file (file2, current_buffer->directory);
1886
1887 if (stat (XSTRING (abspath)->data, &st) < 0)
1888 return Qt;
1889
1890 return (mtime1 > st.st_mtime) ? Qt : Qnil;
1891}
1892\f
1893close_file_unwind (fd)
1894 Lisp_Object fd;
1895{
1896 close (XFASTINT (fd));
1897}
1898
1899DEFUN ("insert-file-contents", Finsert_file_contents, Sinsert_file_contents,
1900 1, 2, 0,
1901 "Insert contents of file FILENAME after point.\n\
1902Returns list of absolute pathname and length of data inserted.\n\
1903If second argument VISIT is non-nil, the buffer's visited filename\n\
1904and last save file modtime are set, and it is marked unmodified.\n\
1905If visiting and the file does not exist, visiting is completed\n\
1906before the error is signaled.")
1907 (filename, visit)
1908 Lisp_Object filename, visit;
1909{
1910 struct stat st;
1911 register int fd;
1912 register int inserted = 0;
1913 register int how_much;
1914 int count = specpdl_ptr - specpdl;
1915 struct gcpro gcpro1;
1916
1917 GCPRO1 (filename);
1918 if (!NULL (current_buffer->read_only))
1919 Fbarf_if_buffer_read_only();
1920
1921 CHECK_STRING (filename, 0);
1922 filename = Fexpand_file_name (filename, Qnil);
1923
1924 fd = -1;
1925
1926#ifndef APOLLO
1927 if (stat (XSTRING (filename)->data, &st) < 0
1928 || (fd = open (XSTRING (filename)->data, 0)) < 0)
1929#else
1930 if ((fd = open (XSTRING (filename)->data, 0)) < 0
1931 || fstat (fd, &st) < 0)
1932#endif /* not APOLLO */
1933 {
1934 if (fd >= 0) close (fd);
1935 if (NULL (visit))
1936 report_file_error ("Opening input file", Fcons (filename, Qnil));
1937 st.st_mtime = -1;
1938 how_much = 0;
1939 goto notfound;
1940 }
1941
1942 record_unwind_protect (close_file_unwind, make_number (fd));
1943
1944 /* Supposedly happens on VMS. */
1945 if (st.st_size < 0)
1946 error ("File size is negative");
1947 {
1948 register Lisp_Object temp;
1949
1950 /* Make sure point-max won't overflow after this insertion. */
1951 XSET (temp, Lisp_Int, st.st_size + Z);
1952 if (st.st_size + Z != XINT (temp))
1953 error ("maximum buffer size exceeded");
1954 }
1955
1956 if (NULL (visit))
1957 prepare_to_modify_buffer (point, point);
1958
1959 move_gap (point);
1960 if (GAP_SIZE < st.st_size)
1961 make_gap (st.st_size - GAP_SIZE);
1962
1963 while (1)
1964 {
1965 int try = min (st.st_size - inserted, 64 << 10);
1966 int this = read (fd, &FETCH_CHAR (point + inserted - 1) + 1, try);
1967
1968 if (this <= 0)
1969 {
1970 how_much = this;
1971 break;
1972 }
1973
1974 GPT += this;
1975 GAP_SIZE -= this;
1976 ZV += this;
1977 Z += this;
1978 inserted += this;
1979 }
1980
1981 if (inserted > 0)
1982 MODIFF++;
1983 record_insert (point, inserted);
1984
1985 close (fd);
1986
1987 /* Discard the unwind protect */
1988 specpdl_ptr = specpdl + count;
1989
1990 if (how_much < 0)
1991 error ("IO error reading %s: %s",
1992 XSTRING (filename)->data, err_str (errno));
1993
1994 notfound:
1995
1996 if (!NULL (visit))
1997 {
1998 current_buffer->undo_list = Qnil;
1999#ifdef APOLLO
2000 stat (XSTRING (filename)->data, &st);
2001#endif
2002 current_buffer->modtime = st.st_mtime;
2003 current_buffer->save_modified = MODIFF;
2004 current_buffer->auto_save_modified = MODIFF;
2005 XFASTINT (current_buffer->save_length) = Z - BEG;
2006#ifdef CLASH_DETECTION
2007 if (!NULL (current_buffer->filename))
2008 unlock_file (current_buffer->filename);
2009 unlock_file (filename);
2010#endif /* CLASH_DETECTION */
2011 current_buffer->filename = filename;
2012 /* If visiting nonexistent file, return nil. */
2013 if (st.st_mtime == -1)
2014 report_file_error ("Opening input file", Fcons (filename, Qnil));
2015 }
2016
2017 signal_after_change (point, 0, inserted);
2018
9b7828a5
JB
2019 RETURN_UNGCPRO (Fcons (filename,
2020 Fcons (make_number (inserted),
2021 Qnil)));
570d7624
JB
2022}
2023
2024DEFUN ("write-region", Fwrite_region, Swrite_region, 3, 5,
2025 "r\nFWrite region to file: ",
2026 "Write current region into specified file.\n\
2027When called from a program, takes three arguments:\n\
2028START, END and FILENAME. START and END are buffer positions.\n\
2029Optional fourth argument APPEND if non-nil means\n\
2030 append to existing file contents (if any).\n\
2031Optional fifth argument VISIT if t means\n\
2032 set the last-save-file-modtime of buffer to this file's modtime\n\
2033 and mark buffer not modified.\n\
2034If VISIT is neither t nor nil, it means do not print\n\
2035 the \"Wrote file\" message.\n\
2036Kludgy feature: if START is a string, then that string is written\n\
2037to the file, instead of any buffer contents, and END is ignored.")
2038 (start, end, filename, append, visit)
2039 Lisp_Object start, end, filename, append, visit;
2040{
2041 register int desc;
2042 int failure;
2043 int save_errno;
2044 unsigned char *fn;
2045 struct stat st;
2046 int tem;
2047 int count = specpdl_ptr - specpdl;
2048#ifdef VMS
2049 unsigned char *fname = 0; /* If non-0, original filename (must rename) */
2050#endif /* VMS */
2051
2052 /* Special kludge to simplify auto-saving */
2053 if (NULL (start))
2054 {
2055 XFASTINT (start) = BEG;
2056 XFASTINT (end) = Z;
2057 }
2058 else if (XTYPE (start) != Lisp_String)
2059 validate_region (&start, &end);
2060
2061 filename = Fexpand_file_name (filename, Qnil);
2062 fn = XSTRING (filename)->data;
2063
2064#ifdef CLASH_DETECTION
2065 if (!auto_saving)
2066 lock_file (filename);
2067#endif /* CLASH_DETECTION */
2068
2069 desc = -1;
2070 if (!NULL (append))
2071 desc = open (fn, O_WRONLY);
2072
2073 if (desc < 0)
2074#ifdef VMS
2075 if (auto_saving) /* Overwrite any previous version of autosave file */
2076 {
2077 vms_truncate (fn); /* if fn exists, truncate to zero length */
2078 desc = open (fn, O_RDWR);
2079 if (desc < 0)
2080 desc = creat_copy_attrs (XTYPE (current_buffer->filename) == Lisp_String
2081 ? XSTRING (current_buffer->filename)->data : 0,
2082 fn);
2083 }
2084 else /* Write to temporary name and rename if no errors */
2085 {
2086 Lisp_Object temp_name;
2087 temp_name = Ffile_name_directory (filename);
2088
2089 if (!NULL (temp_name))
2090 {
2091 temp_name = Fmake_temp_name (concat2 (temp_name,
2092 build_string ("$$SAVE$$")));
2093 fname = XSTRING (filename)->data;
2094 fn = XSTRING (temp_name)->data;
2095 desc = creat_copy_attrs (fname, fn);
2096 if (desc < 0)
2097 {
2098 /* If we can't open the temporary file, try creating a new
2099 version of the original file. VMS "creat" creates a
2100 new version rather than truncating an existing file. */
2101 fn = fname;
2102 fname = 0;
2103 desc = creat (fn, 0666);
2104#if 0 /* This can clobber an existing file and fail to replace it,
2105 if the user runs out of space. */
2106 if (desc < 0)
2107 {
2108 /* We can't make a new version;
2109 try to truncate and rewrite existing version if any. */
2110 vms_truncate (fn);
2111 desc = open (fn, O_RDWR);
2112 }
2113#endif
2114 }
2115 }
2116 else
2117 desc = creat (fn, 0666);
2118 }
2119#else /* not VMS */
2120 desc = creat (fn, auto_saving ? auto_save_mode_bits : 0666);
2121#endif /* not VMS */
2122
2123 if (desc < 0)
2124 {
2125#ifdef CLASH_DETECTION
2126 save_errno = errno;
2127 if (!auto_saving) unlock_file (filename);
2128 errno = save_errno;
2129#endif /* CLASH_DETECTION */
2130 report_file_error ("Opening output file", Fcons (filename, Qnil));
2131 }
2132
2133 record_unwind_protect (close_file_unwind, make_number (desc));
2134
2135 if (!NULL (append))
2136 if (lseek (desc, 0, 2) < 0)
2137 {
2138#ifdef CLASH_DETECTION
2139 if (!auto_saving) unlock_file (filename);
2140#endif /* CLASH_DETECTION */
2141 report_file_error ("Lseek error", Fcons (filename, Qnil));
2142 }
2143
2144#ifdef VMS
2145/*
2146 * Kludge Warning: The VMS C RTL likes to insert carriage returns
2147 * if we do writes that don't end with a carriage return. Furthermore
2148 * it cannot handle writes of more then 16K. The modified
2149 * version of "sys_write" in SYSDEP.C (see comment there) copes with
2150 * this EXCEPT for the last record (iff it doesn't end with a carriage
2151 * return). This implies that if your buffer doesn't end with a carriage
2152 * return, you get one free... tough. However it also means that if
2153 * we make two calls to sys_write (a la the following code) you can
2154 * get one at the gap as well. The easiest way to fix this (honest)
2155 * is to move the gap to the next newline (or the end of the buffer).
2156 * Thus this change.
2157 *
2158 * Yech!
2159 */
2160 if (GPT > BEG && GPT_ADDR[-1] != '\n')
2161 move_gap (find_next_newline (GPT, 1));
2162#endif
2163
2164 failure = 0;
2165 immediate_quit = 1;
2166
2167 if (XTYPE (start) == Lisp_String)
2168 {
2169 failure = 0 > e_write (desc, XSTRING (start)->data,
2170 XSTRING (start)->size);
2171 save_errno = errno;
2172 }
2173 else if (XINT (start) != XINT (end))
2174 {
2175 if (XINT (start) < GPT)
2176 {
2177 register int end1 = XINT (end);
2178 tem = XINT (start);
2179 failure = 0 > e_write (desc, &FETCH_CHAR (tem),
2180 min (GPT, end1) - tem);
2181 save_errno = errno;
2182 }
2183
2184 if (XINT (end) > GPT && !failure)
2185 {
2186 tem = XINT (start);
2187 tem = max (tem, GPT);
2188 failure = 0 > e_write (desc, &FETCH_CHAR (tem), XINT (end) - tem);
2189 save_errno = errno;
2190 }
2191 }
2192
2193 immediate_quit = 0;
2194
2195#ifndef USG
2196#ifndef VMS
2197#ifndef BSD4_1
2198#ifndef alliant /* trinkle@cs.purdue.edu says fsync can return EBUSY
2199 on alliant, for no visible reason. */
2200 /* Note fsync appears to change the modtime on BSD4.2 (both vax and sun).
2201 Disk full in NFS may be reported here. */
2202 if (fsync (desc) < 0)
2203 failure = 1, save_errno = errno;
2204#endif
2205#endif
2206#endif
2207#endif
2208
2209 /* Spurious "file has changed on disk" warnings have been
2210 observed on Suns as well.
2211 It seems that `close' can change the modtime, under nfs.
2212
2213 (This has supposedly been fixed in Sunos 4,
2214 but who knows about all the other machines with NFS?) */
2215#if 0
2216
2217 /* On VMS and APOLLO, must do the stat after the close
2218 since closing changes the modtime. */
2219#ifndef VMS
2220#ifndef APOLLO
2221 /* Recall that #if defined does not work on VMS. */
2222#define FOO
2223 fstat (desc, &st);
2224#endif
2225#endif
2226#endif
2227
2228 /* NFS can report a write failure now. */
2229 if (close (desc) < 0)
2230 failure = 1, save_errno = errno;
2231
2232#ifdef VMS
2233 /* If we wrote to a temporary name and had no errors, rename to real name. */
2234 if (fname)
2235 {
2236 if (!failure)
2237 failure = (rename (fn, fname) != 0), save_errno = errno;
2238 fn = fname;
2239 }
2240#endif /* VMS */
2241
2242#ifndef FOO
2243 stat (fn, &st);
2244#endif
2245 /* Discard the unwind protect */
2246 specpdl_ptr = specpdl + count;
2247
2248#ifdef CLASH_DETECTION
2249 if (!auto_saving)
2250 unlock_file (filename);
2251#endif /* CLASH_DETECTION */
2252
2253 /* Do this before reporting IO error
2254 to avoid a "file has changed on disk" warning on
2255 next attempt to save. */
2256 if (EQ (visit, Qt))
2257 current_buffer->modtime = st.st_mtime;
2258
2259 if (failure)
2260 error ("IO error writing %s: %s", fn, err_str (save_errno));
2261
2262 if (EQ (visit, Qt))
2263 {
2264 current_buffer->save_modified = MODIFF;
2265 XFASTINT (current_buffer->save_length) = Z - BEG;
2266 current_buffer->filename = filename;
2267 }
2268 else if (!NULL (visit))
2269 return Qnil;
2270
2271 if (!auto_saving)
2272 message ("Wrote %s", fn);
2273
2274 return Qnil;
2275}
2276
2277int
2278e_write (desc, addr, len)
2279 int desc;
2280 register char *addr;
2281 register int len;
2282{
2283 char buf[16 * 1024];
2284 register char *p, *end;
2285
2286 if (!EQ (current_buffer->selective_display, Qt))
2287 return write (desc, addr, len) - len;
2288 else
2289 {
2290 p = buf;
2291 end = p + sizeof buf;
2292 while (len--)
2293 {
2294 if (p == end)
2295 {
2296 if (write (desc, buf, sizeof buf) != sizeof buf)
2297 return -1;
2298 p = buf;
2299 }
2300 *p = *addr++;
2301 if (*p++ == '\015')
2302 p[-1] = '\n';
2303 }
2304 if (p != buf)
2305 if (write (desc, buf, p - buf) != p - buf)
2306 return -1;
2307 }
2308 return 0;
2309}
2310
2311DEFUN ("verify-visited-file-modtime", Fverify_visited_file_modtime,
2312 Sverify_visited_file_modtime, 1, 1, 0,
2313 "Return t if last mod time of BUF's visited file matches what BUF records.\n\
2314This means that the file has not been changed since it was visited or saved.")
2315 (buf)
2316 Lisp_Object buf;
2317{
2318 struct buffer *b;
2319 struct stat st;
2320
2321 CHECK_BUFFER (buf, 0);
2322 b = XBUFFER (buf);
2323
2324 if (XTYPE (b->filename) != Lisp_String) return Qt;
2325 if (b->modtime == 0) return Qt;
2326
2327 if (stat (XSTRING (b->filename)->data, &st) < 0)
2328 {
2329 /* If the file doesn't exist now and didn't exist before,
2330 we say that it isn't modified, provided the error is a tame one. */
2331 if (errno == ENOENT || errno == EACCES || errno == ENOTDIR)
2332 st.st_mtime = -1;
2333 else
2334 st.st_mtime = 0;
2335 }
2336 if (st.st_mtime == b->modtime
2337 /* If both are positive, accept them if they are off by one second. */
2338 || (st.st_mtime > 0 && b->modtime > 0
2339 && (st.st_mtime == b->modtime + 1
2340 || st.st_mtime == b->modtime - 1)))
2341 return Qt;
2342 return Qnil;
2343}
2344
2345DEFUN ("clear-visited-file-modtime", Fclear_visited_file_modtime,
2346 Sclear_visited_file_modtime, 0, 0, 0,
2347 "Clear out records of last mod time of visited file.\n\
2348Next attempt to save will certainly not complain of a discrepancy.")
2349 ()
2350{
2351 current_buffer->modtime = 0;
2352 return Qnil;
2353}
2354
2355DEFUN ("set-visited-file-modtime", Fset_visited_file_modtime,
2356 Sset_visited_file_modtime, 0, 0, 0,
2357 "Update buffer's recorded modification time from the visited file's time.\n\
2358Useful if the buffer was not read from the file normally\n\
2359or if the file itself has been changed for some known benign reason.")
2360 ()
2361{
2362 register Lisp_Object filename;
2363 struct stat st;
2364
2365 filename = Fexpand_file_name (current_buffer->filename, Qnil);
2366
2367 if (stat (XSTRING (filename)->data, &st) >= 0)
2368 current_buffer->modtime = st.st_mtime;
2369
2370 return Qnil;
2371}
2372\f
2373Lisp_Object
2374auto_save_error ()
2375{
2376 unsigned char *name = XSTRING (current_buffer->name)->data;
2377
2378 ring_bell ();
2379 message ("Autosaving...error for %s", name);
2380 Fsleep_for (make_number (1));
2381 message ("Autosaving...error!for %s", name);
2382 Fsleep_for (make_number (1));
2383 message ("Autosaving...error for %s", name);
2384 Fsleep_for (make_number (1));
2385 return Qnil;
2386}
2387
2388Lisp_Object
2389auto_save_1 ()
2390{
2391 unsigned char *fn;
2392 struct stat st;
2393
2394 /* Get visited file's mode to become the auto save file's mode. */
2395 if (stat (XSTRING (current_buffer->filename)->data, &st) >= 0)
2396 /* But make sure we can overwrite it later! */
2397 auto_save_mode_bits = st.st_mode | 0600;
2398 else
2399 auto_save_mode_bits = 0666;
2400
2401 return
2402 Fwrite_region (Qnil, Qnil,
2403 current_buffer->auto_save_file_name,
2404 Qnil, Qlambda);
2405}
2406
2407DEFUN ("do-auto-save", Fdo_auto_save, Sdo_auto_save, 0, 2, "",
2408 "Auto-save all buffers that need it.\n\
2409This is all buffers that have auto-saving enabled\n\
2410and are changed since last auto-saved.\n\
2411Auto-saving writes the buffer into a file\n\
2412so that your editing is not lost if the system crashes.\n\
2413This file is not the file you visited; that changes only when you save.\n\n\
2414Non-nil first argument means do not print any message if successful.\n\
2415Non-nil second argumet means save only current buffer.")
2416 (nomsg)
2417 Lisp_Object nomsg;
2418{
2419 struct buffer *old = current_buffer, *b;
2420 Lisp_Object tail, buf;
2421 int auto_saved = 0;
2422 char *omessage = echo_area_glyphs;
2423 extern minibuf_level;
2424
2425 /* No GCPRO needed, because (when it matters) all Lisp_Object variables
2426 point to non-strings reached from Vbuffer_alist. */
2427
2428 auto_saving = 1;
2429 if (minibuf_level)
2430 nomsg = Qt;
2431
2432 /* Vrun_hooks is nil before emacs is dumped, and inc-vers.el will
2433 eventually call do-auto-save, so don't err here in that case. */
2434 if (!NULL (Vrun_hooks))
2435 call1 (Vrun_hooks, intern ("auto-save-hook"));
2436
2437 for (tail = Vbuffer_alist; XGCTYPE (tail) == Lisp_Cons;
2438 tail = XCONS (tail)->cdr)
2439 {
2440 buf = XCONS (XCONS (tail)->car)->cdr;
2441 b = XBUFFER (buf);
2442 /* Check for auto save enabled
2443 and file changed since last auto save
2444 and file changed since last real save. */
2445 if (XTYPE (b->auto_save_file_name) == Lisp_String
2446 && b->save_modified < BUF_MODIFF (b)
2447 && b->auto_save_modified < BUF_MODIFF (b))
2448 {
2449 if ((XFASTINT (b->save_length) * 10
2450 > (BUF_Z (b) - BUF_BEG (b)) * 13)
2451 /* A short file is likely to change a large fraction;
2452 spare the user annoying messages. */
2453 && XFASTINT (b->save_length) > 5000
2454 /* These messages are frequent and annoying for `*mail*'. */
2455 && !EQ (b->filename, Qnil))
2456 {
2457 /* It has shrunk too much; turn off auto-saving here. */
2458 message ("Buffer %s has shrunk a lot; auto save turned off there",
2459 XSTRING (b->name)->data);
2460 /* User can reenable saving with M-x auto-save. */
2461 b->auto_save_file_name = Qnil;
2462 /* Prevent warning from repeating if user does so. */
2463 XFASTINT (b->save_length) = 0;
2464 Fsleep_for (make_number (1));
2465 continue;
2466 }
2467 set_buffer_internal (b);
2468 if (!auto_saved && NULL (nomsg))
2469 message1 ("Auto-saving...");
2470 internal_condition_case (auto_save_1, Qt, auto_save_error);
2471 auto_saved++;
2472 b->auto_save_modified = BUF_MODIFF (b);
2473 XFASTINT (current_buffer->save_length) = Z - BEG;
2474 set_buffer_internal (old);
2475 }
2476 }
2477
2478 if (auto_saved)
2479 record_auto_save ();
2480
2481 if (auto_saved && NULL (nomsg))
2482 message1 (omessage ? omessage : "Auto-saving...done");
2483
2484 auto_saving = 0;
2485 return Qnil;
2486}
2487
2488DEFUN ("set-buffer-auto-saved", Fset_buffer_auto_saved,
2489 Sset_buffer_auto_saved, 0, 0, 0,
2490 "Mark current buffer as auto-saved with its current text.\n\
2491No auto-save file will be written until the buffer changes again.")
2492 ()
2493{
2494 current_buffer->auto_save_modified = MODIFF;
2495 XFASTINT (current_buffer->save_length) = Z - BEG;
2496 return Qnil;
2497}
2498
2499DEFUN ("recent-auto-save-p", Frecent_auto_save_p, Srecent_auto_save_p,
2500 0, 0, 0,
2501 "Return t if buffer has been auto-saved since last read in or saved.")
2502 ()
2503{
2504 return (current_buffer->save_modified < current_buffer->auto_save_modified) ? Qt : Qnil;
2505}
2506\f
2507/* Reading and completing file names */
2508extern Lisp_Object Ffile_name_completion (), Ffile_name_all_completions ();
2509
2510DEFUN ("read-file-name-internal", Fread_file_name_internal, Sread_file_name_internal,
2511 3, 3, 0,
2512 "Internal subroutine for read-file-name. Do not call this.")
2513 (string, dir, action)
2514 Lisp_Object string, dir, action;
2515 /* action is nil for complete, t for return list of completions,
2516 lambda for verify final value */
2517{
2518 Lisp_Object name, specdir, realdir, val, orig_string;
2519
2520 if (XSTRING (string)->size == 0)
2521 {
2522 orig_string = Qnil;
2523 name = string;
2524 realdir = dir;
2525 if (EQ (action, Qlambda))
2526 return Qnil;
2527 }
2528 else
2529 {
2530 orig_string = string;
2531 string = Fsubstitute_in_file_name (string);
2532 name = Ffile_name_nondirectory (string);
2533 realdir = Ffile_name_directory (string);
2534 if (NULL (realdir))
2535 realdir = dir;
2536 else
2537 realdir = Fexpand_file_name (realdir, dir);
2538 }
2539
2540 if (NULL (action))
2541 {
2542 specdir = Ffile_name_directory (string);
2543 val = Ffile_name_completion (name, realdir);
2544 if (XTYPE (val) != Lisp_String)
2545 {
2546 if (NULL (Fstring_equal (string, orig_string)))
2547 return string;
2548 return (val);
2549 }
2550
2551 if (!NULL (specdir))
2552 val = concat2 (specdir, val);
2553#ifndef VMS
2554 {
2555 register unsigned char *old, *new;
2556 register int n;
2557 int osize, count;
2558
2559 osize = XSTRING (val)->size;
2560 /* Quote "$" as "$$" to get it past substitute-in-file-name */
2561 for (n = osize, count = 0, old = XSTRING (val)->data; n > 0; n--)
2562 if (*old++ == '$') count++;
2563 if (count > 0)
2564 {
2565 old = XSTRING (val)->data;
2566 val = Fmake_string (make_number (osize + count), make_number (0));
2567 new = XSTRING (val)->data;
2568 for (n = osize; n > 0; n--)
2569 if (*old != '$')
2570 *new++ = *old++;
2571 else
2572 {
2573 *new++ = '$';
2574 *new++ = '$';
2575 old++;
2576 }
2577 }
2578 }
2579#endif /* Not VMS */
2580 return (val);
2581 }
2582
2583 if (EQ (action, Qt))
2584 return Ffile_name_all_completions (name, realdir);
2585 /* Only other case actually used is ACTION = lambda */
2586#ifdef VMS
2587 /* Supposedly this helps commands such as `cd' that read directory names,
2588 but can someone explain how it helps them? -- RMS */
2589 if (XSTRING (name)->size == 0)
2590 return Qt;
2591#endif /* VMS */
2592 return Ffile_exists_p (string);
2593}
2594
2595DEFUN ("read-file-name", Fread_file_name, Sread_file_name, 1, 5, 0,
2596 "Read file name, prompting with PROMPT and completing in directory DIR.\n\
2597Value is not expanded---you must call `expand-file-name' yourself.\n\
2598Default name to DEFAULT if user enters a null string.\n\
2599 (If DEFAULT is omitted, the visited file name is used.)\n\
2600Fourth arg MUSTMATCH non-nil means require existing file's name.\n\
2601 Non-nil and non-t means also require confirmation after completion.\n\
2602Fifth arg INITIAL specifies text to start with.\n\
2603DIR defaults to current buffer's directory default.")
2604 (prompt, dir, defalt, mustmatch, initial)
2605 Lisp_Object prompt, dir, defalt, mustmatch, initial;
2606{
2607 Lisp_Object val, insdef, tem, backup_n;
2608 struct gcpro gcpro1, gcpro2;
2609 register char *homedir;
2610 int count;
2611
2612 if (NULL (dir))
2613 dir = current_buffer->directory;
2614 if (NULL (defalt))
2615 defalt = current_buffer->filename;
2616
2617 /* If dir starts with user's homedir, change that to ~. */
2618 homedir = (char *) egetenv ("HOME");
2619 if (homedir != 0
2620 && XTYPE (dir) == Lisp_String
2621 && !strncmp (homedir, XSTRING (dir)->data, strlen (homedir))
2622 && XSTRING (dir)->data[strlen (homedir)] == '/')
2623 {
2624 dir = make_string (XSTRING (dir)->data + strlen (homedir) - 1,
2625 XSTRING (dir)->size - strlen (homedir) + 1);
2626 XSTRING (dir)->data[0] = '~';
2627 }
2628
2629 if (insert_default_directory)
2630 {
2631 insdef = dir;
2632 if (!NULL (initial))
2633 {
2634 Lisp_Object args[2];
2635
2636 args[0] = insdef;
2637 args[1] = initial;
2638 insdef = Fconcat (2, args);
2639 backup_n = make_number (- (XSTRING (initial)->size));
2640 }
2641 else
2642 backup_n = Qnil;
2643 }
2644 else
2645 {
2646 insdef = build_string ("");
2647 backup_n = Qnil;
2648 }
2649
2650#ifdef VMS
2651 count = specpdl_ptr - specpdl;
2652 specbind (intern ("completion-ignore-case"), Qt);
2653#endif
2654
2655 GCPRO2 (insdef, defalt);
2656 val = Fcompleting_read (prompt, intern ("read-file-name-internal"),
2657 dir, mustmatch,
2658 insert_default_directory ? insdef : Qnil, backup_n);
2659
2660#ifdef VMS
2661 unbind_to (count, Qnil);
2662#endif
2663
2664 UNGCPRO;
2665 if (NULL (val))
2666 error ("No file name specified");
2667 tem = Fstring_equal (val, insdef);
2668 if (!NULL (tem) && !NULL (defalt))
2669 return defalt;
2670 return Fsubstitute_in_file_name (val);
2671}
2672
2673#if 0 /* Old version */
2674DEFUN ("read-file-name", Fread_file_name, Sread_file_name, 1, 5, 0,
2675 "Read file name, prompting with PROMPT and completing in directory DIR.\n\
2676Value is not expanded---you must call `expand-file-name' yourself.\n\
2677Default name to DEFAULT if user enters a null string.\n\
2678 (If DEFAULT is omitted, the visited file name is used.)\n\
2679Fourth arg MUSTMATCH non-nil means require existing file's name.\n\
2680 Non-nil and non-t means also require confirmation after completion.\n\
2681Fifth arg INITIAL specifies text to start with.\n\
2682DIR defaults to current buffer's directory default.")
2683 (prompt, dir, defalt, mustmatch, initial)
2684 Lisp_Object prompt, dir, defalt, mustmatch, initial;
2685{
2686 Lisp_Object val, insdef, tem;
2687 struct gcpro gcpro1, gcpro2;
2688 register char *homedir;
2689 int count;
2690
2691 if (NULL (dir))
2692 dir = current_buffer->directory;
2693 if (NULL (defalt))
2694 defalt = current_buffer->filename;
2695
2696 /* If dir starts with user's homedir, change that to ~. */
2697 homedir = (char *) egetenv ("HOME");
2698 if (homedir != 0
2699 && XTYPE (dir) == Lisp_String
2700 && !strncmp (homedir, XSTRING (dir)->data, strlen (homedir))
2701 && XSTRING (dir)->data[strlen (homedir)] == '/')
2702 {
2703 dir = make_string (XSTRING (dir)->data + strlen (homedir) - 1,
2704 XSTRING (dir)->size - strlen (homedir) + 1);
2705 XSTRING (dir)->data[0] = '~';
2706 }
2707
2708 if (!NULL (initial))
2709 insdef = initial;
2710 else if (insert_default_directory)
2711 insdef = dir;
2712 else
2713 insdef = build_string ("");
2714
2715#ifdef VMS
2716 count = specpdl_ptr - specpdl;
2717 specbind (intern ("completion-ignore-case"), Qt);
2718#endif
2719
2720 GCPRO2 (insdef, defalt);
2721 val = Fcompleting_read (prompt, intern ("read-file-name-internal"),
2722 dir, mustmatch,
2723 insert_default_directory ? insdef : Qnil, Qnil);
2724
2725#ifdef VMS
2726 unbind_to (count, Qnil);
2727#endif
2728
2729 UNGCPRO;
2730 if (NULL (val))
2731 error ("No file name specified");
2732 tem = Fstring_equal (val, insdef);
2733 if (!NULL (tem) && !NULL (defalt))
2734 return defalt;
2735 return Fsubstitute_in_file_name (val);
2736}
2737#endif /* Old version */
2738\f
2739syms_of_fileio ()
2740{
2741 Qfile_error = intern ("file-error");
2742 staticpro (&Qfile_error);
2743 Qfile_already_exists = intern("file-already-exists");
2744 staticpro (&Qfile_already_exists);
2745
2746 Fput (Qfile_error, Qerror_conditions,
2747 Fcons (Qfile_error, Fcons (Qerror, Qnil)));
2748 Fput (Qfile_error, Qerror_message,
2749 build_string ("File error"));
2750
2751 Fput (Qfile_already_exists, Qerror_conditions,
2752 Fcons (Qfile_already_exists,
2753 Fcons (Qfile_error, Fcons (Qerror, Qnil))));
2754 Fput (Qfile_already_exists, Qerror_message,
2755 build_string ("File already exists"));
2756
2757 DEFVAR_BOOL ("insert-default-directory", &insert_default_directory,
2758 "*Non-nil means when reading a filename start with default dir in minibuffer.");
2759 insert_default_directory = 1;
2760
2761 DEFVAR_BOOL ("vms-stmlf-recfm", &vms_stmlf_recfm,
2762 "*Non-nil means write new files with record format `stmlf'.\n\
2763nil means use format `var'. This variable is meaningful only on VMS.");
2764 vms_stmlf_recfm = 0;
2765
2766 defsubr (&Sfile_name_directory);
2767 defsubr (&Sfile_name_nondirectory);
2768 defsubr (&Sfile_name_as_directory);
2769 defsubr (&Sdirectory_file_name);
2770 defsubr (&Smake_temp_name);
2771 defsubr (&Sexpand_file_name);
2772 defsubr (&Ssubstitute_in_file_name);
2773 defsubr (&Scopy_file);
2774 defsubr (&Smake_directory);
2775 defsubr (&Sremove_directory);
2776 defsubr (&Sdelete_file);
2777 defsubr (&Srename_file);
2778 defsubr (&Sadd_name_to_file);
2779#ifdef S_IFLNK
2780 defsubr (&Smake_symbolic_link);
2781#endif /* S_IFLNK */
2782#ifdef VMS
2783 defsubr (&Sdefine_logical_name);
2784#endif /* VMS */
2785#ifdef HPUX_NET
2786 defsubr (&Ssysnetunam);
2787#endif /* HPUX_NET */
2788 defsubr (&Sfile_name_absolute_p);
2789 defsubr (&Sfile_exists_p);
2790 defsubr (&Sfile_executable_p);
2791 defsubr (&Sfile_readable_p);
2792 defsubr (&Sfile_writable_p);
2793 defsubr (&Sfile_symlink_p);
2794 defsubr (&Sfile_directory_p);
2795 defsubr (&Sfile_modes);
2796 defsubr (&Sset_file_modes);
2797 defsubr (&Sfile_newer_than_file_p);
2798 defsubr (&Sinsert_file_contents);
2799 defsubr (&Swrite_region);
2800 defsubr (&Sverify_visited_file_modtime);
2801 defsubr (&Sclear_visited_file_modtime);
2802 defsubr (&Sset_visited_file_modtime);
2803 defsubr (&Sdo_auto_save);
2804 defsubr (&Sset_buffer_auto_saved);
2805 defsubr (&Srecent_auto_save_p);
2806
2807 defsubr (&Sread_file_name_internal);
2808 defsubr (&Sread_file_name);
2809}