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