1 /* userspec.c -- Parse a user and group string.
2 Copyright (C) 1989-1992, 1997-1998, 2000, 2002-2006 Free Software
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)
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.
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. */
19 /* Written by David MacKenzie <djm@gnu.ai.mit.edu>. */
28 #include <sys/types.h>
33 # include <sys/param.h>
49 #define _(msgid) gettext (msgid)
50 #define N_(msgid) msgid
53 # define endgrent() ((void) 0)
57 # define endpwent() ((void) 0)
61 # define UID_T_MAX TYPE_MAXIMUM (uid_t)
65 # define GID_T_MAX TYPE_MAXIMUM (gid_t)
68 /* MAXUID may come from limits.h or sys/params.h. */
70 # define MAXUID UID_T_MAX
73 # define MAXGID GID_T_MAX
76 /* ISDIGIT differs from isdigit, as follows:
77 - Its arg may be any int or unsigned int; it need not be an unsigned char
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)
87 /* Return true if STR represents an unsigned decimal integer. */
90 is_number (const char *str)
104 parse_with_separator (char const *spec, char const *separator,
105 uid_t *uid, gid_t *gid,
106 char **username, char **groupname)
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");
112 const char *error_msg;
122 *username = *groupname = NULL;
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
129 if (separator == NULL)
136 size_t ulen = separator - spec;
139 u = xmemdup (spec, ulen + 1);
144 g = (separator == NULL || *(separator + 1) == '\0'
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);
162 bool use_login_group = (separator != NULL && g == NULL);
165 /* If there is no group,
166 then there may not be a trailing ":", either. */
167 error_msg = E_bad_spec;
171 unsigned long int tmp;
172 if (xstrtoul (u, NULL, 10, &tmp, "") == LONGINT_OK
176 error_msg = E_invalid_user;
182 if (g == NULL && separator != NULL)
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)];
188 grp = getgrgid (gnum);
189 gname = xstrdup (grp ? grp->gr_name : umaxtostr (gnum, buf));
196 if (g != NULL && error_msg == NULL)
198 /* Explicit group. */
202 unsigned long int tmp;
203 if (xstrtoul (g, NULL, 10, &tmp, "") == LONGINT_OK && tmp <= MAXGID)
206 error_msg = E_invalid_group;
210 endgrent (); /* Save a file descriptor. */
214 if (error_msg == NULL)
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.
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.
243 Return NULL if successful, a static error message string if not. */
246 parse_user_spec (char const *spec, uid_t *uid, gid_t *gid,
247 char **username, char **groupname)
249 char const *colon = strchr (spec, ':');
250 char const *error_msg =
251 parse_with_separator (spec, colon, uid, gid, username, groupname);
253 if (!colon && error_msg)
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. */
261 char const *dot = strchr (spec, '.');
263 && ! parse_with_separator (spec, dot, uid, gid, username, groupname))
272 # define NULL_CHECK(s) ((s) == NULL ? "(null)" : (s))
275 main (int argc, char **argv)
279 for (i = 1; i < argc; i++)
282 char *username, *groupname;
287 tmp = strdup (argv[i]);
288 e = parse_user_spec (tmp, &uid, &gid, &username, &groupname);
290 printf ("%s: %lu %lu %s %s %s\n",
292 (unsigned long int) uid,
293 (unsigned long int) gid,
294 NULL_CHECK (username),
295 NULL_CHECK (groupname),