Tizen 2.0 Release
[external/tizen-coreutils.git] / lib / userspec.c
1 /* userspec.c -- Parse a user and group string.
2    Copyright (C) 1989-1992, 1997-1998, 2000, 2002-2007 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.  */
18
19 /* Written by David MacKenzie <djm@gnu.ai.mit.edu>.  */
20
21 #include <config.h>
22
23 /* Specification.  */
24 #include "userspec.h"
25
26 #include <stdbool.h>
27 #include <stdio.h>
28 #include <sys/types.h>
29 #include <pwd.h>
30 #include <grp.h>
31
32 #if HAVE_SYS_PARAM_H
33 # include <sys/param.h>
34 #endif
35
36 #include <limits.h>
37 #include <stdlib.h>
38 #include <string.h>
39
40 #include <unistd.h>
41
42 #include "intprops.h"
43 #include "inttostr.h"
44 #include "xalloc.h"
45 #include "xstrtol.h"
46
47 #include "gettext.h"
48 #define _(msgid) gettext (msgid)
49 #define N_(msgid) msgid
50
51 #ifndef HAVE_ENDGRENT
52 # define endgrent() ((void) 0)
53 #endif
54
55 #ifndef HAVE_ENDPWENT
56 # define endpwent() ((void) 0)
57 #endif
58
59 #ifndef UID_T_MAX
60 # define UID_T_MAX TYPE_MAXIMUM (uid_t)
61 #endif
62
63 #ifndef GID_T_MAX
64 # define GID_T_MAX TYPE_MAXIMUM (gid_t)
65 #endif
66
67 /* MAXUID may come from limits.h or sys/params.h.  */
68 #ifndef MAXUID
69 # define MAXUID UID_T_MAX
70 #endif
71 #ifndef MAXGID
72 # define MAXGID GID_T_MAX
73 #endif
74
75 /* ISDIGIT differs from isdigit, as follows:
76    - Its arg may be any int or unsigned int; it need not be an unsigned char
77      or EOF.
78    - It's typically faster.
79    POSIX says that only '0' through '9' are digits.  Prefer ISDIGIT to
80    isdigit unless it's important to use the locale's definition
81    of `digit' even when the host does not conform to POSIX.  */
82 #define ISDIGIT(c) ((unsigned int) (c) - '0' <= 9)
83
84 #ifdef __DJGPP__
85
86 /* Return true if STR represents an unsigned decimal integer.  */
87
88 static bool
89 is_number (const char *str)
90 {
91   do
92     {
93       if (!ISDIGIT (*str))
94         return false;
95     }
96   while (*++str);
97
98   return true;
99 }
100 #endif
101
102 static char const *
103 parse_with_separator (char const *spec, char const *separator,
104                       uid_t *uid, gid_t *gid,
105                       char **username, char **groupname)
106 {
107   static const char *E_invalid_user = N_("invalid user");
108   static const char *E_invalid_group = N_("invalid group");
109   static const char *E_bad_spec = N_("invalid spec");
110
111   const char *error_msg;
112   struct passwd *pwd;
113   struct group *grp;
114   char *u;
115   char const *g;
116   char *gname = NULL;
117   uid_t unum = *uid;
118   gid_t gnum = *gid;
119
120   error_msg = NULL;
121   *username = *groupname = NULL;
122
123   /* Set U and G to nonzero length strings corresponding to user and
124      group specifiers or to NULL.  If U is not NULL, it is a newly
125      allocated string.  */
126
127   u = NULL;
128   if (separator == NULL)
129     {
130       if (*spec)
131         u = xstrdup (spec);
132     }
133   else
134     {
135       size_t ulen = separator - spec;
136       if (ulen != 0)
137         {
138           u = xmemdup (spec, ulen + 1);
139           u[ulen] = '\0';
140         }
141     }
142
143   g = (separator == NULL || *(separator + 1) == '\0'
144        ? NULL
145        : separator + 1);
146
147 #ifdef __DJGPP__
148   /* Pretend that we are the user U whose group is G.  This makes
149      pwd and grp functions ``know'' about the UID and GID of these.  */
150   if (u && !is_number (u))
151     setenv ("USER", u, 1);
152   if (g && !is_number (g))
153     setenv ("GROUP", g, 1);
154 #endif
155
156   if (u != NULL)
157     {
158       /* If it starts with "+", skip the look-up.  */
159       pwd = (*u == '+' ? NULL : getpwnam (u));
160       if (pwd == NULL)
161         {
162           bool use_login_group = (separator != NULL && g == NULL);
163           if (use_login_group)
164             {
165               /* If there is no group,
166                  then there may not be a trailing ":", either.  */
167               error_msg = E_bad_spec;
168             }
169           else
170             {
171               unsigned long int tmp;
172               if (xstrtoul (u, NULL, 10, &tmp, "") == LONGINT_OK
173                   && tmp <= MAXUID)
174                 unum = tmp;
175               else
176                 error_msg = E_invalid_user;
177             }
178         }
179       else
180         {
181           unum = pwd->pw_uid;
182           if (g == NULL && separator != NULL)
183             {
184               /* A separator was given, but a group was not specified,
185                  so get the login group.  */
186               char buf[INT_BUFSIZE_BOUND (uintmax_t)];
187               gnum = pwd->pw_gid;
188               grp = getgrgid (gnum);
189               gname = xstrdup (grp ? grp->gr_name : umaxtostr (gnum, buf));
190               endgrent ();
191             }
192         }
193       endpwent ();
194     }
195
196   if (g != NULL && error_msg == NULL)
197     {
198       /* Explicit group.  */
199       /* If it starts with "+", skip the look-up.  */
200       grp = (*g == '+' ? NULL : getgrnam (g));
201       if (grp == NULL)
202         {
203           unsigned long int tmp;
204           if (xstrtoul (g, NULL, 10, &tmp, "") == LONGINT_OK && tmp <= MAXGID)
205             gnum = tmp;
206           else
207             error_msg = E_invalid_group;
208         }
209       else
210         gnum = grp->gr_gid;
211       endgrent ();              /* Save a file descriptor.  */
212       gname = xstrdup (g);
213     }
214
215   if (error_msg == NULL)
216     {
217       *uid = unum;
218       *gid = gnum;
219       *username = u;
220       *groupname = gname;
221       u = NULL;
222     }
223   else
224     free (gname);
225
226   free (u);
227   return _(error_msg);
228 }
229
230 /* Extract from SPEC, which has the form "[user][:.][group]",
231    a USERNAME, UID U, GROUPNAME, and GID G.
232    Either user or group, or both, must be present.
233    If the group is omitted but the separator is given,
234    use the given user's login group.
235    If SPEC contains a `:', then use that as the separator, ignoring
236    any `.'s.  If there is no `:', but there is a `.', then first look
237    up the entire SPEC as a login name.  If that look-up fails, then
238    try again interpreting the `.'  as a separator.
239
240    USERNAME and GROUPNAME will be in newly malloc'd memory.
241    Either one might be NULL instead, indicating that it was not
242    given and the corresponding numeric ID was left unchanged.
243
244    Return NULL if successful, a static error message string if not.  */
245
246 char const *
247 parse_user_spec (char const *spec, uid_t *uid, gid_t *gid,
248                  char **username, char **groupname)
249 {
250   char const *colon = strchr (spec, ':');
251   char const *error_msg =
252     parse_with_separator (spec, colon, uid, gid, username, groupname);
253
254   if (!colon && error_msg)
255     {
256       /* If there's no colon but there is a dot, and if looking up the
257          whole spec failed (i.e., the spec is not a owner name that
258          includes a dot), then try again, but interpret the dot as a
259          separator.  This is a compatible extension to POSIX, since
260          the POSIX-required behavior is always tried first.  */
261
262       char const *dot = strchr (spec, '.');
263       if (dot
264           && ! parse_with_separator (spec, dot, uid, gid, username, groupname))
265         error_msg = NULL;
266     }
267
268   return error_msg;
269 }
270
271 #ifdef TEST
272
273 # define NULL_CHECK(s) ((s) == NULL ? "(null)" : (s))
274
275 int
276 main (int argc, char **argv)
277 {
278   int i;
279
280   for (i = 1; i < argc; i++)
281     {
282       const char *e;
283       char *username, *groupname;
284       uid_t uid;
285       gid_t gid;
286       char *tmp;
287
288       tmp = strdup (argv[i]);
289       e = parse_user_spec (tmp, &uid, &gid, &username, &groupname);
290       free (tmp);
291       printf ("%s: %lu %lu %s %s %s\n",
292               argv[i],
293               (unsigned long int) uid,
294               (unsigned long int) gid,
295               NULL_CHECK (username),
296               NULL_CHECK (groupname),
297               NULL_CHECK (e));
298     }
299
300   exit (0);
301 }
302
303 #endif