fe2596858a2d9ba9d05f3333a2fcb64a50226ddc
[gnulib.git] / lib / userspec.c
1 /* userspec.c -- Parse a user and group string.
2    Copyright (C) 1989-1992, 1997, 1998, 2000 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 Foundation,
16    Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.  */
17
18 /* Written by David MacKenzie <djm@gnu.ai.mit.edu>.  */
19
20 #if HAVE_CONFIG_H
21 # include <config.h>
22 #endif
23
24 #ifdef __GNUC__
25 # define alloca __builtin_alloca
26 #else
27 # if HAVE_ALLOCA_H
28 #  include <alloca.h>
29 # else
30 #  ifdef _AIX
31  #  pragma alloca
32 #  else
33 char *alloca ();
34 #  endif
35 # endif
36 #endif
37
38 #include <stdio.h>
39 #include <sys/types.h>
40 #include <pwd.h>
41 #include <grp.h>
42
43 #if HAVE_STRING_H
44 # include <string.h>
45 #else
46 # include <strings.h>
47 # ifndef strchr
48 #  define strchr index
49 # endif
50 #endif
51
52 #if STDC_HEADERS
53 # include <stdlib.h>
54 #endif
55
56 #if HAVE_UNISTD_H
57 # include <unistd.h>
58 #endif
59
60 #ifndef _POSIX_VERSION
61 struct passwd *getpwnam ();
62 struct group *getgrnam ();
63 struct group *getgrgid ();
64 #endif
65
66 #ifndef HAVE_ENDGRENT
67 # define endgrent() ((void) 0)
68 #endif
69
70 #ifndef HAVE_ENDPWENT
71 # define endpwent() ((void) 0)
72 #endif
73
74 /* Perform the equivalent of the statement `dest = strdup (src);',
75    but obtaining storage via alloca instead of from the heap.  */
76
77 #define V_STRDUP(dest, src)                                             \
78   do                                                                    \
79     {                                                                   \
80       int _len = strlen ((src));                                        \
81       (dest) = (char *) alloca (_len + 1);                              \
82       strcpy (dest, src);                                               \
83     }                                                                   \
84   while (0)
85
86 /* ISDIGIT differs from isdigit, as follows:
87    - Its arg may be any int or unsigned int; it need not be an unsigned char.
88    - It's guaranteed to evaluate its argument exactly once.
89    - It's typically faster.
90    Posix 1003.2-1992 section 2.5.2.1 page 50 lines 1556-1558 says that
91    only '0' through '9' are digits.  Prefer ISDIGIT to isdigit unless
92    it's important to use the locale's definition of `digit' even when the
93    host does not conform to Posix.  */
94 #define ISDIGIT(c) ((unsigned) (c) - '0' <= 9)
95
96 #ifndef strdup
97 char *strdup ();
98 #endif
99
100 /* Return nonzero if STR represents an unsigned decimal integer,
101    otherwise return 0. */
102
103 static int
104 is_number (const char *str)
105 {
106   for (; *str; str++)
107     if (!ISDIGIT (*str))
108       return 0;
109   return 1;
110 }
111
112 /* Extract from NAME, which has the form "[user][:.][group]",
113    a USERNAME, UID U, GROUPNAME, and GID G.
114    Either user or group, or both, must be present.
115    If the group is omitted but the ":" or "." separator is given,
116    use the given user's login group.
117
118    USERNAME and GROUPNAME will be in newly malloc'd memory.
119    Either one might be NULL instead, indicating that it was not
120    given and the corresponding numeric ID was left unchanged.
121
122    Return NULL if successful, a static error message string if not.  */
123
124 const char *
125 parse_user_spec (const char *spec_arg, uid_t *uid, gid_t *gid,
126                  char **username_arg, char **groupname_arg)
127 {
128   static const char *tired = "virtual memory exhausted";
129   const char *error_msg;
130   char *spec;                   /* A copy we can write on.  */
131   struct passwd *pwd;
132   struct group *grp;
133   char *g, *u, *separator;
134   char *groupname;
135
136   error_msg = NULL;
137   *username_arg = *groupname_arg = NULL;
138   groupname = NULL;
139
140   V_STRDUP (spec, spec_arg);
141
142   /* Find the separator if there is one.  */
143   separator = strchr (spec, ':');
144   if (separator == NULL)
145     separator = strchr (spec, '.');
146
147   /* Replace separator with a NUL.  */
148   if (separator != NULL)
149     *separator = '\0';
150
151   /* Set U and G to non-zero length strings corresponding to user and
152      group specifiers or to NULL.  */
153   u = (*spec == '\0' ? NULL : spec);
154
155   g = (separator == NULL || *(separator + 1) == '\0'
156        ? NULL
157        : separator + 1);
158
159   if (u == NULL && g == NULL)
160     return "can not omit both user and group";
161
162 #ifdef __DJGPP__
163   /* Pretend that we are the user U whose group is G.  This makes
164      pwd and grp functions ``know'' about the UID and GID of these.  */
165   if (u && !is_number (u))
166     setenv ("USER", u, 1);
167   if (g && !is_number (g))
168     setenv ("GROUP", g, 1);
169 #endif
170
171   if (u != NULL)
172     {
173       pwd = getpwnam (u);
174       if (pwd == NULL)
175         {
176
177           if (!is_number (u))
178             error_msg = "invalid user";
179           else
180             {
181               int use_login_group;
182               use_login_group = (separator != NULL && g == NULL);
183               if (use_login_group)
184                 error_msg = "cannot get the login group of a numeric UID";
185               else
186                 *uid = atoi (u);
187             }
188         }
189       else
190         {
191           *uid = pwd->pw_uid;
192           if (g == NULL && separator != NULL)
193             {
194               /* A separator was given, but a group was not specified,
195                  so get the login group.  */
196               *gid = pwd->pw_gid;
197               grp = getgrgid (pwd->pw_gid);
198               if (grp == NULL)
199                 {
200                   /* This is enough room to hold the unsigned decimal
201                      representation of any 32-bit quantity and the trailing
202                      zero byte.  */
203                   char uint_buf[21];
204                   sprintf (uint_buf, "%u", (unsigned) (pwd->pw_gid));
205                   V_STRDUP (groupname, uint_buf);
206                 }
207               else
208                 {
209                   V_STRDUP (groupname, grp->gr_name);
210                 }
211               endgrent ();
212             }
213         }
214       endpwent ();
215     }
216
217   if (g != NULL && error_msg == NULL)
218     {
219       /* Explicit group.  */
220       grp = getgrnam (g);
221       if (grp == NULL)
222         {
223           if (!is_number (g))
224             error_msg = "invalid group";
225           else
226             *gid = atoi (g);
227         }
228       else
229         *gid = grp->gr_gid;
230       endgrent ();              /* Save a file descriptor.  */
231
232       if (error_msg == NULL)
233         V_STRDUP (groupname, g);
234     }
235
236   if (error_msg == NULL)
237     {
238       if (u != NULL)
239         {
240           *username_arg = strdup (u);
241           if (*username_arg == NULL)
242             error_msg = tired;
243         }
244
245       if (groupname != NULL && error_msg == NULL)
246         {
247           *groupname_arg = strdup (groupname);
248           if (*groupname_arg == NULL)
249             {
250               if (*username_arg != NULL)
251                 {
252                   free (*username_arg);
253                   *username_arg = NULL;
254                 }
255               error_msg = tired;
256             }
257         }
258     }
259
260   return error_msg;
261 }
262
263 #ifdef TEST
264
265 # define NULL_CHECK(s) ((s) == NULL ? "(null)" : (s))
266
267 int
268 main (int argc, char **argv)
269 {
270   int i;
271
272   for (i = 1; i < argc; i++)
273     {
274       const char *e;
275       char *username, *groupname;
276       uid_t uid;
277       gid_t gid;
278       char *tmp;
279
280       tmp = strdup (argv[i]);
281       e = parse_user_spec (tmp, &uid, &gid, &username, &groupname);
282       free (tmp);
283       printf ("%s: %u %u %s %s %s\n",
284               argv[i],
285               (unsigned int) uid,
286               (unsigned int) gid,
287               NULL_CHECK (username),
288               NULL_CHECK (groupname),
289               NULL_CHECK (e));
290     }
291
292   exit (0);
293 }
294
295 #endif