(hsearch_r): Add back ensurance that hval is not zero.
[glibc/history.git] / locale / programs / charmap-dir.c
blob776f01e78b5e03e68042705669e484f6427012c8
1 /* Copyright (C) 2000, 2001, 2002, 2003, 2005, 2007
2 Free Software Foundation, Inc.
3 This file is part of the GNU C Library.
5 This program is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published
7 by the Free Software Foundation; version 2 of the License, or
8 (at your option) any later version.
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU General Public License for more details.
15 You should have received a copy of the GNU General Public License
16 along with this program; if not, write to the Free Software Foundation,
17 Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */
19 #include <dirent.h>
20 #include <errno.h>
21 #include <error.h>
22 #include <fcntl.h>
23 #include <libintl.h>
24 #include <spawn.h>
25 #include <stdio.h>
26 #include <stdlib.h>
27 #include <string.h>
28 #include <unistd.h>
29 #include <sys/stat.h>
31 #include "localedef.h"
32 #include "charmap-dir.h"
34 /* The data type of a charmap directory being traversed. */
35 struct charmap_dir
37 DIR *dir;
38 /* The directory pathname, ending in a slash. */
39 char *directory;
40 size_t directory_len;
41 /* Scratch area used for returning pathnames. */
42 char *pathname;
43 size_t pathname_size;
46 /* Starts a charmap directory traversal.
47 Returns a CHARMAP_DIR, or NULL if the directory doesn't exist. */
48 CHARMAP_DIR *
49 charmap_opendir (const char *directory)
51 struct charmap_dir *cdir;
52 DIR *dir;
53 size_t len;
54 int add_slash;
56 dir = opendir (directory);
57 if (dir == NULL)
59 WITH_CUR_LOCALE (error (1, errno, gettext ("\
60 cannot read character map directory `%s'"), directory));
61 return NULL;
64 cdir = (struct charmap_dir *) xmalloc (sizeof (struct charmap_dir));
65 cdir->dir = dir;
67 len = strlen (directory);
68 add_slash = (len == 0 || directory[len - 1] != '/');
69 cdir->directory = (char *) xmalloc (len + add_slash + 1);
70 memcpy (cdir->directory, directory, len);
71 if (add_slash)
72 cdir->directory[len] = '/';
73 cdir->directory[len + add_slash] = '\0';
74 cdir->directory_len = len + add_slash;
76 cdir->pathname = NULL;
77 cdir->pathname_size = 0;
79 return cdir;
82 /* Reads the next directory entry.
83 Returns its charmap name, or NULL if past the last entry or upon error.
84 The storage returned may be overwritten by a later charmap_readdir
85 call on the same CHARMAP_DIR. */
86 const char *
87 charmap_readdir (CHARMAP_DIR *cdir)
89 for (;;)
91 struct dirent64 *dirent;
92 size_t len;
93 size_t size;
94 char *filename;
95 mode_t mode;
97 dirent = readdir64 (cdir->dir);
98 if (dirent == NULL)
99 return NULL;
100 if (strcmp (dirent->d_name, ".") == 0)
101 continue;
102 if (strcmp (dirent->d_name, "..") == 0)
103 continue;
105 len = strlen (dirent->d_name);
107 size = cdir->directory_len + len + 1;
108 if (size > cdir->pathname_size)
110 free (cdir->pathname);
111 if (size < 2 * cdir->pathname_size)
112 size = 2 * cdir->pathname_size;
113 cdir->pathname = (char *) xmalloc (size);
114 cdir->pathname_size = size;
117 stpcpy (stpcpy (cdir->pathname, cdir->directory), dirent->d_name);
118 filename = cdir->pathname + cdir->directory_len;
120 #ifdef _DIRENT_HAVE_D_TYPE
121 if (dirent->d_type != DT_UNKNOWN && dirent->d_type != DT_LNK)
122 mode = DTTOIF (dirent->d_type);
123 else
124 #endif
126 struct stat statbuf;
128 if (stat (cdir->pathname, &statbuf) < 0)
129 continue;
131 mode = statbuf.st_mode;
134 if (!S_ISREG (mode))
135 continue;
137 /* For compressed charmaps, the canonical charmap name does not
138 include the extension. */
139 if (len > 3 && memcmp (&filename[len - 3], ".gz", 3) == 0)
140 filename[len - 3] = '\0';
141 else if (len > 4 && memcmp (&filename[len - 4], ".bz2", 4) == 0)
142 filename[len - 4] = '\0';
144 return filename;
148 /* Finishes a charmap directory traversal, and frees the resources
149 attached to the CHARMAP_DIR. */
151 charmap_closedir (CHARMAP_DIR *cdir)
153 DIR *dir = cdir->dir;
155 free (cdir->directory);
156 free (cdir->pathname);
157 free (cdir);
158 return closedir (dir);
161 /* Creates a subprocess decompressing the given pathname, and returns
162 a stream reading its output (the decompressed data). */
163 static
164 FILE *
165 fopen_uncompressed (const char *pathname, const char *compressor)
167 int pfd;
169 pfd = open (pathname, O_RDONLY);
170 if (pfd >= 0)
172 struct stat statbuf;
173 int fd[2];
175 if (fstat (pfd, &statbuf) >= 0
176 && S_ISREG (statbuf.st_mode)
177 && pipe (fd) >= 0)
179 char *argv[4]
180 = { (char *) compressor, (char *) "-d", (char *) "-c", NULL };
181 posix_spawn_file_actions_t actions;
183 if (posix_spawn_file_actions_init (&actions) == 0)
185 if (posix_spawn_file_actions_adddup2 (&actions,
186 fd[1], STDOUT_FILENO) == 0
187 && posix_spawn_file_actions_addclose (&actions, fd[1]) == 0
188 && posix_spawn_file_actions_addclose (&actions, fd[0]) == 0
189 && posix_spawn_file_actions_adddup2 (&actions,
190 pfd, STDIN_FILENO) == 0
191 && posix_spawn_file_actions_addclose (&actions, pfd) == 0
192 && posix_spawnp (NULL, compressor, &actions, NULL,
193 argv, environ) == 0)
195 posix_spawn_file_actions_destroy (&actions);
196 close (fd[1]);
197 close (pfd);
198 return fdopen (fd[0], "r");
200 posix_spawn_file_actions_destroy (&actions);
202 close (fd[1]);
203 close (fd[0]);
205 close (pfd);
207 return NULL;
210 /* Opens a charmap for reading, given its name (not an alias name). */
211 FILE *
212 charmap_open (const char *directory, const char *name)
214 size_t dlen = strlen (directory);
215 int add_slash = (dlen == 0 || directory[dlen - 1] != '/');
216 size_t nlen = strlen (name);
217 char *pathname;
218 char *p;
219 FILE *stream;
221 pathname = alloca (dlen + add_slash + nlen + 5);
222 p = stpcpy (pathname, directory);
223 if (add_slash)
224 *p++ = '/';
225 p = stpcpy (p, name);
227 stream = fopen (pathname, "rm");
228 if (stream != NULL)
229 return stream;
231 memcpy (p, ".gz", 4);
232 stream = fopen_uncompressed (pathname, "gzip");
233 if (stream != NULL)
234 return stream;
236 memcpy (p, ".bz2", 5);
237 stream = fopen_uncompressed (pathname, "bzip2");
238 if (stream != NULL)
239 return stream;
241 return NULL;
244 /* An empty alias list. Avoids the need to return NULL from
245 charmap_aliases. */
246 static char *empty[1];
248 /* Returns a NULL terminated list of alias names of a charmap. */
249 char **
250 charmap_aliases (const char *directory, const char *name)
252 FILE *stream;
253 char **aliases;
254 size_t naliases;
256 stream = charmap_open (directory, name);
257 if (stream == NULL)
258 return empty;
260 aliases = NULL;
261 naliases = 0;
263 while (!feof (stream))
265 char *alias = NULL;
266 char junk[BUFSIZ];
268 if (fscanf (stream, " <code_set_name> %ms", &alias) == 1
269 || fscanf (stream, "%% alias %ms", &alias) == 1)
271 aliases = (char **) xrealloc (aliases,
272 (naliases + 2) * sizeof (char *));
273 aliases[naliases++] = alias;
276 /* Read the rest of the line. */
277 if (fgets (junk, sizeof junk, stream) != NULL)
279 if (strstr (junk, "CHARMAP") != NULL)
280 /* We cannot expect more aliases from now on. */
281 break;
283 while (strchr (junk, '\n') == NULL
284 && fgets (junk, sizeof junk, stream) != NULL)
285 continue;
289 fclose (stream);
291 if (naliases == 0)
292 return empty;
294 aliases[naliases] = NULL;
295 return aliases;
298 /* Frees an alias list returned by charmap_aliases. */
299 void
300 charmap_free_aliases (char **aliases)
302 if (aliases != empty)
304 char **p;
306 for (p = aliases; *p; p++)
307 free (*p);
309 free (aliases);