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