1 /* userspec.c -- Parse a user and group string.
2 Copyright (C) 1989-1992, 1997-1998, 2000, 2002-2004 Free Software
5 This program is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published by
7 the Free Software Foundation; either version 2, or (at your option)
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU General Public License for more details.
15 You should have received a copy of the GNU General Public License
16 along with this program; if not, write to the Free Software Foundation,
17 Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */
19 /* Written by David MacKenzie <djm@gnu.ai.mit.edu>. */
32 #include <sys/types.h>
37 # include <sys/param.h>
54 #define _(msgid) gettext (msgid)
55 #define N_(msgid) msgid
57 #ifndef _POSIX_VERSION
58 struct passwd *getpwnam ();
59 struct group *getgrnam ();
60 struct group *getgrgid ();
64 # define endgrent() ((void) 0)
68 # define endpwent() ((void) 0)
71 /* The extra casts work around common compiler bugs. */
72 #define TYPE_SIGNED(t) (! ((t) 0 < (t) -1))
73 /* The outer cast is needed to work around a bug in Cray C 5.0.3.0.
74 It is necessary at least when t == time_t. */
75 #define TYPE_MINIMUM(t) ((t) (TYPE_SIGNED (t) \
76 ? ~ (t) 0 << (sizeof (t) * CHAR_BIT - 1) : (t) 0))
77 #define TYPE_MAXIMUM(t) ((t) (~ (t) 0 - TYPE_MINIMUM (t)))
80 # define UID_T_MAX TYPE_MAXIMUM (uid_t)
84 # define GID_T_MAX TYPE_MAXIMUM (gid_t)
87 /* MAXUID may come from limits.h or sys/params.h. */
89 # define MAXUID UID_T_MAX
92 # define MAXGID GID_T_MAX
95 /* Perform the equivalent of the statement `dest = strdup (src);',
96 but obtaining storage via alloca instead of from the heap. */
98 #define V_STRDUP(dest, src) \
101 size_t size = strlen (src) + 1; \
102 (dest) = (char *) alloca (size); \
103 memcpy (dest, src, size); \
107 /* ISDIGIT differs from isdigit, as follows:
108 - Its arg may be any int or unsigned int; it need not be an unsigned char.
109 - It's guaranteed to evaluate its argument exactly once.
110 - It's typically faster.
111 POSIX says that only '0' through '9' are digits. Prefer ISDIGIT to
112 ISDIGIT_LOCALE unless it's important to use the locale's definition
113 of `digit' even when the host does not conform to POSIX. */
114 #define ISDIGIT(c) ((unsigned int) (c) - '0' <= 9)
118 /* Return true if STR represents an unsigned decimal integer. */
121 is_number (const char *str)
134 /* Extract from NAME, which has the form "[user][:.][group]",
135 a USERNAME, UID U, GROUPNAME, and GID G.
136 Either user or group, or both, must be present.
137 If the group is omitted but the ":" separator is given,
138 use the given user's login group.
139 If SPEC_ARG contains a `:', then use that as the separator, ignoring
140 any `.'s. If there is no `:', but there is a `.', then first look
141 up the entire SPEC_ARG as a login name. If that look-up fails, then
142 try again interpreting the `.' as a separator.
144 USERNAME and GROUPNAME will be in newly malloc'd memory.
145 Either one might be NULL instead, indicating that it was not
146 given and the corresponding numeric ID was left unchanged.
148 Return NULL if successful, a static error message string if not. */
151 parse_user_spec (const char *spec_arg, uid_t *uid, gid_t *gid,
152 char **username_arg, char **groupname_arg)
154 static const char *E_invalid_user = N_("invalid user");
155 static const char *E_invalid_group = N_("invalid group");
156 static const char *E_bad_spec =
157 N_("cannot get the login group of a numeric UID");
158 static const char *E_cannot_omit_both =
159 N_("cannot omit both user and group");
161 const char *error_msg;
162 char *spec; /* A copy we can write on. */
165 char *g, *u, *separator;
170 *username_arg = *groupname_arg = NULL;
173 V_STRDUP (spec, spec_arg);
175 /* Find the POSIX `:' separator if there is one. */
176 separator = strchr (spec, ':');
178 /* If there is no colon, then see if there's a `.'. */
179 if (separator == NULL)
181 dot = strchr (spec, '.');
182 /* If there's no colon but there is a `.', then first look up the
183 whole spec, in case it's an OWNER name that includes a dot.
184 If that fails, then we'll try again, but interpreting the `.'
185 as a separator. This is a compatible extension to POSIX, since
186 the POSIX-required behavior is always tried first. */
191 /* Replace separator with a NUL. */
192 if (separator != NULL)
195 /* Set U and G to non-zero length strings corresponding to user and
196 group specifiers or to NULL. */
197 u = (*spec == '\0' ? NULL : spec);
199 g = (separator == NULL || *(separator + 1) == '\0'
203 if (u == NULL && g == NULL)
204 return _(E_cannot_omit_both);
207 /* Pretend that we are the user U whose group is G. This makes
208 pwd and grp functions ``know'' about the UID and GID of these. */
209 if (u && !is_number (u))
210 setenv ("USER", u, 1);
211 if (g && !is_number (g))
212 setenv ("GROUP", g, 1);
220 bool use_login_group = (separator != NULL && g == NULL);
222 error_msg = E_bad_spec;
225 unsigned long int tmp_long;
226 if (! (xstrtoul (u, NULL, 10, &tmp_long, "") == LONGINT_OK
227 && tmp_long <= MAXUID))
228 return _(E_invalid_user);
235 if (g == NULL && separator != NULL)
237 /* A separator was given, but a group was not specified,
238 so get the login group. */
240 grp = getgrgid (pwd->pw_gid);
243 char buf[INT_BUFSIZE_BOUND (uintmax_t)];
244 char const *num = umaxtostr (pwd->pw_gid, buf);
245 V_STRDUP (groupname, num);
249 V_STRDUP (groupname, grp->gr_name);
257 if (g != NULL && error_msg == NULL)
259 /* Explicit group. */
263 unsigned long int tmp_long;
264 if (! (xstrtoul (g, NULL, 10, &tmp_long, "") == LONGINT_OK
265 && tmp_long <= MAXGID))
266 return _(E_invalid_group);
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 = xalloc_msg_memory_exhausted;
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 = xalloc_msg_memory_exhausted;
301 if (error_msg && dot)
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: %lu %lu %s %s %s\n",
334 (unsigned long int) uid,
335 (unsigned long int) gid,
336 NULL_CHECK (username),
337 NULL_CHECK (groupname),