(__strtol): Remove decl; it doesn't work if __strtol
[gnulib.git] / lib / userspec.c
index 60c6ecc..cb1f8a7 100644 (file)
@@ -1,5 +1,5 @@
 /* userspec.c -- Parse a user and group string.
 /* userspec.c -- Parse a user and group string.
-   Copyright (C) 1989, 1990, 1991, 1992 Free Software Foundation, Inc.
+   Copyright (C) 1989-1992, 1997, 1998 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
 
    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
    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>.  */
 
 /* Written by David MacKenzie <djm@gnu.ai.mit.edu>.  */
-\f
+
+#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 <sys/types.h>
 #include <pwd.h>
 #include <grp.h>
 
 #include <stdio.h>
 #include <sys/types.h>
 #include <pwd.h>
 #include <grp.h>
 
-#if defined(USG) || defined(STDC_HEADERS)
-#include <string.h>
-#define index strchr
+#if HAVE_STRING_H
+# include <string.h>
 #else
 #else
-#include <strings.h>
+# include <strings.h>
+# ifndef strchr
+#  define strchr index
+# endif
 #endif
 
 #endif
 
-#ifdef STDC_HEADERS
-#include <stdlib.h>
-#else
-char *malloc ();
+#if STDC_HEADERS
+# include <stdlib.h>
 #endif
 
 #endif
 
-#ifdef HAVE_UNISTD_H
-#include <unistd.h>
+#if HAVE_UNISTD_H
+# include <unistd.h>
 #endif
 
 #ifndef _POSIX_VERSION
 #endif
 
 #ifndef _POSIX_VERSION
@@ -45,15 +63,43 @@ struct group *getgrnam ();
 struct group *getgrgid ();
 #endif
 
 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
 
 #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)
+
 #define isdigit(c) ((c) >= '0' && (c) <= '9')
 
 #define isdigit(c) ((c) >= '0' && (c) <= '9')
 
+#ifndef strdup
 char *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.
 
 /* Extract from NAME, which has the form "[user][:.][group]",
    a USERNAME, UID U, GROUPNAME, and GID G.
@@ -64,115 +110,178 @@ static int isnumber ();
    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.
    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.  */
 
 
    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 *tired = "virtual memory exhausted";
+  const char *error_msg;
+  char *spec;                  /* A copy we can write on.  */
   struct passwd *pwd;
   struct group *grp;
   struct passwd *pwd;
   struct group *grp;
-  char *cp;
-  int use_login_group = 0;
+  char *g, *u, *separator;
+  char *groupname;
+
+  error_msg = NULL;
+  *username_arg = *groupname_arg = NULL;
+  groupname = NULL;
+
+  V_STRDUP (spec, spec_arg);
 
 
-  *username = *groupname = NULL;
+  /* Find the separator if there is one.  */
+  separator = strchr (spec, ':');
+  if (separator == NULL)
+    separator = strchr (spec, '.');
 
 
-  /* Check whether a group is given.  */
-  cp = index (name, ':');
-  if (cp == NULL)
-    cp = index (name, '.');
-  if (cp != NULL)
+  /* 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 "can not omit both user and group";
+
+#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)
     {
     {
-      *cp++ = '\0';
-      if (*cp == '\0')
+      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 = "invalid user";
          else
          else
-           /* "user.".  */
-           use_login_group = 1;
+           {
+             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);
+           }
        }
       else
        {
        }
       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 (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.  */
+      grp = getgrnam (g);
+      if (grp == NULL)
+       {
+         if (!is_number (g))
+           error_msg = "invalid group";
+         else
+           *gid = atoi (g);
+       }
+      else
+       *gid = grp->gr_gid;
+      endgrent ();             /* Save a file descriptor.  */
+
+      if (error_msg == NULL)
+       V_STRDUP (groupname, g);
     }
     }
-  else
+
+  if (error_msg == NULL)
     {
     {
-      *uid = pwd->pw_uid;
-      if (use_login_group)
+      if (u != NULL)
        {
        {
-         *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
+         *username_arg = strdup (u);
+         if (*username_arg == NULL)
+           error_msg = tired;
+       }
+
+      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 = tired;
            }
            }
-         endgrent ();
        }
     }
        }
     }
-  endpwent ();
-  return NULL;
+
+  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