+ 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)