.
[coreutils.git] / lib / userspec.c
blob474fb60628cd110c072cf770c7c248c35356727a
1 /* userspec.c -- Parse a user and group string.
2 Copyright (C) 1989-1992, 1997-1998, 2000, 2002-2004 Free Software
3 Foundation, Inc.
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)
8 any later version.
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>. */
21 #if HAVE_CONFIG_H
22 # include <config.h>
23 #endif
25 #include <alloca.h>
27 #include <stdio.h>
28 #include <sys/types.h>
29 #include <pwd.h>
30 #include <grp.h>
32 #if HAVE_SYS_PARAM_H
33 # include <sys/param.h>
34 #endif
36 #include <limits.h>
37 #include <stdlib.h>
38 #include <string.h>
40 #if HAVE_UNISTD_H
41 # include <unistd.h>
42 #endif
44 #include "userspec.h"
45 #include "xalloc.h"
46 #include "xstrtol.h"
48 #include "gettext.h"
49 #define _(msgid) gettext (msgid)
50 #define N_(msgid) msgid
52 #ifndef _POSIX_VERSION
53 struct passwd *getpwnam ();
54 struct group *getgrnam ();
55 struct group *getgrgid ();
56 #endif
58 #ifndef HAVE_ENDGRENT
59 # define endgrent() ((void) 0)
60 #endif
62 #ifndef HAVE_ENDPWENT
63 # define endpwent() ((void) 0)
64 #endif
66 /* The extra casts work around common compiler bugs. */
67 #define TYPE_SIGNED(t) (! ((t) 0 < (t) -1))
68 /* The outer cast is needed to work around a bug in Cray C 5.0.3.0.
69 It is necessary at least when t == time_t. */
70 #define TYPE_MINIMUM(t) ((t) (TYPE_SIGNED (t) \
71 ? ~ (t) 0 << (sizeof (t) * CHAR_BIT - 1) : (t) 0))
72 #define TYPE_MAXIMUM(t) ((t) (~ (t) 0 - TYPE_MINIMUM (t)))
74 #ifndef UID_T_MAX
75 # define UID_T_MAX TYPE_MAXIMUM (uid_t)
76 #endif
78 #ifndef GID_T_MAX
79 # define GID_T_MAX TYPE_MAXIMUM (gid_t)
80 #endif
82 /* MAXUID may come from limits.h or sys/params.h. */
83 #ifndef MAXUID
84 # define MAXUID UID_T_MAX
85 #endif
86 #ifndef MAXGID
87 # define MAXGID GID_T_MAX
88 #endif
90 /* Perform the equivalent of the statement `dest = strdup (src);',
91 but obtaining storage via alloca instead of from the heap. */
93 #define V_STRDUP(dest, src) \
94 do \
95 { \
96 int _len = strlen ((src)); \
97 (dest) = (char *) alloca (_len + 1); \
98 strcpy (dest, src); \
99 } \
100 while (0)
102 /* ISDIGIT differs from isdigit, as follows:
103 - Its arg may be any int or unsigned int; it need not be an unsigned char.
104 - It's guaranteed to evaluate its argument exactly once.
105 - It's typically faster.
106 POSIX says that only '0' through '9' are digits. Prefer ISDIGIT to
107 ISDIGIT_LOCALE unless it's important to use the locale's definition
108 of `digit' even when the host does not conform to POSIX. */
109 #define ISDIGIT(c) ((unsigned) (c) - '0' <= 9)
111 #ifndef strdup
112 char *strdup ();
113 #endif
115 /* Return nonzero if STR represents an unsigned decimal integer,
116 otherwise return 0. */
118 static int
119 is_number (const char *str)
121 for (; *str; str++)
122 if (!ISDIGIT (*str))
123 return 0;
124 return 1;
127 /* Extract from NAME, which has the form "[user][:.][group]",
128 a USERNAME, UID U, GROUPNAME, and GID G.
129 Either user or group, or both, must be present.
130 If the group is omitted but the ":" separator is given,
131 use the given user's login group.
132 If SPEC_ARG contains a `:', then use that as the separator, ignoring
133 any `.'s. If there is no `:', but there is a `.', then first look
134 up the entire SPEC_ARG as a login name. If that look-up fails, then
135 try again interpreting the `.' as a separator.
137 USERNAME and GROUPNAME will be in newly malloc'd memory.
138 Either one might be NULL instead, indicating that it was not
139 given and the corresponding numeric ID was left unchanged.
141 Return NULL if successful, a static error message string if not. */
143 const char *
144 parse_user_spec (const char *spec_arg, uid_t *uid, gid_t *gid,
145 char **username_arg, char **groupname_arg)
147 static const char *E_invalid_user = N_("invalid user");
148 static const char *E_invalid_group = N_("invalid group");
149 static const char *E_bad_spec =
150 N_("cannot get the login group of a numeric UID");
151 static const char *E_cannot_omit_both =
152 N_("cannot omit both user and group");
154 const char *error_msg;
155 char *spec; /* A copy we can write on. */
156 struct passwd *pwd;
157 struct group *grp;
158 char *g, *u, *separator;
159 char *groupname;
160 char *dot = NULL;
162 error_msg = NULL;
163 *username_arg = *groupname_arg = NULL;
164 groupname = NULL;
166 V_STRDUP (spec, spec_arg);
168 /* Find the POSIX `:' separator if there is one. */
169 separator = strchr (spec, ':');
171 /* If there is no colon, then see if there's a `.'. */
172 if (separator == NULL)
174 dot = strchr (spec, '.');
175 /* If there's no colon but there is a `.', then first look up the
176 whole spec, in case it's an OWNER name that includes a dot.
177 If that fails, then we'll try again, but interpreting the `.'
178 as a separator. This is a compatible extension to POSIX, since
179 the POSIX-required behavior is always tried first. */
182 retry:
184 /* Replace separator with a NUL. */
185 if (separator != NULL)
186 *separator = '\0';
188 /* Set U and G to non-zero length strings corresponding to user and
189 group specifiers or to NULL. */
190 u = (*spec == '\0' ? NULL : spec);
192 g = (separator == NULL || *(separator + 1) == '\0'
193 ? NULL
194 : separator + 1);
196 if (u == NULL && g == NULL)
197 return _(E_cannot_omit_both);
199 #ifdef __DJGPP__
200 /* Pretend that we are the user U whose group is G. This makes
201 pwd and grp functions ``know'' about the UID and GID of these. */
202 if (u && !is_number (u))
203 setenv ("USER", u, 1);
204 if (g && !is_number (g))
205 setenv ("GROUP", g, 1);
206 #endif
208 if (u != NULL)
210 pwd = getpwnam (u);
211 if (pwd == NULL)
214 if (!is_number (u))
215 error_msg = E_invalid_user;
216 else
218 int use_login_group;
219 use_login_group = (separator != NULL && g == NULL);
220 if (use_login_group)
221 error_msg = E_bad_spec;
222 else
224 unsigned long int tmp_long;
225 if (xstrtoul (u, NULL, 0, &tmp_long, NULL) != LONGINT_OK
226 || tmp_long > MAXUID)
227 return _(E_invalid_user);
228 *uid = tmp_long;
232 else
234 *uid = pwd->pw_uid;
235 if (g == NULL && separator != NULL)
237 /* A separator was given, but a group was not specified,
238 so get the login group. */
239 *gid = pwd->pw_gid;
240 grp = getgrgid (pwd->pw_gid);
241 if (grp == NULL)
243 /* This is enough room to hold the unsigned decimal
244 representation of any 32-bit quantity and the trailing
245 zero byte. */
246 char uint_buf[21];
247 sprintf (uint_buf, "%u", (unsigned) (pwd->pw_gid));
248 V_STRDUP (groupname, uint_buf);
250 else
252 V_STRDUP (groupname, grp->gr_name);
254 endgrent ();
257 endpwent ();
260 if (g != NULL && error_msg == NULL)
262 /* Explicit group. */
263 grp = getgrnam (g);
264 if (grp == NULL)
266 if (!is_number (g))
267 error_msg = E_invalid_group;
268 else
270 unsigned long int tmp_long;
271 if (xstrtoul (g, NULL, 0, &tmp_long, NULL) != LONGINT_OK
272 || tmp_long > MAXGID)
273 return _(E_invalid_group);
274 *gid = tmp_long;
277 else
278 *gid = grp->gr_gid;
279 endgrent (); /* Save a file descriptor. */
281 if (error_msg == NULL)
282 V_STRDUP (groupname, g);
285 if (error_msg == NULL)
287 if (u != NULL)
289 *username_arg = strdup (u);
290 if (*username_arg == NULL)
291 error_msg = xalloc_msg_memory_exhausted;
294 if (groupname != NULL && error_msg == NULL)
296 *groupname_arg = strdup (groupname);
297 if (*groupname_arg == NULL)
299 if (*username_arg != NULL)
301 free (*username_arg);
302 *username_arg = NULL;
304 error_msg = xalloc_msg_memory_exhausted;
309 if (error_msg && dot)
311 separator = dot;
312 dot = NULL;
313 error_msg = NULL;
314 goto retry;
317 return _(error_msg);
320 #ifdef TEST
322 # define NULL_CHECK(s) ((s) == NULL ? "(null)" : (s))
325 main (int argc, char **argv)
327 int i;
329 for (i = 1; i < argc; i++)
331 const char *e;
332 char *username, *groupname;
333 uid_t uid;
334 gid_t gid;
335 char *tmp;
337 tmp = strdup (argv[i]);
338 e = parse_user_spec (tmp, &uid, &gid, &username, &groupname);
339 free (tmp);
340 printf ("%s: %u %u %s %s %s\n",
341 argv[i],
342 (unsigned int) uid,
343 (unsigned int) gid,
344 NULL_CHECK (username),
345 NULL_CHECK (groupname),
346 NULL_CHECK (e));
349 exit (0);
352 #endif