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