chiark / gitweb /
eglibc (2.11.3-4+deb6u3) squeeze-lts; urgency=medium
[eglibc.git] / locale / programs / charmap-dir.c
1 /* Copyright (C) 2000, 2001, 2002, 2003, 2005, 2007
2    Free Software Foundation, Inc.
3    This file is part of the GNU C Library.
4
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.
9
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.
14
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.  */
18
19 #include <dirent.h>
20 #include <errno.h>
21 #include <error.h>
22 #include <fcntl.h>
23 #include <libintl.h>
24 #ifndef NO_UNCOMPRESS
25 #include <spawn.h>
26 #endif
27 #include <stdio.h>
28 #include <stdlib.h>
29 #include <string.h>
30 #include <unistd.h>
31 #include <sys/stat.h>
32
33 #include "localedef.h"
34 #include "charmap-dir.h"
35
36 /* The data type of a charmap directory being traversed.  */
37 struct 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.  */
50 CHARMAP_DIR *
51 charmap_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       WITH_CUR_LOCALE (error (1, errno, gettext ("\
62 cannot read character map directory `%s'"), 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.  */
88 const char *
89 charmap_readdir (CHARMAP_DIR *cdir)
90 {
91   for (;;)
92     {
93       struct dirent64 *dirent;
94       size_t len;
95       size_t size;
96       char *filename;
97       mode_t mode;
98
99       dirent = readdir64 (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.  */
152 int
153 charmap_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 #ifndef NO_UNCOMPRESS
164 /* Creates a subprocess decompressing the given pathname, and returns
165    a stream reading its output (the decompressed data).  */
166 static
167 FILE *
168 fopen_uncompressed (const char *pathname, const char *compressor)
169 {
170   int pfd;
171
172   pfd = open (pathname, O_RDONLY);
173   if (pfd >= 0)
174     {
175       struct stat statbuf;
176       int fd[2];
177
178       if (fstat (pfd, &statbuf) >= 0
179           && S_ISREG (statbuf.st_mode)
180           && pipe (fd) >= 0)
181         {
182           char *argv[4]
183             = { (char *) compressor, (char *) "-d", (char *) "-c", NULL };
184           posix_spawn_file_actions_t actions;
185
186           if (posix_spawn_file_actions_init (&actions) == 0)
187             {
188               if (posix_spawn_file_actions_adddup2 (&actions,
189                                                     fd[1], STDOUT_FILENO) == 0
190                   && posix_spawn_file_actions_addclose (&actions, fd[1]) == 0
191                   && posix_spawn_file_actions_addclose (&actions, fd[0]) == 0
192                   && posix_spawn_file_actions_adddup2 (&actions,
193                                                        pfd, STDIN_FILENO) == 0
194                   && posix_spawn_file_actions_addclose (&actions, pfd) == 0
195                   && posix_spawnp (NULL, compressor, &actions, NULL,
196                                    argv, environ) == 0)
197                 {
198                   posix_spawn_file_actions_destroy (&actions);
199                   close (fd[1]);
200                   close (pfd);
201                   return fdopen (fd[0], "r");
202                 }
203               posix_spawn_file_actions_destroy (&actions);
204             }
205           close (fd[1]);
206           close (fd[0]);
207         }
208       close (pfd);
209     }
210   return NULL;
211 }
212 #endif
213
214 /* Opens a charmap for reading, given its name (not an alias name).  */
215 FILE *
216 charmap_open (const char *directory, const char *name)
217 {
218   size_t dlen = strlen (directory);
219   int add_slash = (dlen == 0 || directory[dlen - 1] != '/');
220   size_t nlen = strlen (name);
221   char *pathname;
222   char *p;
223   FILE *stream;
224
225   pathname = alloca (dlen + add_slash + nlen + 5);
226   p = stpcpy (pathname, directory);
227   if (add_slash)
228     *p++ = '/';
229   p = stpcpy (p, name);
230
231   stream = fopen (pathname, "rm");
232   if (stream != NULL)
233     return stream;
234
235 #ifndef NO_UNCOMPRESS
236   memcpy (p, ".gz", 4);
237   stream = fopen_uncompressed (pathname, "gzip");
238   if (stream != NULL)
239     return stream;
240
241   memcpy (p, ".bz2", 5);
242   stream = fopen_uncompressed (pathname, "bzip2");
243   if (stream != NULL)
244     return stream;
245 #endif
246
247   return NULL;
248 }
249
250 /* An empty alias list.  Avoids the need to return NULL from
251    charmap_aliases.  */
252 static char *empty[1];
253
254 /* Returns a NULL terminated list of alias names of a charmap.  */
255 char **
256 charmap_aliases (const char *directory, const char *name)
257 {
258   FILE *stream;
259   char **aliases;
260   size_t naliases;
261
262   stream = charmap_open (directory, name);
263   if (stream == NULL)
264     return empty;
265
266   aliases = NULL;
267   naliases = 0;
268
269   while (!feof (stream))
270     {
271       char *alias = NULL;
272       char junk[BUFSIZ];
273
274       if (fscanf (stream, " <code_set_name> %as", &alias) == 1
275           || fscanf (stream, "%% alias %as", &alias) == 1)
276         {
277           aliases = (char **) xrealloc (aliases,
278                                         (naliases + 2) * sizeof (char *));
279           aliases[naliases++] = alias;
280         }
281
282       /* Read the rest of the line.  */
283       if (fgets (junk, sizeof junk, stream) != NULL)
284         {
285           if (strstr (junk, "CHARMAP") != NULL)
286             /* We cannot expect more aliases from now on.  */
287             break;
288
289           while (strchr (junk, '\n') == NULL
290                  && fgets (junk, sizeof junk, stream) != NULL)
291             continue;
292         }
293     }
294
295   fclose (stream);
296
297   if (naliases == 0)
298     return empty;
299
300   aliases[naliases] = NULL;
301   return aliases;
302 }
303
304 /* Frees an alias list returned by charmap_aliases.  */
305 void
306 charmap_free_aliases (char **aliases)
307 {
308   if (aliases != empty)
309     {
310       char **p;
311
312       for (p = aliases; *p; p++)
313         free (*p);
314
315       free (aliases);
316     }
317 }