]>
Commit | Line | Data |
---|---|---|
d8e8097f | 1 | /* Handle configuration data. |
dff8da6b | 2 | Copyright (C) 2021-2024 Free Software Foundation, Inc. |
d8e8097f SP |
3 | This file is part of the GNU C Library. |
4 | ||
5 | The GNU C Library is free software; you can redistribute it and/or | |
6 | modify it under the terms of the GNU Lesser General Public | |
7 | License as published by the Free Software Foundation; either | |
8 | version 2.1 of the License, or (at your option) any later version. | |
9 | ||
10 | The GNU C Library 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 GNU | |
13 | Lesser General Public License for more details. | |
14 | ||
15 | You should have received a copy of the GNU Lesser General Public | |
16 | License along with the GNU C Library; if not, see | |
17 | <https://www.gnu.org/licenses/>. */ | |
18 | ||
19 | #include <dirent.h> | |
20 | #include <libc-symbols.h> | |
21 | #include <locale.h> | |
22 | #include <sys/types.h> | |
23 | ||
24 | #if IS_IN (libc) | |
25 | # include <libio/libioP.h> | |
7fcdb532 | 26 | # define __getdelim(line, len, c, fp) __getdelim (line, len, c, fp) |
d8e8097f SP |
27 | |
28 | # undef isspace | |
29 | # define isspace(__c) __isspace_l ((__c), _nl_C_locobj_ptr) | |
30 | # define asprintf __asprintf | |
31 | # define opendir __opendir | |
f97905f2 | 32 | # define readdir64 __readdir64 |
d8e8097f SP |
33 | # define closedir __closedir |
34 | # define mempcpy __mempcpy | |
f97905f2 FW |
35 | # define struct_stat64 struct __stat64_t64 |
36 | # define lstat64 __lstat64_time64 | |
7f784fab | 37 | # define feof_unlocked __feof_unlocked |
c789e6e4 | 38 | #else |
f97905f2 | 39 | # define struct_stat64 struct stat64 |
d8e8097f SP |
40 | #endif |
41 | ||
42 | /* Name of the file containing the module information in the directories | |
43 | along the path. */ | |
44 | static const char gconv_conf_filename[] = "gconv-modules"; | |
d8e8097f SP |
45 | |
46 | static void add_alias (char *); | |
47 | static void add_module (char *, const char *, size_t, int); | |
48 | ||
49 | /* Read the next configuration file. */ | |
50 | static bool | |
51 | read_conf_file (const char *filename, const char *directory, size_t dir_len) | |
52 | { | |
53 | /* Note the file is opened with cancellation in the I/O functions | |
54 | disabled. */ | |
55 | FILE *fp = fopen (filename, "rce"); | |
56 | char *line = NULL; | |
57 | size_t line_len = 0; | |
58 | static int modcounter; | |
59 | ||
60 | /* Don't complain if a file is not present or readable, simply silently | |
61 | ignore it. */ | |
62 | if (fp == NULL) | |
63 | return false; | |
64 | ||
65 | /* No threads reading from this stream. */ | |
66 | __fsetlocking (fp, FSETLOCKING_BYCALLER); | |
67 | ||
68 | /* Process the known entries of the file. Comments start with `#' and | |
69 | end with the end of the line. Empty lines are ignored. */ | |
7f784fab | 70 | while (!feof_unlocked (fp)) |
d8e8097f SP |
71 | { |
72 | char *rp, *endp, *word; | |
73 | ssize_t n = __getdelim (&line, &line_len, '\n', fp); | |
74 | if (n < 0) | |
75 | /* An error occurred. */ | |
76 | break; | |
77 | ||
78 | rp = line; | |
79 | /* Terminate the line (excluding comments or newline) by an NUL byte | |
80 | to simplify the following code. */ | |
81 | endp = strchr (rp, '#'); | |
82 | if (endp != NULL) | |
83 | *endp = '\0'; | |
84 | else | |
85 | if (rp[n - 1] == '\n') | |
86 | rp[n - 1] = '\0'; | |
87 | ||
88 | while (isspace (*rp)) | |
89 | ++rp; | |
90 | ||
91 | /* If this is an empty line go on with the next one. */ | |
92 | if (rp == endp) | |
93 | continue; | |
94 | ||
95 | word = rp; | |
96 | while (*rp != '\0' && !isspace (*rp)) | |
97 | ++rp; | |
98 | ||
99 | if (rp - word == sizeof ("alias") - 1 | |
100 | && memcmp (word, "alias", sizeof ("alias") - 1) == 0) | |
101 | add_alias (rp); | |
102 | else if (rp - word == sizeof ("module") - 1 | |
103 | && memcmp (word, "module", sizeof ("module") - 1) == 0) | |
104 | add_module (rp, directory, dir_len, modcounter++); | |
105 | /* else */ | |
106 | /* Otherwise ignore the line. */ | |
107 | } | |
108 | ||
109 | free (line); | |
110 | ||
111 | fclose (fp); | |
112 | return true; | |
113 | } | |
114 | ||
43cea6d5 SP |
115 | /* Prefix DIR (with length DIR_LEN) with PREFIX if the latter is non-NULL and |
116 | parse configuration in it. */ | |
117 | ||
d8e8097f | 118 | static __always_inline bool |
43cea6d5 | 119 | gconv_parseconfdir (const char *prefix, const char *dir, size_t dir_len) |
d8e8097f | 120 | { |
43cea6d5 SP |
121 | /* No slash needs to be inserted between dir and gconv_conf_filename; dir |
122 | already ends in a slash. The additional 2 is to accommodate the ".d" | |
123 | when looking for configuration files in gconv-modules.d. */ | |
124 | size_t buflen = dir_len + sizeof (gconv_conf_filename) + 2; | |
125 | char *buf = malloc (buflen + (prefix != NULL ? strlen (prefix) : 0)); | |
126 | char *cp = buf; | |
d8e8097f SP |
127 | bool found = false; |
128 | ||
129 | if (buf == NULL) | |
130 | return false; | |
131 | ||
43cea6d5 SP |
132 | if (prefix != NULL) |
133 | cp = stpcpy (cp, prefix); | |
134 | ||
135 | cp = mempcpy (mempcpy (cp, dir, dir_len), gconv_conf_filename, | |
136 | sizeof (gconv_conf_filename)); | |
d8e8097f SP |
137 | |
138 | /* Read the gconv-modules configuration file first. */ | |
139 | found = read_conf_file (buf, dir, dir_len); | |
140 | ||
141 | /* Next, see if there is a gconv-modules.d directory containing | |
142 | configuration files and if it is non-empty. */ | |
143 | cp--; | |
144 | cp[0] = '.'; | |
145 | cp[1] = 'd'; | |
146 | cp[2] = '\0'; | |
147 | ||
148 | DIR *confdir = opendir (buf); | |
149 | if (confdir != NULL) | |
150 | { | |
f97905f2 FW |
151 | struct dirent64 *ent; |
152 | while ((ent = readdir64 (confdir)) != NULL) | |
d8e8097f | 153 | { |
f3629a4b | 154 | if (ent->d_type != DT_REG && ent->d_type != DT_UNKNOWN) |
d8e8097f SP |
155 | continue; |
156 | ||
157 | size_t len = strlen (ent->d_name); | |
158 | const char *suffix = ".conf"; | |
159 | ||
160 | if (len > strlen (suffix) | |
161 | && strcmp (ent->d_name + len - strlen (suffix), suffix) == 0) | |
162 | { | |
163 | char *conf; | |
f97905f2 | 164 | struct_stat64 st; |
d8e8097f SP |
165 | if (asprintf (&conf, "%s/%s", buf, ent->d_name) < 0) |
166 | continue; | |
f3629a4b | 167 | |
5f9b78fe | 168 | if (ent->d_type != DT_UNKNOWN |
f97905f2 | 169 | || (lstat64 (conf, &st) != -1 && S_ISREG (st.st_mode))) |
5f9b78fe SP |
170 | found |= read_conf_file (conf, dir, dir_len); |
171 | ||
d8e8097f SP |
172 | free (conf); |
173 | } | |
174 | } | |
175 | closedir (confdir); | |
176 | } | |
177 | free (buf); | |
178 | return found; | |
179 | } |