* _fpending.c: Include <config.h> unconditionally, since we no
[gnulib.git] / lib / userspec.c
1 /* userspec.c -- Parse a user and group string.
2    Copyright (C) 1989-1992, 1997-1998, 2000, 2002-2006 Free Software
3    Foundation, Inc.
4
5    This program is free software; you can redistribute it and/or modify
6    it under the terms of the GNU General Public License as published by
7    the Free Software Foundation; either version 2, or (at your option)
8    any later version.
9
10    This program 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
13    GNU General Public License for more details.
14
15    You should have received a copy of the GNU General Public License
16    along with this program; if not, write to the Free Software Foundation,
17    Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.  */
18
19 /* Written by David MacKenzie <djm@gnu.ai.mit.edu>.  */
20
21 #include <config.h>
22
23 /* Specification.  */
24 #include "userspec.h"
25
26 #include <stdbool.h>
27 #include <stdio.h>
28 #include <sys/types.h>
29 #include <pwd.h>
30 #include <grp.h>
31
32 #if HAVE_SYS_PARAM_H
33 # include <sys/param.h>
34 #endif
35
36 #include <limits.h>
37 #include <stdlib.h>
38 #include <string.h>
39
40 #include <unistd.h>
41
42 #include "intprops.h"
43 #include "inttostr.h"
44 #include "strdup.h"
45 #include "xalloc.h"
46 #include "xstrtol.h"
47
48 #include "gettext.h"
49 #define _(msgid) gettext (msgid)
50 #define N_(msgid) msgid
51
52 #ifndef HAVE_ENDGRENT
53 # define endgrent() ((void) 0)
54 #endif
55
56 #ifndef HAVE_ENDPWENT
57 # define endpwent() ((void) 0)
58 #endif
59
60 #ifndef UID_T_MAX
61 # define UID_T_MAX TYPE_MAXIMUM (uid_t)
62 #endif
63
64 #ifndef GID_T_MAX
65 # define GID_T_MAX TYPE_MAXIMUM (gid_t)
66 #endif
67
68 /* MAXUID may come from limits.h or sys/params.h.  */
69 #ifndef MAXUID
70 # define MAXUID UID_T_MAX
71 #endif
72 #ifndef MAXGID
73 # define MAXGID GID_T_MAX
74 #endif
75
76 /* ISDIGIT differs from isdigit, as follows:
77    - Its arg may be any int or unsigned int; it need not be an unsigned char
78      or EOF.
79    - It's typically faster.
80    POSIX says that only '0' through '9' are digits.  Prefer ISDIGIT to
81    isdigit unless it's important to use the locale's definition
82    of `digit' even when the host does not conform to POSIX.  */
83 #define ISDIGIT(c) ((unsigned int) (c) - '0' <= 9)
84
85 #ifdef __DJGPP__
86
87 /* Return true if STR represents an unsigned decimal integer.  */
88
89 static bool
90 is_number (const char *str)
91 {
92   do
93     {
94       if (!ISDIGIT (*str))
95         return false;
96     }
97   while (*++str);
98
99   return true;
100 }
101 #endif
102
103 static char const *
104 parse_with_separator (char const *spec, char const *separator,
105                       uid_t *uid, gid_t *gid,
106                       char **username, char **groupname)
107 {
108   static const char *E_invalid_user = N_("invalid user");
109   static const char *E_invalid_group = N_("invalid group");
110   static const char *E_bad_spec = N_("invalid spec");
111
112   const char *error_msg;
113   struct passwd *pwd;
114   struct group *grp;
115   char *u;
116   char const *g;
117   char *gname = NULL;
118   uid_t unum = *uid;
119   gid_t gnum = *gid;
120
121   error_msg = NULL;
122   *username = *groupname = NULL;
123
124   /* Set U and G to nonzero length strings corresponding to user and
125      group specifiers or to NULL.  If U is not NULL, it is a newly
126      allocated string.  */
127
128   u = NULL;
129   if (separator == NULL)
130     {
131       if (*spec)
132         u = xstrdup (spec);
133     }
134   else
135     {
136       size_t ulen = separator - spec;
137       if (ulen != 0)
138         {
139           u = xmemdup (spec, ulen + 1);
140           u[ulen] = '\0';
141         }
142     }
143
144   g = (separator == NULL || *(separator + 1) == '\0'
145        ? NULL
146        : separator + 1);
147
148 #ifdef __DJGPP__
149   /* Pretend that we are the user U whose group is G.  This makes
150      pwd and grp functions ``know'' about the UID and GID of these.  */
151   if (u && !is_number (u))
152     setenv ("USER", u, 1);
153   if (g && !is_number (g))
154     setenv ("GROUP", g, 1);
155 #endif
156
157   if (u != NULL)
158     {
159       pwd = getpwnam (u);
160       if (pwd == NULL)
161         {
162           bool use_login_group = (separator != NULL && g == NULL);
163           if (use_login_group)
164             {
165               /* If there is no group,
166                  then there may not be a trailing ":", either.  */
167               error_msg = E_bad_spec;
168             }
169           else
170             {
171               unsigned long int tmp;
172               if (xstrtoul (u, NULL, 10, &tmp, "") == LONGINT_OK
173                   && tmp <= MAXUID)
174                 unum = tmp;
175               else
176                 error_msg = E_invalid_user;
177             }
178         }
179       else
180         {
181           unum = pwd->pw_uid;
182           if (g == NULL && separator != NULL)
183             {
184               /* A separator was given, but a group was not specified,
185                  so get the login group.  */
186               char buf[INT_BUFSIZE_BOUND (uintmax_t)];
187               gnum = pwd->pw_gid;
188               grp = getgrgid (gnum);
189               gname = xstrdup (grp ? grp->gr_name : umaxtostr (gnum, buf));
190               endgrent ();
191             }
192         }
193       endpwent ();
194     }
195
196   if (g != NULL && error_msg == NULL)
197     {
198       /* Explicit group.  */
199       grp = getgrnam (g);
200       if (grp == NULL)
201         {
202           unsigned long int tmp;
203           if (xstrtoul (g, NULL, 10, &tmp, "") == LONGINT_OK && tmp <= MAXGID)
204             gnum = tmp;
205           else
206             error_msg = E_invalid_group;
207         }
208       else
209         gnum = grp->gr_gid;
210       endgrent ();              /* Save a file descriptor.  */
211       gname = xstrdup (g);
212     }
213
214   if (error_msg == NULL)
215     {
216       *uid = unum;
217       *gid = gnum;
218       *username = u;
219       *groupname = gname;
220       u = NULL;
221     }
222   else
223     free (gname);
224
225   free (u);
226   return _(error_msg);
227 }
228
229 /* Extract from SPEC, which has the form "[user][:.][group]",
230    a USERNAME, UID U, GROUPNAME, and GID G.
231    Either user or group, or both, must be present.
232    If the group is omitted but the separator is given,
233    use the given user's login group.
234    If SPEC contains a `:', then use that as the separator, ignoring
235    any `.'s.  If there is no `:', but there is a `.', then first look
236    up the entire SPEC as a login name.  If that look-up fails, then
237    try again interpreting the `.'  as a separator.
238
239    USERNAME and GROUPNAME will be in newly malloc'd memory.
240    Either one might be NULL instead, indicating that it was not
241    given and the corresponding numeric ID was left unchanged.
242
243    Return NULL if successful, a static error message string if not.  */
244
245 char const *
246 parse_user_spec (char const *spec, uid_t *uid, gid_t *gid,
247                  char **username, char **groupname)
248 {
249   char const *colon = strchr (spec, ':');
250   char const *error_msg =
251     parse_with_separator (spec, colon, uid, gid, username, groupname);
252
253   if (!colon && error_msg)
254     {
255       /* If there's no colon but there is a dot, and if looking up the
256          whole spec failed (i.e., the spec is not a owner name that
257          includes a dot), then try again, but interpret the dot as a
258          separator.  This is a compatible extension to POSIX, since
259          the POSIX-required behavior is always tried first.  */
260
261       char const *dot = strchr (spec, '.');
262       if (dot
263           && ! parse_with_separator (spec, dot, uid, gid, username, groupname))
264         error_msg = NULL;
265     }
266
267   return error_msg;
268 }
269
270 #ifdef TEST
271
272 # define NULL_CHECK(s) ((s) == NULL ? "(null)" : (s))
273
274 int
275 main (int argc, char **argv)
276 {
277   int i;
278
279   for (i = 1; i < argc; i++)
280     {
281       const char *e;
282       char *username, *groupname;
283       uid_t uid;
284       gid_t gid;
285       char *tmp;
286
287       tmp = strdup (argv[i]);
288       e = parse_user_spec (tmp, &uid, &gid, &username, &groupname);
289       free (tmp);
290       printf ("%s: %lu %lu %s %s %s\n",
291               argv[i],
292               (unsigned long int) uid,
293               (unsigned long int) gid,
294               NULL_CHECK (username),
295               NULL_CHECK (groupname),
296               NULL_CHECK (e));
297     }
298
299   exit (0);
300 }
301
302 #endif