s/can not/cannot/
[coreutils.git] / lib / userspec.c
blob3e67b1b56305b8269a4c87074680ab3c7be8e358
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)
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 #if ENABLE_NLS
72 # include <libintl.h>
73 # define _(Text) gettext (Text)
74 #else
75 # define _(Text) Text
76 #endif
77 #define N_(Text) Text
79 #ifndef _POSIX_VERSION
80 struct passwd *getpwnam ();
81 struct group *getgrnam ();
82 struct group *getgrgid ();
83 #endif
85 #ifndef HAVE_ENDGRENT
86 # define endgrent() ((void) 0)
87 #endif
89 #ifndef HAVE_ENDPWENT
90 # define endpwent() ((void) 0)
91 #endif
93 #ifndef CHAR_BIT
94 # define CHAR_BIT 8
95 #endif
97 /* The extra casts work around common compiler bugs. */
98 #define TYPE_SIGNED(t) (! ((t) 0 < (t) -1))
99 /* The outer cast is needed to work around a bug in Cray C 5.0.3.0.
100 It is necessary at least when t == time_t. */
101 #define TYPE_MINIMUM(t) ((t) (TYPE_SIGNED (t) \
102 ? ~ (t) 0 << (sizeof (t) * CHAR_BIT - 1) : (t) 0))
103 #define TYPE_MAXIMUM(t) ((t) (~ (t) 0 - TYPE_MINIMUM (t)))
105 #ifndef UID_T_MAX
106 # define UID_T_MAX TYPE_MAXIMUM (uid_t)
107 #endif
109 #ifndef GID_T_MAX
110 # define GID_T_MAX TYPE_MAXIMUM (gid_t)
111 #endif
113 /* MAXUID may come from limits.h or sys/params.h. */
114 #ifndef MAXUID
115 # define MAXUID UID_T_MAX
116 #endif
117 #ifndef MAXGID
118 # define MAXGID GID_T_MAX
119 #endif
121 /* Perform the equivalent of the statement `dest = strdup (src);',
122 but obtaining storage via alloca instead of from the heap. */
124 #define V_STRDUP(dest, src) \
125 do \
127 int _len = strlen ((src)); \
128 (dest) = (char *) alloca (_len + 1); \
129 strcpy (dest, src); \
131 while (0)
133 /* ISDIGIT differs from isdigit, as follows:
134 - Its arg may be any int or unsigned int; it need not be an unsigned char.
135 - It's guaranteed to evaluate its argument exactly once.
136 - It's typically faster.
137 Posix 1003.2-1992 section 2.5.2.1 page 50 lines 1556-1558 says that
138 only '0' through '9' are digits. Prefer ISDIGIT to isdigit unless
139 it's important to use the locale's definition of `digit' even when the
140 host does not conform to Posix. */
141 #define ISDIGIT(c) ((unsigned) (c) - '0' <= 9)
143 #ifndef strdup
144 char *strdup ();
145 #endif
147 /* Return nonzero if STR represents an unsigned decimal integer,
148 otherwise return 0. */
150 static int
151 is_number (const char *str)
153 for (; *str; str++)
154 if (!ISDIGIT (*str))
155 return 0;
156 return 1;
159 /* Extract from NAME, which has the form "[user][:.][group]",
160 a USERNAME, UID U, GROUPNAME, and GID G.
161 Either user or group, or both, must be present.
162 If the group is omitted but the ":" separator is given,
163 use the given user's login group.
164 If SPEC_ARG contains a `:', then use that as the separator, ignoring
165 any `.'s. If there is no `:', but there is a `.', then first look
166 up the entire SPEC_ARG as a login name. If that look-up fails, then
167 try again interpreting the `.' as a separator.
169 USERNAME and GROUPNAME will be in newly malloc'd memory.
170 Either one might be NULL instead, indicating that it was not
171 given and the corresponding numeric ID was left unchanged.
173 Return NULL if successful, a static error message string if not. */
175 const char *
176 parse_user_spec (const char *spec_arg, uid_t *uid, gid_t *gid,
177 char **username_arg, char **groupname_arg)
179 static const char *E_invalid_user = N_("invalid user");
180 static const char *E_invalid_group = N_("invalid group");
181 static const char *E_bad_spec =
182 N_("cannot get the login group of a numeric UID");
183 static const char *E_cannot_omit_both =
184 N_("cannot omit both user and group");
186 const char *error_msg;
187 char *spec; /* A copy we can write on. */
188 struct passwd *pwd;
189 struct group *grp;
190 char *g, *u, *separator;
191 char *groupname;
192 int maybe_retry = 0;
193 char *dot = NULL;
195 error_msg = NULL;
196 *username_arg = *groupname_arg = NULL;
197 groupname = NULL;
199 V_STRDUP (spec, spec_arg);
201 /* Find the POSIX `:' separator if there is one. */
202 separator = strchr (spec, ':');
204 /* If there is no colon, then see if there's a `.'. */
205 if (separator == NULL)
207 dot = strchr (spec, '.');
208 /* If there's no colon but there is a `.', then first look up the
209 whole spec, in case it's an OWNER name that includes a dot.
210 If that fails, then we'll try again, but interpreting the `.'
211 as a separator. */
212 /* FIXME: accepting `.' as the separator is contrary to POSIX.
213 someday we should drop support for this. */
214 if (dot)
215 maybe_retry = 1;
218 retry:
220 /* Replace separator with a NUL. */
221 if (separator != NULL)
222 *separator = '\0';
224 /* Set U and G to non-zero length strings corresponding to user and
225 group specifiers or to NULL. */
226 u = (*spec == '\0' ? NULL : spec);
228 g = (separator == NULL || *(separator + 1) == '\0'
229 ? NULL
230 : separator + 1);
232 if (u == NULL && g == NULL)
233 return _(E_cannot_omit_both);
235 #ifdef __DJGPP__
236 /* Pretend that we are the user U whose group is G. This makes
237 pwd and grp functions ``know'' about the UID and GID of these. */
238 if (u && !is_number (u))
239 setenv ("USER", u, 1);
240 if (g && !is_number (g))
241 setenv ("GROUP", g, 1);
242 #endif
244 if (u != NULL)
246 pwd = getpwnam (u);
247 if (pwd == NULL)
250 if (!is_number (u))
251 error_msg = E_invalid_user;
252 else
254 int use_login_group;
255 use_login_group = (separator != NULL && g == NULL);
256 if (use_login_group)
257 error_msg = E_bad_spec;
258 else
260 unsigned long int tmp_long;
261 if (xstrtoul (u, NULL, 0, &tmp_long, NULL) != LONGINT_OK
262 || tmp_long > MAXUID)
263 return _(E_invalid_user);
264 *uid = tmp_long;
268 else
270 *uid = pwd->pw_uid;
271 if (g == NULL && separator != NULL)
273 /* A separator was given, but a group was not specified,
274 so get the login group. */
275 *gid = pwd->pw_gid;
276 grp = getgrgid (pwd->pw_gid);
277 if (grp == NULL)
279 /* This is enough room to hold the unsigned decimal
280 representation of any 32-bit quantity and the trailing
281 zero byte. */
282 char uint_buf[21];
283 sprintf (uint_buf, "%u", (unsigned) (pwd->pw_gid));
284 V_STRDUP (groupname, uint_buf);
286 else
288 V_STRDUP (groupname, grp->gr_name);
290 endgrent ();
293 endpwent ();
296 if (g != NULL && error_msg == NULL)
298 /* Explicit group. */
299 grp = getgrnam (g);
300 if (grp == NULL)
302 if (!is_number (g))
303 error_msg = E_invalid_group;
304 else
306 unsigned long int tmp_long;
307 if (xstrtoul (g, NULL, 0, &tmp_long, NULL) != LONGINT_OK
308 || tmp_long > MAXGID)
309 return _(E_invalid_group);
310 *gid = tmp_long;
313 else
314 *gid = grp->gr_gid;
315 endgrent (); /* Save a file descriptor. */
317 if (error_msg == NULL)
318 V_STRDUP (groupname, g);
321 if (error_msg == NULL)
323 if (u != NULL)
325 *username_arg = strdup (u);
326 if (*username_arg == NULL)
327 error_msg = xalloc_msg_memory_exhausted;
330 if (groupname != NULL && error_msg == NULL)
332 *groupname_arg = strdup (groupname);
333 if (*groupname_arg == NULL)
335 if (*username_arg != NULL)
337 free (*username_arg);
338 *username_arg = NULL;
340 error_msg = xalloc_msg_memory_exhausted;
345 if (error_msg && maybe_retry)
347 maybe_retry = 0;
348 separator = dot;
349 error_msg = NULL;
350 goto retry;
353 return _(error_msg);
356 #ifdef TEST
358 # define NULL_CHECK(s) ((s) == NULL ? "(null)" : (s))
361 main (int argc, char **argv)
363 int i;
365 for (i = 1; i < argc; i++)
367 const char *e;
368 char *username, *groupname;
369 uid_t uid;
370 gid_t gid;
371 char *tmp;
373 tmp = strdup (argv[i]);
374 e = parse_user_spec (tmp, &uid, &gid, &username, &groupname);
375 free (tmp);
376 printf ("%s: %u %u %s %s %s\n",
377 argv[i],
378 (unsigned int) uid,
379 (unsigned int) gid,
380 NULL_CHECK (username),
381 NULL_CHECK (groupname),
382 NULL_CHECK (e));
385 exit (0);
388 #endif