Change everything to store error code through provided pointer and not
[kopensolaris-gnu/glibc.git] / nis / nss_compat / compat-pwd.c
1 /* Copyright (C) 1996, 1997 Free Software Foundation, Inc.
2    This file is part of the GNU C Library.
3    Contributed by Thorsten Kukuk <kukuk@vt.uni-paderborn.de>, 1996.
4
5    The GNU C Library is free software; you can redistribute it and/or
6    modify it under the terms of the GNU Library General Public License as
7    published by the Free Software Foundation; either version 2 of the
8    License, or (at your option) any later version.
9
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    Library General Public License for more details.
14
15    You should have received a copy of the GNU Library General Public
16    License along with the GNU C Library; see the file COPYING.LIB.  If not,
17    write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18    Boston, MA 02111-1307, USA.  */
19
20 #include <nss.h>
21 #include <pwd.h>
22 #include <errno.h>
23 #include <ctype.h>
24 #include <fcntl.h>
25 #include <netdb.h>
26 #include <string.h>
27 #include <bits/libc-lock.h>
28 #include <rpcsvc/yp.h>
29 #include <rpcsvc/ypclnt.h>
30 #include <rpcsvc/nis.h>
31 #include <nsswitch.h>
32
33 #include "netgroup.h"
34 #include "nss-nisplus.h"
35 #include "nisplus-parser.h"
36
37 static service_user *ni = NULL;
38 static bool_t use_nisplus = FALSE; /* default: passwd_compat: nis */
39 static nis_name pwdtable = NULL; /* Name of the pwd table */
40 static size_t pwdtablelen = 0;
41
42 /* Get the declaration of the parser function.  */
43 #define ENTNAME pwent
44 #define STRUCTURE passwd
45 #define EXTERN_PARSER
46 #include <nss/nss_files/files-parse.c>
47
48 /* Structure for remembering -@netgroup and -user members ... */
49 #define BLACKLIST_INITIAL_SIZE 512
50 #define BLACKLIST_INCREMENT 256
51 struct blacklist_t
52   {
53     char *data;
54     int current;
55     int size;
56   };
57
58 struct ent_t
59   {
60     bool_t netgroup;
61     bool_t nis;
62     bool_t first;
63     char *oldkey;
64     int oldkeylen;
65     nis_result *result;
66     FILE *stream;
67     struct blacklist_t blacklist;
68     struct passwd pwd;
69     struct __netgrent netgrdata;
70   };
71 typedef struct ent_t ent_t;
72
73 static ent_t ext_ent = {0, 0, 0, NULL, 0, NULL, NULL, {NULL, 0, 0},
74                         {NULL, NULL, 0, 0, NULL, NULL, NULL}};
75
76 /* Protect global state against multiple changers.  */
77 __libc_lock_define_initialized (static, lock)
78
79 /* Prototypes for local functions.  */
80 static void blacklist_store_name (const char *, ent_t *);
81 static int in_blacklist (const char *, int, ent_t *);
82
83 static void
84 give_pwd_free (struct passwd *pwd)
85 {
86   if (pwd->pw_name != NULL)
87     free (pwd->pw_name);
88   if (pwd->pw_passwd != NULL)
89     free (pwd->pw_passwd);
90   if (pwd->pw_gecos != NULL)
91     free (pwd->pw_gecos);
92   if (pwd->pw_dir != NULL)
93     free (pwd->pw_dir);
94   if (pwd->pw_shell != NULL)
95     free (pwd->pw_shell);
96
97   memset (pwd, '\0', sizeof (struct passwd));
98 }
99
100 static size_t
101 pwd_need_buflen (struct passwd *pwd)
102 {
103   size_t len = 0;
104
105   if (pwd->pw_passwd != NULL)
106     len += strlen (pwd->pw_passwd) + 1;
107
108   if (pwd->pw_gecos != NULL)
109     len += strlen (pwd->pw_gecos) + 1;
110
111   if (pwd->pw_dir != NULL)
112     len += strlen (pwd->pw_dir) + 1;
113
114   if (pwd->pw_shell != NULL)
115     len += strlen (pwd->pw_shell) + 1;
116
117   return len;
118 }
119
120 static void
121 copy_pwd_changes (struct passwd *dest, struct passwd *src,
122                   char *buffer, size_t buflen)
123 {
124   if (src->pw_passwd != NULL && strlen (src->pw_passwd))
125     {
126       if (buffer == NULL)
127         dest->pw_passwd = strdup (src->pw_passwd);
128       else if (dest->pw_passwd &&
129                strlen (dest->pw_passwd) >= strlen (src->pw_passwd))
130         strcpy (dest->pw_passwd, src->pw_passwd);
131       else
132         {
133           dest->pw_passwd = buffer;
134           strcpy (dest->pw_passwd, src->pw_passwd);
135           buffer += strlen (dest->pw_passwd) + 1;
136           buflen = buflen - (strlen (dest->pw_passwd) + 1);
137         }
138     }
139
140   if (src->pw_gecos != NULL && strlen (src->pw_gecos))
141     {
142       if (buffer == NULL)
143         dest->pw_gecos = strdup (src->pw_gecos);
144       else if (dest->pw_gecos &&
145                strlen (dest->pw_gecos) >= strlen (src->pw_gecos))
146         strcpy (dest->pw_gecos, src->pw_gecos);
147       else
148         {
149           dest->pw_gecos = buffer;
150           strcpy (dest->pw_gecos, src->pw_gecos);
151           buffer += strlen (dest->pw_gecos) + 1;
152           buflen = buflen - (strlen (dest->pw_gecos) + 1);
153         }
154     }
155   if (src->pw_dir != NULL && strlen (src->pw_dir))
156     {
157       if (buffer == NULL)
158         dest->pw_dir = strdup (src->pw_dir);
159       else if (dest->pw_dir &&
160                strlen (dest->pw_dir) >= strlen (src->pw_dir))
161         strcpy (dest->pw_dir, src->pw_dir);
162       else
163         {
164           dest->pw_dir = buffer;
165           strcpy (dest->pw_dir, src->pw_dir);
166           buffer += strlen (dest->pw_dir) + 1;
167           buflen = buflen - (strlen (dest->pw_dir) + 1);
168         }
169     }
170
171   if (src->pw_shell != NULL && strlen (src->pw_shell))
172     {
173       if (buffer == NULL)
174         dest->pw_shell = strdup (src->pw_shell);
175       else if (dest->pw_shell &&
176                strlen (dest->pw_shell) >= strlen (src->pw_shell))
177         strcpy (dest->pw_shell, src->pw_shell);
178       else
179         {
180           dest->pw_shell = buffer;
181           strcpy (dest->pw_shell, src->pw_shell);
182           buffer += strlen (dest->pw_shell) + 1;
183           buflen = buflen - (strlen (dest->pw_shell) + 1);
184         }
185     }
186 }
187
188 static enum nss_status
189 internal_setpwent (ent_t *ent)
190 {
191   enum nss_status status = NSS_STATUS_SUCCESS;
192
193   ent->nis = ent->first = ent->netgroup = 0;
194
195   /* If something was left over free it.  */
196   if (ent->netgroup)
197     __internal_endnetgrent (&ent->netgrdata);
198
199   if (ent->oldkey != NULL)
200     {
201       free (ent->oldkey);
202       ent->oldkey = NULL;
203       ent->oldkeylen = 0;
204     }
205
206   if (ent->result != NULL)
207     {
208       nis_freeresult (ent->result);
209       ent->result = NULL;
210     }
211
212   if (pwdtable == NULL)
213     {
214       static const char key[] = "passwd.org_dir.";
215       const char *local_dir = nis_local_directory ();
216       size_t len_local_dir = strlen (local_dir);
217
218       pwdtable = malloc (sizeof (key) + len_local_dir);
219       if (pwdtable == NULL)
220         return NSS_STATUS_TRYAGAIN;
221
222       pwdtablelen = ((char *) mempcpy (mempcpy (pwdtable,
223                                                 key, sizeof (key) - 1),
224                                        local_dir, len_local_dir + 1)
225                      - pwdtable) - 1;
226     }
227
228   ent->blacklist.current = 0;
229   if (ent->blacklist.data != NULL)
230     ent->blacklist.data[0] = '\0';
231
232   if (ent->stream == NULL)
233     {
234       ent->stream = fopen ("/etc/passwd", "r");
235
236       if (ent->stream == NULL)
237         status = errno == EAGAIN ? NSS_STATUS_TRYAGAIN : NSS_STATUS_UNAVAIL;
238       else
239         {
240           /* We have to make sure the file is  `closed on exec'.  */
241           int result, flags;
242
243           result = flags = fcntl (fileno (ent->stream), F_GETFD, 0);
244           if (result >= 0)
245             {
246               flags |= FD_CLOEXEC;
247               result = fcntl (fileno (ent->stream), F_SETFD, flags);
248             }
249           if (result < 0)
250             {
251               /* Something went wrong.  Close the stream and return a
252                  failure.  */
253               fclose (ent->stream);
254               ent->stream = NULL;
255               status = NSS_STATUS_UNAVAIL;
256             }
257         }
258     }
259   else
260     rewind (ent->stream);
261
262   give_pwd_free (&ent->pwd);
263
264   return status;
265 }
266
267
268 enum nss_status
269 _nss_compat_setpwent (void)
270 {
271   enum nss_status result;
272
273   __libc_lock_lock (lock);
274
275   if (ni == NULL)
276     {
277       __nss_database_lookup ("passwd_compat", NULL, "nis", &ni);
278       use_nisplus = (strcmp (ni->name, "nisplus") == 0);
279     }
280
281   result = internal_setpwent (&ext_ent);
282
283   __libc_lock_unlock (lock);
284
285   return result;
286 }
287
288
289 static enum nss_status
290 internal_endpwent (ent_t *ent)
291 {
292   if (ent->stream != NULL)
293     {
294       fclose (ent->stream);
295       ent->stream = NULL;
296     }
297
298   if (ent->netgroup)
299     __internal_endnetgrent (&ent->netgrdata);
300
301   ent->nis = ent->first = ent->netgroup = 0;
302
303   if (ent->oldkey != NULL)
304     {
305       free (ent->oldkey);
306       ent->oldkey = NULL;
307       ent->oldkeylen = 0;
308     }
309
310   if (ent->result != NULL)
311     {
312       nis_freeresult (ent->result);
313       ent->result = NULL;
314     }
315
316   ent->blacklist.current = 0;
317   if (ent->blacklist.data != NULL)
318     ent->blacklist.data[0] = '\0';
319
320   give_pwd_free (&ent->pwd);
321
322   return NSS_STATUS_SUCCESS;
323 }
324
325 enum nss_status
326 _nss_compat_endpwent (void)
327 {
328   enum nss_status result;
329
330   __libc_lock_lock (lock);
331
332   result = internal_endpwent (&ext_ent);
333
334   __libc_lock_unlock (lock);
335
336   return result;
337 }
338
339 static enum nss_status
340 getpwent_next_nis_netgr (const char *name, struct passwd *result, ent_t *ent,
341                          char *group, char *buffer, size_t buflen, int *errnop)
342 {
343   struct parser_data *data = (void *) buffer;
344   char *ypdomain, *host, *user, *domain, *outval, *p, *p2;
345   int status, outvallen;
346   size_t p2len;
347
348   if (yp_get_default_domain (&ypdomain) != YPERR_SUCCESS)
349     {
350       ent->netgroup = 0;
351       ent->first = 0;
352       give_pwd_free (&ent->pwd);
353       return NSS_STATUS_UNAVAIL;
354     }
355
356   if (ent->first == TRUE)
357     {
358       memset (&ent->netgrdata, 0, sizeof (struct __netgrent));
359       __internal_setnetgrent (group, &ent->netgrdata);
360       ent->first = FALSE;
361     }
362
363   while (1)
364     {
365       char *saved_cursor;
366       int parse_res;
367
368       saved_cursor = ent->netgrdata.cursor;
369       status = __internal_getnetgrent_r (&host, &user, &domain,
370                                          &ent->netgrdata, buffer, buflen,
371                                          errnop);
372       if (status != 1)
373         {
374           __internal_endnetgrent (&ent->netgrdata);
375           ent->netgroup = 0;
376           give_pwd_free (&ent->pwd);
377           return NSS_STATUS_RETURN;
378         }
379
380       if (user == NULL || user[0] == '-')
381         continue;
382
383       if (domain != NULL && strcmp (ypdomain, domain) != 0)
384         continue;
385
386       /* If name != NULL, we are called from getpwnam */
387       if (name != NULL)
388         if (strcmp (user, name) != 0)
389           continue;
390
391       if (yp_match (ypdomain, "passwd.byname", user,
392                     strlen (user), &outval, &outvallen)
393           != YPERR_SUCCESS)
394         continue;
395
396       p2len = pwd_need_buflen (&ent->pwd);
397       if (p2len > buflen)
398         {
399           *errnop = ERANGE;
400           return NSS_STATUS_TRYAGAIN;
401         }
402       p2 = buffer + (buflen - p2len);
403       buflen -= p2len;
404       p = strncpy (buffer, outval, buflen);
405       while (isspace (*p))
406         p++;
407       free (outval);
408       parse_res = _nss_files_parse_pwent (p, result, data, buflen, errnop);
409       if (parse_res == -1)
410         {
411           ent->netgrdata.cursor = saved_cursor;
412           return NSS_STATUS_TRYAGAIN;
413         }
414
415       if (parse_res)
416         {
417           /* Store the User in the blacklist for the "+" at the end of
418              /etc/passwd */
419           blacklist_store_name (result->pw_name, ent);
420           copy_pwd_changes (result, &ent->pwd, p2, p2len);
421           break;
422         }
423     }
424
425   return NSS_STATUS_SUCCESS;
426 }
427
428 static enum nss_status
429 getpwent_next_nisplus_netgr (const char *name, struct passwd *result,
430                              ent_t *ent, char *group, char *buffer,
431                              size_t buflen, int *errnop)
432 {
433   char *ypdomain, *host, *user, *domain, *p2;
434   int status, parse_res;
435   size_t p2len;
436   nis_result *nisres;
437
438   /* Maybe we should use domainname here ? We need the current
439      domainname for the domain field in netgroups */
440   if (yp_get_default_domain (&ypdomain) != YPERR_SUCCESS)
441     {
442       ent->netgroup = 0;
443       ent->first = 0;
444       give_pwd_free (&ent->pwd);
445       return NSS_STATUS_UNAVAIL;
446     }
447
448   if (ent->first == TRUE)
449     {
450       bzero (&ent->netgrdata, sizeof (struct __netgrent));
451       __internal_setnetgrent (group, &ent->netgrdata);
452       ent->first = FALSE;
453     }
454
455   while (1)
456     {
457       char *saved_cursor;
458
459       saved_cursor = ent->netgrdata.cursor;
460       status = __internal_getnetgrent_r (&host, &user, &domain,
461                                          &ent->netgrdata, buffer, buflen,
462                                          errnop);
463       if (status != 1)
464         {
465           __internal_endnetgrent (&ent->netgrdata);
466           ent->netgroup = 0;
467           give_pwd_free (&ent->pwd);
468           return NSS_STATUS_RETURN;
469         }
470
471       if (user == NULL || user[0] == '-')
472         continue;
473
474       if (domain != NULL && strcmp (ypdomain, domain) != 0)
475         continue;
476
477       /* If name != NULL, we are called from getpwnam */
478       if (name != NULL)
479         if (strcmp (user, name) != 0)
480           continue;
481
482       p2len = pwd_need_buflen (&ent->pwd);
483       if (p2len > buflen)
484         {
485           *errnop = ERANGE;
486           return NSS_STATUS_TRYAGAIN;
487         }
488       p2 = buffer + (buflen - p2len);
489       buflen -= p2len;
490       {
491         char buf[strlen (user) + 30 + pwdtablelen];
492         sprintf(buf, "[name=%s],%s", user, pwdtable);
493         nisres = nis_list(buf, FOLLOW_PATH | FOLLOW_LINKS, NULL, NULL);
494       }
495       if (niserr2nss (nisres->status) != NSS_STATUS_SUCCESS)
496         {
497           nis_freeresult (nisres);
498           continue;
499         }
500       parse_res = _nss_nisplus_parse_pwent (nisres, result, buffer,
501                                             buflen, errnop);
502       if (parse_res == -1)
503         {
504           nis_freeresult (nisres);
505           ent->netgrdata.cursor = saved_cursor;
506           *errnop = ERANGE;
507           return NSS_STATUS_TRYAGAIN;
508         }
509       nis_freeresult (nisres);
510
511       if (parse_res)
512         {
513           /* Store the User in the blacklist for the "+" at the end of
514              /etc/passwd */
515           blacklist_store_name (result->pw_name, ent);
516           copy_pwd_changes (result, &ent->pwd, p2, p2len);
517           break;
518         }
519     }
520
521   return NSS_STATUS_SUCCESS;
522 }
523
524 static enum nss_status
525 getpwent_next_nisplus (struct passwd *result, ent_t *ent, char *buffer,
526                        size_t buflen, int *errnop)
527 {
528   int parse_res;
529   size_t p2len;
530   char *p2;
531
532   p2len = pwd_need_buflen (&ent->pwd);
533   if (p2len > buflen)
534     {
535       *errnop = ERANGE;
536       return NSS_STATUS_TRYAGAIN;
537     }
538   p2 = buffer + (buflen - p2len);
539   buflen -= p2len;
540   do
541     {
542       bool_t saved_first;
543       nis_result *saved_res;
544
545       if (ent->first)
546         {
547           saved_first = TRUE;
548           saved_res = ent->result;
549
550           ent->result = nis_first_entry (pwdtable);
551           if (niserr2nss (ent->result->status) != NSS_STATUS_SUCCESS)
552             {
553               ent->nis = 0;
554               give_pwd_free (&ent->pwd);
555               return niserr2nss (ent->result->status);
556             }
557           ent->first = FALSE;
558         }
559       else
560         {
561           nis_result *res;
562
563           res = nis_next_entry (pwdtable, &ent->result->cookie);
564           saved_res = ent->result;
565           saved_first = FALSE;
566           ent->result = res;
567           if (niserr2nss (ent->result->status) != NSS_STATUS_SUCCESS)
568             {
569               ent->nis = 0;
570               nis_freeresult (saved_res);
571               give_pwd_free (&ent->pwd);
572               return niserr2nss (ent->result->status);
573             }
574         }
575       parse_res = _nss_nisplus_parse_pwent (ent->result, result, buffer,
576                                             buflen, errnop);
577       if (parse_res == -1)
578         {
579           nis_freeresult (ent->result);
580           ent->result = saved_res;
581           ent->first = saved_first;
582           *errnop = ERANGE;
583           return NSS_STATUS_TRYAGAIN;
584         }
585       else
586         {
587           if (!saved_first)
588             nis_freeresult (saved_res);
589         }
590
591       if (parse_res &&
592           in_blacklist (result->pw_name, strlen (result->pw_name), ent))
593         parse_res = 0; /* if result->pw_name in blacklist,search next entry */
594     }
595   while (!parse_res);
596
597   copy_pwd_changes (result, &ent->pwd, p2, p2len);
598
599   return NSS_STATUS_SUCCESS;
600 }
601
602 static enum nss_status
603 getpwent_next_nis (struct passwd *result, ent_t *ent, char *buffer,
604                    size_t buflen, int *errnop)
605 {
606   struct parser_data *data = (void *) buffer;
607   char *domain, *outkey, *outval, *p, *p2;
608   int outkeylen, outvallen, parse_res;
609   size_t p2len;
610
611   if (yp_get_default_domain (&domain) != YPERR_SUCCESS)
612     {
613       ent->nis = 0;
614       give_pwd_free (&ent->pwd);
615       return NSS_STATUS_UNAVAIL;
616     }
617
618   p2len = pwd_need_buflen (&ent->pwd);
619   if (p2len > buflen)
620     {
621       *errnop = ERANGE;
622       return NSS_STATUS_TRYAGAIN;
623     }
624   p2 = buffer + (buflen - p2len);
625   buflen -= p2len;
626   do
627     {
628       bool_t saved_first;
629       char *saved_oldkey;
630       int saved_oldlen;
631
632       if (ent->first)
633         {
634           if (yp_first (domain, "passwd.byname", &outkey, &outkeylen,
635                         &outval, &outvallen) != YPERR_SUCCESS)
636             {
637               ent->nis = 0;
638               give_pwd_free (&ent->pwd);
639               return NSS_STATUS_UNAVAIL;
640             }
641
642           saved_first = TRUE;
643           saved_oldkey = ent->oldkey;
644           saved_oldlen = ent->oldkeylen;
645           ent->oldkey = outkey;
646           ent->oldkeylen = outkeylen;
647           ent->first = FALSE;
648         }
649       else
650         {
651           if (yp_next (domain, "passwd.byname", ent->oldkey, ent->oldkeylen,
652                        &outkey, &outkeylen, &outval, &outvallen)
653               != YPERR_SUCCESS)
654             {
655               ent->nis = 0;
656               give_pwd_free (&ent->pwd);
657               return NSS_STATUS_NOTFOUND;
658             }
659
660           saved_first = FALSE;
661           saved_oldkey = ent->oldkey;
662           saved_oldlen = ent->oldkeylen;
663           ent->oldkey = outkey;
664           ent->oldkeylen = outkeylen;
665         }
666
667       /* Copy the found data to our buffer  */
668       p = strncpy (buffer, outval, buflen);
669
670       /* ...and free the data.  */
671       free (outval);
672
673       while (isspace (*p))
674         ++p;
675       parse_res = _nss_files_parse_pwent (p, result, data, buflen, errnop);
676       if (parse_res == -1)
677         {
678           free (ent->oldkey);
679           ent->oldkey = saved_oldkey;
680           ent->oldkeylen = saved_oldlen;
681           ent->first = saved_first;
682           *errnop = ERANGE;
683           return NSS_STATUS_TRYAGAIN;
684         }
685       else
686         {
687           if (!saved_first)
688             free (saved_oldkey);
689         }
690       if (parse_res
691           && in_blacklist (result->pw_name, strlen (result->pw_name), ent))
692         parse_res = 0;
693     }
694   while (!parse_res);
695
696   copy_pwd_changes (result, &ent->pwd, p2, p2len);
697
698   return NSS_STATUS_SUCCESS;
699 }
700
701 /* This function handle the +user entrys in /etc/passwd */
702 static enum nss_status
703 getpwnam_plususer (const char *name, struct passwd *result, char *buffer,
704                    size_t buflen, int *errnop)
705 {
706   struct parser_data *data = (void *) buffer;
707   struct passwd pwd;
708   int parse_res;
709   char *p;
710   size_t plen;
711
712   memset (&pwd, '\0', sizeof (struct passwd));
713
714   copy_pwd_changes (&pwd, result, NULL, 0);
715
716   plen = pwd_need_buflen (&pwd);
717   if (plen > buflen)
718     {
719       *errnop = ERANGE;
720       return NSS_STATUS_TRYAGAIN;
721     }
722   p = buffer + (buflen - plen);
723   buflen -= plen;
724
725   if (use_nisplus) /* Do the NIS+ query here */
726     {
727       nis_result *res;
728       char buf[strlen (name) + 24 + pwdtablelen];
729
730       sprintf(buf, "[name=%s],%s", name, pwdtable);
731       res = nis_list(buf, FOLLOW_PATH | FOLLOW_LINKS, NULL, NULL);
732       if (niserr2nss (res->status) != NSS_STATUS_SUCCESS)
733         {
734           enum nss_status status =  niserr2nss (res->status);
735
736           nis_freeresult (res);
737           return status;
738         }
739       parse_res = _nss_nisplus_parse_pwent (res, result, buffer,
740                                             buflen, errnop);
741       if (parse_res == -1)
742         {
743           nis_freeresult (res);
744           *errnop = ERANGE;
745           return NSS_STATUS_TRYAGAIN;
746         }
747       nis_freeresult (res);
748     }
749   else /* Use NIS */
750     {
751       char *domain, *outval, *ptr;
752       int outvallen;
753
754       if (yp_get_default_domain (&domain) != YPERR_SUCCESS)
755         {
756           *errnop = errno;
757           return NSS_STATUS_TRYAGAIN;
758         }
759
760       if (yp_match (domain, "passwd.byname", name, strlen (name),
761                     &outval, &outvallen)
762           != YPERR_SUCCESS)
763         {
764           *errnop = errno;
765           return NSS_STATUS_TRYAGAIN;
766         }
767       ptr = strncpy (buffer, outval, buflen < (size_t) outvallen ?
768                      buflen : (size_t) outvallen);
769       buffer[buflen < (size_t) outvallen ? buflen : (size_t) outvallen] = '\0';
770       free (outval);
771       while (isspace (*ptr))
772         ptr++;
773       parse_res = _nss_files_parse_pwent (ptr, result, data, buflen, errnop);
774       if (parse_res == -1)
775         return NSS_STATUS_TRYAGAIN;
776     }
777
778   if (parse_res > 0)
779     {
780       copy_pwd_changes (result, &pwd, p, plen);
781       give_pwd_free (&pwd);
782       /* We found the entry.  */
783       return NSS_STATUS_SUCCESS;
784     }
785   else
786     {
787       /* Give buffer the old len back */
788       buflen += plen;
789       give_pwd_free (&pwd);
790     }
791   return NSS_STATUS_RETURN;
792 }
793
794 static enum nss_status
795 getpwent_next_file (struct passwd *result, ent_t *ent,
796                     char *buffer, size_t buflen, int *errnop)
797 {
798   struct parser_data *data = (void *) buffer;
799   while (1)
800     {
801       fpos_t pos;
802       char *p;
803       int parse_res;
804
805       do
806         {
807           fgetpos (ent->stream, &pos);
808           p = fgets (buffer, buflen, ent->stream);
809           if (p == NULL)
810             {
811               if (feof (ent->stream))
812                 return NSS_STATUS_NOTFOUND;
813               else
814                 {
815                   fsetpos (ent->stream, &pos);
816                   *errnop = ERANGE;
817                   return NSS_STATUS_TRYAGAIN;
818                 }
819             }
820
821           /* Terminate the line for any case.  */
822           buffer[buflen - 1] = '\0';
823
824           /* Skip leading blanks.  */
825           while (isspace (*p))
826             ++p;
827         }
828       while (*p == '\0' || *p == '#' || /* Ignore empty and comment lines.  */
829       /* Parse the line.  If it is invalid, loop to
830          get the next line of the file to parse.  */
831              !(parse_res = _nss_files_parse_pwent (p, result, data, buflen,
832                                                    errnop)));
833
834       if (parse_res == -1)
835         {
836           /* The parser ran out of space.  */
837           fsetpos (ent->stream, &pos);
838           *errnop = ERANGE;
839           return NSS_STATUS_TRYAGAIN;
840         }
841
842       if (result->pw_name[0] != '+' && result->pw_name[0] != '-')
843         /* This is a real entry.  */
844         break;
845
846       /* -@netgroup */
847       if (result->pw_name[0] == '-' && result->pw_name[1] == '@'
848           && result->pw_name[2] != '\0')
849         {
850           /* XXX Do not use fixed length buffer.  */
851           char buf2[1024];
852           char *user, *host, *domain;
853           struct __netgrent netgrdata;
854
855           bzero (&netgrdata, sizeof (struct __netgrent));
856           __internal_setnetgrent (&result->pw_name[2], &netgrdata);
857           while (__internal_getnetgrent_r (&host, &user, &domain, &netgrdata,
858                                            buf2, sizeof (buf2), errnop))
859             {
860               if (user != NULL && user[0] != '-')
861                 blacklist_store_name (user, ent);
862             }
863           __internal_endnetgrent (&netgrdata);
864           continue;
865         }
866
867       /* +@netgroup */
868       if (result->pw_name[0] == '+' && result->pw_name[1] == '@'
869           && result->pw_name[2] != '\0')
870         {
871           int status;
872
873           ent->netgroup = TRUE;
874           ent->first = TRUE;
875           copy_pwd_changes (&ent->pwd, result, NULL, 0);
876
877           if (use_nisplus)
878             status =  getpwent_next_nisplus_netgr (NULL, result, ent,
879                                                    &result->pw_name[2],
880                                                    buffer, buflen, errnop);
881           else
882             status =  getpwent_next_nis_netgr (NULL, result, ent,
883                                                &result->pw_name[2],
884                                                buffer, buflen, errnop);
885           if (status == NSS_STATUS_RETURN)
886             continue;
887           else
888             return status;
889         }
890
891       /* -user */
892       if (result->pw_name[0] == '-' && result->pw_name[1] != '\0'
893           && result->pw_name[1] != '@')
894         {
895           blacklist_store_name (&result->pw_name[1], ent);
896           continue;
897         }
898
899       /* +user */
900       if (result->pw_name[0] == '+' && result->pw_name[1] != '\0'
901           && result->pw_name[1] != '@')
902         {
903           enum nss_status status;
904
905           /* Store the User in the blacklist for the "+" at the end of
906              /etc/passwd */
907           blacklist_store_name (&result->pw_name[1], ent);
908           status = getpwnam_plususer (&result->pw_name[1], result, buffer,
909                                       buflen, errnop);
910           if (status == NSS_STATUS_SUCCESS) /* We found the entry. */
911             break;
912           else
913             if (status == NSS_STATUS_RETURN) /* We couldn't parse the entry */
914               continue;
915             else
916               {
917                 if (status == NSS_STATUS_TRYAGAIN)
918                   {
919                     /* The parser ran out of space */
920                     fsetpos (ent->stream, &pos);
921                     *errnop = ERANGE;
922                   }
923                 return status;
924               }
925         }
926
927       /* +:... */
928       if (result->pw_name[0] == '+' && result->pw_name[1] == '\0')
929         {
930           ent->nis = TRUE;
931           ent->first = TRUE;
932           copy_pwd_changes (&ent->pwd, result, NULL, 0);
933
934           if (use_nisplus)
935             return getpwent_next_nisplus (result, ent, buffer, buflen, errnop);
936           else
937             return getpwent_next_nis (result, ent, buffer, buflen, errnop);
938         }
939     }
940
941   return NSS_STATUS_SUCCESS;
942 }
943
944
945 static enum nss_status
946 internal_getpwent_r (struct passwd *pw, ent_t *ent, char *buffer,
947                      size_t buflen, int *errnop)
948 {
949   if (ent->netgroup)
950     {
951       int status;
952
953       /* We are searching members in a netgroup */
954       /* Since this is not the first call, we don't need the group name */
955       if (use_nisplus)
956         status = getpwent_next_nisplus_netgr (NULL, pw, ent, NULL, buffer,
957                                               buflen, errnop);
958       else
959         status = getpwent_next_nis_netgr (NULL, pw, ent, NULL, buffer, buflen,
960                                           errnop);
961       if (status == NSS_STATUS_RETURN)
962         return getpwent_next_file (pw, ent, buffer, buflen, errnop);
963       else
964         return status;
965     }
966   else
967     if (ent->nis)
968       {
969         if (use_nisplus)
970           return getpwent_next_nisplus (pw, ent, buffer, buflen, errnop);
971         else
972           return getpwent_next_nis (pw, ent, buffer, buflen, errnop);
973       }
974     else
975       return getpwent_next_file (pw, ent, buffer, buflen, errnop);
976 }
977
978 enum nss_status
979 _nss_compat_getpwent_r (struct passwd *pwd, char *buffer, size_t buflen,
980                         int *errnop)
981 {
982   enum nss_status status = NSS_STATUS_SUCCESS;
983
984   __libc_lock_lock (lock);
985
986   if (ni == NULL)
987     {
988       __nss_database_lookup ("passwd_compat", NULL, "nis", &ni);
989       use_nisplus = (strcmp (ni->name, "nisplus") == 0);
990     }
991
992   /* Be prepared that the setpwent function was not called before.  */
993   if (ext_ent.stream == NULL)
994     status = internal_setpwent (&ext_ent);
995
996   if (status == NSS_STATUS_SUCCESS)
997     status = internal_getpwent_r (pwd, &ext_ent, buffer, buflen, errnop);
998
999   __libc_lock_unlock (lock);
1000
1001   return status;
1002 }
1003
1004 /* Searches in /etc/passwd and the NIS/NIS+ map for a special user */
1005 static enum nss_status
1006 internal_getpwnam_r (const char *name, struct passwd *result, ent_t *ent,
1007                      char *buffer, size_t buflen, int *errnop)
1008 {
1009   struct parser_data *data = (void *) buffer;
1010
1011   while (1)
1012     {
1013       fpos_t pos;
1014       char *p;
1015       int parse_res;
1016
1017       do
1018         {
1019           fgetpos (ent->stream, &pos);
1020           p = fgets (buffer, buflen, ent->stream);
1021           if (p == NULL)
1022             {
1023               if (feof (ent->stream))
1024                 return NSS_STATUS_NOTFOUND;
1025               else
1026                 {
1027                   fsetpos (ent->stream, &pos);
1028                   *errnop = ERANGE;
1029                   return NSS_STATUS_TRYAGAIN;
1030                 }
1031             }
1032
1033           /* Terminate the line for any case.  */
1034           buffer[buflen - 1] = '\0';
1035
1036           /* Skip leading blanks.  */
1037           while (isspace (*p))
1038             ++p;
1039         }
1040       while (*p == '\0' || *p == '#' || /* Ignore empty and comment lines.  */
1041              /* Parse the line.  If it is invalid, loop to
1042                 get the next line of the file to parse.  */
1043              !(parse_res = _nss_files_parse_pwent (p, result, data, buflen,
1044                                                    errnop)));
1045
1046       if (parse_res == -1)
1047         {
1048           /* The parser ran out of space.  */
1049           fsetpos (ent->stream, &pos);
1050           *errnop = ERANGE;
1051           return NSS_STATUS_TRYAGAIN;
1052         }
1053
1054       /* This is a real entry.  */
1055       if (result->pw_name[0] != '+' && result->pw_name[0] != '-')
1056         {
1057           if (strcmp (result->pw_name, name) == 0)
1058             return NSS_STATUS_SUCCESS;
1059           else
1060             continue;
1061         }
1062
1063       /* -@netgroup */
1064       if (result->pw_name[0] == '-' && result->pw_name[1] == '@'
1065           && result->pw_name[2] != '\0')
1066         {
1067           /* XXX Do not use fixed length buffers.  */
1068           char buf2[1024];
1069           char *user, *host, *domain;
1070           struct __netgrent netgrdata;
1071
1072           bzero (&netgrdata, sizeof (struct __netgrent));
1073           __internal_setnetgrent (&result->pw_name[2], &netgrdata);
1074           while (__internal_getnetgrent_r (&host, &user, &domain, &netgrdata,
1075                                            buf2, sizeof (buf2), errnop))
1076             {
1077               if (user != NULL && user[0] != '-')
1078                 if (strcmp (user, name) == 0)
1079                   return NSS_STATUS_NOTFOUND;
1080             }
1081           __internal_endnetgrent (&netgrdata);
1082           continue;
1083         }
1084
1085       /* +@netgroup */
1086       if (result->pw_name[0] == '+' && result->pw_name[1] == '@'
1087           && result->pw_name[2] != '\0')
1088         {
1089           char buf[strlen (result->pw_name)];
1090           int status;
1091
1092           strcpy (buf, &result->pw_name[2]);
1093           ent->netgroup = TRUE;
1094           ent->first = TRUE;
1095           copy_pwd_changes (&ent->pwd, result, NULL, 0);
1096
1097           do
1098             {
1099               if (use_nisplus)
1100                 status = getpwent_next_nisplus_netgr (name, result, ent, buf,
1101                                                       buffer, buflen, errnop);
1102               else
1103                 status = getpwent_next_nis_netgr (name, result, ent, buf,
1104                                                   buffer, buflen, errnop);
1105               if (status == NSS_STATUS_RETURN)
1106                 continue;
1107
1108               if (status == NSS_STATUS_SUCCESS &&
1109                   strcmp (result->pw_name, name) == 0)
1110                 return NSS_STATUS_SUCCESS;
1111             } while (status == NSS_STATUS_SUCCESS);
1112           continue;
1113         }
1114
1115       /* -user */
1116       if (result->pw_name[0] == '-' && result->pw_name[1] != '\0'
1117           && result->pw_name[1] != '@')
1118         {
1119           if (strcmp (&result->pw_name[1], name) == 0)
1120             return NSS_STATUS_NOTFOUND;
1121           else
1122             continue;
1123         }
1124
1125       /* +user */
1126       if (result->pw_name[0] == '+' && result->pw_name[1] != '\0'
1127           && result->pw_name[1] != '@')
1128         {
1129           if (strcmp (name, &result->pw_name[1]) == 0)
1130             {
1131               enum nss_status status;
1132
1133               status = getpwnam_plususer (name, result, buffer, buflen,
1134                                           errnop);
1135               if (status == NSS_STATUS_RETURN)
1136                 /* We couldn't parse the entry */
1137                 return NSS_STATUS_NOTFOUND;
1138               else
1139                 return status;
1140             }
1141         }
1142
1143       /* +:... */
1144       if (result->pw_name[0] == '+' && result->pw_name[1] == '\0')
1145         {
1146           enum nss_status status;
1147
1148           status = getpwnam_plususer (name, result, buffer, buflen, errnop);
1149           if (status == NSS_STATUS_SUCCESS) /* We found the entry. */
1150             break;
1151           else
1152             if (status == NSS_STATUS_RETURN) /* We couldn't parse the entry */
1153               return NSS_STATUS_NOTFOUND;
1154             else
1155               return status;
1156         }
1157     }
1158   return NSS_STATUS_SUCCESS;
1159 }
1160
1161 enum nss_status
1162 _nss_compat_getpwnam_r (const char *name, struct passwd *pwd,
1163                         char *buffer, size_t buflen, int *errnop)
1164 {
1165   ent_t ent = {0, 0, 0, NULL, 0, NULL, NULL, {NULL, 0, 0},
1166                {NULL, NULL, 0, 0, NULL, NULL, NULL}};
1167   enum nss_status status;
1168
1169   if (name[0] == '-' || name[0] == '+')
1170     return NSS_STATUS_NOTFOUND;
1171
1172   __libc_lock_lock (lock);
1173
1174   if (ni == NULL)
1175     {
1176       __nss_database_lookup ("passwd_compat", NULL, "nis", &ni);
1177       use_nisplus = (strcmp (ni->name, "nisplus") == 0);
1178     }
1179
1180   __libc_lock_unlock (lock);
1181
1182   status = internal_setpwent (&ent);
1183   if (status != NSS_STATUS_SUCCESS)
1184     return status;
1185
1186   status = internal_getpwnam_r (name, pwd, &ent, buffer, buflen, errnop);
1187
1188   internal_endpwent (&ent);
1189
1190   return status;
1191 }
1192
1193 /* This function handle the + entry in /etc/passwd for getpwuid */
1194 static enum nss_status
1195 getpwuid_plususer (uid_t uid, struct passwd *result, char *buffer,
1196                    size_t buflen, int *errnop)
1197 {
1198   struct parser_data *data = (void *) buffer;
1199   struct passwd pwd;
1200   int parse_res;
1201   char *p;
1202   size_t plen;
1203
1204   memset (&pwd, '\0', sizeof (struct passwd));
1205
1206   copy_pwd_changes (&pwd, result, NULL, 0);
1207
1208   plen = pwd_need_buflen (&pwd);
1209   if (plen > buflen)
1210     {
1211       *errnop = ERANGE;
1212       return NSS_STATUS_TRYAGAIN;
1213     }
1214   p = buffer + (buflen - plen);
1215   buflen -= plen;
1216
1217   if (use_nisplus) /* Do the NIS+ query here */
1218     {
1219       nis_result *res;
1220       char buf[1024 + pwdtablelen];
1221
1222       snprintf(buf, sizeof (buf), "[uid=%d],%s", uid, pwdtable);
1223       res = nis_list(buf, FOLLOW_PATH | FOLLOW_LINKS, NULL, NULL);
1224       if (niserr2nss (res->status) != NSS_STATUS_SUCCESS)
1225         {
1226           enum nss_status status =  niserr2nss (res->status);
1227
1228           nis_freeresult (res);
1229           return status;
1230         }
1231       if ((parse_res = _nss_nisplus_parse_pwent (res, result, buffer,
1232                                                  buflen, errnop)) == -1)
1233         {
1234           nis_freeresult (res);
1235           *errnop = ERANGE;
1236           return NSS_STATUS_TRYAGAIN;
1237         }
1238       nis_freeresult (res);
1239     }
1240   else /* Use NIS */
1241     {
1242       char buf[1024];
1243       char *domain, *outval, *ptr;
1244       int outvallen;
1245
1246       if (yp_get_default_domain (&domain) != YPERR_SUCCESS)
1247         {
1248           *errnop = errno;
1249           return NSS_STATUS_TRYAGAIN;
1250         }
1251
1252       sprintf (buf, "%d", uid);
1253       if (yp_match (domain, "passwd.byuid", buf, strlen (buf),
1254                     &outval, &outvallen)
1255           != YPERR_SUCCESS)
1256         {
1257           *errnop = errno;
1258           return NSS_STATUS_TRYAGAIN;
1259         }
1260       ptr = strncpy (buffer, outval, buflen < (size_t) outvallen ?
1261                      buflen : (size_t) outvallen);
1262       buffer[buflen < (size_t) outvallen ? buflen : (size_t) outvallen] = '\0';
1263       free (outval);
1264       while (isspace (*ptr))
1265         ptr++;
1266       parse_res = _nss_files_parse_pwent (ptr, result, data, buflen, errnop);
1267       if (parse_res == -1)
1268         return NSS_STATUS_TRYAGAIN;
1269     }
1270
1271   if (parse_res > 0)
1272     {
1273       copy_pwd_changes (result, &pwd, p, plen);
1274       give_pwd_free (&pwd);
1275       /* We found the entry.  */
1276       return NSS_STATUS_SUCCESS;
1277     }
1278   else
1279     {
1280       /* Give buffer the old len back */
1281       buflen += plen;
1282       give_pwd_free (&pwd);
1283     }
1284   return NSS_STATUS_RETURN;
1285 }
1286
1287 /* Searches in /etc/passwd and the NIS/NIS+ map for a special user id */
1288 static enum nss_status
1289 internal_getpwuid_r (uid_t uid, struct passwd *result, ent_t *ent,
1290                      char *buffer, size_t buflen, int *errnop)
1291 {
1292   struct parser_data *data = (void *) buffer;
1293
1294   while (1)
1295     {
1296       fpos_t pos;
1297       char *p;
1298       int parse_res;
1299
1300       do
1301         {
1302           fgetpos (ent->stream, &pos);
1303           p = fgets (buffer, buflen, ent->stream);
1304           if (p == NULL)
1305             {
1306               if (feof (ent->stream))
1307                 return NSS_STATUS_NOTFOUND;
1308               else
1309                 {
1310                   fsetpos (ent->stream, &pos);
1311                   *errnop = ERANGE;
1312                   return NSS_STATUS_TRYAGAIN;
1313                 }
1314             }
1315
1316           /* Terminate the line for any case.  */
1317           buffer[buflen - 1] = '\0';
1318
1319           /* Skip leading blanks.  */
1320           while (isspace (*p))
1321             ++p;
1322         }
1323       while (*p == '\0' || *p == '#' || /* Ignore empty and comment lines.  */
1324              /* Parse the line.  If it is invalid, loop to
1325                 get the next line of the file to parse.  */
1326              !(parse_res = _nss_files_parse_pwent (p, result, data, buflen,
1327                                                    errnop)));
1328
1329       if (parse_res == -1)
1330         {
1331           /* The parser ran out of space.  */
1332           fsetpos (ent->stream, &pos);
1333           *errnop = ERANGE;
1334           return NSS_STATUS_TRYAGAIN;
1335         }
1336
1337       /* This is a real entry.  */
1338       if (result->pw_name[0] != '+' && result->pw_name[0] != '-')
1339         {
1340           if (result->pw_uid == uid)
1341             return NSS_STATUS_SUCCESS;
1342           else
1343             continue;
1344         }
1345
1346       /* -@netgroup */
1347       if (result->pw_name[0] == '-' && result->pw_name[1] == '@'
1348           && result->pw_name[2] != '\0')
1349         {
1350           /* XXX Do not use fixed length buffers.  */
1351           char buf2[1024];
1352           char *user, *host, *domain;
1353           struct __netgrent netgrdata;
1354
1355           bzero (&netgrdata, sizeof (struct __netgrent));
1356           __internal_setnetgrent (&result->pw_name[2], &netgrdata);
1357           while (__internal_getnetgrent_r (&host, &user, &domain, &netgrdata,
1358                                            buf2, sizeof (buf2), errnop))
1359             {
1360               if (user != NULL && user[0] != '-')
1361                 blacklist_store_name (user, ent);
1362             }
1363           __internal_endnetgrent (&netgrdata);
1364           continue;
1365         }
1366
1367       /* +@netgroup */
1368       if (result->pw_name[0] == '+' && result->pw_name[1] == '@'
1369           && result->pw_name[2] != '\0')
1370         {
1371           char buf[strlen (result->pw_name)];
1372           int status;
1373
1374           strcpy (buf, &result->pw_name[2]);
1375           ent->netgroup = TRUE;
1376           ent->first = TRUE;
1377           copy_pwd_changes (&ent->pwd, result, NULL, 0);
1378
1379           do
1380             {
1381               if (use_nisplus)
1382                 status = getpwent_next_nisplus_netgr (NULL, result, ent, buf,
1383                                                       buffer, buflen, errnop);
1384               else
1385                 status = getpwent_next_nis_netgr (NULL, result, ent, buf,
1386                                                   buffer, buflen, errnop);
1387               if (status == NSS_STATUS_RETURN)
1388                 continue;
1389
1390               if (status == NSS_STATUS_SUCCESS && uid == result->pw_uid)
1391                 return NSS_STATUS_SUCCESS;
1392             } while (status == NSS_STATUS_SUCCESS);
1393           continue;
1394         }
1395
1396       /* -user */
1397       if (result->pw_name[0] == '-' && result->pw_name[1] != '\0'
1398           && result->pw_name[1] != '@')
1399         {
1400           blacklist_store_name (&result->pw_name[1], ent);
1401           continue;
1402         }
1403
1404       /* +user */
1405       if (result->pw_name[0] == '+' && result->pw_name[1] != '\0'
1406           && result->pw_name[1] != '@')
1407         {
1408           enum nss_status status;
1409
1410           /* Store the User in the blacklist for the "+" at the end of
1411              /etc/passwd */
1412           blacklist_store_name (&result->pw_name[1], ent);
1413           status = getpwnam_plususer (&result->pw_name[1], result, buffer,
1414                                       buflen, errnop);
1415           if (status == NSS_STATUS_SUCCESS && result->pw_uid == uid)
1416             break;
1417           else
1418             continue;
1419         }
1420
1421       /* +:... */
1422       if (result->pw_name[0] == '+' && result->pw_name[1] == '\0')
1423         {
1424           enum nss_status status;
1425
1426           status = getpwuid_plususer (uid, result, buffer, buflen, errnop);
1427           if (status == NSS_STATUS_SUCCESS) /* We found the entry. */
1428             break;
1429           else
1430             if (status == NSS_STATUS_RETURN) /* We couldn't parse the entry */
1431               return NSS_STATUS_NOTFOUND;
1432             else
1433               return status;
1434         }
1435     }
1436   return NSS_STATUS_SUCCESS;
1437 }
1438
1439 enum nss_status
1440 _nss_compat_getpwuid_r (uid_t uid, struct passwd *pwd,
1441                         char *buffer, size_t buflen, int *errnop)
1442 {
1443   ent_t ent = {0, 0, 0, NULL, 0, NULL, NULL, {NULL, 0, 0},
1444                {NULL, NULL, 0, 0, NULL, NULL, NULL}};
1445   enum nss_status status;
1446
1447   __libc_lock_lock (lock);
1448
1449   if (ni == NULL)
1450     {
1451       __nss_database_lookup ("passwd_compat", NULL, "nis", &ni);
1452       use_nisplus = (strcmp (ni->name, "nisplus") == 0);
1453     }
1454
1455   __libc_lock_unlock (lock);
1456
1457   status = internal_setpwent (&ent);
1458   if (status != NSS_STATUS_SUCCESS)
1459     return status;
1460
1461   status = internal_getpwuid_r (uid, pwd, &ent, buffer, buflen, errnop);
1462
1463   internal_endpwent (&ent);
1464
1465   return status;
1466 }
1467
1468
1469 /* Support routines for remembering -@netgroup and -user entries.
1470    The names are stored in a single string with `|' as separator. */
1471 static void
1472 blacklist_store_name (const char *name, ent_t *ent)
1473 {
1474   int namelen = strlen (name);
1475   char *tmp;
1476
1477   /* first call, setup cache */
1478   if (ent->blacklist.size == 0)
1479     {
1480       ent->blacklist.size = MAX (BLACKLIST_INITIAL_SIZE, 2 * namelen);
1481       ent->blacklist.data = malloc (ent->blacklist.size);
1482       if (ent->blacklist.data == NULL)
1483         return;
1484       ent->blacklist.data[0] = '|';
1485       ent->blacklist.data[1] = '\0';
1486       ent->blacklist.current = 1;
1487     }
1488   else
1489     {
1490       if (in_blacklist (name, namelen, ent))
1491         return;                 /* no duplicates */
1492
1493       if (ent->blacklist.current + namelen + 1 >= ent->blacklist.size)
1494         {
1495           ent->blacklist.size += MAX (BLACKLIST_INCREMENT, 2 * namelen);
1496           tmp = realloc (ent->blacklist.data, ent->blacklist.size);
1497           if (tmp == NULL)
1498             {
1499               free (ent->blacklist.data);
1500               ent->blacklist.size = 0;
1501               return;
1502             }
1503           ent->blacklist.data = tmp;
1504         }
1505     }
1506
1507   tmp = stpcpy (ent->blacklist.data + ent->blacklist.current, name);
1508   *tmp++ = '|';
1509   *tmp = '\0';
1510   ent->blacklist.current += namelen + 1;
1511
1512   return;
1513 }
1514
1515 /* returns TRUE if ent->blacklist contains name, else FALSE */
1516 static bool_t
1517 in_blacklist (const char *name, int namelen, ent_t *ent)
1518 {
1519   char buf[namelen + 3];
1520   char *cp;
1521
1522   if (ent->blacklist.data == NULL)
1523     return FALSE;
1524
1525   buf[0] = '|';
1526   cp = stpcpy (&buf[1], name);
1527   *cp++= '|';
1528   *cp = '\0';
1529   return strstr (ent->blacklist.data, buf) != NULL;
1530 }