+ *uid = unum;
+ if (gid)
+ *gid = gnum;
+ if (username)
+ {
+ *username = u;
+ u = NULL;
+ }
+ if (groupname)
+ {
+ *groupname = gname;
+ gname = NULL;
+ }
+ }
+
+ free (u);
+ free (gname);
+ return _(error_msg);
+}
+
+/* Extract from SPEC, which has the form "[user][:.][group]",
+ a USERNAME, UID U, GROUPNAME, and GID G.
+ If the GID parameter is NULL the entire SPEC is treated as a user.
+ If the USERNAME and GROUPNAME parameters are NULL they're ignored.
+ 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 = gid ? strchr (spec, ':') : NULL;
+ char const *error_msg =
+ parse_with_separator (spec, colon, uid, gid, username, groupname);
+
+ if (gid && !colon && error_msg)
+ {
+ /* 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;