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>
58 #define _(msgid) gettext (msgid)
59 #define N_(msgid) msgid
61 #ifndef _POSIX_VERSION
62 struct passwd *getpwnam ();
63 struct group *getgrnam ();
64 struct group *getgrgid ();
68 # define endgrent() ((void) 0)
72 # define endpwent() ((void) 0)
75 /* The extra casts work around common compiler bugs. */
76 #define TYPE_SIGNED(t) (! ((t) 0 < (t) -1))
77 /* The outer cast is needed to work around a bug in Cray C 5.0.3.0.
78 It is necessary at least when t == time_t. */
79 #define TYPE_MINIMUM(t) ((t) (TYPE_SIGNED (t) \
80 ? ~ (t) 0 << (sizeof (t) * CHAR_BIT - 1) : (t) 0))
81 #define TYPE_MAXIMUM(t) ((t) (~ (t) 0 - TYPE_MINIMUM (t)))
84 # define UID_T_MAX TYPE_MAXIMUM (uid_t)
88 # define GID_T_MAX TYPE_MAXIMUM (gid_t)
91 /* MAXUID may come from limits.h or sys/params.h. */
93 # define MAXUID UID_T_MAX
96 # define MAXGID GID_T_MAX
99 /* Perform the equivalent of the statement `dest = strdup (src);',
100 but obtaining storage via alloca instead of from the heap. */
102 #define V_STRDUP(dest, src) \
105 int _len = strlen ((src)); \
106 (dest) = (char *) alloca (_len + 1); \
107 strcpy (dest, src); \
111 /* ISDIGIT differs from isdigit, as follows:
112 - Its arg may be any int or unsigned int; it need not be an unsigned char.
113 - It's guaranteed to evaluate its argument exactly once.
114 - It's typically faster.
115 POSIX says that only '0' through '9' are digits. Prefer ISDIGIT to
116 ISDIGIT_LOCALE unless it's important to use the locale's definition
117 of `digit' even when the host does not conform to POSIX. */
118 #define ISDIGIT(c) ((unsigned) (c) - '0' <= 9)
124 /* Return nonzero if STR represents an unsigned decimal integer,
125 otherwise return 0. */
128 is_number (const char *str)
136 /* Extract from NAME, which has the form "[user][:.][group]",
137 a USERNAME, UID U, GROUPNAME, and GID G.
138 Either user or group, or both, must be present.
139 If the group is omitted but the ":" separator is given,
140 use the given user's login group.
141 If SPEC_ARG contains a `:', then use that as the separator, ignoring
142 any `.'s. If there is no `:', but there is a `.', then first look
143 up the entire SPEC_ARG as a login name. If that look-up fails, then
144 try again interpreting the `.' as a separator.
146 USERNAME and GROUPNAME will be in newly malloc'd memory.
147 Either one might be NULL instead, indicating that it was not
148 given and the corresponding numeric ID was left unchanged.
150 Return NULL if successful, a static error message string if not. */
153 parse_user_spec (const char *spec_arg, uid_t *uid, gid_t *gid,
154 char **username_arg, char **groupname_arg)
156 static const char *E_invalid_user = N_("invalid user");
157 static const char *E_invalid_group = N_("invalid group");
158 static const char *E_bad_spec =
159 N_("cannot get the login group of a numeric UID");
160 static const char *E_cannot_omit_both =
161 N_("cannot omit both user and group");
163 const char *error_msg;
164 char *spec; /* A copy we can write on. */
167 char *g, *u, *separator;
173 *username_arg = *groupname_arg = NULL;
176 V_STRDUP (spec, spec_arg);
178 /* Find the POSIX `:' separator if there is one. */
179 separator = strchr (spec, ':');
181 /* If there is no colon, then see if there's a `.'. */
182 if (separator == NULL)
184 dot = strchr (spec, '.');
185 /* If there's no colon but there is a `.', then first look up the
186 whole spec, in case it's an OWNER name that includes a dot.
187 If that fails, then we'll try again, but interpreting the `.'
189 /* FIXME: accepting `.' as the separator is contrary to POSIX.
190 someday we should drop support for this. */
197 /* Replace separator with a NUL. */
198 if (separator != NULL)
201 /* Set U and G to non-zero length strings corresponding to user and
202 group specifiers or to NULL. */
203 u = (*spec == '\0' ? NULL : spec);
205 g = (separator == NULL || *(separator + 1) == '\0'
209 if (u == NULL && g == NULL)
210 return _(E_cannot_omit_both);
213 /* Pretend that we are the user U whose group is G. This makes
214 pwd and grp functions ``know'' about the UID and GID of these. */
215 if (u && !is_number (u))
216 setenv ("USER", u, 1);
217 if (g && !is_number (g))
218 setenv ("GROUP", g, 1);
228 error_msg = E_invalid_user;
232 use_login_group = (separator != NULL && g == NULL);
234 error_msg = E_bad_spec;
237 unsigned long int tmp_long;
238 if (xstrtoul (u, NULL, 0, &tmp_long, NULL) != LONGINT_OK
239 || tmp_long > MAXUID)
240 return _(E_invalid_user);
248 if (g == NULL && separator != NULL)
250 /* A separator was given, but a group was not specified,
251 so get the login group. */
253 grp = getgrgid (pwd->pw_gid);
256 /* This is enough room to hold the unsigned decimal
257 representation of any 32-bit quantity and the trailing
260 sprintf (uint_buf, "%u", (unsigned) (pwd->pw_gid));
261 V_STRDUP (groupname, uint_buf);
265 V_STRDUP (groupname, grp->gr_name);
273 if (g != NULL && error_msg == NULL)
275 /* Explicit group. */
280 error_msg = E_invalid_group;
283 unsigned long int tmp_long;
284 if (xstrtoul (g, NULL, 0, &tmp_long, NULL) != LONGINT_OK
285 || tmp_long > MAXGID)
286 return _(E_invalid_group);
292 endgrent (); /* Save a file descriptor. */
294 if (error_msg == NULL)
295 V_STRDUP (groupname, g);
298 if (error_msg == NULL)
302 *username_arg = strdup (u);
303 if (*username_arg == NULL)
304 error_msg = xalloc_msg_memory_exhausted;
307 if (groupname != NULL && error_msg == NULL)
309 *groupname_arg = strdup (groupname);
310 if (*groupname_arg == NULL)
312 if (*username_arg != NULL)
314 free (*username_arg);
315 *username_arg = NULL;
317 error_msg = xalloc_msg_memory_exhausted;
322 if (error_msg && maybe_retry)
335 # define NULL_CHECK(s) ((s) == NULL ? "(null)" : (s))
338 main (int argc, char **argv)
342 for (i = 1; i < argc; i++)
345 char *username, *groupname;
350 tmp = strdup (argv[i]);
351 e = parse_user_spec (tmp, &uid, &gid, &username, &groupname);
353 printf ("%s: %u %u %s %s %s\n",
357 NULL_CHECK (username),
358 NULL_CHECK (groupname),