X-Git-Url: http://erislabs.net/gitweb/?a=blobdiff_plain;ds=sidebyside;f=lib%2Fuserspec.c;h=a96b63a003b364f8aa5007ff04f91bd648890a71;hb=b344de996cd51f8a2f2558a3172016b64d99c622;hp=fc93267f839ee543a9281b6b74291010534ddd02;hpb=a89ae060ff117b21f797a78ac9e4004164266694;p=gnulib.git
diff --git a/lib/userspec.c b/lib/userspec.c
index fc93267f8..a96b63a00 100644
--- a/lib/userspec.c
+++ b/lib/userspec.c
@@ -1,10 +1,11 @@
/* 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-2011 Free Software
+ Foundation, Inc.
- This program is free software; you can redistribute it and/or modify
+ 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
- the Free Software Foundation; either version 2, or (at your option)
- any later version.
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
@@ -12,180 +13,297 @@
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, see . */
/* Written by David MacKenzie . */
-
-#ifdef HAVE_CONFIG_H
-#if defined (CONFIG_BROKETS)
-/* We use 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
-#else
-#include "config.h"
-#endif
-#endif
+/* Specification. */
+#include "userspec.h"
+
+#include
#include
#include
#include
#include
-#if defined(STDC_HEADERS) || defined(HAVE_STRING_H)
-#include
-#ifndef index
-#define index strchr
-#endif
-#else
-#include
+#if HAVE_SYS_PARAM_H
+# include
#endif
-#ifdef STDC_HEADERS
+#include
#include
-#else
-char *malloc ();
-#endif
+#include
-#ifdef HAVE_UNISTD_H
#include
+
+#include "intprops.h"
+#include "inttostr.h"
+#include "xalloc.h"
+#include "xstrtol.h"
+
+#include "gettext.h"
+#define _(msgid) gettext (msgid)
+#define N_(msgid) msgid
+
+#ifndef HAVE_ENDGRENT
+# define endgrent() ((void) 0)
#endif
-#ifndef _POSIX_VERSION
-struct passwd *getpwnam ();
-struct group *getgrnam ();
-struct group *getgrgid ();
+#ifndef HAVE_ENDPWENT
+# define endpwent() ((void) 0)
#endif
-#ifdef _POSIX_SOURCE
-#define endpwent()
-#define endgrent()
+#ifndef UID_T_MAX
+# define UID_T_MAX TYPE_MAXIMUM (uid_t)
#endif
-#define isdigit(c) ((c) >= '0' && (c) <= '9')
+#ifndef GID_T_MAX
+# define GID_T_MAX TYPE_MAXIMUM (gid_t)
+#endif
-char *strdup ();
-static int isnumber ();
+/* 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
-/* 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,
- use the given user's login group.
+#ifdef __DJGPP__
- USERNAME and GROUPNAME will be in newly malloc'd memory.
- Either one might be NULL instead, indicating that it was not
- given and the corresponding numeric ID was left unchanged.
- Might write NULs into NAME.
+/* ISDIGIT differs from isdigit, as follows:
+ - Its arg may be any int or unsigned int; it need not be an unsigned char
+ or EOF.
+ - It's typically faster.
+ POSIX says that only '0' through '9' are digits. Prefer ISDIGIT to
+ isdigit 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)
- Return NULL if successful, a static error message string if not. */
+/* Return true if STR represents an unsigned decimal integer. */
-char *
-parse_user_spec (name, uid, gid, username, groupname)
- char *name;
- uid_t *uid;
- gid_t *gid;
- char **username, **groupname;
+static bool
+is_number (const char *str)
{
- static char *tired = "virtual memory exhausted";
+ do
+ {
+ if (!ISDIGIT (*str))
+ return false;
+ }
+ while (*++str);
+
+ return true;
+}
+#endif
+
+static char const *
+parse_with_separator (char const *spec, char const *separator,
+ uid_t *uid, gid_t *gid,
+ char **username, char **groupname)
+{
+ 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_("invalid spec");
+
+ const char *error_msg;
struct passwd *pwd;
struct group *grp;
- char *cp;
- int use_login_group = 0;
+ char *u;
+ char const *g;
+ char *gname = NULL;
+ uid_t unum = *uid;
+ gid_t gnum = *gid;
+ error_msg = NULL;
*username = *groupname = NULL;
- /* Check whether a group is given. */
- cp = index (name, ':');
- if (cp == NULL)
- cp = index (name, '.');
- if (cp != NULL)
+ /* Set U and G to nonzero length strings corresponding to user and
+ group specifiers or to NULL. If U is not NULL, it is a newly
+ allocated string. */
+
+ u = NULL;
+ if (separator == NULL)
{
- *cp++ = '\0';
- if (*cp == '\0')
- {
- if (cp == name + 1)
- /* Neither user nor group given, just "." or ":". */
- return "can not omit both user and group";
- else
- /* "user.". */
- use_login_group = 1;
- }
- else
- {
- /* Explicit group. */
- *groupname = strdup (cp);
- if (*groupname == NULL)
- return tired;
- grp = getgrnam (cp);
- if (grp == NULL)
- {
- if (!isnumber (cp))
- return "invalid group";
- *gid = atoi (cp);
- }
- else
- *gid = grp->gr_gid;
- endgrent (); /* Save a file descriptor. */
- }
+ if (*spec)
+ u = xstrdup (spec);
+ }
+ else
+ {
+ size_t ulen = separator - spec;
+ if (ulen != 0)
+ {
+ u = xmemdup (spec, ulen + 1);
+ u[ulen] = '\0';
+ }
}
- /* Parse the user name, now that any group has been removed. */
+ g = (separator == NULL || *(separator + 1) == '\0'
+ ? NULL
+ : separator + 1);
- if (name[0] == '\0')
- /* No user name was given, just a group. */
- return NULL;
+#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
- *username = strdup (name);
- if (*username == NULL)
- return tired;
+ if (u != NULL)
+ {
+ /* If it starts with "+", skip the look-up. */
+ pwd = (*u == '+' ? NULL : getpwnam (u));
+ if (pwd == NULL)
+ {
+ bool use_login_group = (separator != NULL && g == NULL);
+ if (use_login_group)
+ {
+ /* If there is no group,
+ then there may not be a trailing ":", either. */
+ error_msg = E_bad_spec;
+ }
+ else
+ {
+ unsigned long int tmp;
+ if (xstrtoul (u, NULL, 10, &tmp, "") == LONGINT_OK
+ && tmp <= MAXUID && (uid_t) tmp != (uid_t) -1)
+ unum = tmp;
+ else
+ error_msg = E_invalid_user;
+ }
+ }
+ else
+ {
+ unum = pwd->pw_uid;
+ if (g == NULL && separator != NULL)
+ {
+ /* A separator was given, but a group was not specified,
+ so get the login group. */
+ char buf[INT_BUFSIZE_BOUND (uintmax_t)];
+ gnum = pwd->pw_gid;
+ grp = getgrgid (gnum);
+ gname = xstrdup (grp ? grp->gr_name : umaxtostr (gnum, buf));
+ endgrent ();
+ }
+ }
+ endpwent ();
+ }
- pwd = getpwnam (name);
- if (pwd == NULL)
+ if (g != NULL && error_msg == NULL)
{
- if (!isnumber (name))
- return "invalid user";
- if (use_login_group)
- return "cannot get the login group of a numeric UID";
- *uid = atoi (name);
+ /* Explicit group. */
+ /* If it starts with "+", skip the look-up. */
+ grp = (*g == '+' ? NULL : getgrnam (g));
+ if (grp == NULL)
+ {
+ unsigned long int tmp;
+ if (xstrtoul (g, NULL, 10, &tmp, "") == LONGINT_OK
+ && tmp <= MAXGID && (gid_t) tmp != (gid_t) -1)
+ gnum = tmp;
+ else
+ error_msg = E_invalid_group;
+ }
+ else
+ gnum = grp->gr_gid;
+ endgrent (); /* Save a file descriptor. */
+ gname = xstrdup (g);
+ }
+
+ if (error_msg == NULL)
+ {
+ *uid = unum;
+ *gid = gnum;
+ *username = u;
+ *groupname = gname;
+ u = NULL;
}
else
+ free (gname);
+
+ free (u);
+ return _(error_msg);
+}
+
+/* Extract from SPEC, 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 separator is given,
+ use the given user's login group.
+ If SPEC 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 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
+ given and the corresponding numeric ID was left unchanged.
+
+ Return NULL if successful, a static error message string if not. */
+
+char const *
+parse_user_spec (char const *spec, uid_t *uid, gid_t *gid,
+ char **username, char **groupname)
+{
+ char const *colon = strchr (spec, ':');
+ char const *error_msg =
+ parse_with_separator (spec, colon, uid, gid, username, groupname);
+
+ if (!colon && error_msg)
{
- *uid = pwd->pw_uid;
- if (use_login_group)
- {
- *gid = pwd->pw_gid;
- grp = getgrgid (pwd->pw_gid);
- if (grp == NULL)
- {
- *groupname = malloc (15);
- if (*groupname == NULL)
- return tired;
- sprintf (*groupname, "%u", pwd->pw_gid);
- }
- else
- {
- *groupname = strdup (grp->gr_name);
- if (*groupname == NULL)
- return tired;
- }
- endgrent ();
- }
+ /* If there's no colon but there is a dot, and if looking up the
+ whole spec failed (i.e., the spec is not an owner name that
+ includes a dot), then try again, but interpret the dot as a
+ separator. This is a compatible extension to POSIX, since
+ the POSIX-required behavior is always tried first. */
+
+ char const *dot = strchr (spec, '.');
+ if (dot
+ && ! parse_with_separator (spec, dot, uid, gid, username, groupname))
+ error_msg = NULL;
}
- endpwent ();
- return NULL;
+
+ return error_msg;
}
-/* Return nonzero if STR represents an unsigned decimal integer,
- otherwise return 0. */
+#ifdef TEST
+
+# define NULL_CHECK(s) ((s) == NULL ? "(null)" : (s))
-static int
-isnumber (str)
- char *str;
+int
+main (int argc, char **argv)
{
- for (; *str; str++)
- if (!isdigit (*str))
- return 0;
- return 1;
+ int i;
+
+ for (i = 1; i < argc; i++)
+ {
+ const char *e;
+ char *username, *groupname;
+ uid_t uid;
+ gid_t gid;
+ char *tmp;
+
+ tmp = strdup (argv[i]);
+ e = parse_user_spec (tmp, &uid, &gid, &username, &groupname);
+ free (tmp);
+ printf ("%s: %lu %lu %s %s %s\n",
+ argv[i],
+ (unsigned long int) uid,
+ (unsigned long int) gid,
+ NULL_CHECK (username),
+ NULL_CHECK (groupname),
+ NULL_CHECK (e));
+ }
+
+ exit (0);
}
+
+#endif
+
+/*
+Local Variables:
+indent-tabs-mode: nil
+End:
+*/