(jm_PREREQ): Add jm_PREREQ_SIG2STR.
[gnulib.git] / lib / userspec.c
blob1d3df6e442c60d5e62682a43819fe520c64866d2
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 #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 says that only '0' through '9' are digits. Prefer ISDIGIT to
138 ISDIGIT_LOCALE unless it's important to use the locale's definition
139 of `digit' even when the host does not conform to POSIX. */
140 #define ISDIGIT(c) ((unsigned) (c) - '0' <= 9)
142 #ifndef strdup
143 char *strdup ();
144 #endif
146 /* Return nonzero if STR represents an unsigned decimal integer,
147 otherwise return 0. */
149 static int
150 is_number (const char *str)
152 for (; *str; str++)
153 if (!ISDIGIT (*str))
154 return 0;
155 return 1;
158 /* Extract from NAME, which has the form "[user][:.][group]",
159 a USERNAME, UID U, GROUPNAME, and GID G.
160 Either user or group, or both, must be present.
161 If the group is omitted but the ":" separator is given,
162 use the given user's login group.
163 If SPEC_ARG contains a `:', then use that as the separator, ignoring
164 any `.'s. If there is no `:', but there is a `.', then first look
165 up the entire SPEC_ARG as a login name. If that look-up fails, then
166 try again interpreting the `.' as a separator.
168 USERNAME and GROUPNAME will be in newly malloc'd memory.
169 Either one might be NULL instead, indicating that it was not
170 given and the corresponding numeric ID was left unchanged.
172 Return NULL if successful, a static error message string if not. */
174 const char *
175 parse_user_spec (const char *spec_arg, uid_t *uid, gid_t *gid,
176 char **username_arg, char **groupname_arg)
178 static const char *E_invalid_user = N_("invalid user");
179 static const char *E_invalid_group = N_("invalid group");
180 static const char *E_bad_spec =
181 N_("cannot get the login group of a numeric UID");
182 static const char *E_cannot_omit_both =
183 N_("cannot omit both user and group");
185 const char *error_msg;
186 char *spec; /* A copy we can write on. */
187 struct passwd *pwd;
188 struct group *grp;
189 char *g, *u, *separator;
190 char *groupname;
191 int maybe_retry = 0;
192 char *dot = NULL;
194 error_msg = NULL;
195 *username_arg = *groupname_arg = NULL;
196 groupname = NULL;
198 V_STRDUP (spec, spec_arg);
200 /* Find the POSIX `:' separator if there is one. */
201 separator = strchr (spec, ':');
203 /* If there is no colon, then see if there's a `.'. */
204 if (separator == NULL)
206 dot = strchr (spec, '.');
207 /* If there's no colon but there is a `.', then first look up the
208 whole spec, in case it's an OWNER name that includes a dot.
209 If that fails, then we'll try again, but interpreting the `.'
210 as a separator. */
211 /* FIXME: accepting `.' as the separator is contrary to POSIX.
212 someday we should drop support for this. */
213 if (dot)
214 maybe_retry = 1;
217 retry:
219 /* Replace separator with a NUL. */
220 if (separator != NULL)
221 *separator = '\0';
223 /* Set U and G to non-zero length strings corresponding to user and
224 group specifiers or to NULL. */
225 u = (*spec == '\0' ? NULL : spec);
227 g = (separator == NULL || *(separator + 1) == '\0'
228 ? NULL
229 : separator + 1);
231 if (u == NULL && g == NULL)
232 return _(E_cannot_omit_both);
234 #ifdef __DJGPP__
235 /* Pretend that we are the user U whose group is G. This makes
236 pwd and grp functions ``know'' about the UID and GID of these. */
237 if (u && !is_number (u))
238 setenv ("USER", u, 1);
239 if (g && !is_number (g))
240 setenv ("GROUP", g, 1);
241 #endif
243 if (u != NULL)
245 pwd = getpwnam (u);
246 if (pwd == NULL)
249 if (!is_number (u))
250 error_msg = E_invalid_user;
251 else
253 int use_login_group;
254 use_login_group = (separator != NULL && g == NULL);
255 if (use_login_group)
256 error_msg = E_bad_spec;
257 else
259 unsigned long int tmp_long;
260 if (xstrtoul (u, NULL, 0, &tmp_long, NULL) != LONGINT_OK
261 || tmp_long > MAXUID)
262 return _(E_invalid_user);
263 *uid = tmp_long;
267 else
269 *uid = pwd->pw_uid;
270 if (g == NULL && separator != NULL)
272 /* A separator was given, but a group was not specified,
273 so get the login group. */
274 *gid = pwd->pw_gid;
275 grp = getgrgid (pwd->pw_gid);
276 if (grp == NULL)
278 /* This is enough room to hold the unsigned decimal
279 representation of any 32-bit quantity and the trailing
280 zero byte. */
281 char uint_buf[21];
282 sprintf (uint_buf, "%u", (unsigned) (pwd->pw_gid));
283 V_STRDUP (groupname, uint_buf);
285 else
287 V_STRDUP (groupname, grp->gr_name);
289 endgrent ();
292 endpwent ();
295 if (g != NULL && error_msg == NULL)
297 /* Explicit group. */
298 grp = getgrnam (g);
299 if (grp == NULL)
301 if (!is_number (g))
302 error_msg = E_invalid_group;
303 else
305 unsigned long int tmp_long;
306 if (xstrtoul (g, NULL, 0, &tmp_long, NULL) != LONGINT_OK
307 || tmp_long > MAXGID)
308 return _(E_invalid_group);
309 *gid = tmp_long;
312 else
313 *gid = grp->gr_gid;
314 endgrent (); /* Save a file descriptor. */
316 if (error_msg == NULL)
317 V_STRDUP (groupname, g);
320 if (error_msg == NULL)
322 if (u != NULL)
324 *username_arg = strdup (u);
325 if (*username_arg == NULL)
326 error_msg = xalloc_msg_memory_exhausted;
329 if (groupname != NULL && error_msg == NULL)
331 *groupname_arg = strdup (groupname);
332 if (*groupname_arg == NULL)
334 if (*username_arg != NULL)
336 free (*username_arg);
337 *username_arg = NULL;
339 error_msg = xalloc_msg_memory_exhausted;
344 if (error_msg && maybe_retry)
346 maybe_retry = 0;
347 separator = dot;
348 error_msg = NULL;
349 goto retry;
352 return _(error_msg);
355 #ifdef TEST
357 # define NULL_CHECK(s) ((s) == NULL ? "(null)" : (s))
360 main (int argc, char **argv)
362 int i;
364 for (i = 1; i < argc; i++)
366 const char *e;
367 char *username, *groupname;
368 uid_t uid;
369 gid_t gid;
370 char *tmp;
372 tmp = strdup (argv[i]);
373 e = parse_user_spec (tmp, &uid, &gid, &username, &groupname);
374 free (tmp);
375 printf ("%s: %u %u %s %s %s\n",
376 argv[i],
377 (unsigned int) uid,
378 (unsigned int) gid,
379 NULL_CHECK (username),
380 NULL_CHECK (groupname),
381 NULL_CHECK (e));
384 exit (0);
387 #endif