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