update from main archive 961114
[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                   if (*line != '\0')
196                     {
197                       /* OK, we can have a regular entry or an include
198                          request.  */
199                       *first_unused++ = '\0';
200                       ++line;
201                     }
202                   else
203                     ++first_unused;
204
205
206                   if (strncmp (cp, ":include:", 9) != 0)
207                     {
208                       if (room_left < (first_unused - cp) + sizeof (char *))
209                         goto no_more_room;
210                       room_left -= (first_unused - cp) + sizeof (char *);
211
212                       ++result->alias_members_len;
213                     }
214                   else
215                     {
216                       /* Oh well, we have to read the addressed file.  */
217                       FILE *listfile;
218                       char *old_line = NULL;
219
220                       first_unused = cp;
221
222                       listfile = fopen (&cp[9], "r");
223                       /* If the file does not exist we simply ignore
224                          the statement.  */
225                       if (listfile != NULL
226                           && (old_line = strdup (line)) != NULL)
227                         {
228                           while (! feof (listfile))
229                             {
230                               first_unused[room_left - 1] = '\0';
231                               line = fgets (first_unused, room_left, listfile);
232                               if (line == NULL)
233                                 break;
234                               if (first_unused[room_left - 1] != '\0')
235                                 {
236                                   free (old_line);
237                                   goto no_more_room;
238                                 }
239
240                               /* Parse the line.  */
241                               cp = strpbrk (line, "#\n");
242                               if (cp != NULL)
243                                 *cp = '\0';
244
245                               do
246                                 {
247                                   while (isspace (*line))
248                                     ++line;
249
250                                   cp = first_unused;
251                                   while (*line != '\0' && *line != ',')
252                                     *first_unused++ = *line++;
253
254                                   if (*line != '\0')
255                                     ++line;
256
257                                   if (first_unused != cp)
258                                     {
259                                       *first_unused++ = '\0';
260                                       if (room_left < ((first_unused - cp)
261                                                        + __alignof__ (char *)))
262                                         {
263                                           free (old_line);
264                                           goto no_more_room;
265                                         }
266                                       room_left -= ((first_unused - cp)
267                                                     + __alignof__ (char *));
268                                       ++result->alias_members_len;
269                                     }
270                                 }
271                               while (*line != '\0');
272                             }
273                           fclose (listfile);
274
275                           first_unused[room_left - 1] = '\0';
276                           strncpy (first_unused, old_line, room_left);
277
278                           if (old_line != NULL)
279                             free (old_line);
280
281                           if (first_unused[room_left - 1] != '\0')
282                             goto no_more_room;
283                         }
284                     }
285                 }
286
287               if (*line == '\0')
288                 {
289                   /* Get the next line.  But we must be careful.  We
290                      must not read the whole line at once since it
291                      might belong to the current alias.  Simply read
292                      the first character.  If it is a white space we
293                      have a continuation line.  Otherwise it is the
294                      beginning of a new alias and we can push back the
295                      just read character.  */
296                   int ch;
297
298                   first_unused[room_left - 1] = '\0';
299                   line = first_unused;
300                   ch = fgetc (stream);
301                   if (ch == EOF || !isspace (ch))
302                     {
303                       size_t cnt;
304
305                       /* Now prepare the return.  Provide string
306                          pointers for the currently selected aliases.  */
307                       if (ch != EOF)
308                         ungetc (ch, stream);
309
310                       /* Adjust the pointer so it is aligned for
311                          storing pointers.  */
312                       first_unused += __alignof__ (char *) - 1;
313                       first_unused -= ((first_unused - (char *) 0)
314                                        % __alignof__ (char *));
315                       result->alias_members = (char **) first_unused;
316
317                       /* Compute addresses of alias entry strings.  */
318                       cp = result->alias_name;
319                       for (cnt = 0; cnt < result->alias_members_len; ++cnt)
320                         {
321                           cp = strchr (cp, '\0') + 1;
322                           result->alias_members[cnt] = cp;
323                         }
324
325                       status = (result->alias_members_len == 0
326                                 ? NSS_STATUS_RETURN : NSS_STATUS_SUCCESS);
327                       break;
328                     }
329
330                   /* The just read character is a white space and so
331                      can be ignored.  */
332                   cp = strpbrk (line, "#\n");
333                   if (cp != NULL)
334                     *cp = '\0';
335                 }
336             }
337         }
338
339       if (status != NSS_STATUS_NOTFOUND)
340         /* We read something.  In any case break here.  */
341         break;
342     }
343
344   return status;
345 }
346
347
348 enum nss_status
349 _nss_files_getaliasent_r (struct aliasent *result, char *buffer, size_t buflen)
350 {
351   /* Return next entry in host file.  */
352   enum nss_status status = NSS_STATUS_SUCCESS;
353
354   __libc_lock_lock (lock);
355
356   /* Be prepared that the set*ent function was not called before.  */
357   if (stream == NULL)
358     status = internal_setent ();
359
360   if (status == NSS_STATUS_SUCCESS)
361     {
362       /* If the last use was not by the getent function we need the
363          position the stream.  */
364       if (last_use != getent)
365         if (fsetpos (stream, &position) < 0)
366           status = NSS_STATUS_UNAVAIL;
367         else
368           last_use = getent;
369
370       if (status == NSS_STATUS_SUCCESS)
371         {
372           result->alias_local = 1;
373
374           /* Read lines until we get a definite result.  */
375           do
376             status = get_next_alias (NULL, result, buffer, buflen);
377           while (status == NSS_STATUS_RETURN);
378
379           /* If we successfully read an entry remember this position.  */
380           if (status == NSS_STATUS_SUCCESS)
381             fgetpos (stream, &position);
382           else
383             last_use = none;
384         }
385     }
386
387   __libc_lock_unlock (lock);
388
389   return status;
390 }
391
392
393 enum nss_status
394 _nss_files_getaliasbyname_r (const char *name, struct aliasent *result,
395                              char *buffer, size_t buflen)
396 {
397   /* Return next entry in host file.  */
398   enum nss_status status = NSS_STATUS_SUCCESS;
399
400   if (name == NULL)
401     {
402       __set_errno (EINVAL);
403       return NSS_STATUS_UNAVAIL;
404     }
405
406   __libc_lock_lock (lock);
407
408   /* Open the stream or rest it.  */
409   status = internal_setent ();
410   last_use = getby;
411
412   if (status == NSS_STATUS_SUCCESS)
413     {
414       result->alias_local = 1;
415
416       /* Read lines until we get a definite result.  */
417       do
418         status = get_next_alias (name, result, buffer, buflen);
419       while (status == NSS_STATUS_RETURN);
420     }
421
422   __libc_lock_unlock (lock);
423
424   return status;
425 }