* system.h [rindex, incl, bcopy, bzero]: Ditto.
[gnulib.git] / lib / userspec.c
1 /* userspec.c -- Parse a user and group string.
2    Copyright (C) 1989, 1990, 1991, 1992 Free Software Foundation, Inc.
3
4    This program is free software; you can redistribute it and/or modify
5    it under the terms of the GNU General Public License as published by
6    the Free Software Foundation; either version 2, or (at your option)
7    any later version.
8
9    This program is distributed in the hope that it will be useful,
10    but WITHOUT ANY WARRANTY; without even the implied warranty of
11    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12    GNU General Public License for more details.
13
14    You should have received a copy of the GNU General Public License
15    along with this program; if not, write to the Free Software
16    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.  */
17
18 /* Written by David MacKenzie <djm@gnu.ai.mit.edu>.  */
19 \f
20 #include <stdio.h>
21 #include <sys/types.h>
22 #include <pwd.h>
23 #include <grp.h>
24
25 #if defined(STDC_HEADERS) || defined(HAVE_STRING_H)
26 #include <string.h>
27 #ifndef index
28 #define index strchr
29 #endif
30 #else
31 #include <strings.h>
32 #endif
33
34 #ifdef STDC_HEADERS
35 #include <stdlib.h>
36 #else
37 char *malloc ();
38 #endif
39
40 #ifdef HAVE_UNISTD_H
41 #include <unistd.h>
42 #endif
43
44 #ifndef _POSIX_VERSION
45 struct passwd *getpwnam ();
46 struct group *getgrnam ();
47 struct group *getgrgid ();
48 #endif
49
50 #ifdef _POSIX_SOURCE
51 #define endpwent()
52 #define endgrent()
53 #endif
54
55 #define isdigit(c) ((c) >= '0' && (c) <= '9')
56
57 char *strdup ();
58 static int isnumber ();
59
60 /* Extract from NAME, which has the form "[user][:.][group]",
61    a USERNAME, UID U, GROUPNAME, and GID G.
62    Either user or group, or both, must be present.
63    If the group is omitted but the ":" or "." separator is given,
64    use the given user's login group.
65
66    USERNAME and GROUPNAME will be in newly malloc'd memory.
67    Either one might be NULL instead, indicating that it was not
68    given and the corresponding numeric ID was left unchanged.
69    Might write NULs into NAME.
70
71    Return NULL if successful, a static error message string if not.  */
72
73 char *
74 parse_user_spec (name, uid, gid, username, groupname)
75      char *name;
76      uid_t *uid;
77      gid_t *gid;
78      char **username, **groupname;
79 {
80   static char *tired = "virtual memory exhausted";
81   struct passwd *pwd;
82   struct group *grp;
83   char *cp;
84   int use_login_group = 0;
85
86   *username = *groupname = NULL;
87
88   /* Check whether a group is given.  */
89   cp = index (name, ':');
90   if (cp == NULL)
91     cp = index (name, '.');
92   if (cp != NULL)
93     {
94       *cp++ = '\0';
95       if (*cp == '\0')
96         {
97           if (cp == name + 1)
98             /* Neither user nor group given, just "." or ":".  */
99             return "can not omit both user and group";
100           else
101             /* "user.".  */
102             use_login_group = 1;
103         }
104       else
105         {
106           /* Explicit group.  */
107           *groupname = strdup (cp);
108           if (*groupname == NULL)
109             return tired;
110           grp = getgrnam (cp);
111           if (grp == NULL)
112             {
113               if (!isnumber (cp))
114                 return "invalid group";
115               *gid = atoi (cp);
116             }
117           else
118             *gid = grp->gr_gid;
119           endgrent ();          /* Save a file descriptor.  */
120         }
121     }
122
123   /* Parse the user name, now that any group has been removed.  */
124
125   if (name[0] == '\0')
126     /* No user name was given, just a group.  */
127     return NULL;
128
129   *username = strdup (name);
130   if (*username == NULL)
131     return tired;
132
133   pwd = getpwnam (name);
134   if (pwd == NULL)
135     {
136       if (!isnumber (name))
137         return "invalid user";
138       if (use_login_group)
139         return "cannot get the login group of a numeric UID";
140       *uid = atoi (name);
141     }
142   else
143     {
144       *uid = pwd->pw_uid;
145       if (use_login_group)
146         {
147           *gid = pwd->pw_gid;
148           grp = getgrgid (pwd->pw_gid);
149           if (grp == NULL)
150             {
151               *groupname = malloc (15);
152               if (*groupname == NULL)
153                 return tired;
154               sprintf (*groupname, "%u", pwd->pw_gid);
155             }
156           else
157             {
158               *groupname = strdup (grp->gr_name);
159               if (*groupname == NULL)
160                 return tired;
161             }
162           endgrent ();
163         }
164     }
165   endpwent ();
166   return NULL;
167 }
168
169 /* Return nonzero if STR represents an unsigned decimal integer,
170    otherwise return 0. */
171
172 static int
173 isnumber (str)
174      char *str;
175 {
176   for (; *str; str++)
177     if (!isdigit (*str))
178       return 0;
179   return 1;
180 }