* data.c (Fset): See if current_alist_element points to itself
[bpt/emacs.git] / src / dired.c
CommitLineData
14d55bce 1/* Lisp functions for making directory listings.
69b57d35 2 Copyright (C) 1985, 1986, 1992 Free Software Foundation, Inc.
14d55bce
RS
3
4This file is part of GNU Emacs.
5
6GNU Emacs is free software; you can redistribute it and/or modify
7it under the terms of the GNU General Public License as published by
8the Free Software Foundation; either version 1, or (at your option)
9any later version.
10
11GNU Emacs is distributed in the hope that it will be useful,
12but WITHOUT ANY WARRANTY; without even the implied warranty of
13MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14GNU General Public License for more details.
15
16You should have received a copy of the GNU General Public License
17along with GNU Emacs; see the file COPYING. If not, write to
18the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
19
20
21#include <stdio.h>
22#include <sys/types.h>
23#include <sys/stat.h>
24
25#include "config.h"
26
3ed991aa
RS
27#ifdef VMS
28#include <string.h>
29#include <rms.h>
30#include <rmsdef.h>
31#endif
32
14d55bce
RS
33#ifdef SYSV_SYSTEM_DIR
34
35#include <dirent.h>
36#define DIRENTRY struct dirent
37#define NAMLEN(p) strlen (p->d_name)
38
39#else
40
41#ifdef NONSYSTEM_DIR_LIBRARY
42#include "ndir.h"
43#else /* not NONSYSTEM_DIR_LIBRARY */
44#include <sys/dir.h>
45#endif /* not NONSYSTEM_DIR_LIBRARY */
46
47#define DIRENTRY struct direct
48#define NAMLEN(p) p->d_namlen
49
50extern DIR *opendir ();
51extern struct direct *readdir ();
52
53#endif
54
14d55bce
RS
55#include "lisp.h"
56#include "buffer.h"
57#include "commands.h"
58
59#include "regex.h"
14d55bce
RS
60
61#define min(a, b) ((a) < (b) ? (a) : (b))
62
63/* if system does not have symbolic links, it does not have lstat.
64 In that case, use ordinary stat instead. */
65
66#ifndef S_IFLNK
67#define lstat stat
68#endif
69
70Lisp_Object Vcompletion_ignored_extensions;
71
72Lisp_Object Qcompletion_ignore_case;
32f4334d
RS
73
74Lisp_Object Qdirectory_files;
75Lisp_Object Qfile_name_completion;
76Lisp_Object Qfile_name_all_completions;
434e6714 77Lisp_Object Qfile_attributes;
14d55bce
RS
78\f
79DEFUN ("directory-files", Fdirectory_files, Sdirectory_files, 1, 4, 0,
80 "Return a list of names of files in DIRECTORY.\n\
81There are three optional arguments:\n\
82If FULL is non-nil, absolute pathnames of the files are returned.\n\
83If MATCH is non-nil, only pathnames containing that regexp are returned.\n\
84If NOSORT is non-nil, the list is not sorted--its order is unpredictable.\n\
85 NOSORT is useful if you plan to sort the result yourself.")
86 (dirname, full, match, nosort)
87 Lisp_Object dirname, full, match, nosort;
88{
89 DIR *d;
90 int length;
91 Lisp_Object list, name;
32f4334d
RS
92 Lisp_Object handler;
93
94 /* If the file name has special constructs in it,
95 call the corresponding file handler. */
434e6714 96 handler = find_file_handler (dirname);
32f4334d
RS
97 if (!NILP (handler))
98 {
99 Lisp_Object args[6];
100
101 args[0] = handler;
102 args[1] = Qdirectory_files;
103 args[2] = dirname;
104 args[3] = full;
105 args[4] = match;
106 args[5] = nosort;
107 return Ffuncall (6, args);
108 }
14d55bce 109
265a9e55 110 if (!NILP (match))
14d55bce
RS
111 {
112 CHECK_STRING (match, 3);
ebb9e16f
JB
113
114 /* MATCH might be a flawed regular expression. Rather than
115 catching and signalling our own errors, we just call
116 compile_pattern to do the work for us. */
14d55bce 117#ifdef VMS
ebb9e16f 118 compile_pattern (match, &searchbuf, 0
14d55bce
RS
119 buffer_defaults.downcase_table->contents);
120#else
ebb9e16f 121 compile_pattern (match, &searchbuf, 0, 0);
14d55bce
RS
122#endif
123 }
124
125 dirname = Fexpand_file_name (dirname, Qnil);
126 if (!(d = opendir (XSTRING (Fdirectory_file_name (dirname))->data)))
127 report_file_error ("Opening directory", Fcons (dirname, Qnil));
128
129 list = Qnil;
130 length = XSTRING (dirname)->size;
131
132 /* Loop reading blocks */
133 while (1)
134 {
135 DIRENTRY *dp = readdir (d);
136 int len;
137
138 if (!dp) break;
139 len = NAMLEN (dp);
140 if (dp->d_ino)
141 {
265a9e55 142 if (NILP (match)
14d55bce
RS
143 || (0 <= re_search (&searchbuf, dp->d_name, len, 0, len, 0)))
144 {
265a9e55 145 if (!NILP (full))
14d55bce
RS
146 {
147 int index = XSTRING (dirname)->size;
148 int total = len + index;
149#ifndef VMS
150 if (length == 0
151 || XSTRING (dirname)->data[length - 1] != '/')
152 total++;
153#endif /* VMS */
154
155 name = make_uninit_string (total);
156 bcopy (XSTRING (dirname)->data, XSTRING (name)->data,
157 index);
158#ifndef VMS
159 if (length == 0
160 || XSTRING (dirname)->data[length - 1] != '/')
161 XSTRING (name)->data[index++] = '/';
162#endif /* VMS */
163 bcopy (dp->d_name, XSTRING (name)->data + index, len);
164 }
165 else
166 name = make_string (dp->d_name, len);
167 list = Fcons (name, list);
168 }
169 }
170 }
171 closedir (d);
265a9e55 172 if (!NILP (nosort))
14d55bce
RS
173 return list;
174 return Fsort (Fnreverse (list), Qstring_lessp);
175}
176\f
177Lisp_Object file_name_completion ();
178
179DEFUN ("file-name-completion", Ffile_name_completion, Sfile_name_completion,
180 2, 2, 0,
181 "Complete file name FILE in directory DIR.\n\
182Returns the longest string\n\
183common to all filenames in DIR that start with FILE.\n\
184If there is only one and FILE matches it exactly, returns t.\n\
185Returns nil if DIR contains no name starting with FILE.")
186 (file, dirname)
187 Lisp_Object file, dirname;
188{
32f4334d 189 Lisp_Object handler;
14d55bce
RS
190 /* Don't waste time trying to complete a null string.
191 Besides, this case happens when user is being asked for
192 a directory name and has supplied one ending in a /.
193 We would not want to add anything in that case
194 even if there are some unique characters in that directory. */
195 if (XTYPE (file) == Lisp_String && XSTRING (file)->size == 0)
196 return file;
32f4334d
RS
197
198 /* If the file name has special constructs in it,
199 call the corresponding file handler. */
434e6714 200 handler = find_file_handler (dirname);
32f4334d
RS
201 if (!NILP (handler))
202 return call3 (handler, Qfile_name_completion, file, dirname);
203
14d55bce
RS
204 return file_name_completion (file, dirname, 0, 0);
205}
206
207DEFUN ("file-name-all-completions", Ffile_name_all_completions,
208 Sfile_name_all_completions, 2, 2, 0,
209 "Return a list of all completions of file name FILE in directory DIR.\n\
210These are all file names in directory DIR which begin with FILE.")
211 (file, dirname)
212 Lisp_Object file, dirname;
213{
32f4334d
RS
214 Lisp_Object handler;
215
216 /* If the file name has special constructs in it,
217 call the corresponding file handler. */
434e6714 218 handler = find_file_handler (dirname);
32f4334d
RS
219 if (!NILP (handler))
220 return call3 (handler, Qfile_name_all_completions, file, dirname);
221
14d55bce
RS
222 return file_name_completion (file, dirname, 1, 0);
223}
224
14d55bce
RS
225Lisp_Object
226file_name_completion (file, dirname, all_flag, ver_flag)
227 Lisp_Object file, dirname;
228 int all_flag, ver_flag;
229{
230 DIR *d;
231 DIRENTRY *dp;
232 int bestmatchsize, skip;
233 register int compare, matchsize;
234 unsigned char *p1, *p2;
235 int matchcount = 0;
236 Lisp_Object bestmatch, tem, elt, name;
237 struct stat st;
238 int directoryp;
239 int passcount;
240 int count = specpdl_ptr - specpdl;
241#ifdef VMS
242 extern DIRENTRY * readdirver ();
243
244 DIRENTRY *((* readfunc) ());
245
246 /* Filename completion on VMS ignores case, since VMS filesys does. */
247 specbind (Qcompletion_ignore_case, Qt);
248
249 readfunc = readdir;
250 if (ver_flag)
251 readfunc = readdirver;
252 file = Fupcase (file);
253#else /* not VMS */
254 CHECK_STRING (file, 0);
255#endif /* not VMS */
256
257 dirname = Fexpand_file_name (dirname, Qnil);
258 bestmatch = Qnil;
259
260 /* With passcount = 0, ignore files that end in an ignored extension.
261 If nothing found then try again with passcount = 1, don't ignore them.
262 If looking for all completions, start with passcount = 1,
263 so always take even the ignored ones.
264
265 ** It would not actually be helpful to the user to ignore any possible
266 completions when making a list of them.** */
267
265a9e55 268 for (passcount = !!all_flag; NILP (bestmatch) && passcount < 2; passcount++)
14d55bce
RS
269 {
270 if (!(d = opendir (XSTRING (Fdirectory_file_name (dirname))->data)))
271 report_file_error ("Opening directory", Fcons (dirname, Qnil));
272
273 /* Loop reading blocks */
274 /* (att3b compiler bug requires do a null comparison this way) */
275 while (1)
276 {
277 DIRENTRY *dp;
278 int len;
279
280#ifdef VMS
281 dp = (*readfunc) (d);
282#else
283 dp = readdir (d);
284#endif
285 if (!dp) break;
286
287 len = NAMLEN (dp);
288
265a9e55 289 if (!NILP (Vquit_flag) && NILP (Vinhibit_quit))
14d55bce
RS
290 goto quit;
291 if (!dp->d_ino
292 || len < XSTRING (file)->size
293 || 0 <= scmp (dp->d_name, XSTRING (file)->data,
294 XSTRING (file)->size))
295 continue;
296
297 if (file_name_completion_stat (dirname, dp, &st) < 0)
298 continue;
299
300 directoryp = ((st.st_mode & S_IFMT) == S_IFDIR);
301 tem = Qnil;
302 if (!directoryp)
303 {
304 /* Compare extensions-to-be-ignored against end of this file name */
305 /* if name is not an exact match against specified string */
306 if (!passcount && len > XSTRING (file)->size)
307 /* and exit this for loop if a match is found */
308 for (tem = Vcompletion_ignored_extensions;
309 CONSP (tem); tem = XCONS (tem)->cdr)
310 {
311 elt = XCONS (tem)->car;
312 if (XTYPE (elt) != Lisp_String) continue;
313 skip = len - XSTRING (elt)->size;
314 if (skip < 0) continue;
315
316 if (0 <= scmp (dp->d_name + skip,
317 XSTRING (elt)->data,
318 XSTRING (elt)->size))
319 continue;
320 break;
321 }
322 }
323
324 /* Unless an ignored-extensions match was found,
325 process this name as a completion */
326 if (passcount || !CONSP (tem))
327 {
328 /* Update computation of how much all possible completions match */
329
330 matchcount++;
331
265a9e55 332 if (all_flag || NILP (bestmatch))
14d55bce
RS
333 {
334 /* This is a possible completion */
335 if (directoryp)
336 {
337 /* This completion is a directory; make it end with '/' */
338 name = Ffile_name_as_directory (make_string (dp->d_name, len));
339 }
340 else
341 name = make_string (dp->d_name, len);
342 if (all_flag)
343 {
344 bestmatch = Fcons (name, bestmatch);
345 }
346 else
347 {
348 bestmatch = name;
349 bestmatchsize = XSTRING (name)->size;
350 }
351 }
352 else
353 {
354 compare = min (bestmatchsize, len);
355 p1 = XSTRING (bestmatch)->data;
356 p2 = (unsigned char *) dp->d_name;
357 matchsize = scmp(p1, p2, compare);
358 if (matchsize < 0)
359 matchsize = compare;
360 /* If this dirname all matches,
361 see if implicit following slash does too. */
362 if (directoryp
363 && compare == matchsize
364 && bestmatchsize > matchsize
365 && p1[matchsize] == '/')
366 matchsize++;
367 bestmatchsize = min (matchsize, bestmatchsize);
368 }
369 }
370 }
371 closedir (d);
372 }
373
374 unbind_to (count, Qnil);
375
265a9e55 376 if (all_flag || NILP (bestmatch))
14d55bce
RS
377 return bestmatch;
378 if (matchcount == 1 && bestmatchsize == XSTRING (file)->size)
379 return Qt;
380 return Fsubstring (bestmatch, make_number (0), make_number (bestmatchsize));
381 quit:
382 if (d) closedir (d);
383 Vquit_flag = Qnil;
384 return Fsignal (Qquit, Qnil);
385}
386
387file_name_completion_stat (dirname, dp, st_addr)
388 Lisp_Object dirname;
389 DIRENTRY *dp;
390 struct stat *st_addr;
391{
392 int len = NAMLEN (dp);
393 int pos = XSTRING (dirname)->size;
394 char *fullname = (char *) alloca (len + pos + 2);
395
396 bcopy (XSTRING (dirname)->data, fullname, pos);
397#ifndef VMS
398 if (fullname[pos - 1] != '/')
399 fullname[pos++] = '/';
400#endif
401
402 bcopy (dp->d_name, fullname + pos, len);
403 fullname[pos + len] = 0;
404
405 return stat (fullname, st_addr);
406}
407\f
3ed991aa
RS
408#ifdef VMS
409
410DEFUN ("file-name-all-versions", Ffile_name_all_versions,
411 Sfile_name_all_versions, 2, 2, 0,
412 "Return a list of all versions of file name FILE in directory DIR.")
413 (file, dirname)
414 Lisp_Object file, dirname;
415{
416 return file_name_completion (file, dirname, 1, 1);
417}
418
419DEFUN ("file-version-limit", Ffile_version_limit, Sfile_version_limit, 1, 1, 0,
420 "Return the maximum number of versions allowed for FILE.\n\
421Returns nil if the file cannot be opened or if there is no version limit.")
422 (filename)
423 Lisp_Object filename;
424{
425 Lisp_Object retval;
426 struct FAB fab;
427 struct RAB rab;
428 struct XABFHC xabfhc;
429 int status;
430
431 filename = Fexpand_file_name (filename, Qnil);
432 fab = cc$rms_fab;
433 xabfhc = cc$rms_xabfhc;
434 fab.fab$l_fna = XSTRING (filename)->data;
435 fab.fab$b_fns = strlen (fab.fab$l_fna);
436 fab.fab$l_xab = (char *) &xabfhc;
437 status = sys$open (&fab, 0, 0);
438 if (status != RMS$_NORMAL) /* Probably non-existent file */
439 return Qnil;
440 sys$close (&fab, 0, 0);
441 if (xabfhc.xab$w_verlimit == 32767)
442 return Qnil; /* No version limit */
443 else
444 return make_number (xabfhc.xab$w_verlimit);
445}
446
447#endif /* VMS */
448\f
14d55bce
RS
449Lisp_Object
450make_time (time)
451 int time;
452{
453 return Fcons (make_number (time >> 16),
454 Fcons (make_number (time & 0177777), Qnil));
455}
456
457DEFUN ("file-attributes", Ffile_attributes, Sfile_attributes, 1, 1, 0,
458 "Return a list of attributes of file FILENAME.\n\
459Value is nil if specified file cannot be opened.\n\
460Otherwise, list elements are:\n\
461 0. t for directory, string (name linked to) for symbolic link, or nil.\n\
462 1. Number of links to file.\n\
463 2. File uid.\n\
464 3. File gid.\n\
465 4. Last access time, as a list of two integers.\n\
466 First integer has high-order 16 bits of time, second has low 16 bits.\n\
467 5. Last modification time, likewise.\n\
468 6. Last status change time, likewise.\n\
469 7. Size in bytes.\n\
470 8. File modes, as a string of ten letters or dashes as in ls -l.\n\
471 9. t iff file's gid would change if file were deleted and recreated.\n\
47210. inode number.\n\
47311. Device number.\n\
474\n\
475If file does not exists, returns nil.")
476 (filename)
477 Lisp_Object filename;
478{
479 Lisp_Object values[12];
480 Lisp_Object dirname;
481 struct stat s;
482 struct stat sdir;
483 char modes[10];
32f4334d 484 Lisp_Object handler;
14d55bce
RS
485
486 filename = Fexpand_file_name (filename, Qnil);
32f4334d
RS
487
488 /* If the file name has special constructs in it,
489 call the corresponding file handler. */
490 handler = find_file_handler (filename);
491 if (!NILP (handler))
492 return call2 (handler, Qfile_attributes, filename);
493
14d55bce
RS
494 if (lstat (XSTRING (filename)->data, &s) < 0)
495 return Qnil;
496
497 switch (s.st_mode & S_IFMT)
498 {
499 default:
500 values[0] = Qnil; break;
501 case S_IFDIR:
502 values[0] = Qt; break;
503#ifdef S_IFLNK
504 case S_IFLNK:
505 values[0] = Ffile_symlink_p (filename); break;
506#endif
507 }
508 values[1] = make_number (s.st_nlink);
509 values[2] = make_number (s.st_uid);
510 values[3] = make_number (s.st_gid);
511 values[4] = make_time (s.st_atime);
512 values[5] = make_time (s.st_mtime);
513 values[6] = make_time (s.st_ctime);
514 /* perhaps we should set this to most-positive-fixnum if it is too large? */
515 values[7] = make_number (s.st_size);
516 filemodestring (&s, modes);
517 values[8] = make_string (modes, 10);
518#ifdef BSD4_3 /* Gross kludge to avoid lack of "#if defined(...)" in VMS */
519#define BSD4_2 /* A new meaning to the term `backwards compatability' */
520#endif
521#ifdef BSD4_2 /* file gid will be dir gid */
522 dirname = Ffile_name_directory (filename);
523 if (dirname != Qnil && stat (XSTRING (dirname)->data, &sdir) == 0)
524 values[9] = (sdir.st_gid != s.st_gid) ? Qt : Qnil;
525 else /* if we can't tell, assume worst */
526 values[9] = Qt;
527#else /* file gid will be egid */
528 values[9] = (s.st_gid != getegid ()) ? Qt : Qnil;
529#endif /* BSD4_2 (or BSD4_3) */
530#ifdef BSD4_3
531#undef BSD4_2 /* ok, you can look again without throwing up */
532#endif
533 values[10] = make_number (s.st_ino);
534 values[11] = make_number (s.st_dev);
535 return Flist (sizeof(values) / sizeof(values[0]), values);
536}
537\f
538syms_of_dired ()
539{
32f4334d
RS
540 Qdirectory_files = intern ("directory-files");
541 Qfile_name_completion = intern ("file-name-completion");
542 Qfile_name_all_completions = intern ("file-name-all-completions");
434e6714 543 Qfile_attributes = intern ("file-attributes");
32f4334d 544
14d55bce
RS
545 defsubr (&Sdirectory_files);
546 defsubr (&Sfile_name_completion);
547#ifdef VMS
548 defsubr (&Sfile_name_all_versions);
3ed991aa 549 defsubr (&Sfile_version_limit);
14d55bce
RS
550#endif /* VMS */
551 defsubr (&Sfile_name_all_completions);
552 defsubr (&Sfile_attributes);
553
554#ifdef VMS
555 Qcompletion_ignore_case = intern ("completion-ignore-case");
556 staticpro (&Qcompletion_ignore_case);
557#endif /* VMS */
558
559 DEFVAR_LISP ("completion-ignored-extensions", &Vcompletion_ignored_extensions,
560 "*Completion ignores filenames ending in any string in this list.\n\
561This variable does not affect lists of possible completions,\n\
562but does affect the commands that actually do completions.");
563 Vcompletion_ignored_extensions = Qnil;
564}