1/* savedirinfo.c -- Save the list of files in a directory, with additional information. 2 3 Copyright 1990, 1997, 1998, 1999, 2000, 2001, 2003, 2004, 2005 Free 4 Software Foundation, Inc. 5 6 This program 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 3 of the License, or 9 (at your option) any later version. 10 11 This program 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 this program. If not, see <http://www.gnu.org/licenses/>. 18*/ 19/* Written by James Youngman, <jay@gnu.org>. */ 20/* Derived from savedir.c, written by David MacKenzie <djm@gnu.org>. */ 21 22#if HAVE_CONFIG_H 23# include <config.h> 24#endif 25 26#if HAVE_SYS_STAT_H 27# include <sys/stat.h> 28#endif 29 30#if HAVE_SYS_TYPES_H 31# include <sys/types.h> 32#endif 33 34/* The presence of unistd.h is assumed by gnulib these days, so we 35 * might as well assume it too. 36 */ 37#include <unistd.h> 38 39#include <errno.h> 40 41#if HAVE_DIRENT_H 42# include <dirent.h> 43#else 44# define dirent direct 45# if HAVE_SYS_NDIR_H 46# include <sys/ndir.h> 47# endif 48# if HAVE_SYS_DIR_H 49# include <sys/dir.h> 50# endif 51# if HAVE_NDIR_H 52# include <ndir.h> 53# endif 54#endif 55 56#ifdef CLOSEDIR_VOID 57/* Fake a return value. */ 58# define CLOSEDIR(d) (closedir (d), 0) 59#else 60# define CLOSEDIR(d) closedir (d) 61#endif 62 63#include <stddef.h> 64#include <stdlib.h> 65#include <string.h> 66 67#include "xalloc.h" 68#include "extendbuf.h" 69#include "savedirinfo.h" 70 71/* In order to use struct dirent.d_type, it has to be enabled on the 72 * configure command line, and we have to have a d_type member in 73 * 'struct dirent'. 74 */ 75#if !defined(USE_STRUCT_DIRENT_D_TYPE) 76/* Not enabled, hence pretend it is absent. */ 77#undef HAVE_STRUCT_DIRENT_D_TYPE 78#endif 79#if !defined(HAVE_STRUCT_DIRENT_D_TYPE) 80/* Not present, so cannot use it. */ 81#undef USE_STRUCT_DIRENT_D_TYPE 82#endif 83 84 85#if defined(HAVE_STRUCT_DIRENT_D_TYPE) && defined(USE_STRUCT_DIRENT_D_TYPE) 86/* Convert the value of struct dirent.d_type into a value for 87 * struct stat.st_mode (at least the file type bits), or zero 88 * if the type is DT_UNKNOWN or is a value we don't know about. 89 */ 90static mode_t 91type_to_mode(unsigned type) 92{ 93 switch (type) 94 { 95#ifdef DT_FIFO 96 case DT_FIFO: return S_IFIFO; 97#endif 98#ifdef DT_CHR 99 case DT_CHR: return S_IFCHR; 100#endif 101#ifdef DT_DIR 102 case DT_DIR: return S_IFDIR; 103#endif 104#ifdef DT_BLK 105 case DT_BLK: return S_IFBLK; 106#endif 107#ifdef DT_REG 108 case DT_REG: return S_IFREG; 109#endif 110#ifdef DT_LNK 111 case DT_LNK: return S_IFLNK; 112#endif 113#ifdef DT_SOCK 114 case DT_SOCK: return S_IFSOCK; 115#endif 116 default: 117 return 0; /* Unknown. */ 118 } 119} 120 121#endif 122 123struct new_savedir_direntry_internal 124{ 125 int flags; /* from SaveDirDataFlags */ 126 mode_t type_info; 127 size_t buffer_offset; 128}; 129 130 131 132static int 133savedir_cmp(const void *p1, const void *p2) 134{ 135 const struct savedir_direntry *de1, *de2; 136 de1 = p1; 137 de2 = p2; 138 return strcmp(de1->name, de2->name); /* POSIX order, not locale order. */ 139} 140 141 142static struct savedir_direntry* 143convertentries(const struct savedir_dirinfo *info, 144 struct new_savedir_direntry_internal *internal) 145{ 146 char *p = info->buffer; 147 struct savedir_direntry *result; 148 int n =info->size; 149 int i; 150 151 152 result = xmalloc(sizeof(*result) * info->size); 153 154 for (i=0; i<n; ++i) 155 { 156 result[i].flags = internal[i].flags; 157 result[i].type_info = internal[i].type_info; 158 result[i].name = &p[internal[i].buffer_offset]; 159 } 160 return result; 161} 162 163 164struct savedir_dirinfo * 165xsavedir(const char *dir, int flags) 166{ 167 DIR *dirp; 168 struct dirent *dp; 169 struct savedir_dirinfo *result = NULL; 170 struct new_savedir_direntry_internal *internal; 171 172 size_t namebuf_allocated = 0u, namebuf_used = 0u; 173 size_t entrybuf_allocated = 0u; 174 int save_errno; 175 176 dirp = opendir (dir); 177 if (dirp == NULL) 178 return NULL; 179 180 errno = 0; 181 result = xmalloc(sizeof(*result)); 182 result->buffer = NULL; 183 result->size = 0u; 184 result->entries = NULL; 185 internal = NULL; 186 187 while ((dp = readdir (dirp)) != NULL) 188 { 189 /* Skip "", ".", and "..". "" is returned by at least one buggy 190 implementation: Solaris 2.4 readdir on NFS file systems. */ 191 char const *entry = dp->d_name; 192 if (entry[entry[0] != '.' ? 0 : entry[1] != '.' ? 1 : 2] != '\0') 193 { 194 /* Remember the name. */ 195 size_t entry_size = strlen (entry) + 1; 196 result->buffer = extendbuf(result->buffer, namebuf_used+entry_size, &namebuf_allocated); 197 memcpy ((result->buffer) + namebuf_used, entry, entry_size); 198 199 /* Remember the other stuff. */ 200 internal = extendbuf(internal, (1+result->size)*sizeof(*internal), &entrybuf_allocated); 201 internal[result->size].flags = 0; 202 203#if defined(HAVE_STRUCT_DIRENT_D_TYPE) && defined(USE_STRUCT_DIRENT_D_TYPE) 204 internal[result->size].type_info = type_to_mode(dp->d_type); 205 if (dp->d_type != DT_UNKNOWN) 206 internal[result->size].flags |= SavedirHaveFileType; 207#else 208 internal[result->size].type_info = 0; 209#endif 210 internal[result->size].buffer_offset = namebuf_used; 211 212 /* Prepare for the next iteration */ 213 ++(result->size); 214 namebuf_used += entry_size; 215 } 216 } 217 218 result->buffer = extendbuf(result->buffer, namebuf_used+1, &namebuf_allocated); 219 result->buffer[namebuf_used] = '\0'; 220 221 /* convert the result to its externally-usable form. */ 222 result->entries = convertentries(result, internal); 223 free(internal); 224 internal = NULL; 225 226 227 if (flags & SavedirSort) 228 { 229 qsort(result->entries, 230 result->size, sizeof(*result->entries), 231 savedir_cmp); 232 } 233 234 235 save_errno = errno; 236 if (CLOSEDIR (dirp) != 0) 237 save_errno = errno; 238 if (save_errno != 0) 239 { 240 free (result->buffer); 241 free (result); 242 errno = save_errno; 243 return NULL; 244 } 245 246 return result; 247} 248 249void free_dirinfo(struct savedir_dirinfo *p) 250{ 251 free(p->entries); 252 p->entries = NULL; 253 free(p->buffer); 254 p->buffer = NULL; 255 free(p); 256} 257 258 259 260static char * 261new_savedirinfo (const char *dir, struct savedir_extrainfo **extra) 262{ 263 struct savedir_dirinfo *p = xsavedir(dir, SavedirSort); 264 char *buf, *s; 265 size_t bufbytes = 0; 266 int i; 267 268 if (p) 269 { 270 struct savedir_extrainfo *pex = xmalloc(p->size * sizeof(*extra)); 271 for (i=0; i<p->size; ++i) 272 { 273 bufbytes += strlen(p->entries[i].name); 274 ++bufbytes; /* the \0 */ 275 276 pex[i].type_info = p->entries[i].type_info; 277 } 278 279 s = buf = xmalloc(bufbytes+1); 280 for (i=0; i<p->size; ++i) 281 { 282 size_t len = strlen(p->entries[i].name); 283 memcpy(s, p->entries[i].name, len); 284 s += len; 285 *s = 0; /* Place a NUL */ 286 ++s; /* Skip the NUL. */ 287 } 288 *s = 0; /* final (doubled) terminating NUL */ 289 290 if (extra) 291 *extra = pex; 292 else 293 free (pex); 294 return buf; 295 } 296 else 297 { 298 return NULL; 299 } 300} 301 302 303#if 0 304/* Return a freshly allocated string containing the filenames 305 in directory DIR, separated by '\0' characters; 306 the end is marked by two '\0' characters in a row. 307 Return NULL (setting errno) if DIR cannot be opened, read, or closed. */ 308 309static char * 310old_savedirinfo (const char *dir, struct savedir_extrainfo **extra) 311{ 312 DIR *dirp; 313 struct dirent *dp; 314 char *name_space; 315 size_t namebuf_allocated = 0u, namebuf_used = 0u; 316#if defined(HAVE_STRUCT_DIRENT_D_TYPE) && defined(USE_STRUCT_DIRENT_D_TYPE) 317 size_t extra_allocated = 0u, extra_used = 0u; 318 struct savedir_extrainfo *info = NULL; 319#endif 320 int save_errno; 321 322 if (extra) 323 *extra = NULL; 324 325 dirp = opendir (dir); 326 if (dirp == NULL) 327 return NULL; 328 329 errno = 0; 330 name_space = NULL; 331 while ((dp = readdir (dirp)) != NULL) 332 { 333 /* Skip "", ".", and "..". "" is returned by at least one buggy 334 implementation: Solaris 2.4 readdir on NFS file systems. */ 335 char const *entry = dp->d_name; 336 if (entry[entry[0] != '.' ? 0 : entry[1] != '.' ? 1 : 2] != '\0') 337 { 338 /* Remember the name. */ 339 size_t entry_size = strlen (entry) + 1; 340 name_space = extendbuf(name_space, namebuf_used+entry_size, &namebuf_allocated); 341 memcpy (name_space + namebuf_used, entry, entry_size); 342 namebuf_used += entry_size; 343 344 345#if defined(HAVE_STRUCT_DIRENT_D_TYPE) && defined(USE_STRUCT_DIRENT_D_TYPE) 346 /* Remember the type. */ 347 if (extra) 348 { 349 info = extendbuf(info, 350 (extra_used+1) * sizeof(struct savedir_dirinfo), 351 &extra_allocated); 352 info[extra_used].type_info = type_to_mode(dp->d_type); 353 ++extra_used; 354 } 355#endif 356 } 357 } 358 359 name_space = extendbuf(name_space, namebuf_used+1, &namebuf_allocated); 360 name_space[namebuf_used] = '\0'; 361 362 save_errno = errno; 363 if (CLOSEDIR (dirp) != 0) 364 save_errno = errno; 365 if (save_errno != 0) 366 { 367 free (name_space); 368 errno = save_errno; 369 return NULL; 370 } 371 372#if defined(HAVE_STRUCT_DIRENT_D_TYPE) && defined(USE_STRUCT_DIRENT_D_TYPE) 373 if (extra && info) 374 *extra = info; 375#endif 376 377 return name_space; 378} 379#endif 380 381 382char * 383savedirinfo (const char *dir, struct savedir_extrainfo **extra) 384{ 385 return new_savedirinfo(dir, extra); 386} 387