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>
62 # define _(Text) gettext (Text)
68 #ifndef _POSIX_VERSION
69 struct passwd *getpwnam ();
70 struct group *getgrnam ();
71 struct group *getgrgid ();
75 # define endgrent() ((void) 0)
79 # define endpwent() ((void) 0)
82 /* Perform the equivalent of the statement `dest = strdup (src);',
83 but obtaining storage via alloca instead of from the heap. */
85 #define V_STRDUP(dest, src) \
88 int _len = strlen ((src)); \
89 (dest) = (char *) alloca (_len + 1); \
94 /* ISDIGIT differs from isdigit, as follows:
95 - Its arg may be any int or unsigned int; it need not be an unsigned char.
96 - It's guaranteed to evaluate its argument exactly once.
97 - It's typically faster.
98 Posix 1003.2-1992 section 2.5.2.1 page 50 lines 1556-1558 says that
99 only '0' through '9' are digits. Prefer ISDIGIT to isdigit unless
100 it's important to use the locale's definition of `digit' even when the
101 host does not conform to Posix. */
102 #define ISDIGIT(c) ((unsigned) (c) - '0' <= 9)
108 /* Return nonzero if STR represents an unsigned decimal integer,
109 otherwise return 0. */
112 is_number (const char *str)
120 /* Extract from NAME, which has the form "[user][:.][group]",
121 a USERNAME, UID U, GROUPNAME, and GID G.
122 Either user or group, or both, must be present.
123 If the group is omitted but the ":" separator is given,
124 use the given user's login group.
125 If SPEC_ARG contains a `:', then use that as the separator, ignoring
126 any `.'s. If there is no `:', but there is a `.', then first look
127 up SPEC_ARG as a login name. If that look-up fails, then try again
128 interpreting the `.' as a separator.
130 USERNAME and GROUPNAME will be in newly malloc'd memory.
131 Either one might be NULL instead, indicating that it was not
132 given and the corresponding numeric ID was left unchanged.
134 Return NULL if successful, a static error message string if not. */
137 parse_user_spec (const char *spec_arg, uid_t *uid, gid_t *gid,
138 char **username_arg, char **groupname_arg)
140 static const char *E_no_memory = N_("virtual memory exhausted");
141 static const char *E_invalid_user = N_("invalid user");
142 static const char *E_invalid_group = N_("invalid group");
143 static const char *E_bad_spec =
144 N_("cannot get the login group of a numeric UID");
145 static const char *E_cannot_omit_both =
146 N_("cannot omit both user and group");
148 const char *error_msg;
149 char *spec; /* A copy we can write on. */
152 char *g, *u, *separator;
158 *username_arg = *groupname_arg = NULL;
161 V_STRDUP (spec, spec_arg);
163 /* Find the POSIX `:' separator if there is one. */
164 separator = strchr (spec, ':');
166 /* If there is no colon, then see if there's a `.'. */
167 if (separator == NULL)
169 dot = strchr (spec, '.');
170 /* If there's no colon but there is a `.', then first look up the
171 whole spec, in case it's an OWNER name that includes a dot.
172 If that fails, then we'll try again, but interpreting the `.'
174 /* FIXME: accepting `.' as the separator is contrary to POSIX.
175 someday we should drop support for this. */
182 /* Replace separator with a NUL. */
183 if (separator != NULL)
186 /* Set U and G to non-zero length strings corresponding to user and
187 group specifiers or to NULL. */
188 u = (*spec == '\0' ? NULL : spec);
190 g = (separator == NULL || *(separator + 1) == '\0'
194 if (u == NULL && g == NULL)
195 return _(E_cannot_omit_both);
198 /* Pretend that we are the user U whose group is G. This makes
199 pwd and grp functions ``know'' about the UID and GID of these. */
200 if (u && !is_number (u))
201 setenv ("USER", u, 1);
202 if (g && !is_number (g))
203 setenv ("GROUP", g, 1);
213 error_msg = _(E_invalid_user);
217 use_login_group = (separator != NULL && g == NULL);
219 error_msg = _(E_bad_spec);
222 /* FIXME: don't use atoi! */
230 if (g == NULL && separator != NULL)
232 /* A separator was given, but a group was not specified,
233 so get the login group. */
235 grp = getgrgid (pwd->pw_gid);
238 /* This is enough room to hold the unsigned decimal
239 representation of any 32-bit quantity and the trailing
242 sprintf (uint_buf, "%u", (unsigned) (pwd->pw_gid));
243 V_STRDUP (groupname, uint_buf);
247 V_STRDUP (groupname, grp->gr_name);
255 if (g != NULL && error_msg == NULL)
257 /* Explicit group. */
262 error_msg = _(E_invalid_group);
265 /* FIXME: don't use atoi! */
271 endgrent (); /* Save a file descriptor. */
273 if (error_msg == NULL)
274 V_STRDUP (groupname, g);
277 if (error_msg == NULL)
281 *username_arg = strdup (u);
282 if (*username_arg == NULL)
283 error_msg = _(E_no_memory);
286 if (groupname != NULL && error_msg == NULL)
288 *groupname_arg = strdup (groupname);
289 if (*groupname_arg == NULL)
291 if (*username_arg != NULL)
293 free (*username_arg);
294 *username_arg = NULL;
296 error_msg = _(E_no_memory);
301 if (error_msg && maybe_retry)
314 # define NULL_CHECK(s) ((s) == NULL ? "(null)" : (s))
317 main (int argc, char **argv)
321 for (i = 1; i < argc; i++)
324 char *username, *groupname;
329 tmp = strdup (argv[i]);
330 e = parse_user_spec (tmp, &uid, &gid, &username, &groupname);
332 printf ("%s: %u %u %s %s %s\n",
336 NULL_CHECK (username),
337 NULL_CHECK (groupname),