/* savedirinfo.c -- Save the list of files in a directory, with additional information. Copyright 1990, 1997, 1998, 1999, 2000, 2001, 2003, 2004, 2005 Free Software Foundation, Inc. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ /* Written by James Youngman, . */ /* Derived from savedir.c, written by David MacKenzie . */ #if HAVE_CONFIG_H # include #endif #if HAVE_SYS_STAT_H # include #endif #if HAVE_SYS_TYPES_H # include #endif /* The presence of unistd.h is assumed by gnulib these days, so we * might as well assume it too. */ #include #include #if HAVE_DIRENT_H # include #else # define dirent direct # if HAVE_SYS_NDIR_H # include # endif # if HAVE_SYS_DIR_H # include # endif # if HAVE_NDIR_H # include # endif #endif #ifdef CLOSEDIR_VOID /* Fake a return value. */ # define CLOSEDIR(d) (closedir (d), 0) #else # define CLOSEDIR(d) closedir (d) #endif #include #include #include #include "xalloc.h" #include "extendbuf.h" #include "savedirinfo.h" /* In order to use struct dirent.d_type, it has to be enabled on the * configure command line, and we have to have a d_type member in * 'struct dirent'. */ #if !defined(USE_STRUCT_DIRENT_D_TYPE) /* Not enabled, hence pretend it is absent. */ #undef HAVE_STRUCT_DIRENT_D_TYPE #endif #if !defined(HAVE_STRUCT_DIRENT_D_TYPE) /* Not present, so cannot use it. */ #undef USE_STRUCT_DIRENT_D_TYPE #endif #if defined(HAVE_STRUCT_DIRENT_D_TYPE) && defined(USE_STRUCT_DIRENT_D_TYPE) /* Convert the value of struct dirent.d_type into a value for * struct stat.st_mode (at least the file type bits), or zero * if the type is DT_UNKNOWN or is a value we don't know about. */ static mode_t type_to_mode(unsigned type) { switch (type) { #ifdef DT_FIFO case DT_FIFO: return S_IFIFO; #endif #ifdef DT_CHR case DT_CHR: return S_IFCHR; #endif #ifdef DT_DIR case DT_DIR: return S_IFDIR; #endif #ifdef DT_BLK case DT_BLK: return S_IFBLK; #endif #ifdef DT_REG case DT_REG: return S_IFREG; #endif #ifdef DT_LNK case DT_LNK: return S_IFLNK; #endif #ifdef DT_SOCK case DT_SOCK: return S_IFSOCK; #endif default: return 0; /* Unknown. */ } } #endif struct new_savedir_direntry_internal { int flags; /* from SaveDirDataFlags */ mode_t type_info; size_t buffer_offset; }; static int savedir_cmp(const void *p1, const void *p2) { const struct savedir_direntry *de1, *de2; de1 = p1; de2 = p2; return strcmp(de1->name, de2->name); /* POSIX order, not locale order. */ } static struct savedir_direntry* convertentries(const struct savedir_dirinfo *info, struct new_savedir_direntry_internal *internal) { char *p = info->buffer; struct savedir_direntry *result; int n =info->size; int i; result = xmalloc(sizeof(*result) * info->size); for (i=0; ibuffer = NULL; result->size = 0u; result->entries = NULL; internal = NULL; while ((dp = readdir (dirp)) != NULL) { /* Skip "", ".", and "..". "" is returned by at least one buggy implementation: Solaris 2.4 readdir on NFS file systems. */ char const *entry = dp->d_name; if (entry[entry[0] != '.' ? 0 : entry[1] != '.' ? 1 : 2] != '\0') { /* Remember the name. */ size_t entry_size = strlen (entry) + 1; result->buffer = extendbuf(result->buffer, namebuf_used+entry_size, &namebuf_allocated); memcpy ((result->buffer) + namebuf_used, entry, entry_size); /* Remember the other stuff. */ internal = extendbuf(internal, (1+result->size)*sizeof(*internal), &entrybuf_allocated); internal[result->size].flags = 0; #if defined(HAVE_STRUCT_DIRENT_D_TYPE) && defined(USE_STRUCT_DIRENT_D_TYPE) internal[result->size].type_info = type_to_mode(dp->d_type); if (dp->d_type != DT_UNKNOWN) internal[result->size].flags |= SavedirHaveFileType; #else internal[result->size].type_info = 0; #endif internal[result->size].buffer_offset = namebuf_used; /* Prepare for the next iteration */ ++(result->size); namebuf_used += entry_size; } } result->buffer = extendbuf(result->buffer, namebuf_used+1, &namebuf_allocated); result->buffer[namebuf_used] = '\0'; /* convert the result to its externally-usable form. */ result->entries = convertentries(result, internal); free(internal); internal = NULL; if (flags & SavedirSort) { qsort(result->entries, result->size, sizeof(*result->entries), savedir_cmp); } save_errno = errno; if (CLOSEDIR (dirp) != 0) save_errno = errno; if (save_errno != 0) { free (result->buffer); free (result); errno = save_errno; return NULL; } return result; } void free_dirinfo(struct savedir_dirinfo *p) { free(p->entries); p->entries = NULL; free(p->buffer); p->buffer = NULL; free(p); } static char * new_savedirinfo (const char *dir, struct savedir_extrainfo **extra) { struct savedir_dirinfo *p = xsavedir(dir, SavedirSort); char *buf, *s; size_t bufbytes = 0; int i; if (p) { struct savedir_extrainfo *pex = xmalloc(p->size * sizeof(*extra)); for (i=0; isize; ++i) { bufbytes += strlen(p->entries[i].name); ++bufbytes; /* the \0 */ pex[i].type_info = p->entries[i].type_info; } s = buf = xmalloc(bufbytes+1); for (i=0; isize; ++i) { size_t len = strlen(p->entries[i].name); memcpy(s, p->entries[i].name, len); s += len; *s = 0; /* Place a NUL */ ++s; /* Skip the NUL. */ } *s = 0; /* final (doubled) terminating NUL */ if (extra) *extra = pex; else free (pex); return buf; } else { return NULL; } } #if 0 /* Return a freshly allocated string containing the filenames in directory DIR, separated by '\0' characters; the end is marked by two '\0' characters in a row. Return NULL (setting errno) if DIR cannot be opened, read, or closed. */ static char * old_savedirinfo (const char *dir, struct savedir_extrainfo **extra) { DIR *dirp; struct dirent *dp; char *name_space; size_t namebuf_allocated = 0u, namebuf_used = 0u; #if defined(HAVE_STRUCT_DIRENT_D_TYPE) && defined(USE_STRUCT_DIRENT_D_TYPE) size_t extra_allocated = 0u, extra_used = 0u; struct savedir_extrainfo *info = NULL; #endif int save_errno; if (extra) *extra = NULL; dirp = opendir (dir); if (dirp == NULL) return NULL; errno = 0; name_space = NULL; while ((dp = readdir (dirp)) != NULL) { /* Skip "", ".", and "..". "" is returned by at least one buggy implementation: Solaris 2.4 readdir on NFS file systems. */ char const *entry = dp->d_name; if (entry[entry[0] != '.' ? 0 : entry[1] != '.' ? 1 : 2] != '\0') { /* Remember the name. */ size_t entry_size = strlen (entry) + 1; name_space = extendbuf(name_space, namebuf_used+entry_size, &namebuf_allocated); memcpy (name_space + namebuf_used, entry, entry_size); namebuf_used += entry_size; #if defined(HAVE_STRUCT_DIRENT_D_TYPE) && defined(USE_STRUCT_DIRENT_D_TYPE) /* Remember the type. */ if (extra) { info = extendbuf(info, (extra_used+1) * sizeof(struct savedir_dirinfo), &extra_allocated); info[extra_used].type_info = type_to_mode(dp->d_type); ++extra_used; } #endif } } name_space = extendbuf(name_space, namebuf_used+1, &namebuf_allocated); name_space[namebuf_used] = '\0'; save_errno = errno; if (CLOSEDIR (dirp) != 0) save_errno = errno; if (save_errno != 0) { free (name_space); errno = save_errno; return NULL; } #if defined(HAVE_STRUCT_DIRENT_D_TYPE) && defined(USE_STRUCT_DIRENT_D_TYPE) if (extra && info) *extra = info; #endif return name_space; } #endif char * savedirinfo (const char *dir, struct savedir_extrainfo **extra) { return new_savedirinfo(dir, extra); }