|
|
1.1 root 1: /* Lisp functions for making directory listings.
2: Copyright (C) 1985 Richard M. Stallman.
3:
4: This file is part of GNU Emacs.
5:
6: GNU Emacs is distributed in the hope that it will be useful,
7: but WITHOUT ANY WARRANTY. No author or distributor
8: accepts responsibility to anyone for the consequences of using it
9: or for whether it serves any particular purpose or works at all,
10: unless he says so in writing. Refer to the GNU Emacs General Public
11: License for full details.
12:
13: Everyone is granted permission to copy, modify and redistribute
14: GNU Emacs, but only under the conditions described in the
15: GNU Emacs General Public License. A copy of this license is
16: supposed to have been given to you along with GNU Emacs so you
17: can know your rights and responsibilities. It should be in a
18: file named COPYING. Among other things, the copyright notice
19: and this notice must be preserved on all copies. */
20:
21:
22: #include <stdio.h>
23: #include <sys/types.h>
24: #include <sys/stat.h>
25:
26: #include "config.h"
27:
28: #ifdef NONSYSTEM_DIR_LIBRARY
29: #include "ndir.h"
30: #else /* not NONSYSTEM_DIR_LIBRARY */
31: #include <sys/dir.h>
32: #endif /* not NONSYSTEM_DIR_LIBRARY */
33:
34: #undef NULL
35:
36: #include "lisp.h"
37: #include "buffer.h"
38: #include "commands.h"
39:
40: #define min(a, b) ((a) < (b) ? (a) : (b))
41:
42: /* if system does not have symbolic links, it does not have lstat.
43: In that case, use ordinary stat instead. */
44:
45: #ifndef S_IFLNK
46: #define lstat stat
47: #endif
48:
49: extern DIR *opendir ();
50: extern struct direct *readdir ();
51:
52: Lisp_Object Vcompletion_ignored_extensions;
53:
54: DEFUN ("directory-files", Fdirectory_files, Sdirectory_files, 1, 2, 0,
55: "Return a list of names of files in DIRECTORY.\n\
56: If FULL is non-NIL, absolute pathnames of the files are returned.")
57: (dirname, full)
58: Lisp_Object dirname, full;
59: {
60: DIR *d;
61: struct direct *dp;
62: char slashfilename[MAXNAMLEN+2];
63: char *filename = slashfilename;
64: int length;
65: Lisp_Object list, name;
66:
67: dirname = Fexpand_file_name (dirname, Qnil);
68: if (!(d = opendir (XSTRING (dirname)->data)))
69: report_file_error ("Opening directory", Fcons (dirname, Qnil));
70:
71: list = Qnil;
72: length = XSTRING (dirname)->size;
73: if (length == 0 || XSTRING (dirname)->data[length - 1] != '/')
74: *filename++ = '/';
75:
76: /* Loop reading blocks */
77: while (1)
78: {
79: dp = readdir (d);
80: if (!dp) break;
81: if (dp->d_ino)
82: {
83: strncpy (filename, dp->d_name, dp->d_namlen);
84: filename[dp->d_namlen] = 0;
85: if (!NULL (full))
86: name = concat2 (dirname, build_string (slashfilename));
87: else
88: name = build_string (filename);
89: list = Fcons (name, list);
90: }
91: }
92: closedir (d);
93: return Fsort (Fnreverse (list), Qstring_lessp);
94: }
95:
96: Lisp_Object file_name_completion ();
97:
98: DEFUN ("file-name-completion", Ffile_name_completion, Sfile_name_completion,
99: 2, 2, 0,
100: "Complete file name FILE in directory DIR.\n\
101: Returns the longest string common to all filenames in DIR\n\
102: that start with FILE.\n\
103: If there is only one and FILE matches it exactly, returns t.\n\
104: Returns nil if DIR contains no name starting with FILE.")
105: (file, dirname)
106: Lisp_Object file, dirname;
107: {
108: /* Don't waste time trying to complete a null string.
109: Besides, this case happens when user is being asked for
110: a directory name and has supplied one ending in a /.
111: We would not want to add anything in that case
112: even if there are some unique characters in that directory. */
113: if (XTYPE (file) == Lisp_String && XSTRING (file)->size == 0)
114: return file;
115: return file_name_completion (file, dirname, 0);
116: }
117:
118: DEFUN ("file-name-all-completions", Ffile_name_all_completions,
119: Sfile_name_all_completions, 2, 2, 0,
120: "Return a list of all completions of file name FILE in directory DIR.")
121: (file, dirname)
122: Lisp_Object file, dirname;
123: {
124: return file_name_completion (file, dirname, 1);
125: }
126:
127: Lisp_Object
128: file_name_completion (file, dirname, all_flag)
129: Lisp_Object file, dirname;
130: int all_flag;
131: {
132: DIR *d;
133: struct direct *dp;
134: int bestmatchsize, skip;
135: register int compare, matchsize;
136: unsigned char *p1, *p2;
137: int matchcount = 0;
138: Lisp_Object bestmatch, tem, elt, name;
139: struct stat st;
140: int directoryp;
141: int passcount;
142:
143: dirname = Fexpand_file_name (dirname, Qnil);
144: bestmatch = Qnil;
145:
146: /* passcount = 0, ignore files that end in an ignored extension.
147: If nothing found then try again with passcount = 1, don't ignore them.
148: If looking for all completions, start with passcount = 1,
149: so always take even the ignored ones. */
150: for (passcount = !!all_flag; NULL (bestmatch) && passcount < 2; passcount++)
151: {
152: if (!(d = opendir (XSTRING (dirname)->data)))
153: report_file_error ("Opening directory", Fcons (dirname, Qnil));
154:
155: /* Loop reading blocks */
156: while (dp = readdir (d))
157: {
158: if (!NULL (Vquit_flag) && NULL (Vinhibit_quit))
159: goto quit;
160: if (!dp->d_ino ||
161: dp->d_namlen < XSTRING (file)->size ||
162: bcmp (dp->d_name, XSTRING (file)->data, XSTRING (file)->size))
163: continue;
164:
165: tem = Qnil;
166: /* Compare extensions-to-be-ignored against end of this file name */
167: /* if name is not an exact match against specified string */
168: if (!passcount && dp->d_namlen > XSTRING (file)->size)
169: /* and exit this for loop if a match is found */
170: for (tem = Vcompletion_ignored_extensions;
171: LISTP (tem); tem = XCONS (tem)->cdr)
172: {
173: elt = XCONS (tem)->car;
174: if (XTYPE (elt) != Lisp_String) continue;
175: skip = dp->d_namlen - XSTRING (elt)->size;
176: if (skip < 0) continue;
177:
178: if (bcmp (dp->d_name + skip,
179: XSTRING (elt)->data,
180: XSTRING (elt)->size))
181: continue;
182: break;
183: }
184:
185: /* Unless a match was found, process this name as a completion */
186: if ((passcount || NULL (tem))
187: && file_name_completion_stat (dirname, dp, &st) >= 0)
188: {
189: /* Update computation of how much all possible completions match */
190:
191: matchcount++;
192: directoryp = ((st.st_mode & S_IFMT) == S_IFDIR);
193: if (all_flag || NULL (bestmatch))
194: {
195: /* This is a possible completion */
196: if (directoryp)
197: {
198: /* This completion is a directory; make it end with '/' */
199: name = make_string (dp->d_name, dp->d_namlen + 1);
200: XSTRING (name)->data[dp->d_namlen] = '/';
201: }
202: else
203: name = make_string (dp->d_name, dp->d_namlen);
204: if (all_flag)
205: {
206: bestmatch = Fcons (name, bestmatch);
207: }
208: else
209: {
210: bestmatch = name;
211: bestmatchsize = XSTRING (name)->size;
212: }
213: }
214: else
215: {
216: compare = min (bestmatchsize, dp->d_namlen);
217: p1 = XSTRING (bestmatch)->data;
218: p2 = (unsigned char *) dp->d_name;
219: for (matchsize = 0; matchsize < compare; matchsize++)
220: if (p1[matchsize] != p2[matchsize]) break;
221: /* If this dirname all matches,
222: see if implicit following slash does too. */
223: if (directoryp &&
224: compare == matchsize &&
225: bestmatchsize > matchsize &&
226: p1[matchsize] == '/')
227: matchsize++;
228: bestmatchsize = min (matchsize, bestmatchsize);
229: }
230: }
231: }
232: closedir (d);
233: }
234:
235: if (all_flag || NULL (bestmatch))
236: return bestmatch;
237: if (matchcount == 1 && bestmatchsize == XSTRING (file)->size)
238: return Qt;
239: return Fsubstring (bestmatch, make_number (0), make_number (bestmatchsize));
240: quit:
241: if (d) closedir (d);
242: Vquit_flag = Qnil;
243: return Fsignal (Qquit, Qnil);
244: }
245:
246: file_name_completion_stat (dirname, dp, st_addr)
247: Lisp_Object dirname;
248: struct direct *dp;
249: struct stat *st_addr;
250: {
251: char *fullname = (char *) alloca (dp->d_namlen + XSTRING (dirname)->size + 2);
252: int pos = XSTRING (dirname)->size;
253:
254: bcopy (XSTRING (dirname)->data, fullname, pos);
255: if (fullname[pos - 1] != '/')
256: fullname[pos++] = '/';
257:
258: bcopy (dp->d_name, fullname + pos, dp->d_namlen);
259: fullname[pos + dp->d_namlen] = 0;
260:
261: return stat (fullname, st_addr);
262: }
263:
264: Lisp_Object
265: make_time (time)
266: int time;
267: {
268: return Fcons (make_number (time >> 16),
269: Fcons (make_number (time & 0177777), Qnil));
270: }
271:
272: DEFUN ("file-attributes", Ffile_attributes, Sfile_attributes, 1, 1, 0,
273: "Return a list of attributes of file FILENAME.\n\
274: Elements are:\n\
275: 0. t for directory, string (name linked to) for symbolic link, or nil.\n\
276: 1. Number of links to file.\n\
277: 2. File uid.\n\
278: 3. File gid.\n\
279: 4. Last access time, as a list of two integers.\n\
280: First integer has high-order 16 bits of time, second has low 16 bits.\n\
281: 5. Last modification time, likewise.\n\
282: 6. Last status change time, likewise.\n\
283: 7. Size in bytes.\n\
284: 8. File modes, as a string of nine letters or dashes as in ls -l.")
285: (filename)
286: Lisp_Object filename;
287: {
288: Lisp_Object values[9];
289: struct stat s;
290: char modes[10];
291:
292: filename = Fexpand_file_name (filename, Qnil);
293: if (lstat(XSTRING (filename)->data, &s)<0)
294: return Qnil;
295:
296: values[0] = ((s.st_mode & S_IFMT)==S_IFDIR) ? Qt : Qnil;
297: #ifdef S_IFLNK
298: if ((s.st_mode & S_IFMT) == S_IFLNK)
299: values[0] = Ffile_symlink_p (filename);
300: #endif
301: XFASTINT (values[1]) = s.st_nlink;
302: XFASTINT (values[2]) = s.st_uid;
303: XFASTINT (values[3]) = s.st_gid;
304: values[4] = make_time(s.st_atime);
305: values[5] = make_time(s.st_mtime);
306: values[6] = make_time(s.st_ctime);
307: XFASTINT (values[7]) = s.st_size;
308: filemodestring (&s, modes);
309: values[8] = make_string (modes, 10);
310: return Flist (9, values);
311: }
312:
313: syms_of_dired ()
314: {
315: defsubr (&Sdirectory_files);
316: defsubr (&Sfile_name_completion);
317: defsubr (&Sfile_name_all_completions);
318: defsubr (&Sfile_attributes);
319:
320: DefLispVar ("completion-ignored-extensions", &Vcompletion_ignored_extensions,
321: "*Completion ignores filenames ending in any string in this list.");
322: Vcompletion_ignored_extensions = Qnil;
323: }
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.