*** empty log message ***
[gnulib.git] / lib / userspec.c
index 8536030..f00308d 100644 (file)
@@ -1,5 +1,5 @@
 /* userspec.c -- Parse a user and group string.
-   Copyright (C) 1989, 1990, 1991, 1992, 1997, 1998 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
@@ -57,6 +57,14 @@ char *alloca ();
 # 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 ();
@@ -83,7 +91,15 @@ struct group *getgrgid ();
     }                                                                  \
   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 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 ();
@@ -96,7 +112,7 @@ static int
 is_number (const char *str)
 {
   for (; *str; str++)
-    if (!isdigit (*str))
+    if (!ISDIGIT (*str))
       return 0;
   return 1;
 }
@@ -104,8 +120,12 @@ is_number (const char *str)
 /* 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
@@ -114,15 +134,25 @@ is_number (const char *str)
    Return NULL if successful, a static error message string if not.  */
 
 const char *
-parse_user_spec (const char *spec_arg, uid_t *uid, gid_t *gid, char **username_arg, char **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_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 *g, *u, *separator;
   char *groupname;
+  int maybe_retry = 0;
+  char *dot = NULL;
 
   error_msg = NULL;
   *username_arg = *groupname_arg = NULL;
@@ -130,10 +160,24 @@ parse_user_spec (const char *spec_arg, uid_t *uid, gid_t *gid, char **username_a
 
   V_STRDUP (spec, spec_arg);
 
-  /* Find the separator if there is one.  */
+  /* 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 = strchr (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.  */
+      /* 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)
@@ -148,7 +192,7 @@ parse_user_spec (const char *spec_arg, uid_t *uid, gid_t *gid, char **username_a
        : 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
@@ -166,15 +210,18 @@ parse_user_spec (const char *spec_arg, uid_t *uid, gid_t *gid, char **username_a
        {
 
          if (!is_number (u))
-           error_msg = "invalid user";
+           error_msg = _(E_invalid_user);
          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";
+               error_msg = _(E_bad_spec);
              else
-               *uid = atoi (u);
+               {
+                 /* FIXME: don't use atoi!  */
+                 *uid = atoi (u);
+               }
            }
        }
       else
@@ -212,9 +259,12 @@ parse_user_spec (const char *spec_arg, uid_t *uid, gid_t *gid, char **username_a
       if (grp == NULL)
        {
          if (!is_number (g))
-           error_msg = "invalid group";
+           error_msg = _(E_invalid_group);
          else
-           *gid = atoi (g);
+           {
+             /* FIXME: don't use atoi!  */
+             *gid = atoi (g);
+           }
        }
       else
        *gid = grp->gr_gid;
@@ -230,7 +280,7 @@ parse_user_spec (const char *spec_arg, uid_t *uid, gid_t *gid, char **username_a
        {
          *username_arg = strdup (u);
          if (*username_arg == NULL)
-           error_msg = tired;
+           error_msg = _(E_no_memory);
        }
 
       if (groupname != NULL && error_msg == NULL)
@@ -243,11 +293,19 @@ parse_user_spec (const char *spec_arg, uid_t *uid, gid_t *gid, char **username_a
                  free (*username_arg);
                  *username_arg = NULL;
                }
-             error_msg = tired;
+             error_msg = _(E_no_memory);
            }
        }
     }
 
+  if (error_msg && maybe_retry)
+    {
+      maybe_retry = 0;
+      separator = dot;
+      error_msg = NULL;
+      goto retry;
+    }
+
   return error_msg;
 }