1 /* Lisp functions for making directory listings.
2 Copyright (C) 1985, 1986 Free Software Foundation, Inc.
4 This file is part of GNU Emacs.
6 GNU Emacs is free software; you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation; either version 1, or (at your option)
11 GNU Emacs is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 GNU General Public License for more details.
16 You should have received a copy of the GNU General Public License
17 along with GNU Emacs; see the file COPYING. If not, write to
18 the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
22 #include <sys/types.h>
27 #ifdef SYSV_SYSTEM_DIR
30 #define DIRENTRY struct dirent
31 #define NAMLEN(p) strlen (p->d_name)
35 #ifdef NONSYSTEM_DIR_LIBRARY
37 #else /* not NONSYSTEM_DIR_LIBRARY */
39 #endif /* not NONSYSTEM_DIR_LIBRARY */
41 #define DIRENTRY struct direct
42 #define NAMLEN(p) p->d_namlen
44 extern DIR *opendir ();
45 extern struct direct
*readdir ();
57 #define min(a, b) ((a) < (b) ? (a) : (b))
59 /* if system does not have symbolic links, it does not have lstat.
60 In that case, use ordinary stat instead. */
66 Lisp_Object Vcompletion_ignored_extensions
;
68 Lisp_Object Qcompletion_ignore_case
;
70 DEFUN ("directory-files", Fdirectory_files
, Sdirectory_files
, 1, 4, 0,
71 "Return a list of names of files in DIRECTORY.\n\
72 There are three optional arguments:\n\
73 If FULL is non-nil, absolute pathnames of the files are returned.\n\
74 If MATCH is non-nil, only pathnames containing that regexp are returned.\n\
75 If NOSORT is non-nil, the list is not sorted--its order is unpredictable.\n\
76 NOSORT is useful if you plan to sort the result yourself.")
77 (dirname
, full
, match
, nosort
)
78 Lisp_Object dirname
, full
, match
, nosort
;
82 Lisp_Object list
, name
;
86 CHECK_STRING (match
, 3);
87 /* Compile it now so we don't get an error after opendir */
89 compile_pattern (match
, &searchbuf
,
90 buffer_defaults
.downcase_table
->contents
);
92 compile_pattern (match
, &searchbuf
, 0);
96 dirname
= Fexpand_file_name (dirname
, Qnil
);
97 if (!(d
= opendir (XSTRING (Fdirectory_file_name (dirname
))->data
)))
98 report_file_error ("Opening directory", Fcons (dirname
, Qnil
));
101 length
= XSTRING (dirname
)->size
;
103 /* Loop reading blocks */
106 DIRENTRY
*dp
= readdir (d
);
114 || (0 <= re_search (&searchbuf
, dp
->d_name
, len
, 0, len
, 0)))
118 int index
= XSTRING (dirname
)->size
;
119 int total
= len
+ index
;
122 || XSTRING (dirname
)->data
[length
- 1] != '/')
126 name
= make_uninit_string (total
);
127 bcopy (XSTRING (dirname
)->data
, XSTRING (name
)->data
,
131 || XSTRING (dirname
)->data
[length
- 1] != '/')
132 XSTRING (name
)->data
[index
++] = '/';
134 bcopy (dp
->d_name
, XSTRING (name
)->data
+ index
, len
);
137 name
= make_string (dp
->d_name
, len
);
138 list
= Fcons (name
, list
);
145 return Fsort (Fnreverse (list
), Qstring_lessp
);
148 Lisp_Object
file_name_completion ();
150 DEFUN ("file-name-completion", Ffile_name_completion
, Sfile_name_completion
,
152 "Complete file name FILE in directory DIR.\n\
153 Returns the longest string\n\
154 common to all filenames in DIR that start with FILE.\n\
155 If there is only one and FILE matches it exactly, returns t.\n\
156 Returns nil if DIR contains no name starting with FILE.")
158 Lisp_Object file
, dirname
;
160 /* Don't waste time trying to complete a null string.
161 Besides, this case happens when user is being asked for
162 a directory name and has supplied one ending in a /.
163 We would not want to add anything in that case
164 even if there are some unique characters in that directory. */
165 if (XTYPE (file
) == Lisp_String
&& XSTRING (file
)->size
== 0)
167 return file_name_completion (file
, dirname
, 0, 0);
170 DEFUN ("file-name-all-completions", Ffile_name_all_completions
,
171 Sfile_name_all_completions
, 2, 2, 0,
172 "Return a list of all completions of file name FILE in directory DIR.\n\
173 These are all file names in directory DIR which begin with FILE.")
175 Lisp_Object file
, dirname
;
177 return file_name_completion (file
, dirname
, 1, 0);
182 DEFUN ("file-name-all-versions", Ffile_name_all_versions
,
183 Sfile_name_all_versions
, 2, 2, 0,
184 "Return a list of all versions of file name FILE in directory DIR.")
186 Lisp_Object file
, dirname
;
188 return file_name_completion (file
, dirname
, 1, 1);
194 file_name_completion (file
, dirname
, all_flag
, ver_flag
)
195 Lisp_Object file
, dirname
;
196 int all_flag
, ver_flag
;
200 int bestmatchsize
, skip
;
201 register int compare
, matchsize
;
202 unsigned char *p1
, *p2
;
204 Lisp_Object bestmatch
, tem
, elt
, name
;
208 int count
= specpdl_ptr
- specpdl
;
210 extern DIRENTRY
* readdirver ();
212 DIRENTRY
*((* readfunc
) ());
214 /* Filename completion on VMS ignores case, since VMS filesys does. */
215 specbind (Qcompletion_ignore_case
, Qt
);
219 readfunc
= readdirver
;
220 file
= Fupcase (file
);
222 CHECK_STRING (file
, 0);
225 dirname
= Fexpand_file_name (dirname
, Qnil
);
228 /* With passcount = 0, ignore files that end in an ignored extension.
229 If nothing found then try again with passcount = 1, don't ignore them.
230 If looking for all completions, start with passcount = 1,
231 so always take even the ignored ones.
233 ** It would not actually be helpful to the user to ignore any possible
234 completions when making a list of them.** */
236 for (passcount
= !!all_flag
; NULL (bestmatch
) && passcount
< 2; passcount
++)
238 if (!(d
= opendir (XSTRING (Fdirectory_file_name (dirname
))->data
)))
239 report_file_error ("Opening directory", Fcons (dirname
, Qnil
));
241 /* Loop reading blocks */
242 /* (att3b compiler bug requires do a null comparison this way) */
249 dp
= (*readfunc
) (d
);
257 if (!NULL (Vquit_flag
) && NULL (Vinhibit_quit
))
260 || len
< XSTRING (file
)->size
261 || 0 <= scmp (dp
->d_name
, XSTRING (file
)->data
,
262 XSTRING (file
)->size
))
265 if (file_name_completion_stat (dirname
, dp
, &st
) < 0)
268 directoryp
= ((st
.st_mode
& S_IFMT
) == S_IFDIR
);
272 /* Compare extensions-to-be-ignored against end of this file name */
273 /* if name is not an exact match against specified string */
274 if (!passcount
&& len
> XSTRING (file
)->size
)
275 /* and exit this for loop if a match is found */
276 for (tem
= Vcompletion_ignored_extensions
;
277 CONSP (tem
); tem
= XCONS (tem
)->cdr
)
279 elt
= XCONS (tem
)->car
;
280 if (XTYPE (elt
) != Lisp_String
) continue;
281 skip
= len
- XSTRING (elt
)->size
;
282 if (skip
< 0) continue;
284 if (0 <= scmp (dp
->d_name
+ skip
,
286 XSTRING (elt
)->size
))
292 /* Unless an ignored-extensions match was found,
293 process this name as a completion */
294 if (passcount
|| !CONSP (tem
))
296 /* Update computation of how much all possible completions match */
300 if (all_flag
|| NULL (bestmatch
))
302 /* This is a possible completion */
305 /* This completion is a directory; make it end with '/' */
306 name
= Ffile_name_as_directory (make_string (dp
->d_name
, len
));
309 name
= make_string (dp
->d_name
, len
);
312 bestmatch
= Fcons (name
, bestmatch
);
317 bestmatchsize
= XSTRING (name
)->size
;
322 compare
= min (bestmatchsize
, len
);
323 p1
= XSTRING (bestmatch
)->data
;
324 p2
= (unsigned char *) dp
->d_name
;
325 matchsize
= scmp(p1
, p2
, compare
);
328 /* If this dirname all matches,
329 see if implicit following slash does too. */
331 && compare
== matchsize
332 && bestmatchsize
> matchsize
333 && p1
[matchsize
] == '/')
335 bestmatchsize
= min (matchsize
, bestmatchsize
);
342 unbind_to (count
, Qnil
);
344 if (all_flag
|| NULL (bestmatch
))
346 if (matchcount
== 1 && bestmatchsize
== XSTRING (file
)->size
)
348 return Fsubstring (bestmatch
, make_number (0), make_number (bestmatchsize
));
352 return Fsignal (Qquit
, Qnil
);
355 file_name_completion_stat (dirname
, dp
, st_addr
)
358 struct stat
*st_addr
;
360 int len
= NAMLEN (dp
);
361 int pos
= XSTRING (dirname
)->size
;
362 char *fullname
= (char *) alloca (len
+ pos
+ 2);
364 bcopy (XSTRING (dirname
)->data
, fullname
, pos
);
366 if (fullname
[pos
- 1] != '/')
367 fullname
[pos
++] = '/';
370 bcopy (dp
->d_name
, fullname
+ pos
, len
);
371 fullname
[pos
+ len
] = 0;
373 return stat (fullname
, st_addr
);
380 return Fcons (make_number (time
>> 16),
381 Fcons (make_number (time
& 0177777), Qnil
));
384 DEFUN ("file-attributes", Ffile_attributes
, Sfile_attributes
, 1, 1, 0,
385 "Return a list of attributes of file FILENAME.\n\
386 Value is nil if specified file cannot be opened.\n\
387 Otherwise, list elements are:\n\
388 0. t for directory, string (name linked to) for symbolic link, or nil.\n\
389 1. Number of links to file.\n\
392 4. Last access time, as a list of two integers.\n\
393 First integer has high-order 16 bits of time, second has low 16 bits.\n\
394 5. Last modification time, likewise.\n\
395 6. Last status change time, likewise.\n\
397 8. File modes, as a string of ten letters or dashes as in ls -l.\n\
398 9. t iff file's gid would change if file were deleted and recreated.\n\
400 11. Device number.\n\
402 If file does not exists, returns nil.")
404 Lisp_Object filename
;
406 Lisp_Object values
[12];
412 filename
= Fexpand_file_name (filename
, Qnil
);
413 if (lstat (XSTRING (filename
)->data
, &s
) < 0)
416 switch (s
.st_mode
& S_IFMT
)
419 values
[0] = Qnil
; break;
421 values
[0] = Qt
; break;
424 values
[0] = Ffile_symlink_p (filename
); break;
427 values
[1] = make_number (s
.st_nlink
);
428 values
[2] = make_number (s
.st_uid
);
429 values
[3] = make_number (s
.st_gid
);
430 values
[4] = make_time (s
.st_atime
);
431 values
[5] = make_time (s
.st_mtime
);
432 values
[6] = make_time (s
.st_ctime
);
433 /* perhaps we should set this to most-positive-fixnum if it is too large? */
434 values
[7] = make_number (s
.st_size
);
435 filemodestring (&s
, modes
);
436 values
[8] = make_string (modes
, 10);
437 #ifdef BSD4_3 /* Gross kludge to avoid lack of "#if defined(...)" in VMS */
438 #define BSD4_2 /* A new meaning to the term `backwards compatability' */
440 #ifdef BSD4_2 /* file gid will be dir gid */
441 dirname
= Ffile_name_directory (filename
);
442 if (dirname
!= Qnil
&& stat (XSTRING (dirname
)->data
, &sdir
) == 0)
443 values
[9] = (sdir
.st_gid
!= s
.st_gid
) ? Qt
: Qnil
;
444 else /* if we can't tell, assume worst */
446 #else /* file gid will be egid */
447 values
[9] = (s
.st_gid
!= getegid ()) ? Qt
: Qnil
;
448 #endif /* BSD4_2 (or BSD4_3) */
450 #undef BSD4_2 /* ok, you can look again without throwing up */
452 values
[10] = make_number (s
.st_ino
);
453 values
[11] = make_number (s
.st_dev
);
454 return Flist (sizeof(values
) / sizeof(values
[0]), values
);
459 defsubr (&Sdirectory_files
);
460 defsubr (&Sfile_name_completion
);
462 defsubr (&Sfile_name_all_versions
);
464 defsubr (&Sfile_name_all_completions
);
465 defsubr (&Sfile_attributes
);
468 Qcompletion_ignore_case
= intern ("completion-ignore-case");
469 staticpro (&Qcompletion_ignore_case
);
472 DEFVAR_LISP ("completion-ignored-extensions", &Vcompletion_ignored_extensions
,
473 "*Completion ignores filenames ending in any string in this list.\n\
474 This variable does not affect lists of possible completions,\n\
475 but does affect the commands that actually do completions.");
476 Vcompletion_ignored_extensions
= Qnil
;