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: }