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