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