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>
60 #ifndef _POSIX_VERSION
61 struct passwd *getpwnam ();
62 struct group *getgrnam ();
63 struct group *getgrgid ();
67 # define endgrent() ((void) 0)
71 # define endpwent() ((void) 0)
74 /* Perform the equivalent of the statement `dest = strdup (src);',
75 but obtaining storage via alloca instead of from the heap. */
77 #define V_STRDUP(dest, src) \
80 int _len = strlen ((src)); \
81 (dest) = (char *) alloca (_len + 1); \
86 /* ISDIGIT differs from isdigit, as follows:
87 - Its arg may be any int or unsigned int; it need not be an unsigned char.
88 - It's guaranteed to evaluate its argument exactly once.
89 - It's typically faster.
90 Posix 1003.2-1992 section 2.5.2.1 page 50 lines 1556-1558 says that
91 only '0' through '9' are digits. Prefer ISDIGIT to isdigit unless
92 it's important to use the locale's definition of `digit' even when the
93 host does not conform to Posix. */
94 #define ISDIGIT(c) ((unsigned) (c) - '0' <= 9)
100 /* Return nonzero if STR represents an unsigned decimal integer,
101 otherwise return 0. */
104 is_number (const char *str)
112 /* Extract from NAME, which has the form "[user][:.][group]",
113 a USERNAME, UID U, GROUPNAME, and GID G.
114 Either user or group, or both, must be present.
115 If the group is omitted but the ":" or "." separator is given,
116 use the given user's login group.
118 USERNAME and GROUPNAME will be in newly malloc'd memory.
119 Either one might be NULL instead, indicating that it was not
120 given and the corresponding numeric ID was left unchanged.
122 Return NULL if successful, a static error message string if not. */
125 parse_user_spec (const char *spec_arg, uid_t *uid, gid_t *gid,
126 char **username_arg, char **groupname_arg)
128 static const char *tired = "virtual memory exhausted";
129 const char *error_msg;
130 char *spec; /* A copy we can write on. */
133 char *g, *u, *separator;
137 *username_arg = *groupname_arg = NULL;
140 V_STRDUP (spec, spec_arg);
142 /* Find the separator if there is one. */
143 separator = strchr (spec, ':');
144 if (separator == NULL)
145 separator = strchr (spec, '.');
147 /* Replace separator with a NUL. */
148 if (separator != NULL)
151 /* Set U and G to non-zero length strings corresponding to user and
152 group specifiers or to NULL. */
153 u = (*spec == '\0' ? NULL : spec);
155 g = (separator == NULL || *(separator + 1) == '\0'
159 if (u == NULL && g == NULL)
160 return "can not omit both user and group";
163 /* Pretend that we are the user U whose group is G. This makes
164 pwd and grp functions ``know'' about the UID and GID of these. */
165 if (u && !is_number (u))
166 setenv ("USER", u, 1);
167 if (g && !is_number (g))
168 setenv ("GROUP", g, 1);
178 error_msg = "invalid user";
182 use_login_group = (separator != NULL && g == NULL);
184 error_msg = "cannot get the login group of a numeric UID";
187 /* FIXME: don't use atoi! */
195 if (g == NULL && separator != NULL)
197 /* A separator was given, but a group was not specified,
198 so get the login group. */
200 grp = getgrgid (pwd->pw_gid);
203 /* This is enough room to hold the unsigned decimal
204 representation of any 32-bit quantity and the trailing
207 sprintf (uint_buf, "%u", (unsigned) (pwd->pw_gid));
208 V_STRDUP (groupname, uint_buf);
212 V_STRDUP (groupname, grp->gr_name);
220 if (g != NULL && error_msg == NULL)
222 /* Explicit group. */
227 error_msg = "invalid group";
230 /* FIXME: don't use atoi! */
236 endgrent (); /* Save a file descriptor. */
238 if (error_msg == NULL)
239 V_STRDUP (groupname, g);
242 if (error_msg == NULL)
246 *username_arg = strdup (u);
247 if (*username_arg == NULL)
251 if (groupname != NULL && error_msg == NULL)
253 *groupname_arg = strdup (groupname);
254 if (*groupname_arg == NULL)
256 if (*username_arg != NULL)
258 free (*username_arg);
259 *username_arg = NULL;
271 # define NULL_CHECK(s) ((s) == NULL ? "(null)" : (s))
274 main (int argc, char **argv)
278 for (i = 1; i < argc; i++)
281 char *username, *groupname;
286 tmp = strdup (argv[i]);
287 e = parse_user_spec (tmp, &uid, &gid, &username, &groupname);
289 printf ("%s: %u %u %s %s %s\n",
293 NULL_CHECK (username),
294 NULL_CHECK (groupname),