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