(parse_user_spec): Remove debugging printf I'd added.
[gnulib.git] / lib / userspec.c
1 /* userspec.c -- Parse a user and group string.
2    Copyright (C) 1989-1992, 1997, 1998, 2000 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_SYS_PARAM_H
44 # include <sys/param.h>
45 #endif
46
47 #if HAVE_LIMITS_H
48 # include <limits.h>
49 #endif
50
51 #if HAVE_STRING_H
52 # include <string.h>
53 #else
54 # include <strings.h>
55 # ifndef strchr
56 #  define strchr index
57 # endif
58 #endif
59
60 #if STDC_HEADERS
61 # include <stdlib.h>
62 #endif
63
64 #if HAVE_UNISTD_H
65 # include <unistd.h>
66 #endif
67
68 #include "xalloc.h"
69 #include "xstrtol.h"
70
71 #if ENABLE_NLS
72 # include <libintl.h>
73 # define _(Text) gettext (Text)
74 #else
75 # define _(Text) Text
76 #endif
77 #define N_(Text) Text
78
79 #ifndef _POSIX_VERSION
80 struct passwd *getpwnam ();
81 struct group *getgrnam ();
82 struct group *getgrgid ();
83 #endif
84
85 #ifndef HAVE_ENDGRENT
86 # define endgrent() ((void) 0)
87 #endif
88
89 #ifndef HAVE_ENDPWENT
90 # define endpwent() ((void) 0)
91 #endif
92
93 #ifndef CHAR_BIT
94 # define CHAR_BIT 8
95 #endif
96
97 /* The extra casts work around common compiler bugs.  */
98 #define TYPE_SIGNED(t) (! ((t) 0 < (t) -1))
99 /* The outer cast is needed to work around a bug in Cray C 5.0.3.0.
100    It is necessary at least when t == time_t.  */
101 #define TYPE_MINIMUM(t) ((t) (TYPE_SIGNED (t) \
102                               ? ~ (t) 0 << (sizeof (t) * CHAR_BIT - 1) : (t) 0))
103 #define TYPE_MAXIMUM(t) ((t) (~ (t) 0 - TYPE_MINIMUM (t)))
104
105 #ifndef UID_T_MAX
106 # define UID_T_MAX TYPE_MAXIMUM (uid_t)
107 #endif
108
109 #ifndef GID_T_MAX
110 # define GID_T_MAX TYPE_MAXIMUM (gid_t)
111 #endif
112
113 /* MAXUID may come from limits.h or sys/params.h.  */
114 #ifndef MAXUID
115 # define MAXUID UID_T_MAX
116 #endif
117 #ifndef MAXGID
118 # define MAXGID GID_T_MAX
119 #endif
120
121 /* Perform the equivalent of the statement `dest = strdup (src);',
122    but obtaining storage via alloca instead of from the heap.  */
123
124 #define V_STRDUP(dest, src)                                             \
125   do                                                                    \
126     {                                                                   \
127       int _len = strlen ((src));                                        \
128       (dest) = (char *) alloca (_len + 1);                              \
129       strcpy (dest, src);                                               \
130     }                                                                   \
131   while (0)
132
133 /* ISDIGIT differs from isdigit, as follows:
134    - Its arg may be any int or unsigned int; it need not be an unsigned char.
135    - It's guaranteed to evaluate its argument exactly once.
136    - It's typically faster.
137    Posix 1003.2-1992 section 2.5.2.1 page 50 lines 1556-1558 says that
138    only '0' through '9' are digits.  Prefer ISDIGIT to isdigit unless
139    it's important to use the locale's definition of `digit' even when the
140    host does not conform to Posix.  */
141 #define ISDIGIT(c) ((unsigned) (c) - '0' <= 9)
142
143 #ifndef strdup
144 char *strdup ();
145 #endif
146
147 /* Return nonzero if STR represents an unsigned decimal integer,
148    otherwise return 0. */
149
150 static int
151 is_number (const char *str)
152 {
153   for (; *str; str++)
154     if (!ISDIGIT (*str))
155       return 0;
156   return 1;
157 }
158
159 /* Extract from NAME, which has the form "[user][:.][group]",
160    a USERNAME, UID U, GROUPNAME, and GID G.
161    Either user or group, or both, must be present.
162    If the group is omitted but the ":" separator is given,
163    use the given user's login group.
164    If SPEC_ARG contains a `:', then use that as the separator, ignoring
165    any `.'s.  If there is no `:', but there is a `.', then first look
166    up SPEC_ARG as a login name.  If that look-up fails, then try again
167    interpreting the `.'  as a separator.
168
169    USERNAME and GROUPNAME will be in newly malloc'd memory.
170    Either one might be NULL instead, indicating that it was not
171    given and the corresponding numeric ID was left unchanged.
172
173    Return NULL if successful, a static error message string if not.  */
174
175 const char *
176 parse_user_spec (const char *spec_arg, uid_t *uid, gid_t *gid,
177                  char **username_arg, char **groupname_arg)
178 {
179   static const char *E_invalid_user = N_("invalid user");
180   static const char *E_invalid_group = N_("invalid group");
181   static const char *E_bad_spec =
182     N_("cannot get the login group of a numeric UID");
183   static const char *E_cannot_omit_both =
184     N_("cannot omit both user and group");
185
186   const char *error_msg;
187   char *spec;                   /* A copy we can write on.  */
188   struct passwd *pwd;
189   struct group *grp;
190   char *g, *u, *separator;
191   char *groupname;
192   int maybe_retry = 0;
193   char *dot = NULL;
194
195   error_msg = NULL;
196   *username_arg = *groupname_arg = NULL;
197   groupname = NULL;
198
199   V_STRDUP (spec, spec_arg);
200
201   /* Find the POSIX `:' separator if there is one.  */
202   separator = strchr (spec, ':');
203
204   /* If there is no colon, then see if there's a `.'.  */
205   if (separator == NULL)
206     {
207       dot = strchr (spec, '.');
208       /* If there's no colon but there is a `.', then first look up the
209          whole spec, in case it's an OWNER name that includes a dot.
210          If that fails, then we'll try again, but interpreting the `.'
211          as a separator.  */
212       /* FIXME: accepting `.' as the separator is contrary to POSIX.
213          someday we should drop support for this.  */
214       if (dot)
215         maybe_retry = 1;
216     }
217
218  retry:
219
220   /* Replace separator with a NUL.  */
221   if (separator != NULL)
222     *separator = '\0';
223
224   /* Set U and G to non-zero length strings corresponding to user and
225      group specifiers or to NULL.  */
226   u = (*spec == '\0' ? NULL : spec);
227
228   g = (separator == NULL || *(separator + 1) == '\0'
229        ? NULL
230        : separator + 1);
231
232   if (u == NULL && g == NULL)
233     return _(E_cannot_omit_both);
234
235 #ifdef __DJGPP__
236   /* Pretend that we are the user U whose group is G.  This makes
237      pwd and grp functions ``know'' about the UID and GID of these.  */
238   if (u && !is_number (u))
239     setenv ("USER", u, 1);
240   if (g && !is_number (g))
241     setenv ("GROUP", g, 1);
242 #endif
243
244   if (u != NULL)
245     {
246       pwd = getpwnam (u);
247       if (pwd == NULL)
248         {
249
250           if (!is_number (u))
251             error_msg = E_invalid_user;
252           else
253             {
254               int use_login_group;
255               use_login_group = (separator != NULL && g == NULL);
256               if (use_login_group)
257                 error_msg = E_bad_spec;
258               else
259                 {
260                   unsigned long int tmp_long;
261                   if (xstrtoul (u, NULL, 0, &tmp_long, NULL) != LONGINT_OK
262                       || tmp_long > MAXUID)
263                     return _(E_invalid_user);
264                   *uid = tmp_long;
265                 }
266             }
267         }
268       else
269         {
270           *uid = pwd->pw_uid;
271           if (g == NULL && separator != NULL)
272             {
273               /* A separator was given, but a group was not specified,
274                  so get the login group.  */
275               *gid = pwd->pw_gid;
276               grp = getgrgid (pwd->pw_gid);
277               if (grp == NULL)
278                 {
279                   /* This is enough room to hold the unsigned decimal
280                      representation of any 32-bit quantity and the trailing
281                      zero byte.  */
282                   char uint_buf[21];
283                   sprintf (uint_buf, "%u", (unsigned) (pwd->pw_gid));
284                   V_STRDUP (groupname, uint_buf);
285                 }
286               else
287                 {
288                   V_STRDUP (groupname, grp->gr_name);
289                 }
290               endgrent ();
291             }
292         }
293       endpwent ();
294     }
295
296   if (g != NULL && error_msg == NULL)
297     {
298       /* Explicit group.  */
299       grp = getgrnam (g);
300       if (grp == NULL)
301         {
302           if (!is_number (g))
303             error_msg = E_invalid_group;
304           else
305             {
306               unsigned long int tmp_long;
307               if (xstrtoul (u, NULL, 0, &tmp_long, NULL) != LONGINT_OK
308                   || tmp_long > MAXGID)
309                 return _(E_invalid_group);
310               *gid = tmp_long;
311             }
312         }
313       else
314         *gid = grp->gr_gid;
315       endgrent ();              /* Save a file descriptor.  */
316
317       if (error_msg == NULL)
318         V_STRDUP (groupname, g);
319     }
320
321   if (error_msg == NULL)
322     {
323       if (u != NULL)
324         {
325           *username_arg = strdup (u);
326           if (*username_arg == NULL)
327             error_msg = xalloc_msg_memory_exhausted;
328         }
329
330       if (groupname != NULL && error_msg == NULL)
331         {
332           *groupname_arg = strdup (groupname);
333           if (*groupname_arg == NULL)
334             {
335               if (*username_arg != NULL)
336                 {
337                   free (*username_arg);
338                   *username_arg = NULL;
339                 }
340               error_msg = xalloc_msg_memory_exhausted;
341             }
342         }
343     }
344
345   if (error_msg && maybe_retry)
346     {
347       maybe_retry = 0;
348       separator = dot;
349       error_msg = NULL;
350       goto retry;
351     }
352
353   return _(error_msg);
354 }
355
356 #ifdef TEST
357
358 # define NULL_CHECK(s) ((s) == NULL ? "(null)" : (s))
359
360 int
361 main (int argc, char **argv)
362 {
363   int i;
364
365   for (i = 1; i < argc; i++)
366     {
367       const char *e;
368       char *username, *groupname;
369       uid_t uid;
370       gid_t gid;
371       char *tmp;
372
373       tmp = strdup (argv[i]);
374       e = parse_user_spec (tmp, &uid, &gid, &username, &groupname);
375       free (tmp);
376       printf ("%s: %u %u %s %s %s\n",
377               argv[i],
378               (unsigned int) uid,
379               (unsigned int) gid,
380               NULL_CHECK (username),
381               NULL_CHECK (groupname),
382               NULL_CHECK (e));
383     }
384
385   exit (0);
386 }
387
388 #endif