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