903bd5cabc2b0fdc0d67c0642b2199ce0c605649
[platform/upstream/glibc.git] / locale / programs / charmap-dir.c
1 /* Copyright (C) 2000, 2001, 2002, 2003 Free Software Foundation, Inc.
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
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.
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
12    Lesser General Public License for more details.
13
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.  */
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 "localedef.h"
32 #include "charmap-dir.h"
33
34 /* The data type of a charmap directory being traversed.  */
35 struct charmap_dir
36 {
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;
44 };
45
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)
50 {
51   struct charmap_dir *cdir;
52   DIR *dir;
53   size_t len;
54   int add_slash;
55
56   dir = opendir (directory);
57   if (dir == NULL)
58     {
59       WITH_CUR_LOCALE (error (1, errno, gettext ("\
60 cannot read character map directory `%s'"), directory));
61       return NULL;
62     }
63
64   cdir = (struct charmap_dir *) xmalloc (sizeof (struct charmap_dir));
65   cdir->dir = dir;
66
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;
75
76   cdir->pathname = NULL;
77   cdir->pathname_size = 0;
78
79   return cdir;
80 }
81
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)
88 {
89   for (;;)
90     {
91       struct dirent64 *dirent;
92       size_t len;
93       size_t size;
94       char *filename;
95       mode_t mode;
96
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;
104
105       len = strlen (dirent->d_name);
106
107       size = cdir->directory_len + len + 1;
108       if (size > cdir->pathname_size)
109         {
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;
115         }
116
117       stpcpy (stpcpy (cdir->pathname, cdir->directory), dirent->d_name);
118       filename = cdir->pathname + cdir->directory_len;
119
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
125         {
126           struct stat statbuf;
127
128           if (stat (cdir->pathname, &statbuf) < 0)
129             continue;
130
131           mode = statbuf.st_mode;
132         }
133
134       if (!S_ISREG (mode))
135         continue;
136
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';
143
144       return filename;
145     }
146 }
147
148 /* Finishes a charmap directory traversal, and frees the resources
149    attached to the CHARMAP_DIR.  */
150 int
151 charmap_closedir (CHARMAP_DIR *cdir)
152 {
153   DIR *dir = cdir->dir;
154
155   free (cdir->directory);
156   free (cdir->pathname);
157   free (cdir);
158   return closedir (dir);
159 }
160
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)
166 {
167   int pfd;
168
169   pfd = open (pathname, O_RDONLY);
170   if (pfd >= 0)
171     {
172       struct stat statbuf;
173       int fd[2];
174
175       if (fstat (pfd, &statbuf) >= 0
176           && S_ISREG (statbuf.st_mode)
177           && pipe (fd) >= 0)
178         {
179           char *argv[4]
180             = { (char *) compressor, (char *) "-d", (char *) "-c", NULL };
181           posix_spawn_file_actions_t actions;
182
183           if (posix_spawn_file_actions_init (&actions) == 0)
184             {
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)
194                 {
195                   posix_spawn_file_actions_destroy (&actions);
196                   close (fd[1]);
197                   close (pfd);
198                   return fdopen (fd[0], "r");
199                 }
200               posix_spawn_file_actions_destroy (&actions);
201             }
202           close (fd[1]);
203           close (fd[0]);
204         }
205       close (pfd);
206     }
207   return NULL;
208 }
209
210 /* Opens a charmap for reading, given its name (not an alias name).  */
211 FILE *
212 charmap_open (const char *directory, const char *name)
213 {
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;
220
221   pathname = alloca (dlen + add_slash + nlen + 5);
222   p = stpcpy (pathname, directory);
223   if (add_slash)
224     *p++ = '/';
225   p = stpcpy (p, name);
226
227   stream = fopen (pathname, "rm");
228   if (stream != NULL)
229     return stream;
230
231   memcpy (p, ".gz", 4);
232   stream = fopen_uncompressed (pathname, "gzip");
233   if (stream != NULL)
234     return stream;
235
236   memcpy (p, ".bz2", 5);
237   stream = fopen_uncompressed (pathname, "bzip2");
238   if (stream != NULL)
239     return stream;
240
241   return NULL;
242 }
243
244 /* An empty alias list.  Avoids the need to return NULL from
245    charmap_aliases.  */
246 static char *empty[1];
247
248 /* Returns a NULL terminated list of alias names of a charmap.  */
249 char **
250 charmap_aliases (const char *directory, const char *name)
251 {
252   FILE *stream;
253   char **aliases;
254   size_t naliases;
255
256   stream = charmap_open (directory, name);
257   if (stream == NULL)
258     return empty;
259
260   aliases = NULL;
261   naliases = 0;
262
263   while (!feof (stream))
264     {
265       char *alias = NULL;
266       char junk[BUFSIZ];
267
268       if (fscanf (stream, " <code_set_name> %as", &alias) == 1
269           || fscanf (stream, "%% alias %as", &alias) == 1)
270         {
271           aliases = (char **) xrealloc (aliases,
272                                         (naliases + 2) * sizeof (char *));
273           aliases[naliases++] = alias;
274         }
275
276       /* Read the rest of the line.  */
277       if (fgets (junk, sizeof junk, stream) != NULL)
278         {
279           if (strstr (junk, "CHARMAP") != NULL)
280             /* We cannot expect more aliases from now on.  */
281             break;
282
283           while (strchr (junk, '\n') == NULL
284                  && fgets (junk, sizeof junk, stream) != NULL)
285             continue;
286         }
287     }
288
289   fclose (stream);
290
291   if (naliases == 0)
292     return empty;
293
294   aliases[naliases] = NULL;
295   return aliases;
296 }
297
298 /* Frees an alias list returned by charmap_aliases.  */
299 void
300 charmap_free_aliases (char **aliases)
301 {
302   if (aliases != empty)
303     {
304       char **p;
305
306       for (p = aliases; *p; p++)
307         free (*p);
308
309       free (aliases);
310     }
311 }