/* userspec.c -- Parse a user and group string.
- Copyright (C) 1989, 1990, 1991, 1992 Free Software Foundation, Inc.
+ Copyright (C) 1989-1992, 1997-1998, 2000, 2002-2004 Free Software
+ Foundation, Inc.
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
- along with this program; if not, write to the Free Software
- Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+ along with this program; if not, write to the Free Software Foundation,
+ Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */
/* Written by David MacKenzie <djm@gnu.ai.mit.edu>. */
-\f
-#ifdef HAVE_CONFIG_H
-#if defined (CONFIG_BROKETS)
-/* We use <config.h> instead of "config.h" so that a compilation
- using -I. -I$srcdir will use ./config.h rather than $srcdir/config.h
- (which it would do because it found this file in $srcdir). */
-#include <config.h>
-#else
-#include "config.h"
-#endif
+
+#if HAVE_CONFIG_H
+# include <config.h>
#endif
-#ifdef __GNUC__
-#define alloca __builtin_alloca
-#else
-#ifdef HAVE_ALLOCA_H
+/* Specification. */
+#include "userspec.h"
+
#include <alloca.h>
-#else
-#ifdef _AIX
- #pragma alloca
-#else
-char *alloca ();
-#endif
-#endif
-#endif
+#include <stdbool.h>
#include <stdio.h>
#include <sys/types.h>
#include <pwd.h>
#include <grp.h>
-#if defined(STDC_HEADERS) || defined(HAVE_STRING_H)
-#include <string.h>
-#ifndef index
-#define index strchr
-#endif
-#else
-#include <strings.h>
+#if HAVE_SYS_PARAM_H
+# include <sys/param.h>
#endif
-#ifdef STDC_HEADERS
+#include <limits.h>
#include <stdlib.h>
-#endif
+#include <string.h>
-#ifdef HAVE_UNISTD_H
-#include <unistd.h>
+#if HAVE_UNISTD_H
+# include <unistd.h>
#endif
+#include "inttostr.h"
+#include "strdup.h"
+#include "xalloc.h"
+#include "xstrtol.h"
+
+#include "gettext.h"
+#define _(msgid) gettext (msgid)
+#define N_(msgid) msgid
+
#ifndef _POSIX_VERSION
struct passwd *getpwnam ();
struct group *getgrnam ();
struct group *getgrgid ();
#endif
-#ifdef _POSIX_SOURCE
-#define endpwent()
-#define endgrent()
+#ifndef HAVE_ENDGRENT
+# define endgrent() ((void) 0)
+#endif
+
+#ifndef HAVE_ENDPWENT
+# define endpwent() ((void) 0)
+#endif
+
+/* The extra casts work around common compiler bugs. */
+#define TYPE_SIGNED(t) (! ((t) 0 < (t) -1))
+/* The outer cast is needed to work around a bug in Cray C 5.0.3.0.
+ It is necessary at least when t == time_t. */
+#define TYPE_MINIMUM(t) ((t) (TYPE_SIGNED (t) \
+ ? ~ (t) 0 << (sizeof (t) * CHAR_BIT - 1) : (t) 0))
+#define TYPE_MAXIMUM(t) ((t) (~ (t) 0 - TYPE_MINIMUM (t)))
+
+#ifndef UID_T_MAX
+# define UID_T_MAX TYPE_MAXIMUM (uid_t)
+#endif
+
+#ifndef GID_T_MAX
+# define GID_T_MAX TYPE_MAXIMUM (gid_t)
+#endif
+
+/* MAXUID may come from limits.h or sys/params.h. */
+#ifndef MAXUID
+# define MAXUID UID_T_MAX
+#endif
+#ifndef MAXGID
+# define MAXGID GID_T_MAX
#endif
/* Perform the equivalent of the statement `dest = strdup (src);',
#define V_STRDUP(dest, src) \
do \
{ \
- int _len = strlen ((src)); \
- (dest) = (char *) alloca (_len + 1); \
- strcpy (dest, src); \
+ size_t size = strlen (src) + 1; \
+ (dest) = (char *) alloca (size); \
+ memcpy (dest, src, size); \
} \
while (0)
-#define isdigit(c) ((c) >= '0' && (c) <= '9')
+/* ISDIGIT differs from isdigit, as follows:
+ - Its arg may be any int or unsigned int; it need not be an unsigned char.
+ - It's guaranteed to evaluate its argument exactly once.
+ - It's typically faster.
+ POSIX says that only '0' through '9' are digits. Prefer ISDIGIT to
+ ISDIGIT_LOCALE unless it's important to use the locale's definition
+ of `digit' even when the host does not conform to POSIX. */
+#define ISDIGIT(c) ((unsigned int) (c) - '0' <= 9)
-char *strdup ();
+#ifdef __DJGPP__
-/* Return nonzero if STR represents an unsigned decimal integer,
- otherwise return 0. */
+/* Return true if STR represents an unsigned decimal integer. */
-static int
-isnumber (str)
- const char *str;
+static bool
+is_number (const char *str)
{
- for (; *str; str++)
- if (!isdigit (*str))
- return 0;
- return 1;
+ do
+ {
+ if (!ISDIGIT (*str))
+ return false;
+ }
+ while (*++str);
+
+ return true;
}
+#endif
/* Extract from NAME, which has the form "[user][:.][group]",
a USERNAME, UID U, GROUPNAME, and GID G.
Either user or group, or both, must be present.
- If the group is omitted but the ":" or "." separator is given,
+ If the group is omitted but the ":" separator is given,
use the given user's login group.
+ If SPEC_ARG contains a `:', then use that as the separator, ignoring
+ any `.'s. If there is no `:', but there is a `.', then first look
+ up the entire SPEC_ARG as a login name. If that look-up fails, then
+ try again interpreting the `.' as a separator.
USERNAME and GROUPNAME will be in newly malloc'd memory.
Either one might be NULL instead, indicating that it was not
Return NULL if successful, a static error message string if not. */
const char *
-parse_user_spec (spec_arg, uid, gid, username_arg, groupname_arg)
- const char *spec_arg;
- uid_t *uid;
- gid_t *gid;
- char **username_arg, **groupname_arg;
+parse_user_spec (const char *spec_arg, uid_t *uid, gid_t *gid,
+ char **username_arg, char **groupname_arg)
{
- static const char *tired = "virtual memory exhausted";
+ static const char *E_invalid_user = N_("invalid user");
+ static const char *E_invalid_group = N_("invalid group");
+ static const char *E_bad_spec =
+ N_("cannot get the login group of a numeric UID");
+ static const char *E_cannot_omit_both =
+ N_("cannot omit both user and group");
+
const char *error_msg;
char *spec; /* A copy we can write on. */
struct passwd *pwd;
struct group *grp;
char *g, *u, *separator;
char *groupname;
+ char *dot = NULL;
error_msg = NULL;
*username_arg = *groupname_arg = NULL;
V_STRDUP (spec, spec_arg);
- /* Find the separator if there is one. */
- separator = index (spec, ':');
+ /* Find the POSIX `:' separator if there is one. */
+ separator = strchr (spec, ':');
+
+ /* If there is no colon, then see if there's a `.'. */
if (separator == NULL)
- separator = index (spec, '.');
+ {
+ dot = strchr (spec, '.');
+ /* If there's no colon but there is a `.', then first look up the
+ whole spec, in case it's an OWNER name that includes a dot.
+ If that fails, then we'll try again, but interpreting the `.'
+ as a separator. This is a compatible extension to POSIX, since
+ the POSIX-required behavior is always tried first. */
+ }
+
+ retry:
/* Replace separator with a NUL. */
if (separator != NULL)
: separator + 1);
if (u == NULL && g == NULL)
- return "can not omit both user and group";
+ return _(E_cannot_omit_both);
+
+#ifdef __DJGPP__
+ /* Pretend that we are the user U whose group is G. This makes
+ pwd and grp functions ``know'' about the UID and GID of these. */
+ if (u && !is_number (u))
+ setenv ("USER", u, 1);
+ if (g && !is_number (g))
+ setenv ("GROUP", g, 1);
+#endif
if (u != NULL)
{
pwd = getpwnam (u);
if (pwd == NULL)
{
-
- if (!isnumber (u))
- error_msg = "invalid user";
+ bool use_login_group = (separator != NULL && g == NULL);
+ if (use_login_group)
+ error_msg = E_bad_spec;
else
{
- int use_login_group;
- use_login_group = (separator != NULL && g == NULL);
- if (use_login_group)
- error_msg = "cannot get the login group of a numeric UID";
- else
- *uid = atoi (u);
+ unsigned long int tmp_long;
+ if (! (xstrtoul (u, NULL, 10, &tmp_long, "") == LONGINT_OK
+ && tmp_long <= MAXUID))
+ return _(E_invalid_user);
+ *uid = tmp_long;
}
}
else
grp = getgrgid (pwd->pw_gid);
if (grp == NULL)
{
- /* This is enough room to hold the unsigned decimal
- representation of any 32-bit quantity and the trailing
- zero byte. */
- char uint_buf[21];
- sprintf (uint_buf, "%u", (unsigned) (pwd->pw_gid));
- V_STRDUP (groupname, uint_buf);
+ char buf[INT_BUFSIZE_BOUND (uintmax_t)];
+ char const *num = umaxtostr (pwd->pw_gid, buf);
+ V_STRDUP (groupname, num);
}
else
{
grp = getgrnam (g);
if (grp == NULL)
{
- if (!isnumber (g))
- error_msg = "invalid group";
- else
- *gid = atoi (g);
+ unsigned long int tmp_long;
+ if (! (xstrtoul (g, NULL, 10, &tmp_long, "") == LONGINT_OK
+ && tmp_long <= MAXGID))
+ return _(E_invalid_group);
+ *gid = tmp_long;
}
else
*gid = grp->gr_gid;
if (error_msg == NULL)
{
if (u != NULL)
- {
- *username_arg = strdup (u);
- if (*username_arg == NULL)
- error_msg = tired;
- }
+ *username_arg = xstrdup (u);
- if (groupname != NULL && error_msg == NULL)
- {
- *groupname_arg = strdup (groupname);
- if (*groupname_arg == NULL)
- {
- if (*username_arg != NULL)
- {
- free (*username_arg);
- *username_arg = NULL;
- }
- error_msg = tired;
- }
- }
+ if (groupname != NULL)
+ *groupname_arg = xstrdup (groupname);
+ }
+
+ if (error_msg && dot)
+ {
+ separator = dot;
+ dot = NULL;
+ error_msg = NULL;
+ goto retry;
}
- return error_msg;
+ return _(error_msg);
}
#ifdef TEST
-#define NULL_CHECK(s) ((s) == NULL ? "(null)" : (s))
+# define NULL_CHECK(s) ((s) == NULL ? "(null)" : (s))
int
main (int argc, char **argv)
tmp = strdup (argv[i]);
e = parse_user_spec (tmp, &uid, &gid, &username, &groupname);
free (tmp);
- printf ("%s: %u %u %s %s %s\n",
+ printf ("%s: %lu %lu %s %s %s\n",
argv[i],
- (unsigned int) uid,
- (unsigned int) gid,
+ (unsigned long int) uid,
+ (unsigned long int) gid,
NULL_CHECK (username),
NULL_CHECK (groupname),
NULL_CHECK (e));