1 /* userspec.c -- Parse a user and group string.
2 Copyright (C) 1989-1992, 1997-1998, 2000, 2002-2003 Free Software Foundation, Inc.
4 This program is free software; you can redistribute it and/or modify
5 it under the terms of the GNU General Public License as published by
6 the Free Software Foundation; either version 2, or (at your option)
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 GNU General Public License for more details.
14 You should have received a copy of the GNU General Public License
15 along with this program; if not, write to the Free Software Foundation,
16 Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */
18 /* Written by David MacKenzie <djm@gnu.ai.mit.edu>. */
27 #include <sys/types.h>
32 # include <sys/param.h>
48 #define _(msgid) gettext (msgid)
49 #define N_(msgid) msgid
51 #ifndef _POSIX_VERSION
52 struct passwd *getpwnam ();
53 struct group *getgrnam ();
54 struct group *getgrgid ();
58 # define endgrent() ((void) 0)
62 # define endpwent() ((void) 0)
65 /* The extra casts work around common compiler bugs. */
66 #define TYPE_SIGNED(t) (! ((t) 0 < (t) -1))
67 /* The outer cast is needed to work around a bug in Cray C 5.0.3.0.
68 It is necessary at least when t == time_t. */
69 #define TYPE_MINIMUM(t) ((t) (TYPE_SIGNED (t) \
70 ? ~ (t) 0 << (sizeof (t) * CHAR_BIT - 1) : (t) 0))
71 #define TYPE_MAXIMUM(t) ((t) (~ (t) 0 - TYPE_MINIMUM (t)))
74 # define UID_T_MAX TYPE_MAXIMUM (uid_t)
78 # define GID_T_MAX TYPE_MAXIMUM (gid_t)
81 /* MAXUID may come from limits.h or sys/params.h. */
83 # define MAXUID UID_T_MAX
86 # define MAXGID GID_T_MAX
89 /* Perform the equivalent of the statement `dest = strdup (src);',
90 but obtaining storage via alloca instead of from the heap. */
92 #define V_STRDUP(dest, src) \
95 int _len = strlen ((src)); \
96 (dest) = (char *) alloca (_len + 1); \
101 /* ISDIGIT differs from isdigit, as follows:
102 - Its arg may be any int or unsigned int; it need not be an unsigned char.
103 - It's guaranteed to evaluate its argument exactly once.
104 - It's typically faster.
105 POSIX says that only '0' through '9' are digits. Prefer ISDIGIT to
106 ISDIGIT_LOCALE unless it's important to use the locale's definition
107 of `digit' even when the host does not conform to POSIX. */
108 #define ISDIGIT(c) ((unsigned) (c) - '0' <= 9)
114 /* Return nonzero if STR represents an unsigned decimal integer,
115 otherwise return 0. */
118 is_number (const char *str)
126 /* Extract from NAME, which has the form "[user][:.][group]",
127 a USERNAME, UID U, GROUPNAME, and GID G.
128 Either user or group, or both, must be present.
129 If the group is omitted but the ":" separator is given,
130 use the given user's login group.
131 If SPEC_ARG contains a `:', then use that as the separator, ignoring
132 any `.'s. If there is no `:', but there is a `.', then first look
133 up the entire SPEC_ARG as a login name. If that look-up fails, then
134 try again interpreting the `.' as a separator.
136 USERNAME and GROUPNAME will be in newly malloc'd memory.
137 Either one might be NULL instead, indicating that it was not
138 given and the corresponding numeric ID was left unchanged.
140 Return NULL if successful, a static error message string if not. */
143 parse_user_spec (const char *spec_arg, uid_t *uid, gid_t *gid,
144 char **username_arg, char **groupname_arg)
146 static const char *E_invalid_user = N_("invalid user");
147 static const char *E_invalid_group = N_("invalid group");
148 static const char *E_bad_spec =
149 N_("cannot get the login group of a numeric UID");
150 static const char *E_cannot_omit_both =
151 N_("cannot omit both user and group");
153 const char *error_msg;
154 char *spec; /* A copy we can write on. */
157 char *g, *u, *separator;
163 *username_arg = *groupname_arg = NULL;
166 V_STRDUP (spec, spec_arg);
168 /* Find the POSIX `:' separator if there is one. */
169 separator = strchr (spec, ':');
171 /* If there is no colon, then see if there's a `.'. */
172 if (separator == NULL && posix2_version () < 200112)
174 dot = strchr (spec, '.');
175 /* If there's no colon but there is a `.', then first look up the
176 whole spec, in case it's an OWNER name that includes a dot.
177 If that fails, then we'll try again, but interpreting the `.'
179 /* FIXME: accepting `.' as the separator is contrary to POSIX.
180 someday we should drop support for this. */
187 /* Replace separator with a NUL. */
188 if (separator != NULL)
191 /* Set U and G to non-zero length strings corresponding to user and
192 group specifiers or to NULL. */
193 u = (*spec == '\0' ? NULL : spec);
195 g = (separator == NULL || *(separator + 1) == '\0'
199 if (u == NULL && g == NULL)
200 return _(E_cannot_omit_both);
203 /* Pretend that we are the user U whose group is G. This makes
204 pwd and grp functions ``know'' about the UID and GID of these. */
205 if (u && !is_number (u))
206 setenv ("USER", u, 1);
207 if (g && !is_number (g))
208 setenv ("GROUP", g, 1);
218 error_msg = E_invalid_user;
222 use_login_group = (separator != NULL && g == NULL);
224 error_msg = E_bad_spec;
227 unsigned long int tmp_long;
228 if (xstrtoul (u, NULL, 0, &tmp_long, NULL) != LONGINT_OK
229 || tmp_long > MAXUID)
230 return _(E_invalid_user);
238 if (g == NULL && separator != NULL)
240 /* A separator was given, but a group was not specified,
241 so get the login group. */
243 grp = getgrgid (pwd->pw_gid);
246 /* This is enough room to hold the unsigned decimal
247 representation of any 32-bit quantity and the trailing
250 sprintf (uint_buf, "%u", (unsigned) (pwd->pw_gid));
251 V_STRDUP (groupname, uint_buf);
255 V_STRDUP (groupname, grp->gr_name);
263 if (g != NULL && error_msg == NULL)
265 /* Explicit group. */
270 error_msg = E_invalid_group;
273 unsigned long int tmp_long;
274 if (xstrtoul (g, NULL, 0, &tmp_long, NULL) != LONGINT_OK
275 || tmp_long > MAXGID)
276 return _(E_invalid_group);
282 endgrent (); /* Save a file descriptor. */
284 if (error_msg == NULL)
285 V_STRDUP (groupname, g);
288 if (error_msg == NULL)
292 *username_arg = strdup (u);
293 if (*username_arg == NULL)
294 error_msg = xalloc_msg_memory_exhausted;
297 if (groupname != NULL && error_msg == NULL)
299 *groupname_arg = strdup (groupname);
300 if (*groupname_arg == NULL)
302 if (*username_arg != NULL)
304 free (*username_arg);
305 *username_arg = NULL;
307 error_msg = xalloc_msg_memory_exhausted;
312 if (error_msg && maybe_retry)
325 # define NULL_CHECK(s) ((s) == NULL ? "(null)" : (s))
328 main (int argc, char **argv)
332 for (i = 1; i < argc; i++)
335 char *username, *groupname;
340 tmp = strdup (argv[i]);
341 e = parse_user_spec (tmp, &uid, &gid, &username, &groupname);
343 printf ("%s: %u %u %s %s %s\n",
347 NULL_CHECK (username),
348 NULL_CHECK (groupname),