Import upstream version 1.28
[debian/tar] / gnu / savedir.c
1 /* -*- buffer-read-only: t -*- vi: set ro: */
2 /* DO NOT EDIT! GENERATED AUTOMATICALLY! */
3 /* savedir.c -- save the list of files in a directory in a string
4
5    Copyright (C) 1990, 1997-2001, 2003-2006, 2009-2014 Free Software
6    Foundation, Inc.
7
8    This program is free software: you can redistribute it and/or modify
9    it under the terms of the GNU General Public License as published by
10    the Free Software Foundation; either version 3 of the License, or
11    (at your option) any later version.
12
13    This program is distributed in the hope that it will be useful,
14    but WITHOUT ANY WARRANTY; without even the implied warranty of
15    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16    GNU General Public License for more details.
17
18    You should have received a copy of the GNU General Public License
19    along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
20
21 /* Written by David MacKenzie <djm@gnu.ai.mit.edu>. */
22
23 #include <config.h>
24
25 #include "savedir.h"
26
27 #include <sys/types.h>
28
29 #include <errno.h>
30
31 #include "dirent--.h"
32 #ifndef _D_EXACT_NAMLEN
33 # define _D_EXACT_NAMLEN(dp)    strlen ((dp)->d_name)
34 #endif
35
36 #include <stddef.h>
37 #include <stdlib.h>
38 #include <string.h>
39
40 #include "xalloc.h"
41
42 typedef struct
43 {
44   char *name;
45 #if D_INO_IN_DIRENT
46   ino_t ino;
47 #endif
48 } direntry_t;
49
50 /* Compare the names of two directory entries */
51
52 static int
53 direntry_cmp_name (void const *a, void const *b)
54 {
55   direntry_t const *dea = a;
56   direntry_t const *deb = b;
57
58   return strcmp (dea->name, deb->name);
59 }
60
61 #if D_INO_IN_DIRENT
62 /* Compare the inode numbers of two directory entries */
63
64 static int
65 direntry_cmp_inode (void const *a, void const *b)
66 {
67   direntry_t const *dea = a;
68   direntry_t const *deb = b;
69
70   return dea->ino < deb->ino ? -1 : dea->ino > deb->ino;
71 }
72 #endif
73
74 typedef int (*comparison_function) (void const *, void const *);
75
76 static comparison_function const comparison_function_table[] =
77   {
78     0,
79     direntry_cmp_name
80 #if D_INO_IN_DIRENT
81     , direntry_cmp_inode
82 #endif
83   };
84
85 /* Return a freshly allocated string containing the file names
86    in directory DIRP, separated by '\0' characters;
87    the end is marked by two '\0' characters in a row.
88    Returned values are sorted according to OPTION.
89    Return NULL (setting errno) if DIRP cannot be read.
90    If DIRP is NULL, return NULL without affecting errno.  */
91
92 char *
93 streamsavedir (DIR *dirp, enum savedir_option option)
94 {
95   char *name_space = NULL;
96   size_t allocated = 0;
97   direntry_t *entries = NULL;
98   size_t entries_allocated = 0;
99   size_t entries_used = 0;
100   size_t used = 0;
101   int readdir_errno;
102   comparison_function cmp = comparison_function_table[option];
103
104   if (dirp == NULL)
105     return NULL;
106
107   for (;;)
108     {
109       struct dirent const *dp;
110       char const *entry;
111
112       errno = 0;
113       dp = readdir (dirp);
114       if (! dp)
115         break;
116
117       /* Skip "", ".", and "..".  "" is returned by at least one buggy
118          implementation: Solaris 2.4 readdir on NFS file systems.  */
119       entry = dp->d_name;
120       if (entry[entry[0] != '.' ? 0 : entry[1] != '.' ? 1 : 2] != '\0')
121         {
122           size_t entry_size = _D_EXACT_NAMLEN (dp) + 1;
123           if (cmp)
124             {
125               if (entries_allocated == entries_used)
126                 {
127                   size_t n = entries_allocated;
128                   entries = x2nrealloc (entries, &n, sizeof *entries);
129                   entries_allocated = n;
130                 }
131               entries[entries_used].name = xstrdup (entry);
132 #if D_INO_IN_DIRENT
133               entries[entries_used].ino = dp->d_ino;
134 #endif
135               entries_used++;
136             }
137           else
138             {
139               if (allocated - used <= entry_size)
140                 {
141                   size_t n = used + entry_size;
142                   if (n < used)
143                     xalloc_die ();
144                   name_space = x2nrealloc (name_space, &n, 1);
145                   allocated = n;
146                 }
147               memcpy (name_space + used, entry, entry_size);
148             }
149           used += entry_size;
150         }
151     }
152
153   readdir_errno = errno;
154   if (readdir_errno != 0)
155     {
156       free (entries);
157       free (name_space);
158       errno = readdir_errno;
159       return NULL;
160     }
161
162   if (cmp)
163     {
164       size_t i;
165
166       qsort (entries, entries_used, sizeof *entries, cmp);
167       name_space = xmalloc (used + 1);
168       used = 0;
169       for (i = 0; i < entries_used; i++)
170         {
171           char *dest = name_space + used;
172           used += stpcpy (dest, entries[i].name) - dest + 1;
173           free (entries[i].name);
174         }
175       free (entries);
176     }
177   else if (used == allocated)
178     name_space = xrealloc (name_space, used + 1);
179
180   name_space[used] = '\0';
181   return name_space;
182 }
183
184 /* Return a freshly allocated string containing the file names
185    in directory DIR, separated by '\0' characters;
186    the end is marked by two '\0' characters in a row.
187    Return NULL (setting errno) if DIR cannot be opened, read, or closed.  */
188
189 char *
190 savedir (char const *dir, enum savedir_option option)
191 {
192   DIR *dirp = opendir (dir);
193   if (! dirp)
194     return NULL;
195   else
196     {
197       char *name_space = streamsavedir (dirp, option);
198       if (closedir (dirp) != 0)
199         {
200           int closedir_errno = errno;
201           free (name_space);
202           errno = closedir_errno;
203           return NULL;
204         }
205       return name_space;
206     }
207 }