/* 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 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"
+
+#if HAVE_CONFIG_H
+# include <config.h>
#endif
+
+#ifdef __GNUC__
+# define alloca __builtin_alloca
+#else
+# if HAVE_ALLOCA_H
+# include <alloca.h>
+# else
+# ifdef _AIX
+ # pragma alloca
+# else
+char *alloca ();
+# endif
+# endif
#endif
#include <stdio.h>
#include <pwd.h>
#include <grp.h>
-#if defined(STDC_HEADERS) || defined(HAVE_STRING_H)
-#include <string.h>
-#ifndef index
-#define index strchr
-#endif
+#if HAVE_STRING_H
+# include <string.h>
#else
-#include <strings.h>
+# include <strings.h>
+# ifndef strchr
+# define strchr index
+# endif
#endif
-#ifdef STDC_HEADERS
-#include <stdlib.h>
-#else
-char *malloc ();
+#if STDC_HEADERS
+# include <stdlib.h>
#endif
-#ifdef HAVE_UNISTD_H
-#include <unistd.h>
+#if HAVE_UNISTD_H
+# include <unistd.h>
#endif
+#if ENABLE_NLS
+# include <libintl.h>
+# define _(Text) gettext (Text)
+#else
+# define _(Text) Text
+#endif
+#define N_(Text) Text
+
#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
-#define isdigit(c) ((c) >= '0' && (c) <= '9')
+#ifndef HAVE_ENDPWENT
+# define endpwent() ((void) 0)
+#endif
+/* Perform the equivalent of the statement `dest = strdup (src);',
+ but obtaining storage via alloca instead of from the heap. */
+
+#define V_STRDUP(dest, src) \
+ do \
+ { \
+ int _len = strlen ((src)); \
+ (dest) = (char *) alloca (_len + 1); \
+ strcpy (dest, src); \
+ } \
+ while (0)
+
+/* 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 1003.2-1992 section 2.5.2.1 page 50 lines 1556-1558 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) (c) - '0' <= 9)
+
+#ifndef strdup
char *strdup ();
-static int isnumber ();
+#endif
+
+/* Return nonzero if STR represents an unsigned decimal integer,
+ otherwise return 0. */
+
+static int
+is_number (const char *str)
+{
+ for (; *str; str++)
+ if (!ISDIGIT (*str))
+ return 0;
+ return 1;
+}
/* 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 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
given and the corresponding numeric ID was left unchanged.
- Might write NULs into NAME.
Return NULL if successful, a static error message string if not. */
-char *
-parse_user_spec (name, uid, gid, username, groupname)
- char *name;
- uid_t *uid;
- gid_t *gid;
- char **username, **groupname;
+const char *
+parse_user_spec (const char *spec_arg, uid_t *uid, gid_t *gid,
+ char **username_arg, char **groupname_arg)
{
- static char *tired = "virtual memory exhausted";
+ static const char *E_no_memory = N_("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 *cp;
- int use_login_group = 0;
+ char *g, *u, *separator;
+ char *groupname;
+ int maybe_retry = 0;
+ char *dot = NULL;
+
+ error_msg = NULL;
+ *username_arg = *groupname_arg = NULL;
+ groupname = NULL;
+
+ V_STRDUP (spec, spec_arg);
- *username = *groupname = NULL;
+ /* Find the POSIX `:' separator if there is one. */
+ separator = strchr (spec, ':');
- /* Check whether a group is given. */
- cp = index (name, ':');
- if (cp == NULL)
- cp = index (name, '.');
- if (cp != NULL)
+ /* If there is no colon, then see if there's a `.'. */
+ if (separator == NULL)
{
- *cp++ = '\0';
- if (*cp == '\0')
+ 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. */
+ /* FIXME: accepting `.' as the separator is contrary to POSIX.
+ someday we should drop support for this. */
+ if (dot)
+ maybe_retry = 1;
+ }
+
+ retry:
+
+ /* Replace separator with a NUL. */
+ if (separator != NULL)
+ *separator = '\0';
+
+ /* Set U and G to non-zero length strings corresponding to user and
+ group specifiers or to NULL. */
+ u = (*spec == '\0' ? NULL : spec);
+
+ g = (separator == NULL || *(separator + 1) == '\0'
+ ? NULL
+ : separator + 1);
+
+ if (u == NULL && g == NULL)
+ 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 (cp == name + 1)
- /* Neither user nor group given, just "." or ":". */
- return "can not omit both user and group";
+
+ if (!is_number (u))
+ error_msg = _(E_invalid_user);
else
- /* "user.". */
- use_login_group = 1;
+ {
+ int use_login_group;
+ use_login_group = (separator != NULL && g == NULL);
+ if (use_login_group)
+ error_msg = _(E_bad_spec);
+ else
+ {
+ /* FIXME: don't use atoi! */
+ *uid = atoi (u);
+ }
+ }
}
else
{
- /* Explicit group. */
- *groupname = strdup (cp);
- if (*groupname == NULL)
- return tired;
- grp = getgrnam (cp);
- if (grp == NULL)
+ *uid = pwd->pw_uid;
+ if (g == NULL && separator != NULL)
{
- if (!isnumber (cp))
- return "invalid group";
- *gid = atoi (cp);
+ /* A separator was given, but a group was not specified,
+ so get the login group. */
+ *gid = pwd->pw_gid;
+ 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);
+ }
+ else
+ {
+ V_STRDUP (groupname, grp->gr_name);
+ }
+ endgrent ();
}
- else
- *gid = grp->gr_gid;
- endgrent (); /* Save a file descriptor. */
}
+ endpwent ();
}
- /* Parse the user name, now that any group has been removed. */
-
- if (name[0] == '\0')
- /* No user name was given, just a group. */
- return NULL;
-
- *username = strdup (name);
- if (*username == NULL)
- return tired;
-
- pwd = getpwnam (name);
- if (pwd == NULL)
- {
- if (!isnumber (name))
- return "invalid user";
- if (use_login_group)
- return "cannot get the login group of a numeric UID";
- *uid = atoi (name);
- }
- else
+ if (g != NULL && error_msg == NULL)
{
- *uid = pwd->pw_uid;
- if (use_login_group)
+ /* Explicit group. */
+ grp = getgrnam (g);
+ if (grp == NULL)
{
- *gid = pwd->pw_gid;
- grp = getgrgid (pwd->pw_gid);
- if (grp == NULL)
+ if (!is_number (g))
+ error_msg = _(E_invalid_group);
+ else
{
- *groupname = malloc (15);
- if (*groupname == NULL)
- return tired;
- sprintf (*groupname, "%u", (unsigned) (pwd->pw_gid));
+ /* FIXME: don't use atoi! */
+ *gid = atoi (g);
}
- else
+ }
+ else
+ *gid = grp->gr_gid;
+ endgrent (); /* Save a file descriptor. */
+
+ if (error_msg == NULL)
+ V_STRDUP (groupname, g);
+ }
+
+ if (error_msg == NULL)
+ {
+ if (u != NULL)
+ {
+ *username_arg = strdup (u);
+ if (*username_arg == NULL)
+ error_msg = _(E_no_memory);
+ }
+
+ if (groupname != NULL && error_msg == NULL)
+ {
+ *groupname_arg = strdup (groupname);
+ if (*groupname_arg == NULL)
{
- *groupname = strdup (grp->gr_name);
- if (*groupname == NULL)
- return tired;
+ if (*username_arg != NULL)
+ {
+ free (*username_arg);
+ *username_arg = NULL;
+ }
+ error_msg = _(E_no_memory);
}
- endgrent ();
}
}
- endpwent ();
- return NULL;
+
+ if (error_msg && maybe_retry)
+ {
+ maybe_retry = 0;
+ separator = dot;
+ error_msg = NULL;
+ goto retry;
+ }
+
+ return error_msg;
}
-/* Return nonzero if STR represents an unsigned decimal integer,
- otherwise return 0. */
+#ifdef TEST
-static int
-isnumber (str)
- char *str;
+# define NULL_CHECK(s) ((s) == NULL ? "(null)" : (s))
+
+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: %u %u %s %s %s\n",
+ argv[i],
+ (unsigned int) uid,
+ (unsigned int) gid,
+ NULL_CHECK (username),
+ NULL_CHECK (groupname),
+ NULL_CHECK (e));
+ }
+
+ exit (0);
}
+
+#endif