applyinf fix for:
[people/mcb30/busybox.git] / libpwdgrp / pwd_grp.c
1 /*  Copyright (C) 2003     Manuel Novoa III
2  *
3  *  This library is free software; you can redistribute it and/or
4  *  modify it under the terms of the GNU Library General Public
5  *  License as published by the Free Software Foundation; either
6  *  version 2 of the License, or (at your option) any later version.
7  *
8  *  This library is distributed in the hope that it will be useful,
9  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
10  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
11  *  Library General Public License for more details.
12  *
13  *  You should have received a copy of the GNU Library General Public
14  *  License along with this library; if not, write to the Free
15  *  Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
16  */
17
18 /*  Nov 6, 2003  Initial version.
19  *
20  *  NOTE: This implementation is quite strict about requiring all
21  *    field seperators.  It also does not allow leading whitespace
22  *    except when processing the numeric fields.  glibc is more
23  *    lenient.  See the various glibc difference comments below.
24  *
25  *  TODO:
26  *    Move to dynamic allocation of (currently staticly allocated)
27  *      buffers; especially for the group-related functions since
28  *      large group member lists will cause error returns.
29  *
30  */
31
32 #include <features.h>
33 #include <stdio.h>
34 #include <stdlib.h>
35 #include <stdint.h>
36 #include <string.h>
37 #include <stddef.h>
38 #include <errno.h>
39 #include <assert.h>
40 #include <ctype.h>
41 #include "busybox.h"
42 #include "pwd_.h"
43 #include "grp_.h"
44 #include "shadow_.h"
45
46 #ifndef _PATH_SHADOW
47 #define _PATH_SHADOW    "/etc/shadow"
48 #endif
49 #ifndef _PATH_PASSWD
50 #define _PATH_PASSWD    "/etc/passwd"
51 #endif
52 #ifndef _PATH_GROUP
53 #define _PATH_GROUP     "/etc/group"
54 #endif
55
56 /**********************************************************************/
57 /* Sizes for staticly allocated buffers. */
58
59 /* If you change these values, also change _SC_GETPW_R_SIZE_MAX and
60  * _SC_GETGR_R_SIZE_MAX in libc/unistd/sysconf.c to match */
61 #define PWD_BUFFER_SIZE 256
62 #define GRP_BUFFER_SIZE 256
63
64 /**********************************************************************/
65 /* Prototypes for internal functions. */
66
67 extern int __parsepwent(void *pw, char *line);
68 extern int __parsegrent(void *gr, char *line);
69 extern int __parsespent(void *sp, char *line);
70
71 extern int __pgsreader(int (*__parserfunc)(void *d, char *line), void *data,
72                                            char *__restrict line_buff, size_t buflen, FILE *f);
73
74 /**********************************************************************/
75 /* For the various fget??ent_r funcs, return
76  *
77  *  0: success
78  *  ENOENT: end-of-file encountered
79  *  ERANGE: buflen too small
80  *  other error values possible. See __pgsreader.
81  *
82  * Also, *result == resultbuf on success and NULL on failure.
83  *
84  * NOTE: glibc difference - For the ENOENT case, glibc also sets errno.
85  *   We do not, as it really isn't an error if we reach the end-of-file.
86  *   Doing so is analogous to having fgetc() set errno on EOF.
87  */
88 /**********************************************************************/
89 #ifdef L_fgetpwent_r
90
91 int fgetpwent_r(FILE *__restrict stream, struct passwd *__restrict resultbuf,
92                                 char *__restrict buffer, size_t buflen,
93                                 struct passwd **__restrict result)
94 {
95         int rv;
96
97         *result = NULL;
98
99         if (!(rv = __pgsreader(__parsepwent, resultbuf, buffer, buflen, stream))) {
100                 *result = resultbuf;
101         }
102
103         return rv;
104 }
105
106 #endif
107 /**********************************************************************/
108 #ifdef L_fgetgrent_r
109
110 int fgetgrent_r(FILE *__restrict stream, struct group *__restrict resultbuf,
111                                 char *__restrict buffer, size_t buflen,
112                                 struct group **__restrict result)
113 {
114         int rv;
115
116         *result = NULL;
117
118         if (!(rv = __pgsreader(__parsegrent, resultbuf, buffer, buflen, stream))) {
119                 *result = resultbuf;
120         }
121
122         return rv;
123 }
124
125 #endif
126 /**********************************************************************/
127 #ifdef L_fgetspent_r
128
129 int fgetspent_r(FILE *__restrict stream, struct spwd *__restrict resultbuf,
130                                 char *__restrict buffer, size_t buflen,
131                                 struct spwd **__restrict result)
132 {
133         int rv;
134
135         *result = NULL;
136
137         if (!(rv = __pgsreader(__parsespent, resultbuf, buffer, buflen, stream))) {
138                 *result = resultbuf;
139         }
140
141         return rv;
142 }
143
144 #endif
145 /**********************************************************************/
146 /* For the various fget??ent funcs, return NULL on failure and a
147  * pointer to the appropriate struct (staticly allocated) on success.
148  */
149 /**********************************************************************/
150 #ifdef L_fgetpwent
151
152 struct passwd *fgetpwent(FILE *stream)
153 {
154         static char buffer[PWD_BUFFER_SIZE];
155         static struct passwd resultbuf;
156         struct passwd *result;
157
158         fgetpwent_r(stream, &resultbuf, buffer, sizeof(buffer), &result);
159         return result;
160 }
161
162 #endif
163 /**********************************************************************/
164 #ifdef L_fgetgrent
165
166 struct group *fgetgrent(FILE *stream)
167 {
168         static char buffer[GRP_BUFFER_SIZE];
169         static struct group resultbuf;
170         struct group *result;
171
172         fgetgrent_r(stream, &resultbuf, buffer, sizeof(buffer), &result);
173         return result;
174 }
175
176 #endif
177 /**********************************************************************/
178 #ifdef L_fgetspent
179
180 extern int fgetspent_r(FILE *__restrict stream, struct spwd *__restrict resultbuf,
181                                 char *__restrict buffer, size_t buflen,
182                                 struct spwd **__restrict result);
183 struct spwd *fgetspent(FILE *stream)
184 {
185         static char buffer[PWD_BUFFER_SIZE];
186         static struct spwd resultbuf;
187         struct spwd *result;
188
189         fgetspent_r(stream, &resultbuf, buffer, sizeof(buffer), &result);
190         return result;
191 }
192
193 #endif
194 /**********************************************************************/
195 #ifdef L_sgetspent_r
196
197 int sgetspent_r(const char *string, struct spwd *result_buf,
198                                 char *buffer, size_t buflen, struct spwd **result)
199 {
200         int rv = ERANGE;
201
202         *result = NULL;
203
204         if (buflen < PWD_BUFFER_SIZE) {
205         DO_ERANGE:
206                 errno=rv;
207                 goto DONE;
208         }
209
210         if (string != buffer) {
211                 if (strlen(string) >= buflen) {
212                         goto DO_ERANGE;
213                 }
214                 strcpy(buffer, string);
215         }
216
217         if (!(rv = __parsespent(result_buf, buffer))) {
218                 *result = result_buf;
219         }
220
221  DONE:
222         return rv;
223 }
224
225 #endif
226 /**********************************************************************/
227
228 #ifdef GETXXKEY_R_FUNC
229 #error GETXXKEY_R_FUNC is already defined!
230 #endif
231
232 #ifdef L_getpwnam_r
233 #define GETXXKEY_R_FUNC                 getpwnam_r
234 #define GETXXKEY_R_PARSER       __parsepwent
235 #define GETXXKEY_R_ENTTYPE              struct passwd
236 #define GETXXKEY_R_TEST(ENT)    (!strcmp((ENT)->pw_name, key))
237 #define DO_GETXXKEY_R_KEYTYPE   const char *__restrict
238 #define DO_GETXXKEY_R_PATHNAME  _PATH_PASSWD
239 #endif
240
241 #ifdef L_getgrnam_r
242 #define GETXXKEY_R_FUNC                 getgrnam_r
243 #define GETXXKEY_R_PARSER       __parsegrent
244 #define GETXXKEY_R_ENTTYPE              struct group
245 #define GETXXKEY_R_TEST(ENT)    (!strcmp((ENT)->gr_name, key))
246 #define DO_GETXXKEY_R_KEYTYPE   const char *__restrict
247 #define DO_GETXXKEY_R_PATHNAME  _PATH_GROUP
248 #endif
249
250 #ifdef L_getspnam_r
251 #define GETXXKEY_R_FUNC                 getspnam_r
252 #define GETXXKEY_R_PARSER       __parsespent
253 #define GETXXKEY_R_ENTTYPE              struct spwd
254 #define GETXXKEY_R_TEST(ENT)    (!strcmp((ENT)->sp_namp, key))
255 #define DO_GETXXKEY_R_KEYTYPE   const char *__restrict
256 #define DO_GETXXKEY_R_PATHNAME  _PATH_SHADOW
257 #endif
258
259 #ifdef L_getpwuid_r
260 #define GETXXKEY_R_FUNC                 getpwuid_r
261 #define GETXXKEY_R_PARSER       __parsepwent
262 #define GETXXKEY_R_ENTTYPE              struct passwd
263 #define GETXXKEY_R_TEST(ENT)    ((ENT)->pw_uid == key)
264 #define DO_GETXXKEY_R_KEYTYPE   uid_t
265 #define DO_GETXXKEY_R_PATHNAME  _PATH_PASSWD
266 #endif
267
268 #ifdef L_getgrgid_r
269 #define GETXXKEY_R_FUNC                 getgrgid_r
270 #define GETXXKEY_R_PARSER       __parsegrent
271 #define GETXXKEY_R_ENTTYPE              struct group
272 #define GETXXKEY_R_TEST(ENT)    ((ENT)->gr_gid == key)
273 #define DO_GETXXKEY_R_KEYTYPE   gid_t
274 #define DO_GETXXKEY_R_PATHNAME  _PATH_GROUP
275 #endif
276
277 /**********************************************************************/
278 #ifdef GETXXKEY_R_FUNC
279
280 int GETXXKEY_R_FUNC(DO_GETXXKEY_R_KEYTYPE key,
281                                         GETXXKEY_R_ENTTYPE *__restrict resultbuf,
282                                         char *__restrict buffer, size_t buflen,
283                                         GETXXKEY_R_ENTTYPE **__restrict result)
284 {
285         FILE *stream;
286         int rv;
287
288         *result = NULL;
289
290         if (!(stream = fopen(DO_GETXXKEY_R_PATHNAME, "r"))) {
291                 rv = errno;
292         } else {
293                 do {
294                         if (!(rv = __pgsreader(GETXXKEY_R_PARSER, resultbuf,
295                                                                    buffer, buflen, stream))
296                                 ) {
297                                 if (GETXXKEY_R_TEST(resultbuf)) { /* Found key? */
298                                         *result = resultbuf;
299                                         break;
300                                 }
301                         } else {
302                                 if (rv == ENOENT) {     /* end-of-file encountered. */
303                                         rv = 0;
304                                 }
305                                 break;
306                         }
307                 } while (1);
308                 fclose(stream);
309         }
310
311         return rv;
312 }
313
314 #endif
315 /**********************************************************************/
316 #ifdef L_getpwuid
317
318 struct passwd *getpwuid(uid_t uid)
319 {
320         static char buffer[PWD_BUFFER_SIZE];
321         static struct passwd resultbuf;
322         struct passwd *result;
323
324         getpwuid_r(uid, &resultbuf, buffer, sizeof(buffer), &result);
325         return result;
326 }
327
328 #endif
329 /**********************************************************************/
330 #ifdef L_getgrgid
331
332 struct group *getgrgid(gid_t gid)
333 {
334         static char buffer[GRP_BUFFER_SIZE];
335         static struct group resultbuf;
336         struct group *result;
337
338         getgrgid_r(gid, &resultbuf, buffer, sizeof(buffer), &result);
339         return result;
340 }
341
342 #endif
343 /**********************************************************************/
344 #ifdef L_getspuid_r
345
346 /* This function is non-standard and is currently not built.  It seems
347  * to have been created as a reentrant version of the non-standard
348  * functions getspuid.  Why getspuid was added, I do not know. */
349
350 int getspuid_r(uid_t uid, struct spwd *__restrict resultbuf,
351                        char *__restrict buffer, size_t buflen,
352                        struct spwd **__restrict result)
353 {
354         int rv;
355         struct passwd *pp;
356         struct passwd password;
357         char pwd_buff[PWD_BUFFER_SIZE];
358
359         *result = NULL;
360         if (!(rv = getpwuid_r(uid, &password, pwd_buff, sizeof(pwd_buff), &pp))) {
361                 rv = getspnam_r(password.pw_name, resultbuf, buffer, buflen, result);
362         }
363
364         return rv;
365 }
366
367 #endif
368 /**********************************************************************/
369 #ifdef L_getspuid
370
371 /* This function is non-standard and is currently not built.
372  * Why it was added, I do not know. */
373
374 struct spwd *getspuid(uid_t uid)
375 {
376         static char buffer[PWD_BUFFER_SIZE];
377         static struct spwd resultbuf;
378         struct spwd *result;
379
380         getspuid_r(uid, &resultbuf, buffer, sizeof(buffer), &result);
381         return result;
382 }
383
384 #endif
385 /**********************************************************************/
386 #ifdef L_getpwnam
387
388 struct passwd *getpwnam(const char *name)
389 {
390         static char buffer[PWD_BUFFER_SIZE];
391         static struct passwd resultbuf;
392         struct passwd *result;
393
394         getpwnam_r(name, &resultbuf, buffer, sizeof(buffer), &result);
395         return result;
396 }
397
398 #endif
399 /**********************************************************************/
400 #ifdef L_getgrnam
401
402 struct group *getgrnam(const char *name)
403 {
404         static char buffer[GRP_BUFFER_SIZE];
405         static struct group resultbuf;
406         struct group *result;
407
408         getgrnam_r(name, &resultbuf, buffer, sizeof(buffer), &result);
409         return result;
410 }
411
412 #endif
413 /**********************************************************************/
414 #ifdef L_getspnam
415
416 struct spwd *getspnam(const char *name)
417 {
418         static char buffer[PWD_BUFFER_SIZE];
419         static struct spwd resultbuf;
420         struct spwd *result;
421
422         getspnam_r(name, &resultbuf, buffer, sizeof(buffer), &result);
423         return result;
424 }
425
426 #endif
427 /**********************************************************************/
428 #ifdef L_getpw
429
430 int getpw(uid_t uid, char *buf)
431 {
432         struct passwd resultbuf;
433         struct passwd *result;
434         char buffer[PWD_BUFFER_SIZE];
435
436         if (!buf) {
437                 errno=EINVAL;
438         } else if (!getpwuid_r(uid, &resultbuf, buffer, sizeof(buffer), &result)) {
439                 if (sprintf(buf, "%s:%s:%lu:%lu:%s:%s:%s\n",
440                                         resultbuf.pw_name, resultbuf.pw_passwd,
441                                         (unsigned long)(resultbuf.pw_uid),
442                                         (unsigned long)(resultbuf.pw_gid),
443                                         resultbuf.pw_gecos, resultbuf.pw_dir,
444                                         resultbuf.pw_shell) >= 0
445                         ) {
446                         return 0;
447                 }
448         }
449
450         return -1;
451 }
452
453 #endif
454 /**********************************************************************/
455 #ifdef L_getpwent_r
456
457 static FILE *pwf /*= NULL*/;
458 void setpwent(void)
459 {
460         if (pwf) {
461                 rewind(pwf);
462         }
463 }
464
465 void endpwent(void)
466 {
467         if (pwf) {
468                 fclose(pwf);
469                 pwf = NULL;
470         }
471 }
472
473
474 int getpwent_r(struct passwd *__restrict resultbuf, 
475                            char *__restrict buffer, size_t buflen,
476                            struct passwd **__restrict result)
477 {
478         int rv;
479
480         *result = NULL;                         /* In case of error... */
481
482         if (!pwf) {
483                 if (!(pwf = fopen(_PATH_PASSWD, "r"))) {
484                         rv = errno;
485                         goto ERR;
486                 }
487         }
488
489         if (!(rv = __pgsreader(__parsepwent, resultbuf,
490                                                    buffer, buflen, pwf))) {
491                 *result = resultbuf;
492         }
493
494  ERR:
495         return rv;
496 }
497
498 #endif
499 /**********************************************************************/
500 #ifdef L_getgrent_r
501
502 static FILE *grf /*= NULL*/;
503 void setgrent(void)
504 {
505         if (grf) {
506                 rewind(grf);
507         }
508 }
509
510 void endgrent(void)
511 {
512         if (grf) {
513                 fclose(grf);
514                 grf = NULL;
515         }
516 }
517
518 int getgrent_r(struct group *__restrict resultbuf,
519                            char *__restrict buffer, size_t buflen,
520                            struct group **__restrict result)
521 {
522         int rv;
523
524         *result = NULL;                         /* In case of error... */
525
526         if (!grf) {
527                 if (!(grf = fopen(_PATH_GROUP, "r"))) {
528                         rv = errno;
529                         goto ERR;
530                 }
531         }
532
533         if (!(rv = __pgsreader(__parsegrent, resultbuf,
534                                                    buffer, buflen, grf))) {
535                 *result = resultbuf;
536         }
537
538  ERR:
539         return rv;
540 }
541
542 #endif
543 /**********************************************************************/
544 #ifdef L_getspent_r
545
546 static FILE *spf /*= NULL*/;
547 void setspent(void)
548 {
549         if (spf) {
550                 rewind(spf);
551         }
552 }
553
554 void endspent(void)
555 {
556         if (spf) {
557                 fclose(spf);
558                 spf = NULL;
559         }
560 }
561
562 int getspent_r(struct spwd *resultbuf, char *buffer, 
563                            size_t buflen, struct spwd **result)
564 {
565         int rv;
566
567         *result = NULL;                         /* In case of error... */
568
569         if (!spf) {
570                 if (!(spf = fopen(_PATH_SHADOW, "r"))) {
571                         rv = errno;
572                         goto ERR;
573                 }
574         }
575
576         if (!(rv = __pgsreader(__parsespent, resultbuf,
577                                                    buffer, buflen, spf))) {
578                 *result = resultbuf;
579         }
580
581  ERR:
582         return rv;
583 }
584
585 #endif
586 /**********************************************************************/
587 #ifdef L_getpwent
588
589 struct passwd *getpwent(void)
590 {
591         static char line_buff[PWD_BUFFER_SIZE];
592         static struct passwd pwd;
593         struct passwd *result;
594
595         getpwent_r(&pwd, line_buff, sizeof(line_buff), &result);
596         return result;
597 }
598
599 #endif
600 /**********************************************************************/
601 #ifdef L_getgrent
602
603 struct group *getgrent(void)
604 {
605         static char line_buff[GRP_BUFFER_SIZE];
606         static struct group gr;
607         struct group *result;
608
609         getgrent_r(&gr, line_buff, sizeof(line_buff), &result);
610         return result;
611 }
612
613 #endif
614 /**********************************************************************/
615 #ifdef L_getspent
616
617 struct spwd *getspent(void)
618 {
619         static char line_buff[PWD_BUFFER_SIZE];
620         static struct spwd spwd;
621         struct spwd *result;
622
623         getspent_r(&spwd, line_buff, sizeof(line_buff), &result);
624         return result;
625 }
626
627 #endif
628 /**********************************************************************/
629 #ifdef L_sgetspent
630
631 struct spwd *sgetspent(const char *string)
632 {
633         static char line_buff[PWD_BUFFER_SIZE];
634         static struct spwd spwd;
635         struct spwd *result;
636
637         sgetspent_r(string, &spwd, line_buff, sizeof(line_buff), &result);
638         return result;
639 }
640
641 #endif
642 /**********************************************************************/
643 #ifdef L_initgroups
644
645 int initgroups(const char *user, gid_t gid)
646 {
647         FILE *grf;
648         gid_t *group_list;
649         int num_groups, rv;
650         char **m;
651         struct group group;
652         char buff[PWD_BUFFER_SIZE];
653
654         rv = -1;
655
656         /* We alloc space for 8 gids at a time. */
657         if (((group_list = (gid_t *) malloc(8*sizeof(gid_t *))) != NULL)
658                 && ((grf = fopen(_PATH_GROUP, "r")) != NULL)
659                 ) {
660
661                 *group_list = gid;
662                 num_groups = 1;
663
664                 while (!__pgsreader(__parsegrent, &group, buff, sizeof(buff), grf)) {
665                         assert(group.gr_mem); /* Must have at least a NULL terminator. */
666                         if (group.gr_gid != gid) {
667                                 for (m=group.gr_mem ; *m ; m++) {
668                                         if (!strcmp(*m, user)) {
669                                                 if (!(num_groups & 7)) {
670                                                         gid_t *tmp = (gid_t *)
671                                                                 realloc(group_list,
672                                                                                 (num_groups+8) * sizeof(gid_t *));
673                                                         if (!tmp) {
674                                                                 rv = -1;
675                                                                 goto DO_CLOSE;
676                                                         }
677                                                         group_list = tmp;
678                                                 }
679                                                 group_list[num_groups++] = group.gr_gid;
680                                                 break;
681                                         }
682                                 }
683                         }
684                 }
685
686                 rv = setgroups(num_groups, group_list);
687         DO_CLOSE:
688                 fclose(grf);
689         }
690
691         /* group_list will be NULL if initial malloc failed, which may trigger
692          * warnings from various malloc debuggers. */
693         free(group_list);
694         return rv;
695 }
696
697 #endif
698 /**********************************************************************/
699 #ifdef L_putpwent
700
701 int putpwent(const struct passwd *__restrict p, FILE *__restrict f)
702 {
703         int rv = -1;
704
705         if (!p || !f) {
706                 errno=EINVAL;
707         } else {
708                 /* No extra thread locking is needed above what fprintf does. */
709                 if (fprintf(f, "%s:%s:%lu:%lu:%s:%s:%s\n",
710                                         p->pw_name, p->pw_passwd,
711                                         (unsigned long)(p->pw_uid),
712                                         (unsigned long)(p->pw_gid),
713                                         p->pw_gecos, p->pw_dir, p->pw_shell) >= 0
714                         ) {
715                         rv = 0;
716                 }
717         }
718
719         return rv;
720 }
721
722 #endif
723 /**********************************************************************/
724 #ifdef L_putgrent
725
726 int putgrent(const struct group *__restrict p, FILE *__restrict f)
727 {
728         static const char format[] = ",%s";
729         char **m;
730         const char *fmt;
731         int rv = -1;
732
733         if (!p || !f) {                         /* Sigh... glibc checks. */
734                 errno=EINVAL;
735         } else {
736                 if (fprintf(f, "%s:%s:%lu:",
737                                         p->gr_name, p->gr_passwd,
738                                         (unsigned long)(p->gr_gid)) >= 0
739                         ) {
740
741                         fmt = format + 1;
742
743                         assert(p->gr_mem);
744                         m = p->gr_mem;
745
746                         do {
747                                 if (!*m) {
748                                         if (fputc_unlocked('\n', f) >= 0) {
749                                                 rv = 0;
750                                         }
751                                         break;
752                                 }
753                                 if (fprintf(f, fmt, *m) < 0) {
754                                         break;
755                                 }
756                                 ++m;
757                                 fmt = format;
758                         } while (1);
759
760                 }
761
762         }
763
764         return rv;
765 }
766
767 #endif
768 /**********************************************************************/
769 #ifdef L_putspent
770
771 static const unsigned char sp_off[] = {
772         offsetof(struct spwd, sp_lstchg),       /* 2 - not a char ptr */
773         offsetof(struct spwd, sp_min),          /* 3 - not a char ptr */
774         offsetof(struct spwd, sp_max),          /* 4 - not a char ptr */
775         offsetof(struct spwd, sp_warn),         /* 5 - not a char ptr */
776         offsetof(struct spwd, sp_inact),        /* 6 - not a char ptr */
777         offsetof(struct spwd, sp_expire),       /* 7 - not a char ptr */
778 };
779
780 int putspent(const struct spwd *p, FILE *stream)
781 {
782         static const char ld_format[] = "%ld:";
783         const char *f;
784         long int x;
785         int i;
786         int rv = -1;
787
788         /* Unlike putpwent and putgrent, glibc does not check the args. */
789         if (fprintf(stream, "%s:%s:", p->sp_namp,
790                                 (p->sp_pwdp ? p->sp_pwdp : "")) < 0
791                 ) {
792                 goto DO_UNLOCK;
793         }
794
795         for (i=0 ; i < sizeof(sp_off) ; i++) {
796                 f = ld_format;
797                 if ((x = *(const long int *)(((const char *) p) + sp_off[i])) == -1) {
798                         f += 3;
799                 }
800                 if (fprintf(stream, f, x) < 0) {
801                         goto DO_UNLOCK;
802                 }
803         }
804
805         if ((p->sp_flag != ~0UL) && (fprintf(stream, "%lu", p->sp_flag) < 0)) {
806                 goto DO_UNLOCK;
807         }
808
809         if (fputc_unlocked('\n', stream) > 0) {
810                 rv = 0;
811         }
812
813 DO_UNLOCK:
814         return rv;
815 }
816
817 #endif
818 /**********************************************************************/
819 /* Internal uClibc functions.                                         */
820 /**********************************************************************/
821 #ifdef L___parsepwent
822
823 static const unsigned char pw_off[] = {
824         offsetof(struct passwd, pw_name),       /* 0 */
825         offsetof(struct passwd, pw_passwd),     /* 1 */
826         offsetof(struct passwd, pw_uid),        /* 2 - not a char ptr */
827         offsetof(struct passwd, pw_gid),        /* 3 - not a char ptr */
828         offsetof(struct passwd, pw_gecos),      /* 4 */
829         offsetof(struct passwd, pw_dir),        /* 5 */
830         offsetof(struct passwd, pw_shell)       /* 6 */
831 };
832
833 int __parsepwent(void *data, char *line)
834 {
835         char *endptr;
836         char *p;
837         int i;
838
839         i = 0;
840         do {
841                 p = ((char *) ((struct passwd *) data)) + pw_off[i];
842
843                 if ((i & 6) ^ 2) {      /* i!=2 and i!=3 */
844                         *((char **) p) = line;
845                         if (i==6) {
846                                 return 0;
847                         }
848                         /* NOTE: glibc difference - glibc allows omission of
849                          * ':' seperators after the gid field if all remaining
850                          * entries are empty.  We require all separators. */
851                         if (!(line = strchr(line, ':'))) {
852                                 break;
853                         }
854                 } else {
855                         unsigned long t = strtoul(line, &endptr, 10);
856                         /* Make sure we had at least one digit, and that the
857                          * failing char is the next field seperator ':'.  See
858                          * glibc difference note above. */
859                         /* TODO: Also check for leading whitespace? */
860                         if ((endptr == line) || (*endptr != ':')) {
861                                 break;
862                         }
863                         line = endptr;
864                         if (i & 1) {            /* i == 3 -- gid */
865                                 *((gid_t *) p) = t;
866                         } else {                        /* i == 2 -- uid */
867                                 *((uid_t *) p) = t;
868                         }
869                 }
870
871                 *line++ = 0;
872                 ++i;
873         } while (1);
874
875         return -1;
876 }
877
878 #endif
879 /**********************************************************************/
880 #ifdef L___parsegrent
881
882 static const unsigned char gr_off[] = {
883         offsetof(struct group, gr_name),        /* 0 */
884         offsetof(struct group, gr_passwd),      /* 1 */
885         offsetof(struct group, gr_gid)          /* 2 - not a char ptr */
886 };
887
888 int __parsegrent(void *data, char *line)
889 {
890         char *endptr;
891         char *p;
892         int i;
893         char **members;
894         char *end_of_buf;
895
896         end_of_buf = ((struct group *) data)->gr_name; /* Evil hack! */
897         i = 0;
898         do {
899                 p = ((char *) ((struct group *) data)) + gr_off[i];
900
901                 if (i < 2) {
902                         *((char **) p) = line;
903                         if (!(line = strchr(line, ':'))) {
904                                 break;
905                         }
906                         *line++ = 0;
907                         ++i;
908                 } else {
909                         *((gid_t *) p) = strtoul(line, &endptr, 10);
910
911                         /* NOTE: glibc difference - glibc allows omission of the
912                          * trailing colon when there is no member list.  We treat
913                          * this as an error. */
914
915                         /* Make sure we had at least one digit, and that the
916                          * failing char is the next field seperator ':'.  See
917                          * glibc difference note above. */
918                         if ((endptr == line) || (*endptr != ':')) {
919                                 break;
920                         }
921
922                         i = 1;                          /* Count terminating NULL ptr. */
923                         p = endptr;
924
925                         if (p[1]) { /* We have a member list to process. */
926                                 /* Overwrite the last ':' with a ',' before counting.
927                                  * This allows us to test for initial ',' and adds
928                                  * one ',' so that the ',' count equals the member
929                                  * count. */
930                                 *p = ',';
931                                 do {
932                                         /* NOTE: glibc difference - glibc allows and trims leading
933                                          * (but not trailing) space.  We treat this as an error. */
934                                         /* NOTE: glibc difference - glibc allows consecutive and
935                                          * trailing commas, and ignores "empty string" users.  We
936                                          * treat this as an error. */
937                                         if (*p == ',') {
938                                                 ++i;
939                                                 *p = 0; /* nul-terminate each member string. */
940                                                 if (!*++p || (*p == ',') || isspace(*p)) {
941                                                         goto ERR;
942                                                 }
943                                         }
944                                 } while (*++p);
945                         }
946
947                         /* Now align (p+1), rounding up. */
948                         /* Assumes sizeof(char **) is a power of 2. */
949                         members = (char **)( (((intptr_t) p) + sizeof(char **))
950                                                                  & ~((intptr_t)(sizeof(char **) - 1)) );
951
952                         if (((char *)(members + i)) > end_of_buf) {     /* No space. */
953                                 break;
954                         }
955
956                         ((struct group *) data)->gr_mem = members;
957
958                         if (--i) {
959                                 p = endptr;     /* Pointing to char prior to first member. */
960                                 do {
961                                         *members++ = ++p;
962                                         if (!--i) break;
963                                         while (*++p) {}
964                                 } while (1);
965                         }                               
966                         *members = NULL;
967
968                         return 0;
969                 }
970         } while (1);
971
972  ERR:
973         return -1;
974 }
975
976 #endif
977 /**********************************************************************/
978 #ifdef L___parsespent
979
980 static const unsigned char sp_off[] = {
981         offsetof(struct spwd, sp_namp),         /* 0 */
982         offsetof(struct spwd, sp_pwdp),         /* 1 */
983         offsetof(struct spwd, sp_lstchg),       /* 2 - not a char ptr */
984         offsetof(struct spwd, sp_min),          /* 3 - not a char ptr */
985         offsetof(struct spwd, sp_max),          /* 4 - not a char ptr */
986         offsetof(struct spwd, sp_warn),         /* 5 - not a char ptr */
987         offsetof(struct spwd, sp_inact),        /* 6 - not a char ptr */
988         offsetof(struct spwd, sp_expire),       /* 7 - not a char ptr */
989         offsetof(struct spwd, sp_flag)          /* 8 - not a char ptr */
990 };
991
992 int __parsespent(void *data, char * line)
993 {
994         char *endptr;
995         char *p;
996         int i;
997
998         i = 0;
999         do {
1000                 p = ((char *) ((struct spwd *) data)) + sp_off[i];
1001                 if (i < 2) {
1002                         *((char **) p) = line;
1003                         if (!(line = strchr(line, ':'))) {
1004                                 break;
1005                         }
1006                 } else {
1007 #if 0
1008                         if (i==5) {                     /* Support for old format. */
1009                                 while (isspace(*line)) ++line; /* glibc eats space here. */
1010                                 if (!*line) {
1011                                         ((struct spwd *) data)->sp_warn = -1;
1012                                         ((struct spwd *) data)->sp_inact = -1;
1013                                         ((struct spwd *) data)->sp_expire = -1;
1014                                         ((struct spwd *) data)->sp_flag = ~0UL;
1015                                         return 0;
1016                                 }
1017                         }
1018 #endif
1019
1020                         *((long *) p) = (long) strtoul(line, &endptr, 10);
1021
1022                         if (endptr == line) {
1023                                 *((long *) p) = ((i != 8) ? -1L : ((long)(~0UL)));
1024                         }
1025
1026                         line = endptr;
1027
1028                         if (i == 8) {
1029                                 if (!*endptr) {
1030                                         return 0;
1031                                 }
1032                                 break;
1033                         }
1034
1035                         if (*endptr != ':') {
1036                                 break;
1037                         }
1038
1039                 }
1040
1041                 *line++ = 0;
1042                 ++i;
1043         } while (1);
1044
1045         return EINVAL;
1046 }
1047
1048 #endif
1049 /**********************************************************************/
1050 #ifdef L___pgsreader
1051
1052 /* Reads until if EOF, or until if finds a line which fits in the buffer
1053  * and for which the parser function succeeds.
1054  *
1055  * Returns 0 on success and ENOENT for end-of-file (glibc concession).
1056  */
1057
1058 int __pgsreader(int (*__parserfunc)(void *d, char *line), void *data,
1059                                 char *__restrict line_buff, size_t buflen, FILE *f)
1060 {
1061         int line_len;
1062         int skip;
1063         int rv = ERANGE;
1064
1065         if (buflen < PWD_BUFFER_SIZE) {
1066                 errno=rv;
1067         } else {
1068                 skip = 0;
1069                 do {
1070                         if (!fgets_unlocked(line_buff, buflen, f)) {
1071                                 if (feof_unlocked(f)) {
1072                                         rv = ENOENT;
1073                                 }
1074                                 break;
1075                         }
1076
1077                         line_len = strlen(line_buff) - 1; /* strlen() must be > 0. */
1078                         if (line_buff[line_len] == '\n') {
1079                                 line_buff[line_len] = 0;
1080                         } else if (line_len + 2 == buflen) { /* line too long */
1081                                 ++skip;
1082                                 continue;
1083                         }
1084
1085                         if (skip) {
1086                                 --skip;
1087                                 continue;
1088                         }
1089
1090                         /* NOTE: glibc difference - glibc strips leading whitespace from
1091                          * records.  We do not allow leading whitespace. */
1092
1093                         /* Skip empty lines, comment lines, and lines with leading
1094                          * whitespace. */
1095                         if (*line_buff && (*line_buff != '#') && !isspace(*line_buff)) {
1096                                 if (__parserfunc == __parsegrent) {     /* Do evil group hack. */
1097                                         /* The group entry parsing function needs to know where
1098                                          * the end of the buffer is so that it can construct the
1099                                          * group member ptr table. */
1100                                         ((struct group *) data)->gr_name = line_buff + buflen;
1101                                 }
1102
1103                                 if (!__parserfunc(data, line_buff)) {
1104                                         rv = 0;
1105                                         break;
1106                                 }
1107                         }
1108                 } while (1);
1109
1110         }
1111
1112         return rv;
1113 }
1114
1115 #endif
1116 /**********************************************************************/