Mercurial > hg > xemacs-beta
comparison src/dired.c @ 428:3ecd8885ac67 r21-2-22
Import from CVS: tag r21-2-22
author | cvs |
---|---|
date | Mon, 13 Aug 2007 11:28:15 +0200 |
parents | |
children | 8de8e3f6228a |
comparison
equal
deleted
inserted
replaced
427:0a0253eac470 | 428:3ecd8885ac67 |
---|---|
1 /* Lisp functions for making directory listings. | |
2 Copyright (C) 1985, 1986, 1992, 1993, 1994 Free Software Foundation, Inc. | |
3 | |
4 This file is part of XEmacs. | |
5 | |
6 XEmacs is free software; you can redistribute it and/or modify it | |
7 under the terms of the GNU General Public License as published by the | |
8 Free Software Foundation; either version 2, or (at your option) any | |
9 later version. | |
10 | |
11 XEmacs is distributed in the hope that it will be useful, but WITHOUT | |
12 ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | |
13 FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License | |
14 for more details. | |
15 | |
16 You should have received a copy of the GNU General Public License | |
17 along with XEmacs; see the file COPYING. If not, write to | |
18 the Free Software Foundation, Inc., 59 Temple Place - Suite 330, | |
19 Boston, MA 02111-1307, USA. */ | |
20 | |
21 /* Synched up with: FSF 19.30. */ | |
22 | |
23 #include <config.h> | |
24 #include "lisp.h" | |
25 | |
26 #include "buffer.h" | |
27 #include "commands.h" | |
28 #include "elhash.h" | |
29 #include "regex.h" | |
30 #include "opaque.h" | |
31 #include "sysfile.h" | |
32 #include "sysdir.h" | |
33 #include "systime.h" | |
34 #include "sysdep.h" | |
35 #include "syspwd.h" | |
36 | |
37 Lisp_Object Vcompletion_ignored_extensions; | |
38 Lisp_Object Qdirectory_files; | |
39 Lisp_Object Qfile_name_completion; | |
40 Lisp_Object Qfile_name_all_completions; | |
41 Lisp_Object Qfile_attributes; | |
42 | |
43 static Lisp_Object | |
44 close_directory_unwind (Lisp_Object unwind_obj) | |
45 { | |
46 DIR *d = (DIR *)get_opaque_ptr (unwind_obj); | |
47 closedir (d); | |
48 free_opaque_ptr (unwind_obj); | |
49 return Qnil; | |
50 } | |
51 | |
52 DEFUN ("directory-files", Fdirectory_files, 1, 5, 0, /* | |
53 Return a list of names of files in DIRECTORY. | |
54 There are four optional arguments: | |
55 If FULL is non-nil, absolute pathnames of the files are returned. | |
56 If MATCH is non-nil, only pathnames containing that regexp are returned. | |
57 If NOSORT is non-nil, the list is not sorted--its order is unpredictable. | |
58 NOSORT is useful if you plan to sort the result yourself. | |
59 If FILES-ONLY is the symbol t, then only the "files" in the directory | |
60 will be returned; subdirectories will be excluded. If FILES-ONLY is not | |
61 nil and not t, then only the subdirectories will be returned. Otherwise, | |
62 if FILES-ONLY is nil (the default) then both files and subdirectories will | |
63 be returned. | |
64 */ | |
65 (directory, full, match, nosort, files_only)) | |
66 { | |
67 /* This function can GC */ | |
68 DIR *d; | |
69 Lisp_Object list = Qnil; | |
70 Bytecount directorylen; | |
71 Lisp_Object handler; | |
72 struct re_pattern_buffer *bufp = NULL; | |
73 int speccount = specpdl_depth (); | |
74 char *statbuf, *statbuf_tail; | |
75 | |
76 struct gcpro gcpro1, gcpro2; | |
77 GCPRO2 (directory, list); | |
78 | |
79 /* If the file name has special constructs in it, | |
80 call the corresponding file handler. */ | |
81 handler = Ffind_file_name_handler (directory, Qdirectory_files); | |
82 if (!NILP (handler)) | |
83 { | |
84 UNGCPRO; | |
85 if (!NILP (files_only)) | |
86 return call6 (handler, Qdirectory_files, directory, full, match, | |
87 nosort, files_only); | |
88 else | |
89 return call5 (handler, Qdirectory_files, directory, full, match, | |
90 nosort); | |
91 } | |
92 | |
93 /* #### why do we do Fexpand_file_name after file handlers here, | |
94 but earlier everywhere else? */ | |
95 directory = Fexpand_file_name (directory, Qnil); | |
96 directory = Ffile_name_as_directory (directory); | |
97 directorylen = XSTRING_LENGTH (directory); | |
98 | |
99 statbuf = (char *)alloca (directorylen + MAXNAMLEN + 1); | |
100 memcpy (statbuf, XSTRING_DATA (directory), directorylen); | |
101 statbuf_tail = statbuf + directorylen; | |
102 | |
103 /* XEmacs: this should come after Ffile_name_as_directory() to avoid | |
104 potential regexp cache smashage. It comes before the opendir() | |
105 because it might signal an error. */ | |
106 if (!NILP (match)) | |
107 { | |
108 CHECK_STRING (match); | |
109 | |
110 /* MATCH might be a flawed regular expression. Rather than | |
111 catching and signalling our own errors, we just call | |
112 compile_pattern to do the work for us. */ | |
113 bufp = compile_pattern (match, 0, 0, 0, ERROR_ME); | |
114 } | |
115 | |
116 /* Now *bufp is the compiled form of MATCH; don't call anything | |
117 which might compile a new regexp until we're done with the loop! */ | |
118 | |
119 /* Do this opendir after anything which might signal an error. | |
120 NOTE: the above comment is old; previously, there was no | |
121 unwind-protection in case of error, but now there is. */ | |
122 d = opendir ((char *) XSTRING_DATA (directory)); | |
123 if (!d) | |
124 report_file_error ("Opening directory", list1 (directory)); | |
125 | |
126 record_unwind_protect (close_directory_unwind, make_opaque_ptr ((void *)d)); | |
127 | |
128 /* Loop reading blocks */ | |
129 while (1) | |
130 { | |
131 DIRENTRY *dp = readdir (d); | |
132 int len; | |
133 | |
134 if (!dp) | |
135 break; | |
136 len = NAMLEN (dp); | |
137 if (DIRENTRY_NONEMPTY (dp) | |
138 && (NILP (match) | |
139 || (0 <= re_search (bufp, dp->d_name, len, 0, len, 0)))) | |
140 { | |
141 if (!NILP (files_only)) | |
142 { | |
143 struct stat st; | |
144 int dir_p = 0; | |
145 | |
146 memcpy (statbuf_tail, dp->d_name, len); | |
147 statbuf_tail[len] = 0; | |
148 | |
149 if (stat (statbuf, &st) == 0 | |
150 && (st.st_mode & S_IFMT) == S_IFDIR) | |
151 dir_p = 1; | |
152 | |
153 if (EQ (files_only, Qt) && dir_p) | |
154 continue; | |
155 else if (!EQ (files_only, Qt) && !dir_p) | |
156 continue; | |
157 } | |
158 | |
159 { | |
160 Lisp_Object name = | |
161 make_string ((Bufbyte *)dp->d_name, len); | |
162 if (!NILP (full)) | |
163 name = concat2 (directory, name); | |
164 | |
165 list = Fcons (name, list); | |
166 } | |
167 } | |
168 } | |
169 unbind_to (speccount, Qnil); /* This will close the dir */ | |
170 | |
171 if (NILP (nosort)) | |
172 list = Fsort (Fnreverse (list), Qstring_lessp); | |
173 | |
174 RETURN_UNGCPRO (list); | |
175 } | |
176 | |
177 static Lisp_Object file_name_completion (Lisp_Object file, | |
178 Lisp_Object directory, | |
179 int all_flag, int ver_flag); | |
180 | |
181 DEFUN ("file-name-completion", Ffile_name_completion, 2, 2, 0, /* | |
182 Complete file name FILE in directory DIRECTORY. | |
183 Returns the longest string common to all filenames in DIRECTORY | |
184 that start with FILE. | |
185 If there is only one and FILE matches it exactly, returns t. | |
186 Returns nil if DIRECTORY contains no name starting with FILE. | |
187 | |
188 Filenames which end with any member of `completion-ignored-extensions' | |
189 are not considered as possible completions for FILE unless there is no | |
190 other possible completion. `completion-ignored-extensions' is not applied | |
191 to the names of directories. | |
192 */ | |
193 (file, directory)) | |
194 { | |
195 /* This function can GC. GC checked 1996.04.06. */ | |
196 Lisp_Object handler; | |
197 | |
198 /* If the directory name has special constructs in it, | |
199 call the corresponding file handler. */ | |
200 handler = Ffind_file_name_handler (directory, Qfile_name_completion); | |
201 if (!NILP (handler)) | |
202 return call3 (handler, Qfile_name_completion, file, directory); | |
203 | |
204 /* If the file name has special constructs in it, | |
205 call the corresponding file handler. */ | |
206 handler = Ffind_file_name_handler (file, Qfile_name_completion); | |
207 if (!NILP (handler)) | |
208 return call3 (handler, Qfile_name_completion, file, directory); | |
209 | |
210 return file_name_completion (file, directory, 0, 0); | |
211 } | |
212 | |
213 DEFUN ("file-name-all-completions", Ffile_name_all_completions, 2, 2, 0, /* | |
214 Return a list of all completions of file name FILE in directory DIRECTORY. | |
215 These are all file names in directory DIRECTORY which begin with FILE. | |
216 | |
217 File names which end with any member of `completion-ignored-extensions' | |
218 are not considered as possible completions for FILE unless there is no | |
219 other possible completion. `completion-ignored-extensions' is not applied | |
220 to the names of directories. | |
221 */ | |
222 (file, directory)) | |
223 { | |
224 /* This function can GC. GC checked 1997.06.04. */ | |
225 Lisp_Object handler; | |
226 struct gcpro gcpro1; | |
227 | |
228 GCPRO1 (directory); | |
229 directory = Fexpand_file_name (directory, Qnil); | |
230 /* If the file name has special constructs in it, | |
231 call the corresponding file handler. */ | |
232 handler = Ffind_file_name_handler (directory, Qfile_name_all_completions); | |
233 UNGCPRO; | |
234 if (!NILP (handler)) | |
235 return call3 (handler, Qfile_name_all_completions, file, | |
236 directory); | |
237 | |
238 return file_name_completion (file, directory, 1, 0); | |
239 } | |
240 | |
241 static int | |
242 file_name_completion_stat (Lisp_Object directory, DIRENTRY *dp, | |
243 struct stat *st_addr) | |
244 { | |
245 Bytecount len = NAMLEN (dp); | |
246 Bytecount pos = XSTRING_LENGTH (directory); | |
247 int value; | |
248 char *fullname = (char *) alloca (len + pos + 2); | |
249 | |
250 memcpy (fullname, XSTRING_DATA (directory), pos); | |
251 if (!IS_DIRECTORY_SEP (fullname[pos - 1])) | |
252 fullname[pos++] = DIRECTORY_SEP; | |
253 | |
254 memcpy (fullname + pos, dp->d_name, len); | |
255 fullname[pos + len] = 0; | |
256 | |
257 #ifdef S_IFLNK | |
258 /* We want to return success if a link points to a nonexistent file, | |
259 but we want to return the status for what the link points to, | |
260 in case it is a directory. */ | |
261 value = lstat (fullname, st_addr); | |
262 if (S_ISLNK (st_addr->st_mode)) | |
263 stat (fullname, st_addr); | |
264 #else | |
265 value = stat (fullname, st_addr); | |
266 #endif | |
267 return value; | |
268 } | |
269 | |
270 static Lisp_Object | |
271 file_name_completion_unwind (Lisp_Object locative) | |
272 { | |
273 DIR *d; | |
274 Lisp_Object obj = XCAR (locative); | |
275 | |
276 if (!NILP (obj)) | |
277 { | |
278 d = (DIR *)get_opaque_ptr (obj); | |
279 closedir (d); | |
280 free_opaque_ptr (obj); | |
281 } | |
282 free_cons (XCONS (locative)); | |
283 return Qnil; | |
284 } | |
285 | |
286 static Lisp_Object | |
287 file_name_completion (Lisp_Object file, Lisp_Object directory, int all_flag, | |
288 int ver_flag) | |
289 { | |
290 /* This function can GC */ | |
291 DIR *d = 0; | |
292 int matchcount = 0; | |
293 Lisp_Object bestmatch = Qnil; | |
294 Charcount bestmatchsize = 0; | |
295 struct stat st; | |
296 int passcount; | |
297 int speccount = specpdl_depth (); | |
298 Charcount file_name_length; | |
299 Lisp_Object locative; | |
300 struct gcpro gcpro1, gcpro2, gcpro3; | |
301 | |
302 GCPRO3 (file, directory, bestmatch); | |
303 | |
304 CHECK_STRING (file); | |
305 | |
306 #ifdef WINDOWSNT | |
307 /* Filename completion on Windows ignores case, since Windows | |
308 filesystems do. */ | |
309 specbind (Qcompletion_ignore_case, Qt); | |
310 #endif /* WINDOWSNT */ | |
311 | |
312 #ifdef FILE_SYSTEM_CASE | |
313 file = FILE_SYSTEM_CASE (file); | |
314 #endif | |
315 directory = Fexpand_file_name (directory, Qnil); | |
316 file_name_length = XSTRING_CHAR_LENGTH (file); | |
317 | |
318 /* With passcount = 0, ignore files that end in an ignored extension. | |
319 If nothing found then try again with passcount = 1, don't ignore them. | |
320 If looking for all completions, start with passcount = 1, | |
321 so always take even the ignored ones. | |
322 | |
323 ** It would not actually be helpful to the user to ignore any possible | |
324 completions when making a list of them.** */ | |
325 | |
326 /* We cannot use close_directory_unwind() because we change the | |
327 directory. The old code used to just avoid signaling errors, and | |
328 call closedir, but it was wrong, because it made sane handling of | |
329 QUIT impossible and, besides, various utility functions like | |
330 regexp_ignore_completion_p can signal errors. */ | |
331 locative = noseeum_cons (Qnil, Qnil); | |
332 record_unwind_protect (file_name_completion_unwind, locative); | |
333 | |
334 for (passcount = !!all_flag; NILP (bestmatch) && passcount < 2; passcount++) | |
335 { | |
336 d = opendir ((char *) XSTRING_DATA (Fdirectory_file_name (directory))); | |
337 if (!d) | |
338 report_file_error ("Opening directory", list1 (directory)); | |
339 XCAR (locative) = make_opaque_ptr ((void *)d); | |
340 | |
341 /* Loop reading blocks */ | |
342 while (1) | |
343 { | |
344 DIRENTRY *dp; | |
345 Bytecount len; | |
346 /* scmp() works in characters, not bytes, so we have to compute | |
347 this value: */ | |
348 Charcount cclen; | |
349 int directoryp; | |
350 int ignored_extension_p = 0; | |
351 Bufbyte *d_name; | |
352 | |
353 dp = readdir (d); | |
354 if (!dp) break; | |
355 | |
356 /* Cast to Bufbyte* is OK, as readdir() Mule-encapsulates. */ | |
357 d_name = (Bufbyte *) dp->d_name; | |
358 len = NAMLEN (dp); | |
359 cclen = bytecount_to_charcount (d_name, len); | |
360 | |
361 QUIT; | |
362 | |
363 if (! DIRENTRY_NONEMPTY (dp) | |
364 || cclen < file_name_length | |
365 || 0 <= scmp (d_name, XSTRING_DATA (file), file_name_length)) | |
366 continue; | |
367 | |
368 if (file_name_completion_stat (directory, dp, &st) < 0) | |
369 continue; | |
370 | |
371 directoryp = ((st.st_mode & S_IFMT) == S_IFDIR); | |
372 if (directoryp) | |
373 { | |
374 #ifndef TRIVIAL_DIRECTORY_ENTRY | |
375 #define TRIVIAL_DIRECTORY_ENTRY(n) (!strcmp (n, ".") || !strcmp (n, "..")) | |
376 #endif | |
377 /* "." and ".." are never interesting as completions, but are | |
378 actually in the way in a directory containing only one file. */ | |
379 if (!passcount && TRIVIAL_DIRECTORY_ENTRY (dp->d_name)) | |
380 continue; | |
381 } | |
382 else | |
383 { | |
384 /* Compare extensions-to-be-ignored against end of this file name */ | |
385 /* if name is not an exact match against specified string. */ | |
386 if (!passcount && cclen > file_name_length) | |
387 { | |
388 Lisp_Object tem; | |
389 /* and exit this for loop if a match is found */ | |
390 EXTERNAL_LIST_LOOP (tem, Vcompletion_ignored_extensions) | |
391 { | |
392 Lisp_Object elt = XCAR (tem); | |
393 Charcount skip; | |
394 | |
395 CHECK_STRING (elt); | |
396 | |
397 skip = cclen - XSTRING_CHAR_LENGTH (elt); | |
398 if (skip < 0) continue; | |
399 | |
400 if (0 > scmp (charptr_n_addr (d_name, skip), | |
401 XSTRING_DATA (elt), | |
402 XSTRING_CHAR_LENGTH (elt))) | |
403 { | |
404 ignored_extension_p = 1; | |
405 break; | |
406 } | |
407 } | |
408 } | |
409 } | |
410 | |
411 /* If an ignored-extensions match was found, | |
412 don't process this name as a completion. */ | |
413 if (!passcount && ignored_extension_p) | |
414 continue; | |
415 | |
416 if (!passcount && regexp_ignore_completion_p (d_name, Qnil, 0, cclen)) | |
417 continue; | |
418 | |
419 /* Update computation of how much all possible completions match */ | |
420 matchcount++; | |
421 | |
422 if (all_flag || NILP (bestmatch)) | |
423 { | |
424 Lisp_Object name = Qnil; | |
425 struct gcpro ngcpro1; | |
426 NGCPRO1 (name); | |
427 /* This is a possible completion */ | |
428 name = make_string (d_name, len); | |
429 if (directoryp) /* Completion is a directory; end it with '/' */ | |
430 name = Ffile_name_as_directory (name); | |
431 if (all_flag) | |
432 { | |
433 bestmatch = Fcons (name, bestmatch); | |
434 } | |
435 else | |
436 { | |
437 bestmatch = name; | |
438 bestmatchsize = XSTRING_CHAR_LENGTH (name); | |
439 } | |
440 NUNGCPRO; | |
441 } | |
442 else | |
443 { | |
444 Charcount compare = min (bestmatchsize, cclen); | |
445 Bufbyte *p1 = XSTRING_DATA (bestmatch); | |
446 Bufbyte *p2 = d_name; | |
447 Charcount matchsize = scmp (p1, p2, compare); | |
448 | |
449 if (matchsize < 0) | |
450 matchsize = compare; | |
451 if (completion_ignore_case) | |
452 { | |
453 /* If this is an exact match except for case, | |
454 use it as the best match rather than one that is not | |
455 an exact match. This way, we get the case pattern | |
456 of the actual match. */ | |
457 if ((matchsize == cclen | |
458 && matchsize + !!directoryp | |
459 < XSTRING_CHAR_LENGTH (bestmatch)) | |
460 || | |
461 /* If there is no exact match ignoring case, | |
462 prefer a match that does not change the case | |
463 of the input. */ | |
464 (((matchsize == cclen) | |
465 == | |
466 (matchsize + !!directoryp | |
467 == XSTRING_CHAR_LENGTH (bestmatch))) | |
468 /* If there is more than one exact match aside from | |
469 case, and one of them is exact including case, | |
470 prefer that one. */ | |
471 && 0 > scmp_1 (p2, XSTRING_DATA (file), | |
472 file_name_length, 0) | |
473 && 0 <= scmp_1 (p1, XSTRING_DATA (file), | |
474 file_name_length, 0))) | |
475 { | |
476 bestmatch = make_string (d_name, len); | |
477 if (directoryp) | |
478 bestmatch = Ffile_name_as_directory (bestmatch); | |
479 } | |
480 } | |
481 | |
482 /* If this directory all matches, | |
483 see if implicit following slash does too. */ | |
484 if (directoryp | |
485 && compare == matchsize | |
486 && bestmatchsize > matchsize | |
487 && IS_ANY_SEP (charptr_emchar_n (p1, matchsize))) | |
488 matchsize++; | |
489 bestmatchsize = matchsize; | |
490 } | |
491 } | |
492 closedir (d); | |
493 free_opaque_ptr (XCAR (locative)); | |
494 XCAR (locative) = Qnil; | |
495 } | |
496 | |
497 unbind_to (speccount, Qnil); | |
498 | |
499 UNGCPRO; | |
500 | |
501 if (all_flag || NILP (bestmatch)) | |
502 return bestmatch; | |
503 if (matchcount == 1 && bestmatchsize == file_name_length) | |
504 return Qt; | |
505 return Fsubstring (bestmatch, Qzero, make_int (bestmatchsize)); | |
506 } | |
507 | |
508 | |
509 | |
510 /* The *pwent() functions do not exist on NT */ | |
511 #ifndef WINDOWSNT | |
512 | |
513 static Lisp_Object user_name_completion (Lisp_Object user, | |
514 int all_flag, | |
515 int *uniq); | |
516 | |
517 DEFUN ("user-name-completion", Fuser_name_completion, 1, 1, 0, /* | |
518 Complete user name USER. | |
519 | |
520 Returns the longest string common to all user names that start | |
521 with USER. If there is only one and USER matches it exactly, | |
522 returns t. Returns nil if there is no user name starting with USER. | |
523 */ | |
524 (user)) | |
525 { | |
526 return user_name_completion (user, 0, NULL); | |
527 } | |
528 | |
529 DEFUN ("user-name-completion-1", Fuser_name_completion_1, 1, 1, 0, /* | |
530 Complete user name USER. | |
531 | |
532 This function is identical to `user-name-completion', except that | |
533 the cons of the completion and an indication of whether the | |
534 completion was unique is returned. | |
535 | |
536 The car of the returned value is the longest string common to all | |
537 user names that start with USER. If there is only one and USER | |
538 matches it exactly, the car is t. The car is nil if there is no | |
539 user name starting with USER. The cdr of the result is non-nil | |
540 if and only if the completion returned in the car was unique. | |
541 */ | |
542 (user)) | |
543 { | |
544 int uniq; | |
545 Lisp_Object completed; | |
546 | |
547 completed = user_name_completion (user, 0, &uniq); | |
548 return Fcons (completed, uniq ? Qt : Qnil); | |
549 } | |
550 | |
551 DEFUN ("user-name-all-completions", Fuser_name_all_completions, 1, 1, 0, /* | |
552 Return a list of all completions of user name USER. | |
553 These are all user names which begin with USER. | |
554 */ | |
555 (user)) | |
556 { | |
557 return user_name_completion (user, 1, NULL); | |
558 } | |
559 | |
560 struct user_cache { | |
561 Bufbyte **data; | |
562 int length; | |
563 int size; | |
564 EMACS_TIME last_rebuild_time; | |
565 }; | |
566 static struct user_cache user_cache; | |
567 | |
568 static void | |
569 free_user_cache (struct user_cache *cache) | |
570 { | |
571 int i; | |
572 for (i = 0; i < cache->length; i++) | |
573 xfree (cache->data[i]); | |
574 xfree (cache->data); | |
575 } | |
576 | |
577 static Lisp_Object | |
578 user_name_completion_unwind (Lisp_Object locative) | |
579 { | |
580 int interrupted = !NILP (XCAR (locative)); | |
581 | |
582 if (interrupted) | |
583 { | |
584 endpwent (); | |
585 speed_up_interrupts (); | |
586 free_user_cache (&user_cache); | |
587 } | |
588 free_cons (XCONS (locative)); | |
589 | |
590 return Qnil; | |
591 } | |
592 | |
593 #define USER_CACHE_REBUILD (24*60*60) /* 1 day, in seconds */ | |
594 | |
595 static Lisp_Object | |
596 user_name_completion (Lisp_Object user, int all_flag, int *uniq) | |
597 { | |
598 /* This function can GC */ | |
599 int matchcount = 0; | |
600 Lisp_Object bestmatch = Qnil; | |
601 Charcount bestmatchsize = 0; | |
602 int speccount = specpdl_depth (); | |
603 Charcount user_name_length; | |
604 EMACS_TIME t; | |
605 int i; | |
606 struct gcpro gcpro1, gcpro2; | |
607 | |
608 GCPRO2 (user, bestmatch); | |
609 | |
610 CHECK_STRING (user); | |
611 | |
612 user_name_length = XSTRING_CHAR_LENGTH (user); | |
613 | |
614 /* Cache user name lookups because it tends to be quite slow. | |
615 * Rebuild the cache occasionally to catch changes */ | |
616 EMACS_GET_TIME (t); | |
617 if (user_cache.data && | |
618 (EMACS_SECS (t) - EMACS_SECS (user_cache.last_rebuild_time) | |
619 > USER_CACHE_REBUILD)) | |
620 { | |
621 free_user_cache (&user_cache); | |
622 xzero (user_cache); | |
623 } | |
624 | |
625 if (!user_cache.data) | |
626 { | |
627 struct passwd *pwd; | |
628 Lisp_Object locative = noseeum_cons (Qt, Qnil); | |
629 slow_down_interrupts (); | |
630 setpwent (); | |
631 record_unwind_protect (user_name_completion_unwind, locative); | |
632 while ((pwd = getpwent ())) | |
633 { | |
634 Bufbyte *pwuser; | |
635 QUIT; | |
636 DO_REALLOC (user_cache.data, user_cache.size, | |
637 user_cache.length + 1, Bufbyte *); | |
638 GET_C_CHARPTR_INT_DATA_ALLOCA (pwd->pw_name, FORMAT_OS, pwuser); | |
639 user_cache.data[user_cache.length++] = | |
640 (Bufbyte *) xstrdup ((char *) pwuser); | |
641 } | |
642 endpwent (); | |
643 speed_up_interrupts (); | |
644 XCAR (locative) = Qnil; | |
645 unbind_to (speccount, Qnil); /* free locative cons */ | |
646 EMACS_GET_TIME (user_cache.last_rebuild_time); | |
647 } | |
648 | |
649 for (i = 0; i < user_cache.length; i++) | |
650 { | |
651 Bufbyte *u_name = user_cache.data[i]; | |
652 Bytecount len = strlen ((char *) u_name); | |
653 /* scmp() works in chars, not bytes, so we have to compute this: */ | |
654 Charcount cclen = bytecount_to_charcount (u_name, len); | |
655 | |
656 QUIT; | |
657 | |
658 if (cclen < user_name_length | |
659 || 0 <= scmp_1 (u_name, XSTRING_DATA (user), user_name_length, 0)) | |
660 continue; | |
661 | |
662 matchcount++; /* count matching completions */ | |
663 | |
664 if (all_flag || NILP (bestmatch)) | |
665 { | |
666 Lisp_Object name = Qnil; | |
667 struct gcpro ngcpro1; | |
668 NGCPRO1 (name); | |
669 /* This is a possible completion */ | |
670 name = make_string (u_name, len); | |
671 if (all_flag) | |
672 { | |
673 bestmatch = Fcons (name, bestmatch); | |
674 } | |
675 else | |
676 { | |
677 bestmatch = name; | |
678 bestmatchsize = XSTRING_CHAR_LENGTH (name); | |
679 } | |
680 NUNGCPRO; | |
681 } | |
682 else | |
683 { | |
684 Charcount compare = min (bestmatchsize, cclen); | |
685 Bufbyte *p1 = XSTRING_DATA (bestmatch); | |
686 Bufbyte *p2 = u_name; | |
687 Charcount matchsize = scmp_1 (p1, p2, compare, 0); | |
688 | |
689 if (matchsize < 0) | |
690 matchsize = compare; | |
691 | |
692 bestmatchsize = matchsize; | |
693 } | |
694 } | |
695 | |
696 UNGCPRO; | |
697 | |
698 if (uniq) | |
699 *uniq = (matchcount == 1); | |
700 | |
701 if (all_flag || NILP (bestmatch)) | |
702 return bestmatch; | |
703 if (matchcount == 1 && bestmatchsize == user_name_length) | |
704 return Qt; | |
705 return Fsubstring (bestmatch, Qzero, make_int (bestmatchsize)); | |
706 } | |
707 #endif /* ! defined WINDOWSNT */ | |
708 | |
709 | |
710 Lisp_Object | |
711 make_directory_hash_table (CONST char *path) | |
712 { | |
713 DIR *d; | |
714 if ((d = opendir (path))) | |
715 { | |
716 DIRENTRY *dp; | |
717 Lisp_Object hash = | |
718 make_lisp_hash_table (20, HASH_TABLE_NON_WEAK, HASH_TABLE_EQUAL); | |
719 | |
720 while ((dp = readdir (d))) | |
721 { | |
722 Bytecount len = NAMLEN (dp); | |
723 if (DIRENTRY_NONEMPTY (dp)) | |
724 /* Cast to Bufbyte* is OK, as readdir() Mule-encapsulates. */ | |
725 Fputhash (make_string ((Bufbyte *) dp->d_name, len), Qt, hash); | |
726 } | |
727 closedir (d); | |
728 return hash; | |
729 } | |
730 else | |
731 return Qnil; | |
732 } | |
733 | |
734 Lisp_Object | |
735 wasteful_word_to_lisp (unsigned int item) | |
736 { | |
737 /* Compatibility: in other versions, file-attributes returns a LIST | |
738 of two 16 bit integers... */ | |
739 Lisp_Object cons = word_to_lisp (item); | |
740 XCDR (cons) = Fcons (XCDR (cons), Qnil); | |
741 return cons; | |
742 } | |
743 | |
744 DEFUN ("file-attributes", Ffile_attributes, 1, 1, 0, /* | |
745 Return a list of attributes of file FILENAME. | |
746 Value is nil if specified file cannot be opened. | |
747 Otherwise, list elements are: | |
748 0. t for directory, string (name linked to) for symbolic link, or nil. | |
749 1. Number of links to file. | |
750 2. File uid. | |
751 3. File gid. | |
752 4. Last access time, as a list of two integers. | |
753 First integer has high-order 16 bits of time, second has low 16 bits. | |
754 5. Last modification time, likewise. | |
755 6. Last status change time, likewise. | |
756 7. Size in bytes. (-1, if number is out of range). | |
757 8. File modes, as a string of ten letters or dashes as in ls -l. | |
758 9. t iff file's gid would change if file were deleted and recreated. | |
759 10. inode number. | |
760 11. Device number. | |
761 | |
762 If file does not exist, returns nil. | |
763 */ | |
764 (filename)) | |
765 { | |
766 /* This function can GC. GC checked 1997.06.04. */ | |
767 Lisp_Object values[12]; | |
768 Lisp_Object directory = Qnil; | |
769 struct stat s; | |
770 char modes[10]; | |
771 Lisp_Object handler; | |
772 struct gcpro gcpro1, gcpro2; | |
773 | |
774 GCPRO2 (filename, directory); | |
775 filename = Fexpand_file_name (filename, Qnil); | |
776 | |
777 /* If the file name has special constructs in it, | |
778 call the corresponding file handler. */ | |
779 handler = Ffind_file_name_handler (filename, Qfile_attributes); | |
780 if (!NILP (handler)) | |
781 { | |
782 UNGCPRO; | |
783 return call2 (handler, Qfile_attributes, filename); | |
784 } | |
785 | |
786 if (lstat ((char *) XSTRING_DATA (filename), &s) < 0) | |
787 { | |
788 UNGCPRO; | |
789 return Qnil; | |
790 } | |
791 | |
792 #ifdef BSD4_2 | |
793 directory = Ffile_name_directory (filename); | |
794 #endif | |
795 | |
796 #ifdef MSDOS | |
797 { | |
798 char *tmpnam = (char *) XSTRING_DATA (Ffile_name_nondirectory (filename)); | |
799 int l = strlen (tmpnam); | |
800 | |
801 if (l >= 5 | |
802 && S_ISREG (s.st_mode) | |
803 && (stricmp (&tmpnam[l - 4], ".com") == 0 || | |
804 stricmp (&tmpnam[l - 4], ".exe") == 0 || | |
805 stricmp (&tmpnam[l - 4], ".bat") == 0)) | |
806 { | |
807 s.st_mode |= S_IEXEC; | |
808 } | |
809 } | |
810 #endif /* MSDOS */ | |
811 | |
812 switch (s.st_mode & S_IFMT) | |
813 { | |
814 default: | |
815 values[0] = Qnil; | |
816 break; | |
817 case S_IFDIR: | |
818 values[0] = Qt; | |
819 break; | |
820 #ifdef S_IFLNK | |
821 case S_IFLNK: | |
822 values[0] = Ffile_symlink_p (filename); | |
823 break; | |
824 #endif | |
825 } | |
826 values[1] = make_int (s.st_nlink); | |
827 values[2] = make_int (s.st_uid); | |
828 values[3] = make_int (s.st_gid); | |
829 values[4] = wasteful_word_to_lisp (s.st_atime); | |
830 values[5] = wasteful_word_to_lisp (s.st_mtime); | |
831 values[6] = wasteful_word_to_lisp (s.st_ctime); | |
832 values[7] = make_int ((EMACS_INT) s.st_size); | |
833 /* If the size is out of range, give back -1. */ | |
834 /* #### Fix when Emacs gets bignums! */ | |
835 if (XINT (values[7]) != s.st_size) | |
836 values[7] = make_int (-1); | |
837 filemodestring (&s, modes); | |
838 values[8] = make_string ((Bufbyte *) modes, 10); | |
839 #if defined (BSD4_2) || defined (BSD4_3) /* file gid will be dir gid */ | |
840 { | |
841 struct stat sdir; | |
842 | |
843 if (!NILP (directory) && stat ((char *) XSTRING_DATA (directory), &sdir) == 0) | |
844 values[9] = (sdir.st_gid != s.st_gid) ? Qt : Qnil; | |
845 else /* if we can't tell, assume worst */ | |
846 values[9] = Qt; | |
847 } | |
848 #else /* file gid will be egid */ | |
849 values[9] = (s.st_gid != getegid ()) ? Qt : Qnil; | |
850 #endif /* BSD4_2 or BSD4_3 */ | |
851 values[10] = make_int (s.st_ino); | |
852 values[11] = make_int (s.st_dev); | |
853 UNGCPRO; | |
854 return Flist (countof (values), values); | |
855 } | |
856 | |
857 | |
858 /************************************************************************/ | |
859 /* initialization */ | |
860 /************************************************************************/ | |
861 | |
862 void | |
863 syms_of_dired (void) | |
864 { | |
865 defsymbol (&Qdirectory_files, "directory-files"); | |
866 defsymbol (&Qfile_name_completion, "file-name-completion"); | |
867 defsymbol (&Qfile_name_all_completions, "file-name-all-completions"); | |
868 defsymbol (&Qfile_attributes, "file-attributes"); | |
869 | |
870 DEFSUBR (Fdirectory_files); | |
871 DEFSUBR (Ffile_name_completion); | |
872 DEFSUBR (Ffile_name_all_completions); | |
873 #ifndef WINDOWSNT | |
874 DEFSUBR (Fuser_name_completion); | |
875 DEFSUBR (Fuser_name_completion_1); | |
876 DEFSUBR (Fuser_name_all_completions); | |
877 #endif | |
878 DEFSUBR (Ffile_attributes); | |
879 } | |
880 | |
881 void | |
882 vars_of_dired (void) | |
883 { | |
884 DEFVAR_LISP ("completion-ignored-extensions", &Vcompletion_ignored_extensions /* | |
885 *Completion ignores filenames ending in any string in this list. | |
886 This variable does not affect lists of possible completions, | |
887 but does affect the commands that actually do completions. | |
888 It is used by the functions `file-name-completion' and | |
889 `file-name-all-completions'. | |
890 */ ); | |
891 Vcompletion_ignored_extensions = Qnil; | |
892 } |