1 /* userspec.c -- Parse a user and group string.
2 Copyright (C) 1989-1992, 1997, 1998, 2000 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>. */
25 # define alloca __builtin_alloca
39 #include <sys/types.h>
64 # define _(Text) gettext (Text)
70 #ifndef _POSIX_VERSION
71 struct passwd *getpwnam ();
72 struct group *getgrnam ();
73 struct group *getgrgid ();
77 # define endgrent() ((void) 0)
81 # define endpwent() ((void) 0)
84 /* Perform the equivalent of the statement `dest = strdup (src);',
85 but obtaining storage via alloca instead of from the heap. */
87 #define V_STRDUP(dest, src) \
90 int _len = strlen ((src)); \
91 (dest) = (char *) alloca (_len + 1); \
96 /* ISDIGIT differs from isdigit, as follows:
97 - Its arg may be any int or unsigned int; it need not be an unsigned char.
98 - It's guaranteed to evaluate its argument exactly once.
99 - It's typically faster.
100 Posix 1003.2-1992 section 2.5.2.1 page 50 lines 1556-1558 says that
101 only '0' through '9' are digits. Prefer ISDIGIT to isdigit unless
102 it's important to use the locale's definition of `digit' even when the
103 host does not conform to Posix. */
104 #define ISDIGIT(c) ((unsigned) (c) - '0' <= 9)
110 /* Return nonzero if STR represents an unsigned decimal integer,
111 otherwise return 0. */
114 is_number (const char *str)
122 /* Extract from NAME, which has the form "[user][:.][group]",
123 a USERNAME, UID U, GROUPNAME, and GID G.
124 Either user or group, or both, must be present.
125 If the group is omitted but the ":" separator is given,
126 use the given user's login group.
127 If SPEC_ARG contains a `:', then use that as the separator, ignoring
128 any `.'s. If there is no `:', but there is a `.', then first look
129 up SPEC_ARG as a login name. If that look-up fails, then try again
130 interpreting the `.' as a separator.
132 USERNAME and GROUPNAME will be in newly malloc'd memory.
133 Either one might be NULL instead, indicating that it was not
134 given and the corresponding numeric ID was left unchanged.
136 Return NULL if successful, a static error message string if not. */
139 parse_user_spec (const char *spec_arg, uid_t *uid, gid_t *gid,
140 char **username_arg, char **groupname_arg)
142 static const char *E_invalid_user = N_("invalid user");
143 static const char *E_invalid_group = N_("invalid group");
144 static const char *E_bad_spec =
145 N_("cannot get the login group of a numeric UID");
146 static const char *E_cannot_omit_both =
147 N_("cannot omit both user and group");
149 const char *error_msg;
150 char *spec; /* A copy we can write on. */
153 char *g, *u, *separator;
159 *username_arg = *groupname_arg = NULL;
162 V_STRDUP (spec, spec_arg);
164 /* Find the POSIX `:' separator if there is one. */
165 separator = strchr (spec, ':');
167 /* If there is no colon, then see if there's a `.'. */
168 if (separator == NULL)
170 dot = strchr (spec, '.');
171 /* If there's no colon but there is a `.', then first look up the
172 whole spec, in case it's an OWNER name that includes a dot.
173 If that fails, then we'll try again, but interpreting the `.'
175 /* FIXME: accepting `.' as the separator is contrary to POSIX.
176 someday we should drop support for this. */
183 /* Replace separator with a NUL. */
184 if (separator != NULL)
187 /* Set U and G to non-zero length strings corresponding to user and
188 group specifiers or to NULL. */
189 u = (*spec == '\0' ? NULL : spec);
191 g = (separator == NULL || *(separator + 1) == '\0'
195 if (u == NULL && g == NULL)
196 return _(E_cannot_omit_both);
199 /* Pretend that we are the user U whose group is G. This makes
200 pwd and grp functions ``know'' about the UID and GID of these. */
201 if (u && !is_number (u))
202 setenv ("USER", u, 1);
203 if (g && !is_number (g))
204 setenv ("GROUP", g, 1);
214 error_msg = E_invalid_user;
218 use_login_group = (separator != NULL && g == NULL);
220 error_msg = E_bad_spec;
223 /* FIXME: don't use atoi! */
231 if (g == NULL && separator != NULL)
233 /* A separator was given, but a group was not specified,
234 so get the login group. */
236 grp = getgrgid (pwd->pw_gid);
239 /* This is enough room to hold the unsigned decimal
240 representation of any 32-bit quantity and the trailing
243 sprintf (uint_buf, "%u", (unsigned) (pwd->pw_gid));
244 V_STRDUP (groupname, uint_buf);
248 V_STRDUP (groupname, grp->gr_name);
256 if (g != NULL && error_msg == NULL)
258 /* Explicit group. */
263 error_msg = E_invalid_group;
266 /* FIXME: don't use atoi! */
272 endgrent (); /* Save a file descriptor. */
274 if (error_msg == NULL)
275 V_STRDUP (groupname, g);
278 if (error_msg == NULL)
282 *username_arg = strdup (u);
283 if (*username_arg == NULL)
284 error_msg = xalloc_msg_memory_exhausted;
287 if (groupname != NULL && error_msg == NULL)
289 *groupname_arg = strdup (groupname);
290 if (*groupname_arg == NULL)
292 if (*username_arg != NULL)
294 free (*username_arg);
295 *username_arg = NULL;
297 error_msg = xalloc_msg_memory_exhausted;
302 if (error_msg && maybe_retry)
315 # define NULL_CHECK(s) ((s) == NULL ? "(null)" : (s))
318 main (int argc, char **argv)
322 for (i = 1; i < argc; i++)
325 char *username, *groupname;
330 tmp = strdup (argv[i]);
331 e = parse_user_spec (tmp, &uid, &gid, &username, &groupname);
333 printf ("%s: %u %u %s %s %s\n",
337 NULL_CHECK (username),
338 NULL_CHECK (groupname),