Annotation of GNUtools/emacs/src/dired.c, revision 1.1.1.1

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

unix.superglobalmegacorp.com

This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.