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