Imported Upstream version 4.5.10
[platform/upstream/findutils.git] / lib / savedirinfo.c
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,
4              2005, 2010 Free 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 #include <config.h>
23
24 #include <sys/stat.h>
25
26 #if HAVE_SYS_TYPES_H
27 # include <sys/types.h>
28 #endif
29
30 /* The presence of unistd.h is assumed by gnulib these days, so we
31  * might as well assume it too.
32  */
33 #include <unistd.h>
34
35 #include <errno.h>
36
37 #if HAVE_DIRENT_H
38 # include <dirent.h>
39 #else
40 # define dirent direct
41 # if HAVE_SYS_NDIR_H
42 #  include <sys/ndir.h>
43 # endif
44 # if HAVE_SYS_DIR_H
45 #  include <sys/dir.h>
46 # endif
47 # if HAVE_NDIR_H
48 #  include <ndir.h>
49 # endif
50 #endif
51
52 #ifdef CLOSEDIR_VOID
53 /* Fake a return value. */
54 # define CLOSEDIR(d) (closedir (d), 0)
55 #else
56 # define CLOSEDIR(d) closedir (d)
57 #endif
58
59 #include <stddef.h>
60 #include <stdlib.h>
61 #include <string.h>
62
63 #include "xalloc.h"
64 #include "extendbuf.h"
65 #include "dirent-safer.h"
66 #include "savedirinfo.h"
67
68 #if defined HAVE_STRUCT_DIRENT_D_TYPE
69 /* Convert the value of struct dirent.d_type into a value for
70  * struct stat.st_mode (at least the file type bits), or zero
71  * if the type is DT_UNKNOWN or is a value we don't know about.
72  */
73 static mode_t
74 type_to_mode (unsigned type)
75 {
76   switch (type)
77     {
78 #ifdef DT_FIFO
79     case DT_FIFO: return S_IFIFO;
80 #endif
81 #ifdef DT_CHR
82     case DT_CHR:  return S_IFCHR;
83 #endif
84 #ifdef DT_DIR
85     case DT_DIR:  return S_IFDIR;
86 #endif
87 #ifdef DT_BLK
88     case DT_BLK:  return S_IFBLK;
89 #endif
90 #ifdef DT_REG
91     case DT_REG:  return S_IFREG;
92 #endif
93 #ifdef DT_LNK
94     case DT_LNK:  return S_IFLNK;
95 #endif
96 #ifdef DT_SOCK
97     case DT_SOCK: return S_IFSOCK;
98 #endif
99     default:
100       return 0;                 /* Unknown. */
101     }
102 }
103 #endif
104
105 struct new_savedir_direntry_internal
106 {
107   int    flags;                 /* from SaveDirDataFlags */
108   mode_t type_info;
109   size_t buffer_offset;
110 };
111
112
113
114 static int
115 savedir_cmp (const void *p1, const void *p2)
116 {
117   const struct savedir_direntry *de1, *de2;
118   de1 = p1;
119   de2 = p2;
120   return strcmp (de1->name, de2->name); /* POSIX order, not locale order. */
121 }
122
123
124 static struct savedir_direntry*
125 convertentries (const struct savedir_dirinfo *info,
126                 struct new_savedir_direntry_internal *internal)
127 {
128   char *p = info->buffer;
129   struct savedir_direntry *result;
130   int n =info->size;
131   int i;
132
133
134   result = xmalloc (sizeof (*result) * info->size);
135
136   for (i=0; i<n; ++i)
137     {
138       result[i].flags = internal[i].flags;
139       result[i].type_info = internal[i].type_info;
140       result[i].name = &p[internal[i].buffer_offset];
141     }
142   return result;
143 }
144
145
146 struct savedir_dirinfo *
147 xsavedir (const char *dir, int flags)
148 {
149   DIR *dirp;
150   struct dirent *dp;
151   struct savedir_dirinfo *result = NULL;
152   struct new_savedir_direntry_internal *internal;
153
154   size_t namebuf_allocated = 0u, namebuf_used = 0u;
155   size_t entrybuf_allocated = 0u;
156   int save_errno;
157
158   dirp = opendir_safer (dir);
159   if (dirp == NULL)
160     return NULL;
161
162   errno = 0;
163   result = xmalloc (sizeof (*result));
164   result->buffer = NULL;
165   result->size = 0u;
166   result->entries = NULL;
167   internal = NULL;
168
169   while ((dp = readdir (dirp)) != NULL)
170     {
171       /* Skip "", ".", and "..".  "" is returned by at least one buggy
172          implementation: Solaris 2.4 readdir on NFS file systems.  */
173       char const *entry = dp->d_name;
174       if (entry[entry[0] != '.' ? 0 : entry[1] != '.' ? 1 : 2] != '\0')
175         {
176           /* Remember the name. */
177           size_t entry_size = strlen (entry) + 1;
178           result->buffer = xextendbuf (result->buffer, namebuf_used+entry_size, &namebuf_allocated);
179           memcpy ((result->buffer) + namebuf_used, entry, entry_size);
180
181           /* Remember the other stuff. */
182           internal = xextendbuf (internal, (1+result->size)*sizeof (*internal), &entrybuf_allocated);
183           internal[result->size].flags = 0;
184
185           internal[result->size].type_info = 0;
186 #if defined HAVE_STRUCT_DIRENT_D_TYPE
187           internal[result->size].type_info = type_to_mode (dp->d_type);
188           if (dp->d_type != DT_UNKNOWN)
189             internal[result->size].flags |= SavedirHaveFileType;
190 #endif
191           internal[result->size].buffer_offset = namebuf_used;
192
193           /* Prepare for the next iteration */
194           ++(result->size);
195           namebuf_used += entry_size;
196         }
197     }
198
199   result->buffer = xextendbuf (result->buffer, namebuf_used+1, &namebuf_allocated);
200   result->buffer[namebuf_used] = '\0';
201
202   /* convert the result to its externally-usable form. */
203   result->entries = convertentries (result, internal);
204   free (internal);
205   internal = NULL;
206
207
208   if (flags & SavedirSort)
209     {
210       qsort (result->entries,
211              result->size, sizeof (*result->entries),
212              savedir_cmp);
213     }
214
215
216   save_errno = errno;
217   if (CLOSEDIR (dirp) != 0)
218     save_errno = errno;
219   if (save_errno != 0)
220     {
221       free (result->buffer);
222       free (result);
223       errno = save_errno;
224       return NULL;
225     }
226
227   return result;
228 }
229
230 void free_dirinfo (struct savedir_dirinfo *p)
231 {
232   free (p->entries);
233   p->entries = NULL;
234   free (p->buffer);
235   p->buffer = NULL;
236   free (p);
237 }
238
239
240
241 char *
242 savedirinfo (const char *dir, struct savedir_extrainfo **extra)
243 {
244   struct savedir_dirinfo *p = xsavedir (dir, SavedirSort);
245   char *buf, *s;
246   size_t bufbytes = 0;
247   unsigned int i;
248
249   if (p)
250     {
251       struct savedir_extrainfo *pex = xmalloc (p->size * sizeof (*extra));
252       for (i=0; i<p->size; ++i)
253         {
254           bufbytes += strlen (p->entries[i].name);
255           ++bufbytes;           /* the \0 */
256
257           pex[i].type_info = p->entries[i].type_info;
258         }
259
260       s = buf = xmalloc (bufbytes+1);
261       for (i=0; i<p->size; ++i)
262         {
263           size_t len = strlen (p->entries[i].name);
264           memcpy (s, p->entries[i].name, len);
265           s += len;
266           *s = 0;               /* Place a NUL */
267           ++s;                  /* Skip the NUL. */
268         }
269       *s = 0;                   /* final (doubled) terminating NUL */
270
271       if (extra)
272         *extra = pex;
273       else
274         free (pex);
275       return buf;
276     }
277   else
278     {
279       return NULL;
280     }
281 }