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>. */
30 #include <sys/types.h>
35 # include <sys/param.h>
51 #define _(msgid) gettext (msgid)
52 #define N_(msgid) msgid
55 # define endgrent() ((void) 0)
59 # define endpwent() ((void) 0)
63 # define UID_T_MAX TYPE_MAXIMUM (uid_t)
67 # define GID_T_MAX TYPE_MAXIMUM (gid_t)
70 /* MAXUID may come from limits.h or sys/params.h. */
72 # define MAXUID UID_T_MAX
75 # define MAXGID GID_T_MAX
78 /* ISDIGIT differs from isdigit, as follows:
79 - Its arg may be any int or unsigned int; it need not be an unsigned char
81 - It's typically faster.
82 POSIX says that only '0' through '9' are digits. Prefer ISDIGIT to
83 isdigit unless it's important to use the locale's definition
84 of `digit' even when the host does not conform to POSIX. */
85 #define ISDIGIT(c) ((unsigned int) (c) - '0' <= 9)
89 /* Return true if STR represents an unsigned decimal integer. */
92 is_number (const char *str)
106 parse_with_separator (char const *spec, char const *separator,
107 uid_t *uid, gid_t *gid,
108 char **username, char **groupname)
110 static const char *E_invalid_user = N_("invalid user");
111 static const char *E_invalid_group = N_("invalid group");
112 static const char *E_bad_spec = N_("invalid spec");
114 const char *error_msg;
124 *username = *groupname = NULL;
126 /* Set U and G to nonzero length strings corresponding to user and
127 group specifiers or to NULL. If U is not NULL, it is a newly
131 if (separator == NULL)
138 size_t ulen = separator - spec;
141 u = xmemdup (spec, ulen + 1);
146 g = (separator == NULL || *(separator + 1) == '\0'
151 /* Pretend that we are the user U whose group is G. This makes
152 pwd and grp functions ``know'' about the UID and GID of these. */
153 if (u && !is_number (u))
154 setenv ("USER", u, 1);
155 if (g && !is_number (g))
156 setenv ("GROUP", g, 1);
164 bool use_login_group = (separator != NULL && g == NULL);
167 /* If there is no group,
168 then there may not be a trailing ":", either. */
169 error_msg = E_bad_spec;
173 unsigned long int tmp;
174 if (xstrtoul (u, NULL, 10, &tmp, "") == LONGINT_OK
178 error_msg = E_invalid_user;
184 if (g == NULL && separator != NULL)
186 /* A separator was given, but a group was not specified,
187 so get the login group. */
188 char buf[INT_BUFSIZE_BOUND (uintmax_t)];
190 grp = getgrgid (gnum);
191 gname = xstrdup (grp ? grp->gr_name : umaxtostr (gnum, buf));
198 if (g != NULL && error_msg == NULL)
200 /* Explicit group. */
204 unsigned long int tmp;
205 if (xstrtoul (g, NULL, 10, &tmp, "") == LONGINT_OK && tmp <= MAXGID)
208 error_msg = E_invalid_group;
212 endgrent (); /* Save a file descriptor. */
216 if (error_msg == NULL)
231 /* Extract from SPEC, which has the form "[user][:.][group]",
232 a USERNAME, UID U, GROUPNAME, and GID G.
233 Either user or group, or both, must be present.
234 If the group is omitted but the separator is given,
235 use the given user's login group.
236 If SPEC contains a `:', then use that as the separator, ignoring
237 any `.'s. If there is no `:', but there is a `.', then first look
238 up the entire SPEC as a login name. If that look-up fails, then
239 try again interpreting the `.' as a separator.
241 USERNAME and GROUPNAME will be in newly malloc'd memory.
242 Either one might be NULL instead, indicating that it was not
243 given and the corresponding numeric ID was left unchanged.
245 Return NULL if successful, a static error message string if not. */
248 parse_user_spec (char const *spec, uid_t *uid, gid_t *gid,
249 char **username, char **groupname)
251 char const *colon = strchr (spec, ':');
252 char const *error_msg =
253 parse_with_separator (spec, colon, uid, gid, username, groupname);
255 if (!colon && error_msg)
257 /* If there's no colon but there is a dot, and if looking up the
258 whole spec failed (i.e., the spec is not a owner name that
259 includes a dot), then try again, but interpret the dot as a
260 separator. This is a compatible extension to POSIX, since
261 the POSIX-required behavior is always tried first. */
263 char const *dot = strchr (spec, '.');
265 && ! parse_with_separator (spec, dot, uid, gid, username, groupname))
274 # define NULL_CHECK(s) ((s) == NULL ? "(null)" : (s))
277 main (int argc, char **argv)
281 for (i = 1; i < argc; i++)
284 char *username, *groupname;
289 tmp = strdup (argv[i]);
290 e = parse_user_spec (tmp, &uid, &gid, &username, &groupname);
292 printf ("%s: %lu %lu %s %s %s\n",
294 (unsigned long int) uid,
295 (unsigned long int) gid,
296 NULL_CHECK (username),
297 NULL_CHECK (groupname),