1 /* userspec.c -- Parse a user and group string.
2 Copyright (C) 1989-1992, 1997-1998, 2000, 2002-2004 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */
19 /* Written by David MacKenzie <djm@gnu.ai.mit.edu>. */
30 #include <sys/types.h>
35 # include <sys/param.h>
52 #define _(msgid) gettext (msgid)
53 #define N_(msgid) msgid
55 #ifndef _POSIX_VERSION
56 struct passwd *getpwnam ();
57 struct group *getgrnam ();
58 struct group *getgrgid ();
62 # define endgrent() ((void) 0)
66 # define endpwent() ((void) 0)
69 /* The extra casts work around common compiler bugs. */
70 #define TYPE_SIGNED(t) (! ((t) 0 < (t) -1))
71 /* The outer cast is needed to work around a bug in Cray C 5.0.3.0.
72 It is necessary at least when t == time_t. */
73 #define TYPE_MINIMUM(t) ((t) (TYPE_SIGNED (t) \
74 ? ~ (t) 0 << (sizeof (t) * CHAR_BIT - 1) : (t) 0))
75 #define TYPE_MAXIMUM(t) ((t) (~ (t) 0 - TYPE_MINIMUM (t)))
78 # define UID_T_MAX TYPE_MAXIMUM (uid_t)
82 # define GID_T_MAX TYPE_MAXIMUM (gid_t)
85 /* MAXUID may come from limits.h or sys/params.h. */
87 # define MAXUID UID_T_MAX
90 # define MAXGID GID_T_MAX
93 /* ISDIGIT differs from isdigit, as follows:
94 - Its arg may be any int or unsigned int; it need not be an unsigned char.
95 - It's guaranteed to evaluate its argument exactly once.
96 - It's typically faster.
97 POSIX says that only '0' through '9' are digits. Prefer ISDIGIT to
98 ISDIGIT_LOCALE unless it's important to use the locale's definition
99 of `digit' even when the host does not conform to POSIX. */
100 #define ISDIGIT(c) ((unsigned int) (c) - '0' <= 9)
104 /* Return true if STR represents an unsigned decimal integer. */
107 is_number (const char *str)
121 parse_with_separator (char const *spec, char const *separator,
122 uid_t *uid, gid_t *gid,
123 char **username, char **groupname)
125 static const char *E_invalid_user = N_("invalid user");
126 static const char *E_invalid_group = N_("invalid group");
127 static const char *E_bad_spec =
128 N_("cannot get the login group of a numeric UID");
130 const char *error_msg;
140 *username = *groupname = NULL;
142 /* Set U and G to nonzero length strings corresponding to user and
143 group specifiers or to NULL. If U is not NULL, it is a newly
147 if (separator == NULL)
154 size_t ulen = separator - spec;
157 u = xmemdup (spec, ulen + 1);
162 g = (separator == NULL || *(separator + 1) == '\0'
167 /* Pretend that we are the user U whose group is G. This makes
168 pwd and grp functions ``know'' about the UID and GID of these. */
169 if (u && !is_number (u))
170 setenv ("USER", u, 1);
171 if (g && !is_number (g))
172 setenv ("GROUP", g, 1);
180 bool use_login_group = (separator != NULL && g == NULL);
182 error_msg = E_bad_spec;
185 unsigned long int tmp;
186 if (xstrtoul (u, NULL, 10, &tmp, "") == LONGINT_OK
190 error_msg = E_invalid_user;
196 if (g == NULL && separator != NULL)
198 /* A separator was given, but a group was not specified,
199 so get the login group. */
200 char buf[INT_BUFSIZE_BOUND (uintmax_t)];
202 grp = getgrgid (gnum);
203 gname = xstrdup (grp ? grp->gr_name : umaxtostr (gnum, buf));
210 if (g != NULL && error_msg == NULL)
212 /* Explicit group. */
216 unsigned long int tmp;
217 if (xstrtoul (g, NULL, 10, &tmp, "") == LONGINT_OK && tmp <= MAXGID)
220 error_msg = E_invalid_group;
224 endgrent (); /* Save a file descriptor. */
228 if (error_msg == NULL)
243 /* Extract from SPEC, which has the form "[user][:.][group]",
244 a USERNAME, UID U, GROUPNAME, and GID G.
245 Either user or group, or both, must be present.
246 If the group is omitted but the separator is given,
247 use the given user's login group.
248 If SPEC contains a `:', then use that as the separator, ignoring
249 any `.'s. If there is no `:', but there is a `.', then first look
250 up the entire SPEC as a login name. If that look-up fails, then
251 try again interpreting the `.' as a separator.
253 USERNAME and GROUPNAME will be in newly malloc'd memory.
254 Either one might be NULL instead, indicating that it was not
255 given and the corresponding numeric ID was left unchanged.
257 Return NULL if successful, a static error message string if not. */
260 parse_user_spec (char const *spec, uid_t *uid, gid_t *gid,
261 char **username, char **groupname)
263 char const *colon = strchr (spec, ':');
264 char const *error_msg =
265 parse_with_separator (spec, colon, uid, gid, username, groupname);
267 if (!colon && error_msg)
269 /* If there's no colon but there is a dot, and if looking up the
270 whole spec failed (i.e., the spec is not a owner name that
271 includes a dot), then try again, but interpret the dot as a
272 separator. This is a compatible extension to POSIX, since
273 the POSIX-required behavior is always tried first. */
275 char const *dot = strchr (spec, '.');
277 && ! parse_with_separator (spec, dot, uid, gid, username, groupname))
286 # define NULL_CHECK(s) ((s) == NULL ? "(null)" : (s))
289 main (int argc, char **argv)
293 for (i = 1; i < argc; i++)
296 char *username, *groupname;
301 tmp = strdup (argv[i]);
302 e = parse_user_spec (tmp, &uid, &gid, &username, &groupname);
304 printf ("%s: %lu %lu %s %s %s\n",
306 (unsigned long int) uid,
307 (unsigned long int) gid,
308 NULL_CHECK (username),
309 NULL_CHECK (groupname),