|
|
1.1 root 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: #undef NULL
50:
51: #include "lisp.h"
52: #include "buffer.h"
53: #include "commands.h"
54:
55: #include "regex.h"
56: #include "filetypes.h"
57:
58: #define min(a, b) ((a) < (b) ? (a) : (b))
59:
60: /* if system does not have symbolic links, it does not have lstat.
61: In that case, use ordinary stat instead. */
62:
63: #ifndef S_IFLNK
64: #define lstat stat
65: #endif
66:
67: extern int completion_ignore_case;
68:
69: Lisp_Object Vcompletion_ignored_extensions;
70:
71: Lisp_Object Qcompletion_ignore_case;
72:
73: DEFUN ("directory-files", Fdirectory_files, Sdirectory_files, 1, 3, 0,
74: "Return a list of names of files in DIRECTORY.\n\
75: If FULL is non-NIL, absolute pathnames of the files are returned.\n\
76: If MATCH is non-NIL, only pathnames containing that regexp are returned.")
77: (dirname, full, match)
78: Lisp_Object dirname, full, match;
79: {
80: DIR *d;
81: char slashfilename[MAXNAMLEN+2];
82: char *filename = slashfilename;
83: int length;
84: Lisp_Object list, name;
85:
86: /* In search.c */
87: extern struct re_pattern_buffer searchbuf;
88:
89: if (!NULL (match))
90: {
91: CHECK_STRING (match, 3);
92: /* Compile it now so we don't get an error after opendir */
93: #ifdef VMS
94: compile_pattern (match, &searchbuf, (char *) downcase_table);
95: #else
96: compile_pattern (match, &searchbuf, 0);
97: #endif
98: }
99:
100: dirname = Fexpand_file_name (dirname, Qnil);
101: if (!(d = opendir (XSTRING (Fdirectory_file_name (dirname))->data)))
102: report_file_error ("Opening directory", Fcons (dirname, Qnil));
103:
104: list = Qnil;
105: length = XSTRING (dirname)->size;
106: #ifndef VMS
107: if (length == 0 || XSTRING (dirname)->data[length - 1] != '/')
108: *filename++ = '/';
109: #endif /* VMS */
110:
111: /* Loop reading blocks */
112: while (1)
113: {
114: DIRENTRY *dp = readdir (d);
115: int len;
116:
117: if (!dp) break;
118: len = NAMLEN (dp);
119: if (dp->d_ino)
120: {
121: strncpy (filename, dp->d_name, len);
122: filename[len] = 0;
123: if (NULL (match) ||
124: (0 <= re_search (&searchbuf, filename, len, 0, len, 0)))
125: {
126: if (!NULL (full))
127: name = concat2 (dirname, build_string (slashfilename));
128: else
129: name = build_string (filename);
130: list = Fcons (name, list);
131: }
132: }
133: }
134: closedir (d);
135: return Fsort (Fnreverse (list), Qstring_lessp);
136: }
137:
138: Lisp_Object file_name_completion ();
139:
140: DEFUN ("file-name-completion", Ffile_name_completion, Sfile_name_completion,
141: 2, 2, 0,
142: "Complete file name FILE in directory DIR.\n\
143: Returns the longest string common to all filenames in DIR\n\
144: that start with FILE.\n\
145: If there is only one and FILE matches it exactly, returns t.\n\
146: Returns nil if DIR contains no name starting with FILE.")
147: (file, dirname)
148: Lisp_Object file, dirname;
149: {
150: /* Don't waste time trying to complete a null string.
151: Besides, this case happens when user is being asked for
152: a directory name and has supplied one ending in a /.
153: We would not want to add anything in that case
154: even if there are some unique characters in that directory. */
155: if (XTYPE (file) == Lisp_String && XSTRING (file)->size == 0)
156: return file;
157: return file_name_completion (file, dirname, 0, 0);
158: }
159:
160: DEFUN ("file-name-all-completions", Ffile_name_all_completions,
161: Sfile_name_all_completions, 2, 2, 0,
162: "Return a list of all completions of file name FILE in directory DIR.")
163: (file, dirname)
164: Lisp_Object file, dirname;
165: {
166: return file_name_completion (file, dirname, 1, 0);
167: }
168:
169: #ifdef VMS
170:
171: DEFUN ("file-name-all-versions", Ffile_name_all_versions,
172: Sfile_name_all_versions, 2, 2, 0,
173: "Return a list of all versions of file name FILE in directory DIR.")
174: (file, dirname)
175: Lisp_Object file, dirname;
176: {
177: return file_name_completion (file, dirname, 1, 1);
178: }
179:
180: #endif /* VMS */
181:
182: Lisp_Object
183: file_name_completion (file, dirname, all_flag, ver_flag)
184: Lisp_Object file, dirname;
185: int all_flag, ver_flag;
186: {
187: DIR *d;
188: int bestmatchsize, skip;
189: register int compare, matchsize;
190: unsigned char *p1, *p2;
191: int matchcount = 0;
192: Lisp_Object bestmatch, tem, elt, name;
193: struct stat st;
194: int directoryp;
195: int passcount;
196: int count = specpdl_ptr - specpdl;
197: #ifdef VMS
198: extern DIRENTRY * readdirver ();
199:
200: DIRENTRY *((* readfunc) ());
201:
202: /* Filename completion on VMS ignores case, since VMS filesys does. */
203: specbind (Qcompletion_ignore_case, Qt);
204:
205: readfunc = readdir;
206: if (ver_flag)
207: readfunc = readdirver;
208: file = Fupcase (file);
209: #endif /* VMS */
210:
211: CHECK_STRING (file, 0);
212:
213: dirname = Fexpand_file_name (dirname, Qnil);
214: bestmatch = Qnil;
215:
216: /* passcount = 0, ignore files that end in an ignored extension.
217: If nothing found then try again with passcount = 1, don't ignore them.
218: If looking for all completions, start with passcount = 1,
219: so always take even the ignored ones. */
220: for (passcount = !!all_flag; NULL (bestmatch) && passcount < 2; passcount++)
221: {
222: if (!(d = opendir (XSTRING (Fdirectory_file_name (dirname))->data)))
223: report_file_error ("Opening directory", Fcons (dirname, Qnil));
224:
225: /* Loop reading blocks */
226: /* (att3b compiler bug requires do a null comparison this way) */
227: while (1)
228: {
229: DIRENTRY *dp;
230: int len;
231:
232: #ifdef VMS
233: dp = (*readfunc) (d);
234: #else
235: dp = readdir (d);
236: #endif
237: if (!dp) break;
238:
239: len = NAMLEN (dp);
240:
241: if (!NULL (Vquit_flag) && NULL (Vinhibit_quit))
242: goto quit;
243: if (!dp->d_ino
244: || len < XSTRING (file)->size
245: || 0 <= scmp (dp->d_name, XSTRING (file)->data,
246: XSTRING (file)->size))
247: continue;
248:
249: if (file_name_completion_stat (dirname, dp, &st) < 0)
250: continue;
251:
252: directoryp = ((st.st_mode & S_IFMT) == S_IFDIR);
253: tem = Qnil;
254: if (!directoryp)
255: {
256: /* Compare extensions-to-be-ignored against end of this file name */
257: /* if name is not an exact match against specified string */
258: if (!passcount && len > XSTRING (file)->size)
259: /* and exit this for loop if a match is found */
260: for (tem = Vcompletion_ignored_extensions;
261: CONSP (tem); tem = XCONS (tem)->cdr)
262: {
263: elt = XCONS (tem)->car;
264: if (XTYPE (elt) != Lisp_String) continue;
265: skip = len - XSTRING (elt)->size;
266: if (skip < 0) continue;
267:
268: if (0 <= scmp (dp->d_name + skip,
269: XSTRING (elt)->data,
270: XSTRING (elt)->size))
271: continue;
272: break;
273: }
274: }
275:
276: /* Unless an ignored-extensions match was found,
277: process this name as a completion */
278: if (passcount || !CONSP (tem))
279: {
280: /* Update computation of how much all possible completions match */
281:
282: matchcount++;
283:
284: if (all_flag || NULL (bestmatch))
285: {
286: /* This is a possible completion */
287: if (directoryp)
288: {
289: /* This completion is a directory; make it end with '/' */
290: name = Ffile_name_as_directory (make_string (dp->d_name, len));
291: }
292: else
293: name = make_string (dp->d_name, len);
294: if (all_flag)
295: {
296: bestmatch = Fcons (name, bestmatch);
297: }
298: else
299: {
300: bestmatch = name;
301: bestmatchsize = XSTRING (name)->size;
302: }
303: }
304: else
305: {
306: compare = min (bestmatchsize, len);
307: p1 = XSTRING (bestmatch)->data;
308: p2 = (unsigned char *) dp->d_name;
309: matchsize = scmp(p1, p2, compare);
310: if (matchsize < 0)
311: matchsize = compare;
312: if (completion_ignore_case)
313: {
314: /* If this is an exact match except for case,
315: use it as the best match rather than one that is not
316: an exact match. This way, we get the case pattern
317: of the actual match. */
318: if ((matchsize == len
319: && matchsize < XSTRING (bestmatch)->size)
320: ||
321: /* If there is no exact match ignoring case,
322: prefer a match that does not change the case
323: of the input. */
324: (((matchsize == len)
325: ==
326: (matchsize == XSTRING (bestmatch)->size))
327: /* If there is more than one exact match aside from
328: case, and one of them is exact including case,
329: prefer that one. */
330: && !bcmp (p2, XSTRING (file)->data, XSTRING (file)->size)
331: && bcmp (p1, XSTRING (file)->data, XSTRING (file)->size)))
332: {
333: bestmatch = make_string (dp->d_name, len);
334: if (directoryp)
335: bestmatch = Ffile_name_as_directory (bestmatch);
336: }
337: }
338: /* If this dirname all matches,
339: see if implicit following slash does too. */
340: if (directoryp
341: && compare == matchsize
342: && bestmatchsize > matchsize
343: && p1[matchsize] == '/')
344: matchsize++;
345: bestmatchsize = matchsize;
346: }
347: }
348: }
349: closedir (d);
350: }
351:
352: unbind_to (count);
353:
354: if (all_flag || NULL (bestmatch))
355: return bestmatch;
356:
357: /* If we are ignoring case, and there is no exact match,
358: and no additional text was supplied,
359: don't change the case of what the user typed. */
360: if (completion_ignore_case && bestmatchsize == XSTRING (file)->size
361: && XSTRING (bestmatch)->size > bestmatchsize)
362: return file;
363:
364: /* Return t if the supplied string is an exact match (counting case);
365: it does not require any change to be made. */
366: if (matchcount == 1 && bestmatchsize == XSTRING (file)->size
367: && !bcmp (XSTRING (bestmatch)->data, XSTRING (file)->data,
368: bestmatchsize))
369: return Qt;
370: return Fsubstring (bestmatch, make_number (0), make_number (bestmatchsize));
371: quit:
372: if (d) closedir (d);
373: Vquit_flag = Qnil;
374: return Fsignal (Qquit, Qnil);
375: }
376:
377: file_name_completion_stat (dirname, dp, st_addr)
378: Lisp_Object dirname;
379: DIRENTRY *dp;
380: struct stat *st_addr;
381: {
382: int len = NAMLEN (dp);
383: int pos = XSTRING (dirname)->size;
384: char *fullname = (char *) alloca (len + pos + 2);
385:
386: bcopy (XSTRING (dirname)->data, fullname, pos);
387: #ifndef VMS
388: if (fullname[pos - 1] != '/')
389: fullname[pos++] = '/';
390: #endif
391:
392: bcopy (dp->d_name, fullname + pos, len);
393: fullname[pos + len] = 0;
394:
395: #ifdef S_IFLNK
396: /* Use ordinary stat first, if that succeeds,
397: so that a symlink pointing to a directory is considered a directory.
398: Afterward, use lstat, so that a link pointing to nowhere
399: at least seems to exist. */
400: if (stat (fullname, st_addr) >= 0)
401: return 0;
402: return lstat (fullname, st_addr);
403: #else
404: return stat (fullname, st_addr);
405: #endif
406: }
407:
408: Lisp_Object
409: make_time (time)
410: int time;
411: {
412: return Fcons (make_number (time >> 16),
413: Fcons (make_number (time & 0177777), Qnil));
414: }
415:
416: DEFUN ("file-attributes", Ffile_attributes, Sfile_attributes, 1, 1, 0,
417: "Return a list of attributes of file FILENAME.\n\
418: Value is nil if specified file cannot be opened.\n\
419: Otherwise, list elements are:\n\
420: 0. t for directory, string (name linked to) for symbolic link, or nil.\n\
421: 1. Number of links to file.\n\
422: 2. File uid.\n\
423: 3. File gid.\n\
424: 4. Last access time, as a list of two integers.\n\
425: First integer has high-order 16 bits of time, second has low 16 bits.\n\
426: 5. Last modification time, likewise.\n\
427: 6. Last status change time, likewise.\n\
428: 7. Size in bytes.\n\
429: 8. File modes, as a string of ten letters or dashes as in ls -l.\n\
430: 9. t iff file's gid would change if file were deleted and recreated.\n\
431: 10. inode number.\n\
432: \n\
433: If file does not exists, returns nil.")
434: (filename)
435: Lisp_Object filename;
436: {
437: Lisp_Object values[11];
438: Lisp_Object dirname;
439: struct stat s;
440: struct stat sdir;
441: char modes[10];
442:
443: filename = Fexpand_file_name (filename, Qnil);
444: if (lstat (XSTRING (filename)->data, &s) < 0)
445: return Qnil;
446:
447: switch (s.st_mode & S_IFMT)
448: {
449: default:
450: values[0] = Qnil; break;
451: case S_IFDIR:
452: values[0] = Qt; break;
453: #ifdef S_IFLNK
454: case S_IFLNK:
455: values[0] = Ffile_symlink_p (filename); break;
456: #endif
457: }
458: values[1] = make_number (s.st_nlink);
459: values[2] = make_number (s.st_uid);
460: values[3] = make_number (s.st_gid);
461: values[4] = make_time (s.st_atime);
462: values[5] = make_time (s.st_mtime);
463: values[6] = make_time (s.st_ctime);
464: /* perhaps we should set this to most-positive-fixnum if it is too large? */
465: values[7] = make_number (s.st_size);
466: filemodestring (&s, modes);
467: values[8] = make_string (modes, 10);
468: #ifdef BSD4_3 /* Gross kludge to avoid lack of "#if defined(...)" in VMS */
469: #define BSD4_2 /* A new meaning to the term `backwards compatability' */
470: #endif
471: #ifdef BSD4_2 /* file gid will be dir gid */
472: dirname = Ffile_name_directory (filename);
473: if (dirname != Qnil && stat (XSTRING (dirname)->data, &sdir) == 0)
474: values[9] = (sdir.st_gid != s.st_gid) ? Qt : Qnil;
475: else /* if we can't tell, assume worst */
476: values[9] = Qt;
477: #else /* file gid will be egid */
478: values[9] = (s.st_gid != getegid ()) ? Qt : Qnil;
479: #endif /* BSD4_2 (or BSD4_3) */
480: #ifdef BSD4_3
481: #undef BSD4_2 /* ok, you can look again without throwing up */
482: #endif
483: values[10] = make_number (s.st_ino);
484: return Flist (11, values);
485: }
486:
487: syms_of_dired ()
488: {
489: defsubr (&Sdirectory_files);
490: defsubr (&Sfile_name_completion);
491: #ifdef VMS
492: defsubr (&Sfile_name_all_versions);
493: #endif /* VMS */
494: defsubr (&Sfile_name_all_completions);
495: defsubr (&Sfile_attributes);
496:
497: #ifdef VMS
498: Qcompletion_ignore_case = intern ("completion-ignore-case");
499: staticpro (&Qcompletion_ignore_case);
500: #endif /* VMS */
501:
502: DEFVAR_LISP ("completion-ignored-extensions", &Vcompletion_ignored_extensions,
503: "*Completion ignores filenames ending in any string in this list.");
504: Vcompletion_ignored_extensions = Qnil;
505: }
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.