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>
60 #define _(msgid) gettext (msgid)
61 #define N_(msgid) msgid
63 #ifndef _POSIX_VERSION
64 struct passwd *getpwnam ();
65 struct group *getgrnam ();
66 struct group *getgrgid ();
70 # define endgrent() ((void) 0)
74 # define endpwent() ((void) 0)
81 /* The extra casts work around common compiler bugs. */
82 #define TYPE_SIGNED(t) (! ((t) 0 < (t) -1))
83 /* The outer cast is needed to work around a bug in Cray C 5.0.3.0.
84 It is necessary at least when t == time_t. */
85 #define TYPE_MINIMUM(t) ((t) (TYPE_SIGNED (t) \
86 ? ~ (t) 0 << (sizeof (t) * CHAR_BIT - 1) : (t) 0))
87 #define TYPE_MAXIMUM(t) ((t) (~ (t) 0 - TYPE_MINIMUM (t)))
90 # define UID_T_MAX TYPE_MAXIMUM (uid_t)
94 # define GID_T_MAX TYPE_MAXIMUM (gid_t)
97 /* MAXUID may come from limits.h or sys/params.h. */
99 # define MAXUID UID_T_MAX
102 # define MAXGID GID_T_MAX
105 /* Perform the equivalent of the statement `dest = strdup (src);',
106 but obtaining storage via alloca instead of from the heap. */
108 #define V_STRDUP(dest, src) \
111 int _len = strlen ((src)); \
112 (dest) = (char *) alloca (_len + 1); \
113 strcpy (dest, src); \
117 /* ISDIGIT differs from isdigit, as follows:
118 - Its arg may be any int or unsigned int; it need not be an unsigned char.
119 - It's guaranteed to evaluate its argument exactly once.
120 - It's typically faster.
121 POSIX says that only '0' through '9' are digits. Prefer ISDIGIT to
122 ISDIGIT_LOCALE unless it's important to use the locale's definition
123 of `digit' even when the host does not conform to POSIX. */
124 #define ISDIGIT(c) ((unsigned) (c) - '0' <= 9)
130 /* Return nonzero if STR represents an unsigned decimal integer,
131 otherwise return 0. */
134 is_number (const char *str)
142 /* Extract from NAME, which has the form "[user][:.][group]",
143 a USERNAME, UID U, GROUPNAME, and GID G.
144 Either user or group, or both, must be present.
145 If the group is omitted but the ":" separator is given,
146 use the given user's login group.
147 If SPEC_ARG contains a `:', then use that as the separator, ignoring
148 any `.'s. If there is no `:', but there is a `.', then first look
149 up the entire SPEC_ARG as a login name. If that look-up fails, then
150 try again interpreting the `.' as a separator.
152 USERNAME and GROUPNAME will be in newly malloc'd memory.
153 Either one might be NULL instead, indicating that it was not
154 given and the corresponding numeric ID was left unchanged.
156 Return NULL if successful, a static error message string if not. */
159 parse_user_spec (const char *spec_arg, uid_t *uid, gid_t *gid,
160 char **username_arg, char **groupname_arg)
162 static const char *E_invalid_user = N_("invalid user");
163 static const char *E_invalid_group = N_("invalid group");
164 static const char *E_bad_spec =
165 N_("cannot get the login group of a numeric UID");
166 static const char *E_cannot_omit_both =
167 N_("cannot omit both user and group");
169 const char *error_msg;
170 char *spec; /* A copy we can write on. */
173 char *g, *u, *separator;
179 *username_arg = *groupname_arg = NULL;
182 V_STRDUP (spec, spec_arg);
184 /* Find the POSIX `:' separator if there is one. */
185 separator = strchr (spec, ':');
187 /* If there is no colon, then see if there's a `.'. */
188 if (separator == NULL)
190 dot = strchr (spec, '.');
191 /* If there's no colon but there is a `.', then first look up the
192 whole spec, in case it's an OWNER name that includes a dot.
193 If that fails, then we'll try again, but interpreting the `.'
195 /* FIXME: accepting `.' as the separator is contrary to POSIX.
196 someday we should drop support for this. */
203 /* Replace separator with a NUL. */
204 if (separator != NULL)
207 /* Set U and G to non-zero length strings corresponding to user and
208 group specifiers or to NULL. */
209 u = (*spec == '\0' ? NULL : spec);
211 g = (separator == NULL || *(separator + 1) == '\0'
215 if (u == NULL && g == NULL)
216 return _(E_cannot_omit_both);
219 /* Pretend that we are the user U whose group is G. This makes
220 pwd and grp functions ``know'' about the UID and GID of these. */
221 if (u && !is_number (u))
222 setenv ("USER", u, 1);
223 if (g && !is_number (g))
224 setenv ("GROUP", g, 1);
234 error_msg = E_invalid_user;
238 use_login_group = (separator != NULL && g == NULL);
240 error_msg = E_bad_spec;
243 unsigned long int tmp_long;
244 if (xstrtoul (u, NULL, 0, &tmp_long, NULL) != LONGINT_OK
245 || tmp_long > MAXUID)
246 return _(E_invalid_user);
254 if (g == NULL && separator != NULL)
256 /* A separator was given, but a group was not specified,
257 so get the login group. */
259 grp = getgrgid (pwd->pw_gid);
262 /* This is enough room to hold the unsigned decimal
263 representation of any 32-bit quantity and the trailing
266 sprintf (uint_buf, "%u", (unsigned) (pwd->pw_gid));
267 V_STRDUP (groupname, uint_buf);
271 V_STRDUP (groupname, grp->gr_name);
279 if (g != NULL && error_msg == NULL)
281 /* Explicit group. */
286 error_msg = E_invalid_group;
289 unsigned long int tmp_long;
290 if (xstrtoul (g, NULL, 0, &tmp_long, NULL) != LONGINT_OK
291 || tmp_long > MAXGID)
292 return _(E_invalid_group);
298 endgrent (); /* Save a file descriptor. */
300 if (error_msg == NULL)
301 V_STRDUP (groupname, g);
304 if (error_msg == NULL)
308 *username_arg = strdup (u);
309 if (*username_arg == NULL)
310 error_msg = xalloc_msg_memory_exhausted;
313 if (groupname != NULL && error_msg == NULL)
315 *groupname_arg = strdup (groupname);
316 if (*groupname_arg == NULL)
318 if (*username_arg != NULL)
320 free (*username_arg);
321 *username_arg = NULL;
323 error_msg = xalloc_msg_memory_exhausted;
328 if (error_msg && maybe_retry)
341 # define NULL_CHECK(s) ((s) == NULL ? "(null)" : (s))
344 main (int argc, char **argv)
348 for (i = 1; i < argc; i++)
351 char *username, *groupname;
356 tmp = strdup (argv[i]);
357 e = parse_user_spec (tmp, &uid, &gid, &username, &groupname);
359 printf ("%s: %u %u %s %s %s\n",
363 NULL_CHECK (username),
364 NULL_CHECK (groupname),