]> CyberLeo.Net >> Repos - FreeBSD/releng/7.2.git/blob - contrib/cpio/src/userspec.c
Create releng/7.2 from stable/7 in preparation for 7.2-RELEASE.
[FreeBSD/releng/7.2.git] / contrib / cpio / src / userspec.c
1 /* $FreeBSD$ */
2
3 /* userspec.c -- Parse a user and group string.
4    Copyright (C) 1989, 1990, 1991, 1992, 2001, 2004 Free Software Foundation, Inc.
5
6    This program is free software; you can redistribute it and/or modify
7    it under the terms of the GNU General Public License as published by
8    the Free Software Foundation; either version 2, or (at your option)
9    any later version.
10
11    This program is distributed in the hope that it will be useful,
12    but WITHOUT ANY WARRANTY; without even the implied warranty of
13    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14    GNU General Public License for more details.
15
16    You should have received a copy of the GNU General Public License along
17    with this program; if not, write to the Free Software Foundation, Inc.,
18    59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.  */
19
20 /* Written by David MacKenzie <djm@gnu.ai.mit.edu>.  */
21 \f
22 #include <system.h>
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 <ctype.h>
40 #include <sys/types.h>
41 #include <pwd.h>
42 #include <grp.h>
43
44 #if !HAVE_DECL_GETPWNAM
45 extern struct passwd *getpwnam (const char *name);
46 #endif
47 #if !HAVE_DECL_GETGRNAM
48 extern struct group *getgrnam (const char *name);
49 #endif
50 #if !HAVE_DECL_GETGRGID
51 extern struct group *getgrgid (gid_t gid);
52 #endif
53
54 #ifndef HAVE_ENDPWENT
55 # define endpwent()
56 #endif
57 #ifndef HAVE_ENDGRENT
58 # define endgrent()
59 #endif
60
61 /* Perform the equivalent of the statement `dest = strdup (src);',
62    but obtaining storage via alloca instead of from the heap.  */
63
64 #define V_STRDUP(dest, src)                                             \
65   do                                                                    \
66     {                                                                   \
67       int _len = strlen ((src));                                        \
68       (dest) = (char *) alloca (_len + 1);                              \
69       strcpy (dest, src);                                               \
70     }                                                                   \
71   while (0)
72
73 /* Return nonzero if STR represents an unsigned decimal integer,
74    otherwise return 0. */
75
76 static int
77 cpio_isnumber (const char *str)
78 {
79   for (; *str; str++)
80     if (!isdigit (*str))
81       return 0;
82   return 1;
83 }
84
85 /* Extract from NAME, which has the form "[user][:.][group]",
86    a USERNAME, UID U, GROUPNAME, and GID G.
87    Either user or group, or both, must be present.
88    If the group is omitted but the ":" or "." separator is given,
89    use the given user's login group.
90
91    USERNAME and GROUPNAME will be in newly malloc'd memory.
92    Either one might be NULL instead, indicating that it was not
93    given and the corresponding numeric ID was left unchanged.
94
95    Return NULL if successful, a static error message string if not.  */
96
97 const char *
98 parse_user_spec (const char *spec_arg, uid_t *uid, gid_t *gid,
99                  char **username_arg, char **groupname_arg)
100 {
101   static const char *tired = "virtual memory exhausted";
102   const char *error_msg;
103   char *spec;                   /* A copy we can write on.  */
104   struct passwd *pwd;
105   struct group *grp;
106   char *g, *u, *separator;
107   char *groupname;
108
109   error_msg = NULL;
110   *username_arg = *groupname_arg = NULL;
111   groupname = NULL;
112
113   V_STRDUP (spec, spec_arg);
114
115   /* Find the separator if there is one.  */
116   separator = index (spec, ':');
117   if (separator == NULL)
118     separator = index (spec, '.');
119
120   /* Replace separator with a NUL.  */
121   if (separator != NULL)
122     *separator = '\0';
123
124   /* Set U and G to non-zero length strings corresponding to user and
125      group specifiers or to NULL.  */
126   u = (*spec == '\0' ? NULL : spec);
127
128   g = (separator == NULL || *(separator + 1) == '\0'
129        ? NULL
130        : separator + 1);
131
132   if (u == NULL && g == NULL)
133     return "can not omit both user and group";
134
135   if (u != NULL)
136     {
137       pwd = getpwnam (u);
138       if (pwd == NULL)
139         {
140
141           if (!cpio_isnumber (u))
142             error_msg = _("invalid user");
143           else
144             {
145               int use_login_group;
146               use_login_group = (separator != NULL && g == NULL);
147               if (use_login_group)
148                 error_msg = _("cannot get the login group of a numeric UID");
149               else
150                 *uid = atoi (u);
151             }
152         }
153       else
154         {
155           *uid = pwd->pw_uid;
156           if (g == NULL && separator != NULL)
157             {
158               /* A separator was given, but a group was not specified,
159                  so get the login group.  */
160               *gid = pwd->pw_gid;
161               grp = getgrgid (pwd->pw_gid);
162               if (grp == NULL)
163                 {
164                   /* This is enough room to hold the unsigned decimal
165                      representation of any 32-bit quantity and the trailing
166                      zero byte.  */
167                   char uint_buf[21];
168                   sprintf (uint_buf, "%u", (unsigned) (pwd->pw_gid));
169                   V_STRDUP (groupname, uint_buf);
170                 }
171               else
172                 {
173                   V_STRDUP (groupname, grp->gr_name);
174                 }
175               endgrent ();
176             }
177         }
178       endpwent ();
179     }
180
181   if (g != NULL && error_msg == NULL)
182     {
183       /* Explicit group.  */
184       grp = getgrnam (g);
185       if (grp == NULL)
186         {
187           if (!cpio_isnumber (g))
188             error_msg = _("invalid group");
189           else
190             *gid = atoi (g);
191         }
192       else
193         *gid = grp->gr_gid;
194       endgrent ();              /* Save a file descriptor.  */
195
196       if (error_msg == NULL)
197         V_STRDUP (groupname, g);
198     }
199
200   if (error_msg == NULL)
201     {
202       if (u != NULL)
203         {
204           *username_arg = strdup (u);
205           if (*username_arg == NULL)
206             error_msg = tired;
207         }
208
209       if (groupname != NULL && error_msg == NULL)
210         {
211           *groupname_arg = strdup (groupname);
212           if (*groupname_arg == NULL)
213             {
214               if (*username_arg != NULL)
215                 {
216                   free (*username_arg);
217                   *username_arg = NULL;
218                 }
219               error_msg = tired;
220             }
221         }
222     }
223
224   return error_msg;
225 }
226
227 #ifdef TEST
228
229 #define NULL_CHECK(s) ((s) == NULL ? "(null)" : (s))
230
231 int
232 main (int argc, char **argv)
233 {
234   int i;
235
236   for (i = 1; i < argc; i++)
237     {
238       const char *e;
239       char *username, *groupname;
240       uid_t uid;
241       gid_t gid;
242       char *tmp;
243
244       tmp = strdup (argv[i]);
245       e = parse_user_spec (tmp, &uid, &gid, &username, &groupname);
246       free (tmp);
247       printf ("%s: %u %u %s %s %s\n",
248               argv[i],
249               (unsigned int) uid,
250               (unsigned int) gid,
251               NULL_CHECK (username),
252               NULL_CHECK (groupname),
253               NULL_CHECK (e));
254     }
255
256   exit (0);
257 }
258
259 #endif