📄 fileio.c
字号:
/* File IO for GNU Emacs. Copyright (C) 1985, 1986, 1987, 1988, 1990 Free Software Foundation, Inc.This file is part of GNU Emacs.GNU Emacs is free software; you can redistribute it and/or modifyit under the terms of the GNU General Public License as published bythe Free Software Foundation; either version 1, or (at your option)any later version.GNU Emacs is distributed in the hope that it will be useful,but WITHOUT ANY WARRANTY; without even the implied warranty ofMERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See theGNU General Public License for more details.You should have received a copy of the GNU General Public Licensealong with GNU Emacs; see the file COPYING. If not, write tothe Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */#include <sys/types.h>#ifdef hpux/* needed by <pwd.h> */#include <stdio.h>#undef NULL#endif#include <sys/stat.h>#include <pwd.h>#include <ctype.h>#include <sys/dir.h>#include <errno.h>#ifndef VMSextern int errno;extern char *sys_errlist[];extern int sys_nerr;#endif#define err_str(a) ((a) < sys_nerr ? sys_errlist[a] : "unknown error")#ifdef APOLLO#include <sys/time.h>#endif#ifdef NULL#undef NULL#endif#include "config.h"#include "lisp.h"#include "buffer.h"#include "window.h"#ifdef VMS#include <perror.h>#include <file.h>#include <rmsdef.h>#include <fab.h>#include <nam.h>#endif#ifdef HAVE_TIMEVAL#ifdef HPUX#include <time.h>#else#include <sys/time.h>#endif#endif#ifdef HPUX#include <netio.h>#include <errnet.h>#endif#ifndef O_WRONLY#define O_WRONLY 1#endif#define min(a, b) ((a) < (b) ? (a) : (b))#define max(a, b) ((a) > (b) ? (a) : (b))/* Nonzero during writing of auto-save files */int auto_saving;/* Nonzero means, when reading a filename in the minibuffer, start out by inserting the default directory into the minibuffer. */int insert_default_directory;/* On VMS, nonzero means write new files with record format stmlf. Zero means use var format. */int vms_stmlf_recfm;Lisp_Object Qfile_error, Qfile_already_exists;report_file_error (string, data) char *string; Lisp_Object data;{ Lisp_Object errstring; if (errno >= 0 && errno < sys_nerr) errstring = build_string (sys_errlist[errno]); else errstring = build_string ("undocumented error code"); /* System error messages are capitalized. Downcase the initial. */ XSTRING (errstring)->data[0] = DOWNCASE (XSTRING (errstring)->data[0]); while (1) Fsignal (Qfile_error, Fcons (build_string (string), Fcons (errstring, data)));}DEFUN ("file-name-directory", Ffile_name_directory, Sfile_name_directory, 1, 1, 0, "Return the directory component in file name NAME.\n\Return nil if NAME does not include a directory.\n\Otherwise returns a directory spec.\n\Given a Unix syntax file name, returns a string ending in slash;\n\on VMS, perhaps instead a string ending in :, ] or >.") (file) Lisp_Object file;{ register unsigned char *beg; register unsigned char *p; CHECK_STRING (file, 0); beg = XSTRING (file)->data; p = beg + XSTRING (file)->size; while (p != beg && p[-1] != '/'#ifdef VMS && p[-1] != ':' && p[-1] != ']' && p[-1] != '>'#endif /* VMS */ ) p--; if (p == beg) return Qnil; return make_string (beg, p - beg);}DEFUN ("file-name-nondirectory", Ffile_name_nondirectory, Sfile_name_nondirectory, 1, 1, 0, "Return file name NAME sans its directory.\n\For example, in a Unix-syntax file name,\n\this is everything after the last slash,\n\or the entire name if it contains no slash.") (file) Lisp_Object file;{ register unsigned char *beg, *p, *end; CHECK_STRING (file, 0); beg = XSTRING (file)->data; end = p = beg + XSTRING (file)->size; while (p != beg && p[-1] != '/'#ifdef VMS && p[-1] != ':' && p[-1] != ']' && p[-1] != '>'#endif /* VMS */ ) p--; return make_string (p, end - p);}char *file_name_as_directory (out, in) char *out, *in;{ int size = strlen (in) - 1; strcpy (out, in);#ifdef VMS /* Is it already a directory string? */ if (in[size] == ':' || in[size] == ']' || in[size] == '>') return out; /* Is it a VMS directory file name? If so, hack VMS syntax. */ else if (! index (in, '/') && ((size > 3 && ! strcmp (&in[size - 3], ".DIR")) || (size > 3 && ! strcmp (&in[size - 3], ".dir")) || (size > 5 && (! strncmp (&in[size - 5], ".DIR", 4) || ! strncmp (&in[size - 5], ".dir", 4)) && (in[size - 1] == '.' || in[size - 1] == ';') && in[size] == '1'))) { register char *p, *dot; char brack; /* x.dir -> [.x] dir:x.dir --> dir:[x] dir:[x]y.dir --> dir:[x.y] */ p = in + size; while (p != in && *p != ':' && *p != '>' && *p != ']') p--; if (p != in) { strncpy (out, in, p - in); out[p - in] = '\0'; if (*p == ':') { brack = ']'; strcat (out, ":["); } else { brack = *p; strcat (out, "."); } p++; } else { brack = ']'; strcpy (out, "[."); } if (dot = index (p, '.')) { /* blindly remove any extension */ size = strlen (out) + (dot - p); strncat (out, p, dot - p); } else { strcat (out, p); size = strlen (out); } out[size++] = brack; out[size] = '\0'; }#else /* not VMS */ /* For Unix syntax, Append a slash if necessary */ if (out[size] != '/') strcat (out, "/");#endif /* not VMS */ return out;}DEFUN ("file-name-as-directory", Ffile_name_as_directory, Sfile_name_as_directory, 1, 1, 0, "Return a string representing file FILENAME interpreted as a directory.\n\This string can be used as the value of default-directory\n\or passed as second argument to expand-file-name.\n\For a Unix-syntax file name, just appends a slash.\n\On VMS, converts \"[X]FOO.DIR\" to \"[X.FOO]\", etc.") (file) Lisp_Object file;{ char *buf; CHECK_STRING (file, 0); if (NULL (file)) return Qnil; buf = (char *) alloca (XSTRING (file)->size + 10); return build_string (file_name_as_directory (buf, XSTRING (file)->data));}/* * Convert from directory name to filename. * On VMS: * xyzzy:[mukesh.emacs] => xyzzy:[mukesh]emacs.dir.1 * xyzzy:[mukesh] => xyzzy:[000000]mukesh.dir.1 * On UNIX, it's simple: just make sure there is a terminating / * Value is nonzero if the string output is different from the input. */directory_file_name (src, dst) char *src, *dst;{ long slen;#ifdef VMS long rlen; char * ptr, * rptr; char bracket; struct FAB fab = cc$rms_fab; struct NAM nam = cc$rms_nam; char esa[NAM$C_MAXRSS];#endif /* VMS */ slen = strlen (src) - 1;#ifdef VMS if (! index (src, '/') && (src[slen] == ']' || src[slen] == ':' || src[slen] == '>')) { /* VMS style - convert [x.y.z] to [x.y]z, [x] to [000000]x */ fab.fab$l_fna = src; fab.fab$b_fns = slen + 1; fab.fab$l_nam = &nam; fab.fab$l_fop = FAB$M_NAM; nam.nam$l_esa = esa; nam.nam$b_ess = sizeof esa; nam.nam$b_nop |= NAM$M_SYNCHK; /* We call SYS$PARSE to handle such things as [--] for us. */ if (SYS$PARSE(&fab, 0, 0) == RMS$_NORMAL) { slen = nam.nam$b_esl - 1; if (esa[slen] == ';' && esa[slen - 1] == '.') slen -= 2; esa[slen + 1] = '\0'; src = esa; } if (src[slen] != ']' && src[slen] != '>') { /* what about when we have logical_name:???? */ if (src[slen] == ':') { /* Xlate logical name and see what we get */ ptr = strcpy (dst, src); /* upper case for getenv */ while (*ptr) { if ('a' <= *ptr && *ptr <= 'z') *ptr -= 040; ptr++; } dst[slen] = 0; /* remove colon */ if (!(src = egetenv (dst))) return 0; /* should we jump to the beginning of this procedure? Good points: allows us to use logical names that xlate to Unix names, Bad points: can be a problem if we just translated to a device name... For now, I'll punt and always expect VMS names, and hope for the best! */ slen = strlen (src) - 1; if (src[slen] != ']' && src[slen] != '>') { /* no recursion here! */ strcpy (dst, src); return 0; } } else { /* not a directory spec */ strcpy (dst, src); return 0; } } bracket = src[slen]; if (!(ptr = index (src, bracket - 2))) { /* no opening bracket */ strcpy (dst, src); return 0; } if (!(rptr = rindex (src, '.'))) rptr = ptr; slen = rptr - src; strncpy (dst, src, slen); dst[slen] = '\0'; if (*rptr == '.') { dst[slen++] = bracket; dst[slen] = '\0'; } else { /* If we have the top-level of a rooted directory (i.e. xx:[000000]), then translate the device and recurse. */ if (dst[slen - 1] == ':' && dst[slen - 2] != ':' /* skip decnet nodes */ && strcmp(src + slen, "[000000]") == 0) { dst[slen - 1] = '\0'; if ((ptr = egetenv (dst)) && (rlen = strlen (ptr) - 1) > 0 && (ptr[rlen] == ']' || ptr[rlen] == '>') && ptr[rlen - 1] == '.') { ptr[rlen - 1] = ']'; ptr[rlen] = '\0'; return directory_file_name (ptr, dst); } else dst[slen - 1] = ':'; } strcat (dst, "[000000]"); slen += 8; } rptr++; rlen = strlen (rptr) - 1; strncat (dst, rptr, rlen); dst[slen + rlen] = '\0'; strcat (dst, ".DIR.1"); return 1; }#endif /* VMS */ /* Process as Unix format: just remove any final slash. But leave "/" unchanged; do not change it to "". */ strcpy (dst, src); if (dst[slen] == '/' && slen > 1) dst[slen] = 0; return 1;}DEFUN ("directory-file-name", Fdirectory_file_name, Sdirectory_file_name, 1, 1, 0, "Returns the file name of the directory named DIR.\n\This is the name of the file that holds the data for the directory DIR.\n\In Unix-syntax, this just removes the final slash.\n\On VMS, given a VMS-syntax directory name such as \"[X.Y]\",\n\returns a file name such as \"[X]Y.DIR.1\".") (directory) Lisp_Object directory;{ char *buf; CHECK_STRING (directory, 0); if (NULL (directory)) return Qnil;#ifdef VMS /* 20 extra chars is insufficient for VMS, since we might perform a logical name translation. an equivalence string can be up to 255 chars long, so grab that much extra space... - sss */ buf = (char *) alloca (XSTRING (directory)->size + 20 + 255);#else buf = (char *) alloca (XSTRING (directory)->size + 20);#endif directory_file_name (XSTRING (directory)->data, buf); return build_string (buf);}DEFUN ("make-temp-name", Fmake_temp_name, Smake_temp_name, 1, 1, 0, "Generate temporary name (string) starting with PREFIX (a string).") (prefix) Lisp_Object prefix;{ Lisp_Object val; val = concat2 (prefix, build_string ("XXXXXX")); mktemp (XSTRING (val)->data); return val;}DEFUN ("expand-file-name", Fexpand_file_name, Sexpand_file_name, 1, 2, 0, "Convert FILENAME to absolute, and canonicalize it.\n\Second arg DEFAULT is directory to start with if FILENAME is relative\n\ (does not start with slash); if DEFAULT is nil or missing,\n\the current buffer's value of default-directory is used.\n\Filenames containing . or .. as components are simplified;\n\initial ~ is expanded. See also the function substitute-in-file-name.") (name, defalt) Lisp_Object name, defalt;{ unsigned char *nm; register unsigned char *newdir, *p, *o; int tlen; unsigned char *target; struct passwd *pw; int lose;#ifdef VMS unsigned char * colon = 0; unsigned char * close = 0; unsigned char * slash = 0; unsigned char * brack = 0; int lbrack = 0, rbrack = 0; int dots = 0;#endif /* VMS */ CHECK_STRING (name, 0);#ifdef VMS /* Filenames on VMS are always upper case. */ name = Fupcase (name);#endif nm = XSTRING (name)->data; /* If nm is absolute, flush ...// and detect /./ and /../. If no /./ or /../ we can return right away. */ if ( nm[0] == '/'#ifdef VMS || index (nm, ':')#endif /* VMS */ ) { p = nm; lose = 0; while (*p) { if (p[0] == '/' && p[1] == '/'#ifdef APOLLO /* // at start of filename is meaningful on Apollo system */ && nm != p#endif /* APOLLO */ ) nm = p + 1; if (p[0] == '/' && p[1] == '~') nm = p + 1, lose = 1; if (p[0] == '/' && p[1] == '.' && (p[2] == '/' || p[2] == 0 || (p[2] == '.' && (p[3] == '/' || p[3] == 0)))) lose = 1;#ifdef VMS if (p[0] == '\\') lose = 1; if (p[0] == '/') { /* if dev:[dir]/, move nm to / */ if (!slash && p > nm && (brack || colon)) { nm = (brack ? brack + 1 : colon + 1); lbrack = rbrack = 0; brack = 0; colon = 0; } slash = p; } if (p[0] == '-')#ifndef VMS4_4 /* VMS pre V4.4,convert '-'s in filenames. */ if (lbrack == rbrack) { if (dots < 2) /* this is to allow negative version numbers */ p[0] = '_'; } else#endif /* VMS4_4 */ if (lbrack > rbrack && ((p[-1] == '.' || p[-1] == '[' || p[-1] == '<') && (p[1] == '.' || p[1] == ']' || p[1] == '>'))) lose = 1;#ifndef VMS4_4 else p[0] = '_';#endif /* VMS4_4 */ /* count open brackets, reset close bracket pointer */ if (p[0] == '[' || p[0] == '<') lbrack++, brack = 0; /* count close brackets, set close bracket pointer */ if (p[0] == ']' || p[0] == '>') rbrack++, brack = p; /* detect ][ or >< */ if ((p[0] == ']' || p[0] == '>') && (p[1] == '[' || p[1] == '<')) lose = 1; if ((p[0] == ':' || p[0] == ']' || p[0] == '>') && p[1] == '~') nm = p + 1, lose = 1; if (p[0] == ':' && (colon || slash)) /* if dev1:[dir]dev2:, move nm to dev2: */ if (brack) { nm = brack + 1; brack = 0; } /* if /pathname/dev:, move nm to dev: */ else if (slash) nm = slash + 1; /* if node::dev:, move colon following dev */ else if (colon && colon[-1] == ':') colon = p; /* if dev1:dev2:, move nm to dev2: */ else if (colon && colon[-1] != ':') { nm = colon + 1; colon = 0; } if (p[0] == ':' && !colon) { if (p[1] == ':') p++; colon = p; } if (lbrack == rbrack) if (p[0] == ';') dots = 2; else if (p[0] == '.') dots++;#endif /* VMS */
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -