(mempcpy): Don't define if a system header defines it.
[gnulib.git] / lib / userspec.c
1 /* userspec.c -- Parse a user and group string.
2    Copyright (C) 1989-1992, 1997-1998, 2000, 2002-2004 Free Software
3    Foundation, Inc.
4
5    This program is free software; you can redistribute it and/or modify
6    it under the terms of the GNU General Public License as published by
7    the Free Software Foundation; either version 2, or (at your option)
8    any later version.
9
10    This program is distributed in the hope that it will be useful,
11    but WITHOUT ANY WARRANTY; without even the implied warranty of
12    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13    GNU General Public License for more details.
14
15    You should have received a copy of the GNU General Public License
16    along with this program; if not, write to the Free Software Foundation,
17    Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.  */
18
19 /* Written by David MacKenzie <djm@gnu.ai.mit.edu>.  */
20
21 #if HAVE_CONFIG_H
22 # include <config.h>
23 #endif
24
25 /* Specification.  */
26 #include "userspec.h"
27
28 #include <alloca.h>
29
30 #include <stdio.h>
31 #include <sys/types.h>
32 #include <pwd.h>
33 #include <grp.h>
34
35 #if HAVE_SYS_PARAM_H
36 # include <sys/param.h>
37 #endif
38
39 #include <limits.h>
40 #include <stdlib.h>
41 #include <string.h>
42
43 #if HAVE_UNISTD_H
44 # include <unistd.h>
45 #endif
46
47 #include "strdup.h"
48 #include "xalloc.h"
49 #include "xstrtol.h"
50
51 #include "gettext.h"
52 #define _(msgid) gettext (msgid)
53 #define N_(msgid) msgid
54
55 #ifndef _POSIX_VERSION
56 struct passwd *getpwnam ();
57 struct group *getgrnam ();
58 struct group *getgrgid ();
59 #endif
60
61 #ifndef HAVE_ENDGRENT
62 # define endgrent() ((void) 0)
63 #endif
64
65 #ifndef HAVE_ENDPWENT
66 # define endpwent() ((void) 0)
67 #endif
68
69 /* The extra casts work around common compiler bugs.  */
70 #define TYPE_SIGNED(t) (! ((t) 0 < (t) -1))
71 /* The outer cast is needed to work around a bug in Cray C 5.0.3.0.
72    It is necessary at least when t == time_t.  */
73 #define TYPE_MINIMUM(t) ((t) (TYPE_SIGNED (t) \
74                               ? ~ (t) 0 << (sizeof (t) * CHAR_BIT - 1) : (t) 0))
75 #define TYPE_MAXIMUM(t) ((t) (~ (t) 0 - TYPE_MINIMUM (t)))
76
77 #ifndef UID_T_MAX
78 # define UID_T_MAX TYPE_MAXIMUM (uid_t)
79 #endif
80
81 #ifndef GID_T_MAX
82 # define GID_T_MAX TYPE_MAXIMUM (gid_t)
83 #endif
84
85 /* MAXUID may come from limits.h or sys/params.h.  */
86 #ifndef MAXUID
87 # define MAXUID UID_T_MAX
88 #endif
89 #ifndef MAXGID
90 # define MAXGID GID_T_MAX
91 #endif
92
93 /* Perform the equivalent of the statement `dest = strdup (src);',
94    but obtaining storage via alloca instead of from the heap.  */
95
96 #define V_STRDUP(dest, src)                                             \
97   do                                                                    \
98     {                                                                   \
99       int _len = strlen ((src));                                        \
100       (dest) = (char *) alloca (_len + 1);                              \
101       strcpy (dest, src);                                               \
102     }                                                                   \
103   while (0)
104
105 /* ISDIGIT differs from isdigit, as follows:
106    - Its arg may be any int or unsigned int; it need not be an unsigned char.
107    - It's guaranteed to evaluate its argument exactly once.
108    - It's typically faster.
109    POSIX says that only '0' through '9' are digits.  Prefer ISDIGIT to
110    ISDIGIT_LOCALE unless it's important to use the locale's definition
111    of `digit' even when the host does not conform to POSIX.  */
112 #define ISDIGIT(c) ((unsigned) (c) - '0' <= 9)
113
114 /* Return nonzero if STR represents an unsigned decimal integer,
115    otherwise return 0. */
116
117 static int
118 is_number (const char *str)
119 {
120   for (; *str; str++)
121     if (!ISDIGIT (*str))
122       return 0;
123   return 1;
124 }
125
126 /* Extract from NAME, which has the form "[user][:.][group]",
127    a USERNAME, UID U, GROUPNAME, and GID G.
128    Either user or group, or both, must be present.
129    If the group is omitted but the ":" separator is given,
130    use the given user's login group.
131    If SPEC_ARG contains a `:', then use that as the separator, ignoring
132    any `.'s.  If there is no `:', but there is a `.', then first look
133    up the entire SPEC_ARG as a login name.  If that look-up fails, then
134    try again interpreting the `.'  as a separator.
135
136    USERNAME and GROUPNAME will be in newly malloc'd memory.
137    Either one might be NULL instead, indicating that it was not
138    given and the corresponding numeric ID was left unchanged.
139
140    Return NULL if successful, a static error message string if not.  */
141
142 const char *
143 parse_user_spec (const char *spec_arg, uid_t *uid, gid_t *gid,
144                  char **username_arg, char **groupname_arg)
145 {
146   static const char *E_invalid_user = N_("invalid user");
147   static const char *E_invalid_group = N_("invalid group");
148   static const char *E_bad_spec =
149     N_("cannot get the login group of a numeric UID");
150   static const char *E_cannot_omit_both =
151     N_("cannot omit both user and group");
152
153   const char *error_msg;
154   char *spec;                   /* A copy we can write on.  */
155   struct passwd *pwd;
156   struct group *grp;
157   char *g, *u, *separator;
158   char *groupname;
159   char *dot = NULL;
160
161   error_msg = NULL;
162   *username_arg = *groupname_arg = NULL;
163   groupname = NULL;
164
165   V_STRDUP (spec, spec_arg);
166
167   /* Find the POSIX `:' separator if there is one.  */
168   separator = strchr (spec, ':');
169
170   /* If there is no colon, then see if there's a `.'.  */
171   if (separator == NULL)
172     {
173       dot = strchr (spec, '.');
174       /* If there's no colon but there is a `.', then first look up the
175          whole spec, in case it's an OWNER name that includes a dot.
176          If that fails, then we'll try again, but interpreting the `.'
177          as a separator.  This is a compatible extension to POSIX, since
178          the POSIX-required behavior is always tried first.  */
179     }
180
181  retry:
182
183   /* Replace separator with a NUL.  */
184   if (separator != NULL)
185     *separator = '\0';
186
187   /* Set U and G to non-zero length strings corresponding to user and
188      group specifiers or to NULL.  */
189   u = (*spec == '\0' ? NULL : spec);
190
191   g = (separator == NULL || *(separator + 1) == '\0'
192        ? NULL
193        : separator + 1);
194
195   if (u == NULL && g == NULL)
196     return _(E_cannot_omit_both);
197
198 #ifdef __DJGPP__
199   /* Pretend that we are the user U whose group is G.  This makes
200      pwd and grp functions ``know'' about the UID and GID of these.  */
201   if (u && !is_number (u))
202     setenv ("USER", u, 1);
203   if (g && !is_number (g))
204     setenv ("GROUP", g, 1);
205 #endif
206
207   if (u != NULL)
208     {
209       pwd = getpwnam (u);
210       if (pwd == NULL)
211         {
212
213           if (!is_number (u))
214             error_msg = E_invalid_user;
215           else
216             {
217               int use_login_group;
218               use_login_group = (separator != NULL && g == NULL);
219               if (use_login_group)
220                 error_msg = E_bad_spec;
221               else
222                 {
223                   unsigned long int tmp_long;
224                   if (xstrtoul (u, NULL, 0, &tmp_long, NULL) != LONGINT_OK
225                       || tmp_long > MAXUID)
226                     return _(E_invalid_user);
227                   *uid = tmp_long;
228                 }
229             }
230         }
231       else
232         {
233           *uid = pwd->pw_uid;
234           if (g == NULL && separator != NULL)
235             {
236               /* A separator was given, but a group was not specified,
237                  so get the login group.  */
238               *gid = pwd->pw_gid;
239               grp = getgrgid (pwd->pw_gid);
240               if (grp == NULL)
241                 {
242                   /* This is enough room to hold the unsigned decimal
243                      representation of any 32-bit quantity and the trailing
244                      zero byte.  */
245                   char uint_buf[21];
246                   sprintf (uint_buf, "%u", (unsigned) (pwd->pw_gid));
247                   V_STRDUP (groupname, uint_buf);
248                 }
249               else
250                 {
251                   V_STRDUP (groupname, grp->gr_name);
252                 }
253               endgrent ();
254             }
255         }
256       endpwent ();
257     }
258
259   if (g != NULL && error_msg == NULL)
260     {
261       /* Explicit group.  */
262       grp = getgrnam (g);
263       if (grp == NULL)
264         {
265           if (!is_number (g))
266             error_msg = E_invalid_group;
267           else
268             {
269               unsigned long int tmp_long;
270               if (xstrtoul (g, NULL, 0, &tmp_long, NULL) != LONGINT_OK
271                   || tmp_long > MAXGID)
272                 return _(E_invalid_group);
273               *gid = tmp_long;
274             }
275         }
276       else
277         *gid = grp->gr_gid;
278       endgrent ();              /* Save a file descriptor.  */
279
280       if (error_msg == NULL)
281         V_STRDUP (groupname, g);
282     }
283
284   if (error_msg == NULL)
285     {
286       if (u != NULL)
287         {
288           *username_arg = strdup (u);
289           if (*username_arg == NULL)
290             error_msg = xalloc_msg_memory_exhausted;
291         }
292
293       if (groupname != NULL && error_msg == NULL)
294         {
295           *groupname_arg = strdup (groupname);
296           if (*groupname_arg == NULL)
297             {
298               if (*username_arg != NULL)
299                 {
300                   free (*username_arg);
301                   *username_arg = NULL;
302                 }
303               error_msg = xalloc_msg_memory_exhausted;
304             }
305         }
306     }
307
308   if (error_msg && dot)
309     {
310       separator = dot;
311       dot = NULL;
312       error_msg = NULL;
313       goto retry;
314     }
315
316   return _(error_msg);
317 }
318
319 #ifdef TEST
320
321 # define NULL_CHECK(s) ((s) == NULL ? "(null)" : (s))
322
323 int
324 main (int argc, char **argv)
325 {
326   int i;
327
328   for (i = 1; i < argc; i++)
329     {
330       const char *e;
331       char *username, *groupname;
332       uid_t uid;
333       gid_t gid;
334       char *tmp;
335
336       tmp = strdup (argv[i]);
337       e = parse_user_spec (tmp, &uid, &gid, &username, &groupname);
338       free (tmp);
339       printf ("%s: %u %u %s %s %s\n",
340               argv[i],
341               (unsigned int) uid,
342               (unsigned int) gid,
343               NULL_CHECK (username),
344               NULL_CHECK (groupname),
345               NULL_CHECK (e));
346     }
347
348   exit (0);
349 }
350
351 #endif