Mercurial > hg > xemacs-beta
diff src/fileio.c @ 211:78478c60bfcd r20-4b4
Import from CVS: tag r20-4b4
author | cvs |
---|---|
date | Mon, 13 Aug 2007 10:05:51 +0200 |
parents | 41ff10fd062f |
children | 78f53ef88e17 |
line wrap: on
line diff
--- a/src/fileio.c Mon Aug 13 10:05:01 2007 +0200 +++ b/src/fileio.c Mon Aug 13 10:05:51 2007 +0200 @@ -20,6 +20,7 @@ Boston, MA 02111-1307, USA. */ /* Synched up with: Mule 2.0, FSF 19.30. */ +/* More syncing: FSF Emacs 19.34.6 by Marc Paquette <marcpa@cam.org> */ #include <config.h> #include "lisp.h" @@ -49,8 +50,34 @@ #include <netio.h> #ifdef HPUX_PRE_8_0 #include <errnet.h> +#endif /* HPUX_PRE_8_0 */ +#endif /* HPUX */ + +#ifdef WINDOWSNT +#define NOMINMAX 1 +#include <windows.h> +#include <stdlib.h> +#include <fcntl.h> +#endif /* not WINDOWSNT */ + +#ifdef DOS_NT +#define CORRECT_DIR_SEPS(s) \ + do { if ('/' == DIRECTORY_SEP) dostounix_filename (s); \ + else unixtodos_filename (s); \ + } while (0) +/* On Windows, drive letters must be alphabetic - on DOS, the Netware + redirector allows the six letters between 'Z' and 'a' as well. */ +#ifdef MSDOS +#define IS_DRIVE(x) ((x) >= 'A' && (x) <= 'z') #endif -#endif /* HPUX */ +#ifdef WINDOWSNT +#define IS_DRIVE(x) isalpha (x) +#endif +/* Need to lower-case the drive letter, or else expanded + filenames will sometimes compare inequal, because + `expand-file-name' doesn't always down-case the drive letter. */ +#define DRIVE_LETTER(x) (tolower (x)) +#endif /* DOS_NT */ /* Nonzero during writing of auto-save files */ static int auto_saving; @@ -86,6 +113,26 @@ Lisp_Object Qfile_name_handler_alist; +#ifdef DOS_NT +/* Until we can figure out how to deal with the functions in this file in + a civilized fashion, this will remain #ifdef'ed out. -slb */ +/* Syncing with FSF 19.34.6 note: although labelled as NT-specific, these + two lisp variables are compiled in even when not defined(DOS_NT). + Need to check if we should bracket them between #ifdef's. + --marcpa */ +/* On NT, specifies the directory separator character, used (eg.) when + expanding file names. This can be bound to / or \. + + This needs to be initialized statically, because file name functions + are called during initialization. */ +Lisp_Object Vdirectory_sep_char = '/'; + +/* For the benefit of backwards compatability with earlier versions of + Emacs on DOS_NT, provide a way to disable the REPLACE option support + in insert-file-contents. */ +Lisp_Object Vinsert_file_contents_allow_replace; +#endif /* DOS_NT */ + /* These variables describe handlers that have "already" had a chance to handle the current operation. @@ -415,8 +462,12 @@ beg = XSTRING_DATA (file); p = beg + XSTRING_LENGTH (file); - while (p != beg && !IS_ANY_SEP (p[-1])) - p--; + while (p != beg && !IS_ANY_SEP (p[-1]) +#ifdef DOS_NT + /* only recognise drive specifier at beginning */ + && !(p[-1] == ':' && p == beg + 2) +#endif + ) p--; if (p == beg) return Qnil; @@ -425,29 +476,17 @@ /* (NT does the right thing.) */ if (p == beg + 2 && beg[1] == ':') { - int drive = (*beg) - 'a'; /* MAXPATHLEN+1 is guaranteed to be enough space for getdefdir. */ - Bufbyte *res = (Bufbyte *) alloca (MAXPATHLEN + 5); - unsigned char *res1; -#ifdef WINDOWSNT - res1 = res; - /* The NT version places the drive letter at the beginning already. */ -#else /* not WINDOWSNT */ - /* On MSDOG we must put the drive letter in by hand. */ - res1 = res + 2; -#endif /* not WINDOWSNT */ - if (getdefdir (drive + 1, res)) + Bufbyte *res = alloca (MAXPATHLEN + 1); + if (getdefdir (toupper (*beg) - 'A' + 1, res)) { -#ifdef MSDOS - res[0] = drive + 'a'; - res[1] = ':'; -#endif /* MSDOS */ - if (IS_DIRECTORY_SEP (res[strlen ((char *) res) - 1])) + if (!IS_DIRECTORY_SEP (res[strlen ((char *) res) - 1])) strcat ((char *) res, "/"); beg = res; p = beg + strlen ((char *) beg); } } + CORRECT_DIR_SEPS (beg); #endif /* DOS_NT */ return make_string (beg, p - beg); } @@ -475,8 +514,12 @@ beg = XSTRING_DATA (file); end = p = beg + XSTRING_LENGTH (file); - while (p != beg && !IS_ANY_SEP (p[-1])) - p--; + while (p != beg && !IS_ANY_SEP (p[-1]) +#ifdef DOS_NT + /* only recognise drive specifier at beginning */ + && !(p[-1] == ':' && p == beg + 2) +#endif + ) p--; return make_string (p, end - p); } @@ -551,7 +594,7 @@ /* * Convert from directory name to filename. - * On UNIX, it's simple: just make sure there is a terminating / + * On UNIX, it's simple: just make sure there isn't a terminating / * Value is nonzero if the string output is different from the input. */ @@ -575,10 +618,13 @@ && IS_DIRECTORY_SEP (dst[slen - 1]) #ifdef DOS_NT && !IS_ANY_SEP (dst[slen - 2]) -#endif +#endif /* DOS_NT */ ) dst[slen - 1] = 0; -#endif +#endif /* APOLLO */ +#ifdef DOS_NT + CORRECT_DIR_SEPS (dst); +#endif /* DOS_NT */ return 1; } @@ -633,6 +679,9 @@ /* !!#### does mktemp() Mule-encapsulate? */ mktemp ((char *) data); +#ifdef DOS_NT + CORRECT_DIR_SEPS (XSTRING_DATA (val)); +#endif /* DOS_NT */ return val; } @@ -659,10 +708,9 @@ Bufbyte *target; struct passwd *pw; #ifdef DOS_NT - /* Demacs 1.1.2 91/10/20 Manabu Higashida */ - int drive = -1; - int relpath = 0; - Bufbyte *tmp, *defdir; + int drive = 0; + int collapse_newdir = 1; + int length; #endif /* DOS_NT */ Lisp_Object handler; @@ -712,10 +760,28 @@ The EQ test avoids infinite recursion. */ if (! NILP(default_) && !EQ (default_, name) - /* This saves time in a common case. */ + /* Save time in some common cases - as long as default_directory + is not relative, it can be canonicalized with name below (if it + is needed at all) without requiring it to be expanded now. */ && ! (XSTRING_LENGTH (default_) >= 3 +#ifdef DOS_NT + /* Detect MSDOS file names with drive specifiers. */ + && (IS_DRIVE (XSTRING_BYTE (default_, 0)) + && (IS_DEVICE_SEP (XSTRING_BYTE (default_, 1)) + IS_DIRECTORY_SEP (XSTRING_BYTE (default_, 2))))) +#ifdef WINDOWSNT + /* Detect Windows file names in UNC format. */ + && ! (XSTRING_LENGTH (default_) >= 2 + && IS_DIRECTORY_SEP (XSTRING_BYTE (default_, 0)) + && IS_DIRECTORY_SEP (XSTRING_BYTE (default_, 1))) +#endif +#else /* not DOS_NT */ + /* Detect Unix absolute file names (/... alone is not absolute on + DOS or Windows). */ && (IS_DIRECTORY_SEP (XSTRING_BYTE (default_, 0)) - || IS_DEVICE_SEP (XSTRING_BYTE (default_, 1))))) + || IS_DEVICE_SEP (XSTRING_BYTE (default_, 1)))) +#endif /* not DOS_NT */ + ) { struct gcpro gcpro1; @@ -732,29 +798,45 @@ pointing into name should be safe during all of this, though. */ nm = XSTRING_DATA (name); -#ifdef MSDOS - /* First map all backslashes to slashes. */ - dostounix_filename (nm = strcpy (alloca (strlen (nm) + 1), nm)); -#endif - #ifdef DOS_NT - /* Now strip drive name. */ + /* We will force directory separators to be either all \ or /, so make + a local copy to modify, even if there ends up being no change. */ + nm = strcpy (alloca (strlen (nm) + 1), nm); + + /* Find and remove drive specifier if present; this makes nm absolute + even if the rest of the name appears to be relative. */ { Bufbyte *colon = strrchr (nm, ':'); if (colon) + /* Only recognize colon as part of drive specifier if there is a + single alphabetic character preceeding the colon (and if the + character before the drive letter, if present, is a directory + separator); this is to support the remote system syntax used by + ange-ftp, and the "po:username" syntax for POP mailboxes. */ + look_again: if (nm == colon) nm++; - else + else if (IS_DRIVE (colon[-1]) + && (colon == nm + 1 || IS_DIRECTORY_SEP (colon[-2]))) { drive = colon[-1]; nm = colon + 1; - if (!IS_DIRECTORY_SEP (*nm)) - { - defdir = alloca (MAXPATHLEN + 1); - relpath = getdefdir (tolower (drive) - 'a' + 1, defdir); - } + } + else + { + while (--colon >= nm) + if (colon[0] == ':') + goto look_again; } } + +#ifdef WINDOWSNT + /* If we see "c://somedir", we want to strip the first slash after the + colon when stripping the drive letter. Otherwise, this expands to + "//somedir". */ + if (drive && IS_DIRECTORY_SEP (nm[0]) && IS_DIRECTORY_SEP (nm[1])) + nm++; +#endif /* WINDOWSNT */ #endif /* DOS_NT */ /* We *don't* want to handle // and /~ that way. */ @@ -784,11 +866,25 @@ p++; } + +#endif /* 0 */ + +#ifdef WINDOWSNT + /* Discard any previous drive specifier if nm is now in UNC format. */ + if (IS_DIRECTORY_SEP (nm[0]) && IS_DIRECTORY_SEP (nm[1])) + { + drive = 0; + } +#endif /* WINDOWSNT */ + + /* If nm is absolute, look for /./ or /../ sequences; if none are + found, we can probably return right away. We will avoid allocating + a new string if name is already fully expanded. */ + if (IS_DIRECTORY_SEP (nm[0]) +#ifdef WINDOWSNT + && (drive || IS_DIRECTORY_SEP (nm[1])) #endif - - /* If nm is absolute, flush ...// and detect /./ and /../. - If no /./ or /../ we can return right away. */ - if (IS_DIRECTORY_SEP (nm[0])) + ) { /* If it turns out that the filename we want to return is just a suffix of FILENAME, we don't need to go through and edit @@ -816,15 +912,51 @@ } if (!lose) { -#ifndef DOS_NT +#ifdef DOS_NT + /* Make sure directories are all separated with / or \ as + desired, but avoid allocation of a new string when not + required. */ + CORRECT_DIR_SEPS (nm); +#ifdef WINDOWSNT + if (IS_DIRECTORY_SEP (nm[1])) + { + if (strcmp (nm, XSTRING_DATA (name)) != 0) + name = build_string (nm); + } + else +#endif + /* drive must be set, so this is okay */ + if (strcmp (nm - 2, XSTRING_DATA (name)) != 0) + { + name = make_string (nm - 2, p - nm + 2); + XSTRING_DATA (name)[0] = DRIVE_LETTER (drive); + XSTRING_DATA (name)[1] = ':'; + } + return name; +#else /* not DOS_NT */ + /* Unix */ if (nm == XSTRING_DATA (name)) return name; return build_string ((char *) nm); -#endif /* not DOS_NT */ +#endif /* DOS_NT */ } } - /* Now determine directory to start with and put it in newdir */ + /* At this point, nm might or might not be an absolute file name. We + need to expand ~ or ~user if present, otherwise prefix nm with + default_directory if nm is not absolute, and finally collapse /./ + and /foo/../ sequences. + + We set newdir to be the appropriate prefix if one is needed: + - the relevant user directory if nm starts with ~ or ~user + - the specified drive's working dir (DOS/NT only) if nm does not + start with / + - the value of default_directory. + + Note that these prefixes are not guaranteed to be absolute (except + for the working dir of a drive). Therefore, to ensure we always + return an absolute name, if the final prefix is not absolute we + append it to the current working directory. */ newdir = 0; @@ -835,6 +967,10 @@ { if (!(newdir = (Bufbyte *) egetenv ("HOME"))) newdir = (Bufbyte *) ""; +/* Syncing with FSF 19.34.6 note: this is not in FSF. Since it is dated 1995, + I doubt it is coming from XEmacs. I (#if 0) it but let the code + stay there just in case. --marcpa */ +#if 0 #ifdef DOS_NT /* Problem when expanding "~\" if HOME is not on current drive. Ulrich Leodolter, Wed Jan 11 10:20:35 1995 */ @@ -842,7 +978,11 @@ drive = newdir[0]; dostounix_filename (newdir); #endif /* DOS_NT */ +#endif /* 0 */ nm++; +#ifdef DOS_NT + collapse_newdir = 0; +#endif /* DOS_NT */ } else /* ~user/filename */ { @@ -851,6 +991,12 @@ memcpy (o, (char *) nm, p - nm); o [p - nm] = 0; +/* Syncing with FSF 19.34.6 note: FSF uses getpwnam even on NT, which does + not work. The following works only if ~USER names the user who runs + this instance of XEmacs. While NT is single-user (for the moment) you + still can have multiple user profiles users defined, each with its + HOME. Therefore, the following should be reworked to handle this case. + --marcpa */ #ifdef WINDOWSNT /* ** Now if the file given is "~foo/file" and HOME="c:/", then we @@ -858,7 +1004,7 @@ ** The variable o has "~foo", so we can use the length of ** that string to offset nm. August Hill, 31 Aug 1998. */ - newdir = (unsigned char *) egetenv ("HOME"); + newdir = (Bufbyte *) egetenv ("HOME"); dostounix_filename (newdir); nm += strlen(o) + 1; #else /* not WINDOWSNT */ @@ -879,10 +1025,39 @@ } } - if (!IS_ANY_SEP (nm[0]) #ifdef DOS_NT - && drive == -1 + /* On DOS and Windows, nm is absolute if a drive name was specified; + use the drive's current directory as the prefix if needed. */ + if (!newdir && drive) + { + /* Get default directory if needed to make nm absolute. */ + if (!IS_DIRECTORY_SEP (nm[0])) + { + newdir = alloca (MAXPATHLEN + 1); + if (!getdefdir (toupper (drive) - 'A' + 1, newdir)) + newdir = NULL; + } + if (!newdir) + { + /* Either nm starts with /, or drive isn't mounted. */ + newdir = alloca (4); + newdir[0] = DRIVE_LETTER (drive); + newdir[1] = ':'; + newdir[2] = '/'; + newdir[3] = 0; + } + } #endif /* DOS_NT */ + + /* Finally, if no prefix has been specified and nm is not absolute, + then it must be expanded relative to default_directory. */ + if (1 +#ifndef DOS_NT + && !IS_ANY_SEP (nm[0]) +#endif /* not DOS_NT */ +#ifdef WINDOWSNT + && !(IS_DIRECTORY_SEP (nm[0]) && IS_DIRECTORY_SEP (nm[1])) +#endif && !newdir && STRINGP (default_)) { @@ -890,20 +1065,88 @@ } #ifdef DOS_NT - if (newdir == 0 && relpath) - newdir = defdir; + if (newdir) + { + /* First ensure newdir is an absolute name. */ + if ( + /* Detect MSDOS file names with drive specifiers. */ + ! (IS_DRIVE (newdir[0]) + && IS_DEVICE_SEP (newdir[1]) && IS_DIRECTORY_SEP (newdir[2])) +#ifdef WINDOWSNT + /* Detect Windows file names in UNC format. */ + && ! (IS_DIRECTORY_SEP (newdir[0]) && IS_DIRECTORY_SEP (newdir[1])) +#endif + ) + { + /* Effectively, let newdir be (expand-file-name newdir cwd). + Because of the admonition against calling expand-file-name + when we have pointers into lisp strings, we accomplish this + indirectly by prepending newdir to nm if necessary, and using + cwd (or the wd of newdir's drive) as the new newdir. */ + + if (IS_DRIVE (newdir[0]) && newdir[1] == ':') + { + drive = newdir[0]; + newdir += 2; + } + if (!IS_DIRECTORY_SEP (nm[0])) + { + char * tmp = alloca (strlen (newdir) + strlen (nm) + 2); + file_name_as_directory (tmp, newdir); + strcat (tmp, nm); + nm = tmp; + } + newdir = alloca (MAXPATHLEN + 1); + if (drive) + { + if (!getdefdir (toupper (drive) - 'A' + 1, newdir)) + newdir = "/"; + } + else + getwd (newdir); + } + + /* Strip off drive name from prefix, if present. */ + if (IS_DRIVE (newdir[0]) && newdir[1] == ':') + { + drive = newdir[0]; + newdir += 2; + } + + /* Keep only a prefix from newdir if nm starts with slash + (//server/share for UNC, nothing otherwise). */ + if (IS_DIRECTORY_SEP (nm[0]) && collapse_newdir) + { +#ifdef WINDOWSNT + if (IS_DIRECTORY_SEP (newdir[0]) && IS_DIRECTORY_SEP (newdir[1])) + { + newdir = strcpy (alloca (strlen (newdir) + 1), newdir); + p = newdir + 2; + while (*p && !IS_DIRECTORY_SEP (*p)) p++; + p++; + while (*p && !IS_DIRECTORY_SEP (*p)) p++; + *p = 0; + } + else +#endif + newdir = ""; + } + } #endif /* DOS_NT */ if (newdir != 0) { - /* Get rid of any slash at the end of newdir. */ + /* Get rid of any slash at the end of newdir, unless newdir is + just // (an incomplete UNC name). */ int length = strlen ((char *) newdir); /* Adding `length > 1 &&' makes ~ expand into / when homedir is the root dir. People disagree about whether that is right. Anyway, we can't take the risk of this change now. */ -#ifdef DOS_NT - if (newdir[1] != ':' && length > 1) + /* Syncing with FSF 19.34.6 note: FSF does the above. */ + if (IS_DIRECTORY_SEP (newdir[length - 1]) +#ifdef WINDOWSNT + && !(length == 2 && IS_DIRECTORY_SEP (newdir[0])) #endif - if (IS_DIRECTORY_SEP (newdir[length - 1])) + ) { Bufbyte *temp = (Bufbyte *) alloca (length); memcpy (temp, newdir, length - 1); @@ -924,7 +1167,7 @@ target += 2; #else /* not DOS_NT */ target = (Bufbyte *) alloca (tlen); -#endif /* not DOS_NT */ +#endif /* DOS_NT */ *target = 0; if (newdir) @@ -953,7 +1196,7 @@ /* // at start of filename is meaningful in Apollo and WindowsNT systems */ && o != target -#endif /* APOLLO */ +#endif /* APOLLO || WINDOWSNT */ ) { o = target; @@ -977,13 +1220,11 @@ { while (o != target && (--o) && !IS_DIRECTORY_SEP (*o)) ; -#if defined (APOLLO) || defined (WINDOWSNT) - if (o == target + 1 - && IS_DIRECTORY_SEP (o[-1]) && IS_DIRECTORY_SEP (o[0])) - ++o; - else -#endif /* APOLLO || WINDOWSNT */ - if (o == target && IS_ANY_SEP (*o)) + if (o == target && IS_ANY_SEP (*o) +#ifdef DOS_NT + && p[3] == 0 +#endif + ) ++o; p += 3; } @@ -994,18 +1235,18 @@ } #ifdef DOS_NT - /* at last, set drive name. */ - if (target[1] != ':' + /* At last, set drive name. */ #ifdef WINDOWSNT - /* Allow network paths that look like "\\foo" */ - && !(IS_DIRECTORY_SEP (target[0]) && IS_DIRECTORY_SEP (target[1])) + /* Except for network file name. */ + if (!(IS_DIRECTORY_SEP (target[0]) && IS_DIRECTORY_SEP (target[1]))) #endif /* WINDOWSNT */ - ) { + if (!drive) abort (); target -= 2; - target[0] = (drive < 0 ? getdisk () + 'A' : drive); + target[0] = DRIVE_LETTER (drive); target[1] = ':'; } + CORRECT_DIR_SEPS (target); #endif /* DOS_NT */ return make_string (target, o - target); @@ -1165,9 +1406,10 @@ string); nm = XSTRING_DATA (string); -#ifdef MSDOS - dostounix_filename (nm = strcpy (alloca (strlen (nm) + 1), nm)); - substituted = !strcmp (nm, XSTRING_DATA (string)); +#ifdef DOS_NT + nm = strcpy (alloca (strlen (nm) + 1), nm); + CORRECT_DIR_SEPS (nm); + substituted = (strcmp (nm, XSTRING_DATA (string)) != 0); #endif endp = nm + XSTRING_LENGTH (string); @@ -1175,17 +1417,14 @@ for (p = nm; p != endp; p++) { - if ((p[0] == '~' || -#ifdef APOLLO - /* // at start of file name is meaningful in Apollo system */ - (p[0] == '/' && p - 1 != nm) -#else /* not APOLLO */ -#ifdef WINDOWSNT - (IS_DIRECTORY_SEP (p[0]) && p - 1 != nm) -#else /* not WINDOWSNT */ - p[0] == '/' -#endif /* not WINDOWSNT */ -#endif /* not APOLLO */ + if ((p[0] == '~' +#if defined (APOLLO) || defined (WINDOWSNT) + /* // at start of file name is meaningful in Apollo and + WindowsNT systems */ + || (IS_DIRECTORY_SEP (p[0]) && p - 1 != nm) +#else /* not (APOLLO || WINDOWSNT) */ + || IS_DIRECTORY_SEP (p[0]) +#endif /* not (APOLLO || WINDOWSNT) */ ) && p != nm && (IS_DIRECTORY_SEP (p[-1]))) @@ -1194,7 +1433,9 @@ substituted = 1; } #ifdef DOS_NT - if (p[0] && p[1] == ':') + /* see comment in expand-file-name about drive specifiers */ + else if (IS_DRIVE (p[0]) && p[1] == ':' + && p > nm && IS_DIRECTORY_SEP (p[-1])) { nm = p; substituted = 1; @@ -1309,22 +1550,18 @@ for (p = xnm; p != x; p++) if ((p[0] == '~' -#ifdef APOLLO - /* // at start of file name is meaningful in Apollo system */ - || (p[0] == '/' && p - 1 != xnm) -#else /* not APOLLO */ -#ifdef WINDOWSNT +#if defined (APOLLO) || defined (WINDOWSNT) || (IS_DIRECTORY_SEP (p[0]) && p - 1 != xnm) -#else /* not WINDOWSNT */ - || p[0] == '/' -#endif /* not WINDOWSNT */ -#endif /* not APOLLO */ +#else /* not (APOLLO || WINDOWSNT) */ + || IS_DIRECTORY_SEP (p[0]) +#endif /* APOLLO || WINDOWSNT */ ) /* don't do p[-1] if that would go off the beginning --jwz */ && p != nm && p > xnm && IS_DIRECTORY_SEP (p[-1])) xnm = p; #ifdef DOS_NT - else if (p[0] && p[1] == ':') + else if (IS_DRIVE (p[0]) && p[1] == ':' + && p > nm && IS_DIRECTORY_SEP (p[-1])) xnm = p; #endif @@ -1342,7 +1579,8 @@ return Qnil; /* suppress compiler warning */ } -/* (directory-file-name (expand-file-name FOO)) */ +/* A slightly faster and more convenient way to get + (directory-file-name (expand-file-name FOO)). */ Lisp_Object expand_and_dir_to_file (Lisp_Object filename, Lisp_Object defdir) @@ -1752,9 +1990,9 @@ barf_or_query_if_file_exists (newname, "rename to it", INTP (ok_if_already_exists), 0); -#ifdef WINDOWSNT - if (!MoveFile (XSTRING (filename)->_data, XSTRING (newname)->_data)) -#else /* not WINDOWSNT */ +/* Syncing with FSF 19.34.6 note: FSF does not have conditional code for + WINDOWSNT here; I've removed it. --marcpa */ + /* FSFmacs only calls rename() here under BSD 4.1, and calls link() and unlink() otherwise, but that's bogus. Sometimes rename() succeeds where link()/unlink() fail, and we have @@ -1762,15 +2000,8 @@ if necessary. */ if (0 > rename ((char *) XSTRING_DATA (filename), (char *) XSTRING_DATA (newname))) -#endif /* not WINDOWSNT */ { -#ifdef WINDOWSNT - /* Why two? And why doesn't MS document what MoveFile will return? */ - if (GetLastError () == ERROR_FILE_EXISTS - || GetLastError () == ERROR_ALREADY_EXISTS) -#else /* not WINDOWSNT */ if (errno == EXDEV) -#endif /* not WINDOWSNT */ { Fcopy_file (filename, newname, /* We have already prompted if it was an integer, @@ -1826,10 +2057,12 @@ || INTP (ok_if_already_exists)) barf_or_query_if_file_exists (newname, "make it a new name", INTP (ok_if_already_exists), 0); -#ifdef WINDOWSNT +/* Syncing with FSF 19.34.6 note: FSF does not report a file error + on NT here. --marcpa */ +#if 0 /* defined(WINDOWSNT) */ /* Windows does not support this operation. */ report_file_error ("Adding new name", Flist (2, &filename)); -#else /* not WINDOWSNT */ +#else /* not 0 -- defined(WINDOWSNT) */ unlink ((char *) XSTRING_DATA (newname)); if (0 > link ((char *) XSTRING_DATA (filename), @@ -1838,7 +2071,7 @@ report_file_error ("Adding new name", list2 (filename, newname)); } -#endif /* not WINDOWSNT */ +#endif /* 0 -- defined(WINDOWSNT) */ UNGCPRO; return Qnil; @@ -1865,7 +2098,7 @@ /* If the link target has a ~, we must expand it to get a truly valid file name. Otherwise, do not expand; we want to permit links to relative file names. */ - if (XSTRING_BYTE (filename, 0) == '~') /* #### Un*x-specific */ + if (XSTRING_BYTE (filename, 0) == '~') filename = Fexpand_file_name (filename, Qnil); linkname = Fexpand_file_name (linkname, Qnil); @@ -1944,7 +2177,7 @@ ptr = XSTRING_DATA (filename); if (IS_DIRECTORY_SEP (*ptr) || *ptr == '~' #ifdef DOS_NT - || (*ptr != 0 && ptr[1] == ':' && (ptr[2] == '/' || ptr[2] == '\\')) + || (IS_DRIVE (*ptr) && ptr[1] == ':' && IS_DIRECTORY_SEP (ptr[2])) #endif ) return Qt; @@ -1963,12 +2196,16 @@ struct stat st; if (stat (filename, &st) < 0) return 0; +#if defined (WINDOWSNT) + return ((st.st_mode & S_IEXEC) != 0); +#else return (S_ISREG (st.st_mode) && len >= 5 && (stricmp ((suffix = filename + len-4), ".com") == 0 || stricmp (suffix, ".exe") == 0 || stricmp (suffix, ".bat") == 0) || (st.st_mode & S_IFMT) == S_IFDIR); +#endif /* not WINDOWSNT */ #else /* not DOS_NT */ #ifdef HAVE_EACCESS return eaccess (filename, 1) >= 0; @@ -2084,11 +2321,18 @@ if (!NILP (handler)) return call2 (handler, Qfile_readable_p, abspath); +#ifdef DOS_NT + /* Under MS-DOS and Windows, open does not work for directories. */ + if (access (XSTRING_DATA (abspath), 0) == 0) + return Qt; + return Qnil; +#else /* not DOS_NT */ desc = open ((char *) XSTRING_DATA (abspath), O_RDONLY, 0); if (desc < 0) return Qnil; close (desc); return Qt; +#endif /* not DOS_NT */ } /* Having this before file-symlink-p mysteriously caused it to be forgotten @@ -2238,10 +2482,28 @@ return call2 (handler, Qfile_accessible_directory_p, filename); +#if !defined(DOS_NT) if (NILP (Ffile_directory_p (filename))) return (Qnil); else return Ffile_executable_p (filename); +#else + { + int tem; + struct gcpro gcpro1; + /* It's an unlikely combination, but yes we really do need to gcpro: + Suppose that file-accessible-directory-p has no handler, but + file-directory-p does have a handler; this handler causes a GC which + relocates the string in `filename'; and finally file-directory-p + returns non-nil. Then we would end up passing a garbaged string + to file-executable-p. */ + GCPRO1 (filename); + tem = (NILP (Ffile_directory_p (filename)) + || NILP (Ffile_executable_p (filename))); + UNGCPRO; + return tem ? Qnil : Qt; + } +#endif /* !defined(DOS_NT) */ } DEFUN ("file-regular-p", Ffile_regular_p, 1, 1, 0, /* @@ -2299,10 +2561,13 @@ if (stat ((char *) XSTRING_DATA (abspath), &st) < 0) return Qnil; + /* Syncing with FSF 19.34.6 note: not in FSF, #if 0'ed out here. */ +#if 0 #ifdef DOS_NT if (check_executable (XSTRING (abspath)->_data)) st.st_mode |= S_IEXEC; #endif /* DOS_NT */ +#endif /* 0 */ return make_int (st.st_mode & 07777); } @@ -2430,6 +2695,56 @@ #ifdef DOS_NT Lisp_Object Qfind_buffer_file_type; + +/* Return 1 if buffer is text, 0 if binary. */ +static int +decide_buffer_type (unsigned char * buffer, int nbytes) +{ + /* Buffer is binary if we find any LF chars not preceeded by CR or if + the buffer doesn't contain at least 1 line. */ + unsigned lines = 0; + unsigned char *p, *q; + + for (p = buffer; nbytes > 0 && (q = memchr (p, '\n', nbytes)) != NULL; + p = q + 1 ) + { + nbytes -= (q + 1 - p); + lines++; + if (q > buffer && q[-1] != '\r') + return 0; + } + + /* If we haven't seen any line endings yet, return -1 (meaning type is + undecided) so we can examine the next bufferful as well. */ + return (lines > 0) ? 1 : -1; +} + +/* XEmacs addition: like decide_buffer_type(), but working on a XEmacs buffer: + first arg is a byte index position instead of a char pointer; + we check each char sequentially. --marcpa */ +static int +buf_decide_buffer_type (struct buffer *buf, Bytind start, int nbytes) +{ + /* Buffer is binary if we find any LF chars not preceeded by CR or if + the buffer doesn't contain at least 1 line. */ + unsigned lines = 0; + Bytind cur = start; + + while (nbytes) + { + if (BI_BUF_FETCH_CHAR(buf, cur) == '\n') + { + lines++; + if (cur != start && BI_BUF_FETCH_CHAR(buf, cur - 1) != '\r') + return 0; + } + nbytes--; + } + + /* If we haven't seen any line endings yet, return -1 (meaning type is + undecided) so we can examine the next bufferful as well. */ + return (lines > 0) ? 1 : -1; +} #endif /* DOS_NT */ /* Stack sizes > 2**16 is a good way to elicit compiler bugs */ @@ -2470,6 +2785,11 @@ struct buffer *buf = current_buffer; Lisp_Object curbuf; int not_regular = 0; +#ifdef DOS_NT + int crlf_conversion_required = 0; + unsigned crlf_count = 0; + unsigned lf_count = 0; +#endif if (buf->base_buffer && ! NILP (visit)) error ("Cannot do file visiting in an indirect buffer"); @@ -2586,17 +2906,30 @@ if (XINT (end) != st.st_size) error ("Maximum buffer size exceeded"); } + +#ifdef DOS_NT + /* Permit old behaviour if desired. */ + if (NILP (Vinsert_file_contents_allow_replace) && !NILP (replace)) + { + replace = Qnil; + /* Surely this was never right! */ + /* XSETFASTINT (beg, 0); + XSETFASTINT (end, st.st_size); */ + buffer_delete_range (buf, BUF_BEGV(buf), BUF_ZV(buf), + !NILP (visit) ? INSDEL_NO_LOCKING : 0); + } +#endif /* DOS_NT */ } /* If requested, replace the accessible part of the buffer with the file contents. Avoid replacing text at the beginning or end of the buffer that matches the file contents; that preserves markers pointing to the unchanged parts. */ -#if !defined (DOS_NT) && !defined (MULE) +#if !defined (MULE) /* The replace-mode code currently only works when the assumption - 'one byte == one char' holds true. This fails under MSDOS and - Windows NT (because newlines are represented as CR-LF in text - files) and under Mule because files may contain multibyte characters. */ + 'one byte == one char' holds true. This fails Mule because + files may contain multibyte characters. It holds under Windows NT + provided we convert CRLF into LF. */ # define FSFMACS_SPEEDY_INSERT #endif #ifndef FSFMACS_SPEEDY_INSERT @@ -2612,6 +2945,38 @@ Bufpos same_at_start = BUF_BEGV (buf); Bufpos same_at_end = BUF_ZV (buf); int overlap; +#ifdef DOS_NT + /* Syncing with 19.34.6 note: same_at_start_in_file and + same_at_end_in_file are not in XEmacs 20.4. + First try to introduce them as-is and see what happens. + Might be necessary to use constructs like + st.st_size - (BUF_ZV (buf) - same_at_end) + instead. + --marcpa + */ + /* Offset into the file where discrepancy begins. */ + int same_at_start_in_file = 0; + /* Offset into the file where discrepancy ends. */ + int same_at_end_in_file = st.st_size; + /* DOS_NT only: is there a `\r' character left in the buffer? */ + int cr_left_in_buffer = 0; + /* DOS_NT only: was `\n' the first character in previous bufferful? */ + int last_was_lf = 0; + + /* Demacs 1.1.1 91/10/16 HIRANO Satoshi, MW July 1993 */ + /* Determine file type (text/binary) from its name. + Note that the buffer_file_type changes here when the file + being inserted is not of the same type as the original buffer. */ + current_buffer->buffer_file_type = call1 (Qfind_buffer_file_type, filename); + if (NILP (current_buffer->buffer_file_type)) + crlf_conversion_required = 1; + else if (current_buffer->buffer_file_type != Qt) + /* Use heuristic to decide whether file is text or binary (based + on the first bufferful) if buffer-file-type is not nil or t. + If no decision is made (because no line endings were ever + seen) then let buffer-file-type default to nil. */ + crlf_conversion_required = -1; +#endif /* DOS_NT */ /* Count how many chars at the start of the file match the text at the beginning of the buffer. */ @@ -2619,7 +2984,16 @@ { int nread; Bufpos bufpos; - +#ifdef DOS_NT + if (cr_left_in_buffer) + { + nread = read_allowing_quit (fd, buffer + 1, sizeof(buffer) - 1); + cr_left_in_buffer = 0; + if (nread >= 0) + nread++; + } + else +#endif /* DOS_NT */ nread = read_allowing_quit (fd, buffer, sizeof buffer); if (nread < 0) error ("IO error reading %s: %s", @@ -2627,17 +3001,73 @@ else if (nread == 0) break; bufpos = 0; +#ifdef DOS_NT + /* If requested, we do a simple check on the first bufferful + to decide whether the file is binary or text. (If text, we + count LF and CRLF occurences to determine whether the file + was in Unix or DOS format.) */ + if (crlf_conversion_required < 0) + { + crlf_conversion_required = decide_buffer_type (buffer, nread); + current_buffer->buffer_file_type = + crlf_conversion_required ? Qnil : Qt; + } + + /* DOS_NT text files require that we ignore a `\r' before a `\n'. */ + if (crlf_conversion_required > 0) + while (bufpos < nread && same_at_start < BUF_ZV (buf)) + { + int filec = buffer[bufpos]; + int bufc = BUF_FETCH_CHAR (buf, same_at_start); + + if (filec == '\n') + lf_count++; + + if (filec == bufc) + same_at_start++, bufpos++, same_at_start_in_file++; + else if (filec == '\r' && bufc == '\n') + { + /* If the `\r' is the last character in this buffer, + it will be examined with the next bufferful. */ + if (bufpos == nread) + { + buffer[0] = filec; + cr_left_in_buffer = 1; + } + else if (buffer[bufpos + 1] == bufc) + { + bufpos += 2; + same_at_start_in_file += 2; + same_at_start++; + crlf_count++; + lf_count++; + } + else + break; + } + else + break; + } + else +#endif /* DOS_NT */ while (bufpos < nread && same_at_start < BUF_ZV (buf) && BUF_FETCH_CHAR (buf, same_at_start) == buffer[bufpos]) +#ifdef DOS_NT + same_at_start_in_file++, +#endif same_at_start++, bufpos++; /* If we found a discrepancy, stop the scan. - Otherwise loop around and scan the next bufferfull. */ + Otherwise loop around and scan the next bufferful. */ if (bufpos != nread) break; } /* If the file matches the buffer completely, there's no need to replace anything. */ +#ifdef DOS_NT + if (same_at_start_in_file == st.st_size) +#else if (same_at_start - BUF_BEGV (buf) == st.st_size) +#endif /* DOS_NT */ { close (fd); unbind_to (speccount, Qnil); @@ -2654,7 +3084,11 @@ Bufpos bufpos, curpos, trial; /* At what file position are we now scanning? */ +#ifdef DOS_NT + curpos = same_at_end_in_file; +#else curpos = st.st_size - (BUF_ZV (buf) - same_at_end); +#endif /* DOS_NT */ /* If the entire file matches the buffer tail, stop the scan. */ if (curpos == 0) break; @@ -2674,17 +3108,64 @@ XSTRING_DATA (filename), strerror (errno)); total_read += nread; } - /* Scan this bufferfull from the end, comparing with + /* Scan this bufferful from the end, comparing with the Emacs buffer. */ bufpos = total_read; +#ifdef DOS_NT + /* DOS_NT text files require that we ignore a `\r' before a `\n'. */ + if (crlf_conversion_required) +#endif /* DOS_NT */ /* Compare with same_at_start to avoid counting some buffer text as matching both at the file's beginning and at the end. */ +#if !defined(DOS_NT) while (bufpos > 0 && same_at_end > same_at_start && BUF_FETCH_CHAR (buf, same_at_end - 1) == buffer[bufpos - 1]) same_at_end--, bufpos--; +#else /* DOS_NT */ + while (bufpos > 0 && same_at_end > same_at_start + && same_at_end_in_file > same_at_start_in_file) + { + int filec = buffer[bufpos - 1]; + int bufc = BUF_FETCH_CHAR (buf, same_at_end - 1); + + /* Account for `\n' in previous bufferful. */ + if (last_was_lf && filec == '\r') + { + same_at_end_in_file--, bufpos--; + last_was_lf = 0; + crlf_count++; + } + else if (filec == bufc) + { + last_was_lf = 0; + same_at_end--, same_at_end_in_file--, bufpos--; + if (bufc == '\n') + { + lf_count++; + if (bufpos <= 0) + last_was_lf = 1; + else if (same_at_end_in_file <= same_at_start_in_file) + break; + else if (buffer[bufpos - 1] == '\r') + same_at_end_in_file--, bufpos--, crlf_count++; + } + } + else + { + last_was_lf = 0; + break; + } + } + else + while (bufpos > 0 && same_at_end > same_at_start + && same_at_end_in_file > same_at_start_in_file + && BUF_FETCH_CHAR (buf, same_at_end - 1) == + buffer[bufpos - 1]) + same_at_end--, same_at_end_in_file--, bufpos--; +#endif /* !defined(DOS_NT) */ /* If we found a discrepancy, stop the scan. - Otherwise loop around and scan the preceding bufferfull. */ + Otherwise loop around and scan the preceding bufferful. */ if (bufpos != 0) break; /* If display current starts at beginning of line, @@ -2701,8 +3182,13 @@ same_at_end += overlap; /* Arrange to read only the nonmatching middle part of the file. */ +#ifdef DOS_NT + beg = make_int (same_at_start_in_file); + end = make_int (same_at_end_in_file); +#else beg = make_int (same_at_start - BUF_BEGV (buf)); end = make_int (st.st_size - (BUF_ZV (buf) - same_at_end)); +#endif /* DOS_NT */ buffer_delete_range (buf, same_at_start, same_at_end, !NILP (visit) ? INSDEL_NO_LOCKING : 0); @@ -2775,6 +3261,85 @@ saverrno = errno; break; } +#ifdef DOS_NT + /* XEmacs (--marcpa) change: FSF does buffer_insert_raw_string_1() first + then checks if conversion is needed, calling lisp + (find-buffer-file-type) which can call a user-function that + might look at the unconverted buffer to decide if + conversion is needed. + I removed the possibility for lisp functions called from + find-buffer-file-type to look at the buffer's content, for + simplicity reasons: it is easier to do the CRLF -> LF + conversion on read_buf than on buffer contents because + BUF_FETCH_CHAR does not return a pointer to an unsigned + char memory location, and because we must cope with bytind + VS bufpos in XEmacs, thus complicating crlf_to_lf(). + This decision (of doing Lstream_read(), crlf_to_lf() then + buffer_insert_raw_string_1()) is debatable. + --marcpa + */ + /* Following FSF note no longer apply now. See comment above. + --marcpa*/ + /* For compatability with earlier versions that did not support the + REPLACE funtionality, we call find-buffer-file-type after inserting + the contents to allow it to inspect the inserted data. (This was + not intentional usage, but proved to be quite useful.) */ + if (NILP (replace)) + { + /* Demacs 1.1.1 91/10/16 HIRANO Satoshi, MW July 1993 */ + /* Determine file type (text/binary) from its name. + Note that the buffer_file_type changes here when the file + being inserted is not of the same type as the original buffer. */ + current_buffer->buffer_file_type = call1 (Qfind_buffer_file_type, filename); + if (NILP (current_buffer->buffer_file_type)) + crlf_conversion_required = 1; + else if (current_buffer->buffer_file_type != Qt) + /* Use heuristic to decide whether file is text or binary (based + on the first bufferful) if buffer-file-type is not nil or t. + If no decision is made (because no line endings were ever + seen) then let buffer-file-type default to nil. */ + crlf_conversion_required = -1; + } + + /* If requested, we check the inserted data to decide whether the file + is binary or text. (If text, we count LF and CRLF occurences to + determine whether the file was in Unix or DOS format.) */ + if (crlf_conversion_required < 0) + { + crlf_conversion_required = + decide_buffer_type (read_buf, this_len); + current_buffer->buffer_file_type = + crlf_conversion_required ? Qnil : Qt; + } + + /* Demacs 1.1.1 91/10/16 HIRANO Satoshi, MW July 1993 */ + /* Remove CRs from CR-LFs if the file is deemed to be a text file. */ + if (crlf_conversion_required) + { + int reduced_size + = this_len - crlf_to_lf (this_len, read_buf, + &lf_count); + crlf_count += reduced_size; + /* XEmacs (--marcpa) change: No need for this since we havent + inserted in buffer yet. */ +#if 0 + ZV -= reduced_size; + Z -= reduced_size; + GPT -= reduced_size; + GAP_SIZE += reduced_size; + inserted -= reduced_size; +#endif + this_len -= reduced_size; + + /* Change buffer_file_type back to binary if Unix eol format. */ + if (crlf_count == 0 && lf_count > 0) + current_buffer->buffer_file_type = Qt; + } + + /* Make crlf_count and lf_count available for inspection. */ + Fset (intern ("buffer-file-lines"), make_int (lf_count)); + Fset (intern ("buffer-file-dos-lines"), make_int (crlf_count)); +#endif /* DOS_NT */ cc_inserted = buffer_insert_raw_string_1 (buf, cur_point, read_buf, this_len, @@ -2793,25 +3358,6 @@ NUNGCPRO; } -#if 0 -/* XXXX Why the #### ? Bogus anyway. If they are there, display em! */ -#ifdef DOS_NT - /* Determine file type from name and remove LFs from CR-LFs if the file - is deemed to be a text file. */ - { - struct gcpro gcpro1; - GCPRO1 (filename); - buf->buffer_file_type - = call1_in_buffer (buf, Qfind_buffer_file_type, filename); - UNGCPRO; - if (NILP (buf->buffer_file_type)) - { - buffer_do_msdos_crlf_to_lf (buf, ####); - } - } -#endif -#endif /* 0 */ - /* Close the file/stream */ unbind_to (speccount, Qnil); @@ -2958,12 +3504,10 @@ to protect the current_buffer from being destroyed, but the multiple return points make this a pain in the butt. */ -#if 0 #ifdef DOS_NT int buffer_file_type = NILP (current_buffer->buffer_file_type) ? O_TEXT : O_BINARY; #endif /* DOS_NT */ -#endif /* 0 */ #ifdef MULE codesys = Fget_coding_system (codesys); @@ -3050,7 +3594,7 @@ if (!NILP (append)) #ifdef DOS_NT desc = open ((char *) XSTRING_DATA (fn), - (O_WRONLY | O_BINARY), 0); + (O_WRONLY | buffer_file_type), 0); #else /* not DOS_NT */ desc = open ((char *) XSTRING_DATA (fn), O_WRONLY, 0); #endif /* not DOS_NT */ @@ -3059,7 +3603,7 @@ { #ifdef DOS_NT desc = open ((char *) XSTRING_DATA (fn), - (O_WRONLY | O_TRUNC | O_CREAT | O_BINARY), + (O_WRONLY | O_TRUNC | O_CREAT | buffer_file_type), (S_IREAD | S_IWRITE)); #else /* not DOS_NT */ desc = creat ((char *) XSTRING_DATA (fn), @@ -4221,4 +4765,24 @@ Saving the buffer normally turns auto-save back on. */ ); disable_auto_save_when_buffer_shrinks = 1; +#ifdef DOS_NT + DEFVAR_LISP ("directory-sep-char", &Vdirectory_sep_char /* + *Directory separator character for built-in functions that return file names. +The value should be either ?/ or ?\\ (any other value is treated as ?\\). +This variable affects the built-in functions only on Windows, +on other platforms, it is initialized so that Lisp code can find out +what the normal separator is. +*/ ); + Vdirectory_sep_char = '/'; + + DEFVAR_LISP ("insert-file-contents-allow-replace", &Vinsert_file_contents_allow_replace /* + *Allow REPLACE option of insert-file-contents to preserve markers. +If non-nil, the REPLACE option works as described, preserving markers. +If nil, the REPLACE option is implemented by deleting the visible region +then inserting the file contents as if REPLACE was nil. + +This option is only meaningful on Windows. +*/ ); + Vinsert_file_contents_allow_replace = Qt; +#endif }