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