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