dd1d88a946758aa40a655acac2a38513ab5b260b
[gnulib.git] / lib / userspec.c
1 /* userspec.c -- Parse a user and group string.
2    Copyright (C) 1989, 1990, 1991, 1992, 1997, 1998 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 #define isdigit(c) ((c) >= '0' && (c) <= '9')
87
88 #ifndef strdup
89 char *strdup ();
90 #endif
91
92 /* Return nonzero if STR represents an unsigned decimal integer,
93    otherwise return 0. */
94
95 static int
96 is_number (str)
97      const char *str;
98 {
99   for (; *str; str++)
100     if (!isdigit (*str))
101       return 0;
102   return 1;
103 }
104
105 /* Extract from NAME, which has the form "[user][:.][group]",
106    a USERNAME, UID U, GROUPNAME, and GID G.
107    Either user or group, or both, must be present.
108    If the group is omitted but the ":" or "." separator is given,
109    use the given user's login group.
110
111    USERNAME and GROUPNAME will be in newly malloc'd memory.
112    Either one might be NULL instead, indicating that it was not
113    given and the corresponding numeric ID was left unchanged.
114
115    Return NULL if successful, a static error message string if not.  */
116
117 const char *
118 parse_user_spec (spec_arg, uid, gid, username_arg, groupname_arg)
119      const char *spec_arg;
120      uid_t *uid;
121      gid_t *gid;
122      char **username_arg, **groupname_arg;
123 {
124   static const char *tired = "virtual memory exhausted";
125   const char *error_msg;
126   char *spec;                   /* A copy we can write on.  */
127   struct passwd *pwd;
128   struct group *grp;
129   char *g, *u, *separator;
130   char *groupname;
131
132   error_msg = NULL;
133   *username_arg = *groupname_arg = NULL;
134   groupname = NULL;
135
136   V_STRDUP (spec, spec_arg);
137
138   /* Find the separator if there is one.  */
139   separator = strchr (spec, ':');
140   if (separator == NULL)
141     separator = strchr (spec, '.');
142
143   /* Replace separator with a NUL.  */
144   if (separator != NULL)
145     *separator = '\0';
146
147   /* Set U and G to non-zero length strings corresponding to user and
148      group specifiers or to NULL.  */
149   u = (*spec == '\0' ? NULL : spec);
150
151   g = (separator == NULL || *(separator + 1) == '\0'
152        ? NULL
153        : separator + 1);
154
155   if (u == NULL && g == NULL)
156     return "can not omit both user and group";
157
158 #ifdef __DJGPP__
159   /* Pretend that we are the user U whose group is G.  This makes
160      pwd and grp functions ``know'' about the UID and GID of these.  */
161   if (u && !is_number (u))
162     setenv ("USER", u, 1);
163   if (g && !is_number (g))
164     setenv ("GROUP", g, 1);
165 #endif
166
167   if (u != NULL)
168     {
169       pwd = getpwnam (u);
170       if (pwd == NULL)
171         {
172
173           if (!is_number (u))
174             error_msg = "invalid user";
175           else
176             {
177               int use_login_group;
178               use_login_group = (separator != NULL && g == NULL);
179               if (use_login_group)
180                 error_msg = "cannot get the login group of a numeric UID";
181               else
182                 *uid = atoi (u);
183             }
184         }
185       else
186         {
187           *uid = pwd->pw_uid;
188           if (g == NULL && separator != NULL)
189             {
190               /* A separator was given, but a group was not specified,
191                  so get the login group.  */
192               *gid = pwd->pw_gid;
193               grp = getgrgid (pwd->pw_gid);
194               if (grp == NULL)
195                 {
196                   /* This is enough room to hold the unsigned decimal
197                      representation of any 32-bit quantity and the trailing
198                      zero byte.  */
199                   char uint_buf[21];
200                   sprintf (uint_buf, "%u", (unsigned) (pwd->pw_gid));
201                   V_STRDUP (groupname, uint_buf);
202                 }
203               else
204                 {
205                   V_STRDUP (groupname, grp->gr_name);
206                 }
207               endgrent ();
208             }
209         }
210       endpwent ();
211     }
212
213   if (g != NULL && error_msg == NULL)
214     {
215       /* Explicit group.  */
216       grp = getgrnam (g);
217       if (grp == NULL)
218         {
219           if (!is_number (g))
220             error_msg = "invalid group";
221           else
222             *gid = atoi (g);
223         }
224       else
225         *gid = grp->gr_gid;
226       endgrent ();              /* Save a file descriptor.  */
227
228       if (error_msg == NULL)
229         V_STRDUP (groupname, g);
230     }
231
232   if (error_msg == NULL)
233     {
234       if (u != NULL)
235         {
236           *username_arg = strdup (u);
237           if (*username_arg == NULL)
238             error_msg = tired;
239         }
240
241       if (groupname != NULL && error_msg == NULL)
242         {
243           *groupname_arg = strdup (groupname);
244           if (*groupname_arg == NULL)
245             {
246               if (*username_arg != NULL)
247                 {
248                   free (*username_arg);
249                   *username_arg = NULL;
250                 }
251               error_msg = tired;
252             }
253         }
254     }
255
256   return error_msg;
257 }
258
259 #ifdef TEST
260
261 # define NULL_CHECK(s) ((s) == NULL ? "(null)" : (s))
262
263 int
264 main (int argc, char **argv)
265 {
266   int i;
267
268   for (i = 1; i < argc; i++)
269     {
270       const char *e;
271       char *username, *groupname;
272       uid_t uid;
273       gid_t gid;
274       char *tmp;
275
276       tmp = strdup (argv[i]);
277       e = parse_user_spec (tmp, &uid, &gid, &username, &groupname);
278       free (tmp);
279       printf ("%s: %u %u %s %s %s\n",
280               argv[i],
281               (unsigned int) uid,
282               (unsigned int) gid,
283               NULL_CHECK (username),
284               NULL_CHECK (groupname),
285               NULL_CHECK (e));
286     }
287
288   exit (0);
289 }
290
291 #endif