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