summaryrefslogtreecommitdiff
path: root/src/dired.c
diff options
context:
space:
mode:
authorRichard M. Stallman <rms@gnu.org>1990-12-31 04:18:02 +0000
committerRichard M. Stallman <rms@gnu.org>1990-12-31 04:18:02 +0000
commit4a7b14034838ab8a0d0cb9a37d2cf43b2bf3528f (patch)
tree75521a7f76aee7688b0ce6467ebca05019c0bece /src/dired.c
parent3ee2f56939fe79dffba4f0afc288e5636d453544 (diff)
downloademacs-4a7b14034838ab8a0d0cb9a37d2cf43b2bf3528f.tar.gz
Initial revision
Diffstat (limited to 'src/dired.c')
-rw-r--r--src/dired.c478
1 files changed, 478 insertions, 0 deletions
diff --git a/src/dired.c b/src/dired.c
new file mode 100644
index 00000000000..feb68ed0506
--- /dev/null
+++ b/src/dired.c
@@ -0,0 +1,478 @@
+/* Lisp functions for making directory listings.
+ Copyright (C) 1985, 1986 Free Software Foundation, Inc.
+
+This file is part of GNU Emacs.
+
+GNU Emacs is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the 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 of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with GNU Emacs; see the file COPYING. If not, write to
+the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+
+#include <stdio.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+
+#include "config.h"
+
+#ifdef SYSV_SYSTEM_DIR
+
+#include <dirent.h>
+#define DIRENTRY struct dirent
+#define NAMLEN(p) strlen (p->d_name)
+
+#else
+
+#ifdef NONSYSTEM_DIR_LIBRARY
+#include "ndir.h"
+#else /* not NONSYSTEM_DIR_LIBRARY */
+#include <sys/dir.h>
+#endif /* not NONSYSTEM_DIR_LIBRARY */
+
+#define DIRENTRY struct direct
+#define NAMLEN(p) p->d_namlen
+
+extern DIR *opendir ();
+extern struct direct *readdir ();
+
+#endif
+
+#undef NULL
+
+#include "lisp.h"
+#include "buffer.h"
+#include "commands.h"
+
+#include "regex.h"
+#include "search.h"
+
+#define min(a, b) ((a) < (b) ? (a) : (b))
+
+/* if system does not have symbolic links, it does not have lstat.
+ In that case, use ordinary stat instead. */
+
+#ifndef S_IFLNK
+#define lstat stat
+#endif
+
+Lisp_Object Vcompletion_ignored_extensions;
+
+Lisp_Object Qcompletion_ignore_case;
+
+DEFUN ("directory-files", Fdirectory_files, Sdirectory_files, 1, 4, 0,
+ "Return a list of names of files in DIRECTORY.\n\
+There are three optional arguments:\n\
+If FULL is non-nil, absolute pathnames of the files are returned.\n\
+If MATCH is non-nil, only pathnames containing that regexp are returned.\n\
+If NOSORT is non-nil, the list is not sorted--its order is unpredictable.\n\
+ NOSORT is useful if you plan to sort the result yourself.")
+ (dirname, full, match, nosort)
+ Lisp_Object dirname, full, match, nosort;
+{
+ DIR *d;
+ int length;
+ Lisp_Object list, name;
+
+ if (!NULL (match))
+ {
+ CHECK_STRING (match, 3);
+ /* Compile it now so we don't get an error after opendir */
+#ifdef VMS
+ compile_pattern (match, &searchbuf,
+ buffer_defaults.downcase_table->contents);
+#else
+ compile_pattern (match, &searchbuf, 0);
+#endif
+ }
+
+ dirname = Fexpand_file_name (dirname, Qnil);
+ if (!(d = opendir (XSTRING (Fdirectory_file_name (dirname))->data)))
+ report_file_error ("Opening directory", Fcons (dirname, Qnil));
+
+ list = Qnil;
+ length = XSTRING (dirname)->size;
+
+ /* Loop reading blocks */
+ while (1)
+ {
+ DIRENTRY *dp = readdir (d);
+ int len;
+
+ if (!dp) break;
+ len = NAMLEN (dp);
+ if (dp->d_ino)
+ {
+ if (NULL (match)
+ || (0 <= re_search (&searchbuf, dp->d_name, len, 0, len, 0)))
+ {
+ if (!NULL (full))
+ {
+ int index = XSTRING (dirname)->size;
+ int total = len + index;
+#ifndef VMS
+ if (length == 0
+ || XSTRING (dirname)->data[length - 1] != '/')
+ total++;
+#endif /* VMS */
+
+ name = make_uninit_string (total);
+ bcopy (XSTRING (dirname)->data, XSTRING (name)->data,
+ index);
+#ifndef VMS
+ if (length == 0
+ || XSTRING (dirname)->data[length - 1] != '/')
+ XSTRING (name)->data[index++] = '/';
+#endif /* VMS */
+ bcopy (dp->d_name, XSTRING (name)->data + index, len);
+ }
+ else
+ name = make_string (dp->d_name, len);
+ list = Fcons (name, list);
+ }
+ }
+ }
+ closedir (d);
+ if (!NULL (nosort))
+ return list;
+ return Fsort (Fnreverse (list), Qstring_lessp);
+}
+
+Lisp_Object file_name_completion ();
+
+DEFUN ("file-name-completion", Ffile_name_completion, Sfile_name_completion,
+ 2, 2, 0,
+ "Complete file name FILE in directory DIR.\n\
+Returns the longest string\n\
+common to all filenames in DIR that start with FILE.\n\
+If there is only one and FILE matches it exactly, returns t.\n\
+Returns nil if DIR contains no name starting with FILE.")
+ (file, dirname)
+ Lisp_Object file, dirname;
+{
+ /* Don't waste time trying to complete a null string.
+ Besides, this case happens when user is being asked for
+ a directory name and has supplied one ending in a /.
+ We would not want to add anything in that case
+ even if there are some unique characters in that directory. */
+ if (XTYPE (file) == Lisp_String && XSTRING (file)->size == 0)
+ return file;
+ return file_name_completion (file, dirname, 0, 0);
+}
+
+DEFUN ("file-name-all-completions", Ffile_name_all_completions,
+ Sfile_name_all_completions, 2, 2, 0,
+ "Return a list of all completions of file name FILE in directory DIR.\n\
+These are all file names in directory DIR which begin with FILE.")
+ (file, dirname)
+ Lisp_Object file, dirname;
+{
+ return file_name_completion (file, dirname, 1, 0);
+}
+
+#ifdef VMS
+
+DEFUN ("file-name-all-versions", Ffile_name_all_versions,
+ Sfile_name_all_versions, 2, 2, 0,
+ "Return a list of all versions of file name FILE in directory DIR.")
+ (file, dirname)
+ Lisp_Object file, dirname;
+{
+ return file_name_completion (file, dirname, 1, 1);
+}
+
+#endif /* VMS */
+
+Lisp_Object
+file_name_completion (file, dirname, all_flag, ver_flag)
+ Lisp_Object file, dirname;
+ int all_flag, ver_flag;
+{
+ DIR *d;
+ DIRENTRY *dp;
+ int bestmatchsize, skip;
+ register int compare, matchsize;
+ unsigned char *p1, *p2;
+ int matchcount = 0;
+ Lisp_Object bestmatch, tem, elt, name;
+ struct stat st;
+ int directoryp;
+ int passcount;
+ int count = specpdl_ptr - specpdl;
+#ifdef VMS
+ extern DIRENTRY * readdirver ();
+
+ DIRENTRY *((* readfunc) ());
+
+ /* Filename completion on VMS ignores case, since VMS filesys does. */
+ specbind (Qcompletion_ignore_case, Qt);
+
+ readfunc = readdir;
+ if (ver_flag)
+ readfunc = readdirver;
+ file = Fupcase (file);
+#else /* not VMS */
+ CHECK_STRING (file, 0);
+#endif /* not VMS */
+
+ dirname = Fexpand_file_name (dirname, Qnil);
+ bestmatch = Qnil;
+
+ /* With passcount = 0, ignore files that end in an ignored extension.
+ If nothing found then try again with passcount = 1, don't ignore them.
+ If looking for all completions, start with passcount = 1,
+ so always take even the ignored ones.
+
+ ** It would not actually be helpful to the user to ignore any possible
+ completions when making a list of them.** */
+
+ for (passcount = !!all_flag; NULL (bestmatch) && passcount < 2; passcount++)
+ {
+ if (!(d = opendir (XSTRING (Fdirectory_file_name (dirname))->data)))
+ report_file_error ("Opening directory", Fcons (dirname, Qnil));
+
+ /* Loop reading blocks */
+ /* (att3b compiler bug requires do a null comparison this way) */
+ while (1)
+ {
+ DIRENTRY *dp;
+ int len;
+
+#ifdef VMS
+ dp = (*readfunc) (d);
+#else
+ dp = readdir (d);
+#endif
+ if (!dp) break;
+
+ len = NAMLEN (dp);
+
+ if (!NULL (Vquit_flag) && NULL (Vinhibit_quit))
+ goto quit;
+ if (!dp->d_ino
+ || len < XSTRING (file)->size
+ || 0 <= scmp (dp->d_name, XSTRING (file)->data,
+ XSTRING (file)->size))
+ continue;
+
+ if (file_name_completion_stat (dirname, dp, &st) < 0)
+ continue;
+
+ directoryp = ((st.st_mode & S_IFMT) == S_IFDIR);
+ tem = Qnil;
+ if (!directoryp)
+ {
+ /* Compare extensions-to-be-ignored against end of this file name */
+ /* if name is not an exact match against specified string */
+ if (!passcount && len > XSTRING (file)->size)
+ /* and exit this for loop if a match is found */
+ for (tem = Vcompletion_ignored_extensions;
+ CONSP (tem); tem = XCONS (tem)->cdr)
+ {
+ elt = XCONS (tem)->car;
+ if (XTYPE (elt) != Lisp_String) continue;
+ skip = len - XSTRING (elt)->size;
+ if (skip < 0) continue;
+
+ if (0 <= scmp (dp->d_name + skip,
+ XSTRING (elt)->data,
+ XSTRING (elt)->size))
+ continue;
+ break;
+ }
+ }
+
+ /* Unless an ignored-extensions match was found,
+ process this name as a completion */
+ if (passcount || !CONSP (tem))
+ {
+ /* Update computation of how much all possible completions match */
+
+ matchcount++;
+
+ if (all_flag || NULL (bestmatch))
+ {
+ /* This is a possible completion */
+ if (directoryp)
+ {
+ /* This completion is a directory; make it end with '/' */
+ name = Ffile_name_as_directory (make_string (dp->d_name, len));
+ }
+ else
+ name = make_string (dp->d_name, len);
+ if (all_flag)
+ {
+ bestmatch = Fcons (name, bestmatch);
+ }
+ else
+ {
+ bestmatch = name;
+ bestmatchsize = XSTRING (name)->size;
+ }
+ }
+ else
+ {
+ compare = min (bestmatchsize, len);
+ p1 = XSTRING (bestmatch)->data;
+ p2 = (unsigned char *) dp->d_name;
+ matchsize = scmp(p1, p2, compare);
+ if (matchsize < 0)
+ matchsize = compare;
+ /* If this dirname all matches,
+ see if implicit following slash does too. */
+ if (directoryp
+ && compare == matchsize
+ && bestmatchsize > matchsize
+ && p1[matchsize] == '/')
+ matchsize++;
+ bestmatchsize = min (matchsize, bestmatchsize);
+ }
+ }
+ }
+ closedir (d);
+ }
+
+ unbind_to (count, Qnil);
+
+ if (all_flag || NULL (bestmatch))
+ return bestmatch;
+ if (matchcount == 1 && bestmatchsize == XSTRING (file)->size)
+ return Qt;
+ return Fsubstring (bestmatch, make_number (0), make_number (bestmatchsize));
+ quit:
+ if (d) closedir (d);
+ Vquit_flag = Qnil;
+ return Fsignal (Qquit, Qnil);
+}
+
+file_name_completion_stat (dirname, dp, st_addr)
+ Lisp_Object dirname;
+ DIRENTRY *dp;
+ struct stat *st_addr;
+{
+ int len = NAMLEN (dp);
+ int pos = XSTRING (dirname)->size;
+ char *fullname = (char *) alloca (len + pos + 2);
+
+ bcopy (XSTRING (dirname)->data, fullname, pos);
+#ifndef VMS
+ if (fullname[pos - 1] != '/')
+ fullname[pos++] = '/';
+#endif
+
+ bcopy (dp->d_name, fullname + pos, len);
+ fullname[pos + len] = 0;
+
+ return stat (fullname, st_addr);
+}
+
+Lisp_Object
+make_time (time)
+ int time;
+{
+ return Fcons (make_number (time >> 16),
+ Fcons (make_number (time & 0177777), Qnil));
+}
+
+DEFUN ("file-attributes", Ffile_attributes, Sfile_attributes, 1, 1, 0,
+ "Return a list of attributes of file FILENAME.\n\
+Value is nil if specified file cannot be opened.\n\
+Otherwise, list elements are:\n\
+ 0. t for directory, string (name linked to) for symbolic link, or nil.\n\
+ 1. Number of links to file.\n\
+ 2. File uid.\n\
+ 3. File gid.\n\
+ 4. Last access time, as a list of two integers.\n\
+ First integer has high-order 16 bits of time, second has low 16 bits.\n\
+ 5. Last modification time, likewise.\n\
+ 6. Last status change time, likewise.\n\
+ 7. Size in bytes.\n\
+ 8. File modes, as a string of ten letters or dashes as in ls -l.\n\
+ 9. t iff file's gid would change if file were deleted and recreated.\n\
+10. inode number.\n\
+11. Device number.\n\
+\n\
+If file does not exists, returns nil.")
+ (filename)
+ Lisp_Object filename;
+{
+ Lisp_Object values[12];
+ Lisp_Object dirname;
+ struct stat s;
+ struct stat sdir;
+ char modes[10];
+
+ filename = Fexpand_file_name (filename, Qnil);
+ if (lstat (XSTRING (filename)->data, &s) < 0)
+ return Qnil;
+
+ switch (s.st_mode & S_IFMT)
+ {
+ default:
+ values[0] = Qnil; break;
+ case S_IFDIR:
+ values[0] = Qt; break;
+#ifdef S_IFLNK
+ case S_IFLNK:
+ values[0] = Ffile_symlink_p (filename); break;
+#endif
+ }
+ values[1] = make_number (s.st_nlink);
+ values[2] = make_number (s.st_uid);
+ values[3] = make_number (s.st_gid);
+ values[4] = make_time (s.st_atime);
+ values[5] = make_time (s.st_mtime);
+ values[6] = make_time (s.st_ctime);
+ /* perhaps we should set this to most-positive-fixnum if it is too large? */
+ values[7] = make_number (s.st_size);
+ filemodestring (&s, modes);
+ values[8] = make_string (modes, 10);
+#ifdef BSD4_3 /* Gross kludge to avoid lack of "#if defined(...)" in VMS */
+#define BSD4_2 /* A new meaning to the term `backwards compatability' */
+#endif
+#ifdef BSD4_2 /* file gid will be dir gid */
+ dirname = Ffile_name_directory (filename);
+ if (dirname != Qnil && stat (XSTRING (dirname)->data, &sdir) == 0)
+ values[9] = (sdir.st_gid != s.st_gid) ? Qt : Qnil;
+ else /* if we can't tell, assume worst */
+ values[9] = Qt;
+#else /* file gid will be egid */
+ values[9] = (s.st_gid != getegid ()) ? Qt : Qnil;
+#endif /* BSD4_2 (or BSD4_3) */
+#ifdef BSD4_3
+#undef BSD4_2 /* ok, you can look again without throwing up */
+#endif
+ values[10] = make_number (s.st_ino);
+ values[11] = make_number (s.st_dev);
+ return Flist (sizeof(values) / sizeof(values[0]), values);
+}
+
+syms_of_dired ()
+{
+ defsubr (&Sdirectory_files);
+ defsubr (&Sfile_name_completion);
+#ifdef VMS
+ defsubr (&Sfile_name_all_versions);
+#endif /* VMS */
+ defsubr (&Sfile_name_all_completions);
+ defsubr (&Sfile_attributes);
+
+#ifdef VMS
+ Qcompletion_ignore_case = intern ("completion-ignore-case");
+ staticpro (&Qcompletion_ignore_case);
+#endif /* VMS */
+
+ DEFVAR_LISP ("completion-ignored-extensions", &Vcompletion_ignored_extensions,
+ "*Completion ignores filenames ending in any string in this list.\n\
+This variable does not affect lists of possible completions,\n\
+but does affect the commands that actually do completions.");
+ Vcompletion_ignored_extensions = Qnil;
+}