update from main archive 961116
[kopensolaris-gnu/glibc.git] / nss / nss_files / files-alias.c
1 /* Mail alias file parser in nss_files module.
2    Copyright (C) 1996 Free Software Foundation, Inc.
3    This file is part of the GNU C Library.
4    Contributed by Ulrich Drepper <drepper@cygnus.com>, 1996.
5
6    The GNU C Library is free software; you can redistribute it and/or
7    modify it under the terms of the GNU Library General Public License as
8    published by the Free Software Foundation; either version 2 of the
9    License, or (at your option) any later version.
10
11    The GNU C Library is distributed in the hope that it will be useful,
12    but WITHOUT ANY WARRANTY; without even the implied warranty of
13    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14    Library General Public License for more details.
15
16    You should have received a copy of the GNU Library General Public
17    License along with the GNU C Library; see the file COPYING.LIB.  If not,
18    write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
19    Boston, MA 02111-1307, USA.  */
20
21 #include <aliases.h>
22 #include <ctype.h>
23 #include <errno.h>
24 #include <libc-lock.h>
25 #include <stdlib.h>
26 #include <stdio.h>
27 #include <string.h>
28
29 #include "nsswitch.h"
30
31 /* Locks the static variables in this file.  */
32 __libc_lock_define_initialized (static, lock)
33 \f
34 /* Maintenance of the shared stream open on the database file.  */
35
36 static FILE *stream;
37 static fpos_t position;
38 static enum { none, getent, getby } last_use;
39
40
41 static enum nss_status
42 internal_setent (void)
43 {
44   enum nss_status status = NSS_STATUS_SUCCESS;
45
46   if (stream == NULL)
47     {
48       stream = fopen ("/etc/aliases", "r");
49
50       if (stream == NULL)
51         status = NSS_STATUS_UNAVAIL;
52     }
53   else
54     rewind (stream);
55
56   return status;
57 }
58
59
60 /* Thread-safe, exported version of that.  */
61 enum nss_status
62 _nss_files_setaliasent (void)
63 {
64   enum nss_status status;
65
66   __libc_lock_lock (lock);
67
68   status = internal_setent ();
69
70   if (status == NSS_STATUS_SUCCESS && fgetpos (stream, &position) < 0)
71     {
72       fclose (stream);
73       stream = NULL;
74       status = NSS_STATUS_UNAVAIL;
75     }
76
77   last_use = getent;
78
79   __libc_lock_unlock (lock);
80
81   return status;
82 }
83
84
85 /* Close the database file.  */
86 static void
87 internal_endent (void)
88 {
89   if (stream != NULL)
90     {
91       fclose (stream);
92       stream = NULL;
93     }
94 }
95
96
97 /* Thread-safe, exported version of that.  */
98 enum nss_status
99 _nss_files_endaliasent (void)
100 {
101   __libc_lock_lock (lock);
102
103   internal_endent ();
104
105   __libc_lock_unlock (lock);
106
107   return NSS_STATUS_SUCCESS;
108 }
109 \f
110 /* Parsing the database file into `struct aliasent' data structures.  */
111 static enum nss_status
112 get_next_alias (const char *match, struct aliasent *result,
113                 char *buffer, size_t buflen)
114 {
115   enum nss_status status = NSS_STATUS_NOTFOUND;
116   int ignore = 0;
117
118   result->alias_members_len = 0;
119
120   while (1)
121     {
122       /* Now we are ready to process the input.  We have to read a
123          line and all its continuations and construct the array of
124          string pointers.  This pointers and the names itself have to
125          be placed in BUFFER.  */
126       char *first_unused = buffer;
127       size_t room_left = buflen - (buflen % __alignof__ (char *));
128       char *line;
129
130       /* Read the first line.  It must contain the alias name and
131          possibly some alias names.  */
132       first_unused[room_left - 1] = '\0';
133       line = fgets (first_unused, room_left, stream);
134       if (line == NULL)
135         /* Nothing to read.  */
136         break;
137       else if (first_unused[room_left - 1] != '\0')
138         {
139           /* The line is too long for our buffer.  */
140         no_more_room:
141           __set_errno (ERANGE);
142           status = NSS_STATUS_TRYAGAIN;
143           break;
144         }
145       else
146         {
147           char *cp;
148
149           /* If we are in IGNORE mode and the first character in the
150              line is a white space we ignore the line and start
151              reading the next.  */
152           if (ignore && isspace (*first_unused))
153             continue;
154
155           /* Terminate the line for any case.  */
156           cp = strpbrk (first_unused, "#\n");
157           if (cp != NULL)
158             *cp = '\0';
159
160           /* Skip leading blanks.  */
161           while (isspace (*line))
162             ++line;
163
164           result->alias_name = first_unused;
165           while (*line != '\0' && *line != ':')
166             *first_unused++ = *line++;
167           if (*line == '\0' || result->alias_name == first_unused)
168             /* No valid name.  Ignore the line.  */
169             continue;
170
171           *first_unused++ = '\0';
172           if (room_left < (size_t) (first_unused - result->alias_name))
173             goto no_more_room;
174           room_left -= first_unused - result->alias_name;
175           ++line;
176
177           /* When we search for a specific alias we can avoid all the
178              difficult parts and compare now with the name we are
179              looking for.  If it does not match we simply ignore all
180              lines until the next line containing the start of a new
181              alias is found.  */
182           ignore = match != NULL && strcmp (result->alias_name, match) != 0;
183
184           while (! ignore)
185             {
186               while (isspace (*line))
187                 ++line;
188
189               cp = first_unused;
190               while (*line != '\0' && *line != ',')
191                 *first_unused++ = *line++;
192
193               if (first_unused != cp)
194                 {
195                   /* OK, we can have a regular entry or an include
196                      request.  */
197                   if (*line != '\0')
198                     ++line;
199                   *first_unused++ = '\0';
200
201                   if (strncmp (cp, ":include:", 9) != 0)
202                     {
203                       if (room_left < (first_unused - cp) + sizeof (char *))
204                         goto no_more_room;
205                       room_left -= (first_unused - cp) + sizeof (char *);
206
207                       ++result->alias_members_len;
208                     }
209                   else
210                     {
211                       /* Oh well, we have to read the addressed file.  */
212                       FILE *listfile;
213                       char *old_line = NULL;
214
215                       first_unused = cp;
216
217                       listfile = fopen (&cp[9], "r");
218                       /* If the file does not exist we simply ignore
219                          the statement.  */
220                       if (listfile != NULL
221                           && (old_line = strdup (line)) != NULL)
222                         {
223                           while (! feof (listfile))
224                             {
225                               first_unused[room_left - 1] = '\0';
226                               line = fgets (first_unused, room_left, listfile);
227                               if (line == NULL)
228                                 break;
229                               if (first_unused[room_left - 1] != '\0')
230                                 {
231                                   free (old_line);
232                                   goto no_more_room;
233                                 }
234
235                               /* Parse the line.  */
236                               cp = strpbrk (line, "#\n");
237                               if (cp != NULL)
238                                 *cp = '\0';
239
240                               do
241                                 {
242                                   while (isspace (*line))
243                                     ++line;
244
245                                   cp = first_unused;
246                                   while (*line != '\0' && *line != ',')
247                                     *first_unused++ = *line++;
248
249                                   if (*line != '\0')
250                                     ++line;
251
252                                   if (first_unused != cp)
253                                     {
254                                       *first_unused++ = '\0';
255                                       if (room_left < ((first_unused - cp)
256                                                        + __alignof__ (char *)))
257                                         {
258                                           free (old_line);
259                                           goto no_more_room;
260                                         }
261                                       room_left -= ((first_unused - cp)
262                                                     + __alignof__ (char *));
263                                       ++result->alias_members_len;
264                                     }
265                                 }
266                               while (*line != '\0');
267                             }
268                           fclose (listfile);
269
270                           first_unused[room_left - 1] = '\0';
271                           strncpy (first_unused, old_line, room_left);
272
273                           if (old_line != NULL)
274                             free (old_line);
275
276                           if (first_unused[room_left - 1] != '\0')
277                             goto no_more_room;
278                         }
279                     }
280                 }
281
282               if (*line == '\0')
283                 {
284                   /* Get the next line.  But we must be careful.  We
285                      must not read the whole line at once since it
286                      might belong to the current alias.  Simply read
287                      the first character.  If it is a white space we
288                      have a continuation line.  Otherwise it is the
289                      beginning of a new alias and we can push back the
290                      just read character.  */
291                   int ch;
292
293                   ch = fgetc (stream);
294                   if (ch == EOF || ch == '\n' || !isspace (ch))
295                     {
296                       size_t cnt;
297
298                       /* Now prepare the return.  Provide string
299                          pointers for the currently selected aliases.  */
300                       if (ch != EOF)
301                         ungetc (ch, stream);
302
303                       /* Adjust the pointer so it is aligned for
304                          storing pointers.  */
305                       first_unused += __alignof__ (char *) - 1;
306                       first_unused -= ((first_unused - (char *) 0)
307                                        % __alignof__ (char *));
308                       result->alias_members = (char **) first_unused;
309
310                       /* Compute addresses of alias entry strings.  */
311                       cp = result->alias_name;
312                       for (cnt = 0; cnt < result->alias_members_len; ++cnt)
313                         {
314                           cp = strchr (cp, '\0') + 1;
315                           result->alias_members[cnt] = cp;
316                         }
317
318                       status = (result->alias_members_len == 0
319                                 ? NSS_STATUS_RETURN : NSS_STATUS_SUCCESS);
320                       break;
321                     }
322
323                   /* The just read character is a white space and so
324                      can be ignored.  */
325                   first_unused[room_left - 1] = '\0';
326                   line = fgets (first_unused, room_left, stream);
327                   if (first_unused[room_left - 1] != '\0')
328                     goto no_more_room;
329                   cp = strpbrk (line, "#\n");
330                   if (cp != NULL)
331                     *cp = '\0';
332                 }
333             }
334         }
335
336       if (status != NSS_STATUS_NOTFOUND)
337         /* We read something.  In any case break here.  */
338         break;
339     }
340
341   return status;
342 }
343
344
345 enum nss_status
346 _nss_files_getaliasent_r (struct aliasent *result, char *buffer, size_t buflen)
347 {
348   /* Return next entry in host file.  */
349   enum nss_status status = NSS_STATUS_SUCCESS;
350
351   __libc_lock_lock (lock);
352
353   /* Be prepared that the set*ent function was not called before.  */
354   if (stream == NULL)
355     status = internal_setent ();
356
357   if (status == NSS_STATUS_SUCCESS)
358     {
359       /* If the last use was not by the getent function we need the
360          position the stream.  */
361       if (last_use != getent)
362         if (fsetpos (stream, &position) < 0)
363           status = NSS_STATUS_UNAVAIL;
364         else
365           last_use = getent;
366
367       if (status == NSS_STATUS_SUCCESS)
368         {
369           result->alias_local = 1;
370
371           /* Read lines until we get a definite result.  */
372           do
373             status = get_next_alias (NULL, result, buffer, buflen);
374           while (status == NSS_STATUS_RETURN);
375
376           /* If we successfully read an entry remember this position.  */
377           if (status == NSS_STATUS_SUCCESS)
378             fgetpos (stream, &position);
379           else
380             last_use = none;
381         }
382     }
383
384   __libc_lock_unlock (lock);
385
386   return status;
387 }
388
389
390 enum nss_status
391 _nss_files_getaliasbyname_r (const char *name, struct aliasent *result,
392                              char *buffer, size_t buflen)
393 {
394   /* Return next entry in host file.  */
395   enum nss_status status = NSS_STATUS_SUCCESS;
396
397   if (name == NULL)
398     {
399       __set_errno (EINVAL);
400       return NSS_STATUS_UNAVAIL;
401     }
402
403   __libc_lock_lock (lock);
404
405   /* Open the stream or rest it.  */
406   status = internal_setent ();
407   last_use = getby;
408
409   if (status == NSS_STATUS_SUCCESS)
410     {
411       result->alias_local = 1;
412
413       /* Read lines until we get a definite result.  */
414       do
415         status = get_next_alias (name, result, buffer, buflen);
416       while (status == NSS_STATUS_RETURN);
417     }
418
419   internal_endent ();
420
421   __libc_lock_unlock (lock);
422
423   return status;
424 }