stdlib: Fix stdbit.h with -Wconversion for clang
[glibc.git] / nss / nss_files / files-alias.c
blob14a59b46555871a20671efaf675afa82cbd64953
1 /* Mail alias file parser in nss_files module.
2 Copyright (C) 1996-2024 Free Software Foundation, Inc.
3 This file is part of the GNU C Library.
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.
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.
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/>. */
19 #include <aliases.h>
20 #include <ctype.h>
21 #include <errno.h>
22 #include <fcntl.h>
23 #include <libc-lock.h>
24 #include <stdlib.h>
25 #include <stdio.h>
26 #include <string.h>
28 #include <kernel-features.h>
30 #include "nsswitch.h"
31 #include <nss_files.h>
34 /* Maintenance of the stream open on the database file. For getXXent
35 operations the stream needs to be held open across calls, the other
36 getXXbyYY operations all use their own stream. */
38 static enum nss_status
39 internal_setent (FILE **stream)
41 enum nss_status status = NSS_STATUS_SUCCESS;
43 if (*stream == NULL)
45 *stream = __nss_files_fopen ("/etc/aliases");
47 if (*stream == NULL)
48 status = errno == EAGAIN ? NSS_STATUS_TRYAGAIN : NSS_STATUS_UNAVAIL;
50 else
51 rewind (*stream);
53 return status;
57 /* Thread-safe, exported version of that. */
58 enum nss_status
59 _nss_files_setaliasent (void)
61 return __nss_files_data_setent (nss_file_aliasent, "/etc/aliases");
63 libc_hidden_def (_nss_files_setaliasent)
65 enum nss_status
66 _nss_files_endaliasent (void)
68 return __nss_files_data_endent (nss_file_aliasent);
70 libc_hidden_def (_nss_files_endaliasent)
72 /* Parsing the database file into `struct aliasent' data structures. */
73 static enum nss_status
74 get_next_alias (FILE *stream, const char *match, struct aliasent *result,
75 char *buffer, size_t buflen, int *errnop)
77 enum nss_status status = NSS_STATUS_NOTFOUND;
78 int ignore = 0;
80 result->alias_members_len = 0;
82 while (1)
84 /* Now we are ready to process the input. We have to read a
85 line and all its continuations and construct the array of
86 string pointers. This pointers and the names itself have to
87 be placed in BUFFER. */
88 char *first_unused = buffer;
89 size_t room_left = buflen - (buflen % __alignof__ (char *));
90 char *line;
92 /* Check whether the buffer is large enough for even trying to
93 read something. */
94 if (room_left < 2)
95 goto no_more_room;
97 /* Read the first line. It must contain the alias name and
98 possibly some alias names. */
99 first_unused[room_left - 1] = '\xff';
100 line = __fgets_unlocked (first_unused, room_left, stream);
101 if (line == NULL)
102 /* Nothing to read. */
103 break;
104 else if (first_unused[room_left - 1] != '\xff')
106 /* The line is too long for our buffer. */
107 no_more_room:
108 *errnop = ERANGE;
109 status = NSS_STATUS_TRYAGAIN;
110 break;
112 else
114 char *cp;
116 /* If we are in IGNORE mode and the first character in the
117 line is a white space we ignore the line and start
118 reading the next. */
119 if (ignore && isspace (*first_unused))
120 continue;
122 /* Terminate the line for any case. */
123 cp = strpbrk (first_unused, "#\n");
124 if (cp != NULL)
125 *cp = '\0';
127 /* Skip leading blanks. */
128 while (isspace (*line))
129 ++line;
131 result->alias_name = first_unused;
132 while (*line != '\0' && *line != ':')
133 *first_unused++ = *line++;
134 if (*line == '\0' || result->alias_name == first_unused)
135 /* No valid name. Ignore the line. */
136 continue;
138 *first_unused++ = '\0';
139 if (room_left < (size_t) (first_unused - result->alias_name))
140 goto no_more_room;
141 room_left -= first_unused - result->alias_name;
142 ++line;
144 /* When we search for a specific alias we can avoid all the
145 difficult parts and compare now with the name we are
146 looking for. If it does not match we simply ignore all
147 lines until the next line containing the start of a new
148 alias is found. */
149 ignore = (match != NULL
150 && __strcasecmp (result->alias_name, match) != 0);
152 while (! ignore)
154 while (isspace (*line))
155 ++line;
157 cp = first_unused;
158 while (*line != '\0' && *line != ',')
159 *first_unused++ = *line++;
161 if (first_unused != cp)
163 /* OK, we can have a regular entry or an include
164 request. */
165 if (*line != '\0')
166 ++line;
167 *first_unused++ = '\0';
169 if (strncmp (cp, ":include:", 9) != 0)
171 if (room_left < (first_unused - cp) + sizeof (char *))
172 goto no_more_room;
173 room_left -= (first_unused - cp) + sizeof (char *);
175 ++result->alias_members_len;
177 else
179 /* Oh well, we have to read the addressed file. */
180 FILE *listfile;
181 char *old_line = NULL;
183 first_unused = cp;
185 listfile = __nss_files_fopen (&cp[9]);
186 /* If the file does not exist we simply ignore
187 the statement. */
188 if (listfile != NULL
189 && (old_line = __strdup (line)) != NULL)
191 while (! __feof_unlocked (listfile))
193 if (room_left < 2)
195 free (old_line);
196 fclose (listfile);
197 goto no_more_room;
200 first_unused[room_left - 1] = '\xff';
201 line = __fgets_unlocked (first_unused, room_left,
202 listfile);
203 if (line == NULL)
204 break;
205 if (first_unused[room_left - 1] != '\xff')
207 free (old_line);
208 fclose (listfile);
209 goto no_more_room;
212 /* Parse the line. */
213 cp = strpbrk (line, "#\n");
214 if (cp != NULL)
215 *cp = '\0';
219 while (isspace (*line))
220 ++line;
222 cp = first_unused;
223 while (*line != '\0' && *line != ',')
224 *first_unused++ = *line++;
226 if (*line != '\0')
227 ++line;
229 if (first_unused != cp)
231 *first_unused++ = '\0';
232 if (room_left < ((first_unused - cp)
233 + __alignof__ (char *)))
235 free (old_line);
236 fclose (listfile);
237 goto no_more_room;
239 room_left -= ((first_unused - cp)
240 + __alignof__ (char *));
241 ++result->alias_members_len;
244 while (*line != '\0');
246 fclose (listfile);
248 first_unused[room_left - 1] = '\0';
249 strncpy (first_unused, old_line, room_left);
251 free (old_line);
252 line = first_unused;
254 if (first_unused[room_left - 1] != '\0')
255 goto no_more_room;
260 if (*line == '\0')
262 /* Get the next line. But we must be careful. We
263 must not read the whole line at once since it
264 might belong to the current alias. Simply read
265 the first character. If it is a white space we
266 have a continuation line. Otherwise it is the
267 beginning of a new alias and we can push back the
268 just read character. */
269 int ch;
271 ch = __getc_unlocked (stream);
272 if (ch == EOF || ch == '\n' || !isspace (ch))
274 size_t cnt;
276 /* Now prepare the return. Provide string
277 pointers for the currently selected aliases. */
278 if (ch != EOF)
279 ungetc (ch, stream);
281 /* Adjust the pointer so it is aligned for
282 storing pointers. */
283 first_unused += __alignof__ (char *) - 1;
284 first_unused -= (((uintptr_t) first_unused)
285 % __alignof__ (char *));
286 result->alias_members = (char **) first_unused;
288 /* Compute addresses of alias entry strings. */
289 cp = result->alias_name;
290 for (cnt = 0; cnt < result->alias_members_len; ++cnt)
292 cp = strchr (cp, '\0') + 1;
293 result->alias_members[cnt] = cp;
296 status = (result->alias_members_len == 0
297 ? NSS_STATUS_RETURN : NSS_STATUS_SUCCESS);
298 break;
301 /* The just read character is a white space and so
302 can be ignored. */
303 first_unused[room_left - 1] = '\xff';
304 line = __fgets_unlocked (first_unused, room_left, stream);
305 if (line == NULL)
307 /* Continuation line without any data and
308 without a newline at the end. Treat it as an
309 empty line and retry, reaching EOF once
310 more. */
311 line = first_unused;
312 *line = '\0';
313 continue;
315 if (first_unused[room_left - 1] != '\xff')
316 goto no_more_room;
317 cp = strpbrk (line, "#\n");
318 if (cp != NULL)
319 *cp = '\0';
324 if (status != NSS_STATUS_NOTFOUND)
325 /* We read something. In any case break here. */
326 break;
329 return status;
333 enum nss_status
334 _nss_files_getaliasent_r (struct aliasent *result, char *buffer, size_t buflen,
335 int *errnop)
337 /* Return next entry in host file. */
339 struct nss_files_per_file_data *data;
340 enum nss_status status = __nss_files_data_open (&data, nss_file_aliasent,
341 "/etc/aliases", errnop, NULL);
342 if (status != NSS_STATUS_SUCCESS)
343 return status;
345 result->alias_local = 1;
347 /* Read lines until we get a definite result. */
349 status = get_next_alias (data->stream, NULL, result, buffer, buflen,
350 errnop);
351 while (status == NSS_STATUS_RETURN);
353 __nss_files_data_put (data);
354 return status;
356 libc_hidden_def (_nss_files_getaliasent_r)
358 enum nss_status
359 _nss_files_getaliasbyname_r (const char *name, struct aliasent *result,
360 char *buffer, size_t buflen, int *errnop)
362 /* Return next entry in host file. */
363 enum nss_status status = NSS_STATUS_SUCCESS;
364 FILE *stream = NULL;
366 if (name == NULL)
368 __set_errno (EINVAL);
369 return NSS_STATUS_UNAVAIL;
372 /* Open the stream. */
373 status = internal_setent (&stream);
375 if (status == NSS_STATUS_SUCCESS)
377 result->alias_local = 1;
379 /* Read lines until we get a definite result. */
381 status = get_next_alias (stream, name, result, buffer, buflen, errnop);
382 while (status == NSS_STATUS_RETURN);
384 fclose (stream);
387 return status;
389 libc_hidden_def (_nss_files_getaliasbyname_r)