*** empty log message ***
[coreutils.git] / lib / userspec.c
blob9def456adb7f20b17a6a3bcb64f463c81b77412c
1 /* userspec.c -- Parse a user and group string.
2 Copyright (C) 1989-1992, 1997, 1998, 2000, 2002 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)
7 any later version.
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>. */
20 #if HAVE_CONFIG_H
21 # include <config.h>
22 #endif
24 #ifdef __GNUC__
25 # define alloca __builtin_alloca
26 #else
27 # if HAVE_ALLOCA_H
28 # include <alloca.h>
29 # else
30 # ifdef _AIX
31 # pragma alloca
32 # else
33 char *alloca ();
34 # endif
35 # endif
36 #endif
38 #include <stdio.h>
39 #include <sys/types.h>
40 #include <pwd.h>
41 #include <grp.h>
43 #if HAVE_SYS_PARAM_H
44 # include <sys/param.h>
45 #endif
47 #if HAVE_LIMITS_H
48 # include <limits.h>
49 #endif
51 #if HAVE_STRING_H
52 # include <string.h>
53 #else
54 # include <strings.h>
55 # ifndef strchr
56 # define strchr index
57 # endif
58 #endif
60 #if STDC_HEADERS
61 # include <stdlib.h>
62 #endif
64 #if HAVE_UNISTD_H
65 # include <unistd.h>
66 #endif
68 #include "xalloc.h"
69 #include "xstrtol.h"
71 #include "gettext.h"
72 #define _(msgid) gettext (msgid)
73 #define N_(msgid) msgid
75 #ifndef _POSIX_VERSION
76 struct passwd *getpwnam ();
77 struct group *getgrnam ();
78 struct group *getgrgid ();
79 #endif
81 #ifndef HAVE_ENDGRENT
82 # define endgrent() ((void) 0)
83 #endif
85 #ifndef HAVE_ENDPWENT
86 # define endpwent() ((void) 0)
87 #endif
89 #ifndef CHAR_BIT
90 # define CHAR_BIT 8
91 #endif
93 /* The extra casts work around common compiler bugs. */
94 #define TYPE_SIGNED(t) (! ((t) 0 < (t) -1))
95 /* The outer cast is needed to work around a bug in Cray C 5.0.3.0.
96 It is necessary at least when t == time_t. */
97 #define TYPE_MINIMUM(t) ((t) (TYPE_SIGNED (t) \
98 ? ~ (t) 0 << (sizeof (t) * CHAR_BIT - 1) : (t) 0))
99 #define TYPE_MAXIMUM(t) ((t) (~ (t) 0 - TYPE_MINIMUM (t)))
101 #ifndef UID_T_MAX
102 # define UID_T_MAX TYPE_MAXIMUM (uid_t)
103 #endif
105 #ifndef GID_T_MAX
106 # define GID_T_MAX TYPE_MAXIMUM (gid_t)
107 #endif
109 /* MAXUID may come from limits.h or sys/params.h. */
110 #ifndef MAXUID
111 # define MAXUID UID_T_MAX
112 #endif
113 #ifndef MAXGID
114 # define MAXGID GID_T_MAX
115 #endif
117 /* Perform the equivalent of the statement `dest = strdup (src);',
118 but obtaining storage via alloca instead of from the heap. */
120 #define V_STRDUP(dest, src) \
121 do \
123 int _len = strlen ((src)); \
124 (dest) = (char *) alloca (_len + 1); \
125 strcpy (dest, src); \
127 while (0)
129 /* ISDIGIT differs from isdigit, as follows:
130 - Its arg may be any int or unsigned int; it need not be an unsigned char.
131 - It's guaranteed to evaluate its argument exactly once.
132 - It's typically faster.
133 POSIX says that only '0' through '9' are digits. Prefer ISDIGIT to
134 ISDIGIT_LOCALE unless it's important to use the locale's definition
135 of `digit' even when the host does not conform to POSIX. */
136 #define ISDIGIT(c) ((unsigned) (c) - '0' <= 9)
138 #ifndef strdup
139 char *strdup ();
140 #endif
142 /* Return nonzero if STR represents an unsigned decimal integer,
143 otherwise return 0. */
145 static int
146 is_number (const char *str)
148 for (; *str; str++)
149 if (!ISDIGIT (*str))
150 return 0;
151 return 1;
154 /* Extract from NAME, which has the form "[user][:.][group]",
155 a USERNAME, UID U, GROUPNAME, and GID G.
156 Either user or group, or both, must be present.
157 If the group is omitted but the ":" separator is given,
158 use the given user's login group.
159 If SPEC_ARG contains a `:', then use that as the separator, ignoring
160 any `.'s. If there is no `:', but there is a `.', then first look
161 up the entire SPEC_ARG as a login name. If that look-up fails, then
162 try again interpreting the `.' as a separator.
164 USERNAME and GROUPNAME will be in newly malloc'd memory.
165 Either one might be NULL instead, indicating that it was not
166 given and the corresponding numeric ID was left unchanged.
168 Return NULL if successful, a static error message string if not. */
170 const char *
171 parse_user_spec (const char *spec_arg, uid_t *uid, gid_t *gid,
172 char **username_arg, char **groupname_arg)
174 static const char *E_invalid_user = N_("invalid user");
175 static const char *E_invalid_group = N_("invalid group");
176 static const char *E_bad_spec =
177 N_("cannot get the login group of a numeric UID");
178 static const char *E_cannot_omit_both =
179 N_("cannot omit both user and group");
181 const char *error_msg;
182 char *spec; /* A copy we can write on. */
183 struct passwd *pwd;
184 struct group *grp;
185 char *g, *u, *separator;
186 char *groupname;
187 int maybe_retry = 0;
188 char *dot = NULL;
190 error_msg = NULL;
191 *username_arg = *groupname_arg = NULL;
192 groupname = NULL;
194 V_STRDUP (spec, spec_arg);
196 /* Find the POSIX `:' separator if there is one. */
197 separator = strchr (spec, ':');
199 /* If there is no colon, then see if there's a `.'. */
200 if (separator == NULL)
202 dot = strchr (spec, '.');
203 /* If there's no colon but there is a `.', then first look up the
204 whole spec, in case it's an OWNER name that includes a dot.
205 If that fails, then we'll try again, but interpreting the `.'
206 as a separator. */
207 /* FIXME: accepting `.' as the separator is contrary to POSIX.
208 someday we should drop support for this. */
209 if (dot)
210 maybe_retry = 1;
213 retry:
215 /* Replace separator with a NUL. */
216 if (separator != NULL)
217 *separator = '\0';
219 /* Set U and G to non-zero length strings corresponding to user and
220 group specifiers or to NULL. */
221 u = (*spec == '\0' ? NULL : spec);
223 g = (separator == NULL || *(separator + 1) == '\0'
224 ? NULL
225 : separator + 1);
227 if (u == NULL && g == NULL)
228 return _(E_cannot_omit_both);
230 #ifdef __DJGPP__
231 /* Pretend that we are the user U whose group is G. This makes
232 pwd and grp functions ``know'' about the UID and GID of these. */
233 if (u && !is_number (u))
234 setenv ("USER", u, 1);
235 if (g && !is_number (g))
236 setenv ("GROUP", g, 1);
237 #endif
239 if (u != NULL)
241 pwd = getpwnam (u);
242 if (pwd == NULL)
245 if (!is_number (u))
246 error_msg = E_invalid_user;
247 else
249 int use_login_group;
250 use_login_group = (separator != NULL && g == NULL);
251 if (use_login_group)
252 error_msg = E_bad_spec;
253 else
255 unsigned long int tmp_long;
256 if (xstrtoul (u, NULL, 0, &tmp_long, NULL) != LONGINT_OK
257 || tmp_long > MAXUID)
258 return _(E_invalid_user);
259 *uid = tmp_long;
263 else
265 *uid = pwd->pw_uid;
266 if (g == NULL && separator != NULL)
268 /* A separator was given, but a group was not specified,
269 so get the login group. */
270 *gid = pwd->pw_gid;
271 grp = getgrgid (pwd->pw_gid);
272 if (grp == NULL)
274 /* This is enough room to hold the unsigned decimal
275 representation of any 32-bit quantity and the trailing
276 zero byte. */
277 char uint_buf[21];
278 sprintf (uint_buf, "%u", (unsigned) (pwd->pw_gid));
279 V_STRDUP (groupname, uint_buf);
281 else
283 V_STRDUP (groupname, grp->gr_name);
285 endgrent ();
288 endpwent ();
291 if (g != NULL && error_msg == NULL)
293 /* Explicit group. */
294 grp = getgrnam (g);
295 if (grp == NULL)
297 if (!is_number (g))
298 error_msg = E_invalid_group;
299 else
301 unsigned long int tmp_long;
302 if (xstrtoul (g, NULL, 0, &tmp_long, NULL) != LONGINT_OK
303 || tmp_long > MAXGID)
304 return _(E_invalid_group);
305 *gid = tmp_long;
308 else
309 *gid = grp->gr_gid;
310 endgrent (); /* Save a file descriptor. */
312 if (error_msg == NULL)
313 V_STRDUP (groupname, g);
316 if (error_msg == NULL)
318 if (u != NULL)
320 *username_arg = strdup (u);
321 if (*username_arg == NULL)
322 error_msg = xalloc_msg_memory_exhausted;
325 if (groupname != NULL && error_msg == NULL)
327 *groupname_arg = strdup (groupname);
328 if (*groupname_arg == NULL)
330 if (*username_arg != NULL)
332 free (*username_arg);
333 *username_arg = NULL;
335 error_msg = xalloc_msg_memory_exhausted;
340 if (error_msg && maybe_retry)
342 maybe_retry = 0;
343 separator = dot;
344 error_msg = NULL;
345 goto retry;
348 return _(error_msg);
351 #ifdef TEST
353 # define NULL_CHECK(s) ((s) == NULL ? "(null)" : (s))
356 main (int argc, char **argv)
358 int i;
360 for (i = 1; i < argc; i++)
362 const char *e;
363 char *username, *groupname;
364 uid_t uid;
365 gid_t gid;
366 char *tmp;
368 tmp = strdup (argv[i]);
369 e = parse_user_spec (tmp, &uid, &gid, &username, &groupname);
370 free (tmp);
371 printf ("%s: %u %u %s %s %s\n",
372 argv[i],
373 (unsigned int) uid,
374 (unsigned int) gid,
375 NULL_CHECK (username),
376 NULL_CHECK (groupname),
377 NULL_CHECK (e));
380 exit (0);
383 #endif