* inet/tst-network.c: Increment ERRORS for failing tests.
[glibc.git] / locale / programs / charmap-dir.c
blobe192eddde3d95cab48876f95ceeb858e12c4a655
1 /* Copyright (C) 2000, 2001, 2002, 2003, 2005 Free Software Foundation, Inc.
2 This file is part of the GNU C Library.
4 This program is free software; you can redistribute it and/or modify
5 it under the terms of the GNU General Public License as published
6 by the Free Software Foundation; version 2 of the License, or
7 (at your option) any later version.
9 This program 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
12 GNU General Public License for more details.
14 You should have received a copy of the GNU General Public License
15 along with this program; if not, write to the Free Software Foundation,
16 Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */
18 #include <dirent.h>
19 #include <errno.h>
20 #include <error.h>
21 #include <fcntl.h>
22 #include <libintl.h>
23 #include <spawn.h>
24 #include <stdio.h>
25 #include <stdlib.h>
26 #include <string.h>
27 #include <unistd.h>
28 #include <sys/stat.h>
30 #include "localedef.h"
31 #include "charmap-dir.h"
33 /* The data type of a charmap directory being traversed. */
34 struct charmap_dir
36 DIR *dir;
37 /* The directory pathname, ending in a slash. */
38 char *directory;
39 size_t directory_len;
40 /* Scratch area used for returning pathnames. */
41 char *pathname;
42 size_t pathname_size;
45 /* Starts a charmap directory traversal.
46 Returns a CHARMAP_DIR, or NULL if the directory doesn't exist. */
47 CHARMAP_DIR *
48 charmap_opendir (const char *directory)
50 struct charmap_dir *cdir;
51 DIR *dir;
52 size_t len;
53 int add_slash;
55 dir = opendir (directory);
56 if (dir == NULL)
58 WITH_CUR_LOCALE (error (1, errno, gettext ("\
59 cannot read character map directory `%s'"), directory));
60 return NULL;
63 cdir = (struct charmap_dir *) xmalloc (sizeof (struct charmap_dir));
64 cdir->dir = dir;
66 len = strlen (directory);
67 add_slash = (len == 0 || directory[len - 1] != '/');
68 cdir->directory = (char *) xmalloc (len + add_slash + 1);
69 memcpy (cdir->directory, directory, len);
70 if (add_slash)
71 cdir->directory[len] = '/';
72 cdir->directory[len + add_slash] = '\0';
73 cdir->directory_len = len + add_slash;
75 cdir->pathname = NULL;
76 cdir->pathname_size = 0;
78 return cdir;
81 /* Reads the next directory entry.
82 Returns its charmap name, or NULL if past the last entry or upon error.
83 The storage returned may be overwritten by a later charmap_readdir
84 call on the same CHARMAP_DIR. */
85 const char *
86 charmap_readdir (CHARMAP_DIR *cdir)
88 for (;;)
90 struct dirent64 *dirent;
91 size_t len;
92 size_t size;
93 char *filename;
94 mode_t mode;
96 dirent = readdir64 (cdir->dir);
97 if (dirent == NULL)
98 return NULL;
99 if (strcmp (dirent->d_name, ".") == 0)
100 continue;
101 if (strcmp (dirent->d_name, "..") == 0)
102 continue;
104 len = strlen (dirent->d_name);
106 size = cdir->directory_len + len + 1;
107 if (size > cdir->pathname_size)
109 free (cdir->pathname);
110 if (size < 2 * cdir->pathname_size)
111 size = 2 * cdir->pathname_size;
112 cdir->pathname = (char *) xmalloc (size);
113 cdir->pathname_size = size;
116 stpcpy (stpcpy (cdir->pathname, cdir->directory), dirent->d_name);
117 filename = cdir->pathname + cdir->directory_len;
119 #ifdef _DIRENT_HAVE_D_TYPE
120 if (dirent->d_type != DT_UNKNOWN && dirent->d_type != DT_LNK)
121 mode = DTTOIF (dirent->d_type);
122 else
123 #endif
125 struct stat statbuf;
127 if (stat (cdir->pathname, &statbuf) < 0)
128 continue;
130 mode = statbuf.st_mode;
133 if (!S_ISREG (mode))
134 continue;
136 /* For compressed charmaps, the canonical charmap name does not
137 include the extension. */
138 if (len > 3 && memcmp (&filename[len - 3], ".gz", 3) == 0)
139 filename[len - 3] = '\0';
140 else if (len > 4 && memcmp (&filename[len - 4], ".bz2", 4) == 0)
141 filename[len - 4] = '\0';
143 return filename;
147 /* Finishes a charmap directory traversal, and frees the resources
148 attached to the CHARMAP_DIR. */
150 charmap_closedir (CHARMAP_DIR *cdir)
152 DIR *dir = cdir->dir;
154 free (cdir->directory);
155 free (cdir->pathname);
156 free (cdir);
157 return closedir (dir);
160 /* Creates a subprocess decompressing the given pathname, and returns
161 a stream reading its output (the decompressed data). */
162 static
163 FILE *
164 fopen_uncompressed (const char *pathname, const char *compressor)
166 int pfd;
168 pfd = open (pathname, O_RDONLY);
169 if (pfd >= 0)
171 struct stat statbuf;
172 int fd[2];
174 if (fstat (pfd, &statbuf) >= 0
175 && S_ISREG (statbuf.st_mode)
176 && pipe (fd) >= 0)
178 char *argv[4]
179 = { (char *) compressor, (char *) "-d", (char *) "-c", NULL };
180 posix_spawn_file_actions_t actions;
182 if (posix_spawn_file_actions_init (&actions) == 0)
184 if (posix_spawn_file_actions_adddup2 (&actions,
185 fd[1], STDOUT_FILENO) == 0
186 && posix_spawn_file_actions_addclose (&actions, fd[1]) == 0
187 && posix_spawn_file_actions_addclose (&actions, fd[0]) == 0
188 && posix_spawn_file_actions_adddup2 (&actions,
189 pfd, STDIN_FILENO) == 0
190 && posix_spawn_file_actions_addclose (&actions, pfd) == 0
191 && posix_spawnp (NULL, compressor, &actions, NULL,
192 argv, environ) == 0)
194 posix_spawn_file_actions_destroy (&actions);
195 close (fd[1]);
196 close (pfd);
197 return fdopen (fd[0], "r");
199 posix_spawn_file_actions_destroy (&actions);
201 close (fd[1]);
202 close (fd[0]);
204 close (pfd);
206 return NULL;
209 /* Opens a charmap for reading, given its name (not an alias name). */
210 FILE *
211 charmap_open (const char *directory, const char *name)
213 size_t dlen = strlen (directory);
214 int add_slash = (dlen == 0 || directory[dlen - 1] != '/');
215 size_t nlen = strlen (name);
216 char *pathname;
217 char *p;
218 FILE *stream;
220 pathname = alloca (dlen + add_slash + nlen + 5);
221 p = stpcpy (pathname, directory);
222 if (add_slash)
223 *p++ = '/';
224 p = stpcpy (p, name);
226 stream = fopen (pathname, "rm");
227 if (stream != NULL)
228 return stream;
230 memcpy (p, ".gz", 4);
231 stream = fopen_uncompressed (pathname, "gzip");
232 if (stream != NULL)
233 return stream;
235 memcpy (p, ".bz2", 5);
236 stream = fopen_uncompressed (pathname, "bzip2");
237 if (stream != NULL)
238 return stream;
240 return NULL;
243 /* An empty alias list. Avoids the need to return NULL from
244 charmap_aliases. */
245 static char *empty[1];
247 /* Returns a NULL terminated list of alias names of a charmap. */
248 char **
249 charmap_aliases (const char *directory, const char *name)
251 FILE *stream;
252 char **aliases;
253 size_t naliases;
255 stream = charmap_open (directory, name);
256 if (stream == NULL)
257 return empty;
259 aliases = NULL;
260 naliases = 0;
262 while (!feof (stream))
264 char *alias = NULL;
265 char junk[BUFSIZ];
267 if (fscanf (stream, " <code_set_name> %as", &alias) == 1
268 || fscanf (stream, "%% alias %as", &alias) == 1)
270 aliases = (char **) xrealloc (aliases,
271 (naliases + 2) * sizeof (char *));
272 aliases[naliases++] = alias;
275 /* Read the rest of the line. */
276 if (fgets (junk, sizeof junk, stream) != NULL)
278 if (strstr (junk, "CHARMAP") != NULL)
279 /* We cannot expect more aliases from now on. */
280 break;
282 while (strchr (junk, '\n') == NULL
283 && fgets (junk, sizeof junk, stream) != NULL)
284 continue;
288 fclose (stream);
290 if (naliases == 0)
291 return empty;
293 aliases[naliases] = NULL;
294 return aliases;
297 /* Frees an alias list returned by charmap_aliases. */
298 void
299 charmap_free_aliases (char **aliases)
301 if (aliases != empty)
303 char **p;
305 for (p = aliases; *p; p++)
306 free (*p);
308 free (aliases);