]> git.ipfire.org Git - thirdparty/glibc.git/blame - locale/programs/charmap-dir.c
Update.
[thirdparty/glibc.git] / locale / programs / charmap-dir.c
CommitLineData
8a0746ae 1/* Copyright (C) 2000,2001 Free Software Foundation, Inc.
3e076219
UD
2 This file is part of the GNU C Library.
3
4 The GNU C Library is free software; you can redistribute it and/or
41bdb6e2
AJ
5 modify it under the terms of the GNU Lesser General Public
6 License as published by the Free Software Foundation; either
7 version 2.1 of the License, or (at your option) any later version.
3e076219
UD
8
9 The GNU C Library is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
41bdb6e2 12 Lesser General Public License for more details.
3e076219 13
41bdb6e2
AJ
14 You should have received a copy of the GNU Lesser General Public
15 License along with the GNU C Library; if not, write to the Free
16 Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
17 02111-1307 USA. */
3e076219
UD
18
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>
30
31#include "charmap-dir.h"
32
33extern void *xmalloc (size_t n);
34extern void *xrealloc (void *p, size_t n);
35
36/* The data type of a charmap directory being traversed. */
37struct charmap_dir
38{
39 DIR *dir;
40 /* The directory pathname, ending in a slash. */
41 char *directory;
42 size_t directory_len;
43 /* Scratch area used for returning pathnames. */
44 char *pathname;
45 size_t pathname_size;
46};
47
48/* Starts a charmap directory traversal.
49 Returns a CHARMAP_DIR, or NULL if the directory doesn't exist. */
50CHARMAP_DIR *
51charmap_opendir (const char *directory)
52{
53 struct charmap_dir *cdir;
54 DIR *dir;
55 size_t len;
56 int add_slash;
57
58 dir = opendir (directory);
59 if (dir == NULL)
60 {
61 error (1, errno, gettext ("cannot read character map directory `%s'"),
62 directory);
63 return NULL;
64 }
65
66 cdir = (struct charmap_dir *) xmalloc (sizeof (struct charmap_dir));
67 cdir->dir = dir;
68
69 len = strlen (directory);
70 add_slash = (len == 0 || directory[len - 1] != '/');
71 cdir->directory = (char *) xmalloc (len + add_slash + 1);
72 memcpy (cdir->directory, directory, len);
73 if (add_slash)
74 cdir->directory[len] = '/';
75 cdir->directory[len + add_slash] = '\0';
76 cdir->directory_len = len + add_slash;
77
78 cdir->pathname = NULL;
79 cdir->pathname_size = 0;
80
81 return cdir;
82}
83
84/* Reads the next directory entry.
85 Returns its charmap name, or NULL if past the last entry or upon error.
86 The storage returned may be overwritten by a later charmap_readdir
87 call on the same CHARMAP_DIR. */
88const char *
89charmap_readdir (CHARMAP_DIR *cdir)
90{
91 for (;;)
92 {
93 struct dirent *dirent;
94 size_t len;
95 size_t size;
96 char *filename;
97 mode_t mode;
98
99 dirent = readdir (cdir->dir);
100 if (dirent == NULL)
101 return NULL;
102 if (strcmp (dirent->d_name, ".") == 0)
103 continue;
104 if (strcmp (dirent->d_name, "..") == 0)
105 continue;
106
107 len = strlen (dirent->d_name);
108
109 size = cdir->directory_len + len + 1;
110 if (size > cdir->pathname_size)
111 {
112 free (cdir->pathname);
113 if (size < 2 * cdir->pathname_size)
114 size = 2 * cdir->pathname_size;
115 cdir->pathname = (char *) xmalloc (size);
116 cdir->pathname_size = size;
117 }
118
119 stpcpy (stpcpy (cdir->pathname, cdir->directory), dirent->d_name);
120 filename = cdir->pathname + cdir->directory_len;
121
122#ifdef _DIRENT_HAVE_D_TYPE
123 if (dirent->d_type != DT_UNKNOWN && dirent->d_type != DT_LNK)
124 mode = DTTOIF (dirent->d_type);
125 else
126#endif
127 {
128 struct stat statbuf;
129
130 if (stat (cdir->pathname, &statbuf) < 0)
131 continue;
132
133 mode = statbuf.st_mode;
134 }
135
136 if (!S_ISREG (mode))
137 continue;
138
139 /* For compressed charmaps, the canonical charmap name does not
140 include the extension. */
141 if (len > 3 && memcmp (&filename[len - 3], ".gz", 3) == 0)
142 filename[len - 3] = '\0';
143 else if (len > 4 && memcmp (&filename[len - 4], ".bz2", 4) == 0)
144 filename[len - 4] = '\0';
145
146 return filename;
147 }
148}
149
150/* Finishes a charmap directory traversal, and frees the resources
151 attached to the CHARMAP_DIR. */
152int
153charmap_closedir (CHARMAP_DIR *cdir)
154{
155 DIR *dir = cdir->dir;
156
157 free (cdir->directory);
158 free (cdir->pathname);
159 free (cdir);
160 return closedir (dir);
161}
162
163/* Creates a subprocess decompressing the given pathname, and returns
164 a stream reading its output (the decompressed data). */
165static
166FILE *
8a0746ae 167fopen_uncompressed (const char *pathname, const char *compressor)
3e076219
UD
168{
169 int pfd;
170
171 pfd = open (pathname, O_RDONLY);
172 if (pfd >= 0)
173 {
174 struct stat statbuf;
175 int fd[2];
176
177 if (fstat (pfd, &statbuf) >= 0
178 && S_ISREG (statbuf.st_mode)
179 && pipe (fd) >= 0)
180 {
8a0746ae
RM
181 char *argv[4]
182 = { (char *) compressor, (char *) "-d", (char *) "-c", NULL };
3e076219
UD
183 posix_spawn_file_actions_t actions;
184
185 if (posix_spawn_file_actions_init (&actions) == 0)
186 {
187 if (posix_spawn_file_actions_adddup2 (&actions,
188 fd[1], STDOUT_FILENO) == 0
189 && posix_spawn_file_actions_addclose (&actions, fd[1]) == 0
190 && posix_spawn_file_actions_addclose (&actions, fd[0]) == 0
191 && posix_spawn_file_actions_adddup2 (&actions,
192 pfd, STDIN_FILENO) == 0
193 && posix_spawn_file_actions_addclose (&actions, pfd) == 0
194 && posix_spawnp (NULL, compressor, &actions, NULL,
195 argv, environ) == 0)
196 {
197 posix_spawn_file_actions_destroy (&actions);
198 close (fd[1]);
199 close (pfd);
200 return fdopen (fd[0], "r");
201 }
202 posix_spawn_file_actions_destroy (&actions);
203 }
204 close (fd[1]);
205 close (fd[0]);
206 }
207 close (pfd);
208 }
209 return NULL;
210}
211
212/* Opens a charmap for reading, given its name (not an alias name). */
213FILE *
214charmap_open (const char *directory, const char *name)
215{
216 size_t dlen = strlen (directory);
217 int add_slash = (dlen == 0 || directory[dlen - 1] != '/');
218 size_t nlen = strlen (name);
219 char *pathname;
220 char *p;
221 FILE *stream;
222
223 pathname = alloca (dlen + add_slash + nlen + 5);
224 p = stpcpy (pathname, directory);
225 if (add_slash)
226 *p++ = '/';
227 p = stpcpy (p, name);
228
229 stream = fopen (pathname, "r");
230 if (stream != NULL)
231 return stream;
232
233 memcpy (p, ".gz", 4);
234 stream = fopen_uncompressed (pathname, "gzip");
235 if (stream != NULL)
236 return stream;
237
238 memcpy (p, ".bz2", 5);
239 stream = fopen_uncompressed (pathname, "bzip2");
240 if (stream != NULL)
241 return stream;
242
243 return NULL;
244}
245
246/* An empty alias list. Avoids the need to return NULL from
247 charmap_aliases. */
248static char *empty[1];
249
250/* Returns a NULL terminated list of alias names of a charmap. */
251char **
252charmap_aliases (const char *directory, const char *name)
253{
254 FILE *stream;
255 char **aliases;
256 size_t naliases;
257
258 stream = charmap_open (directory, name);
259 if (stream == NULL)
260 return empty;
261
262 aliases = NULL;
263 naliases = 0;
264
265 while (!feof (stream))
266 {
267 char *alias = NULL;
268 char junk[BUFSIZ];
269
270 if (fscanf (stream, " <code_set_name> %as", &alias) == 1
271 || fscanf (stream, "%% alias %as", &alias) == 1)
272 {
273 aliases = (char **) xrealloc (aliases,
274 (naliases + 2) * sizeof (char *));
275 aliases[naliases++] = alias;
276 }
277
278 /* Read the rest of the line. */
279 if (fgets (junk, sizeof junk, stream) != NULL)
280 {
281 if (strstr (junk, "CHARMAP") != NULL)
282 /* We cannot expect more aliases from now on. */
283 break;
284
285 while (strchr (junk, '\n') == NULL
286 && fgets (junk, sizeof junk, stream) != NULL)
287 continue;
288 }
289 }
290
291 fclose (stream);
292
293 if (naliases == 0)
294 return empty;
295
296 aliases[naliases] = NULL;
297 return aliases;
298}
299
300/* Frees an alias list returned by charmap_aliases. */
301void
302charmap_free_aliases (char **aliases)
303{
304 if (aliases != empty)
305 {
306 char **p;
307
308 for (p = aliases; *p; p++)
309 free (*p);
310
311 free (aliases);
312 }
313}