[!MAXUID]: Define after inclusion of system.h to avoid
[platform/upstream/coreutils.git] / src / chgrp.c
1 /* chgrp -- change group ownership of files
2    Copyright (C) 89, 90, 91, 95, 1996 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 #include <config.h>
21 #include <stdio.h>
22 #include <sys/types.h>
23 #include <grp.h>
24 #include <getopt.h>
25
26 #if HAVE_LIMITS_H
27 # include <limits.h>
28 #endif
29
30 #ifndef UINT_MAX
31 # define UINT_MAX ((unsigned int) ~(unsigned int) 0)
32 #endif
33
34 #ifndef INT_MAX
35 # define INT_MAX ((int) (UINT_MAX >> 1))
36 #endif
37
38 #include "system.h"
39 #include "xstrtoul.h"
40 #include "error.h"
41
42 /* MAXUID may come from limits.h *or* sys/params.h (via system.h) above. */
43 #ifndef MAXUID
44 # define MAXUID INT_MAX
45 #endif
46
47 #ifndef _POSIX_VERSION
48 struct group *getgrnam ();
49 #endif
50
51 #ifndef HAVE_ENDGRENT
52 # define endgrent() ((void) 0)
53 #endif
54
55 #ifdef HAVE_LCHOWN
56 # define LCHOWN(FILE, OWNER, GROUP) lchown (FILE, OWNER, GROUP)
57 #else
58 # define LCHOWN(FILE, OWNER, GROUP) 1
59 #endif
60
61 char *group_member ();
62 char *savedir ();
63 char *xmalloc ();
64 char *xrealloc ();
65
66 static int change_dir_group __P ((char *dir, int group, struct stat *statp));
67
68 /* The name the program was run with. */
69 char *program_name;
70
71 /* If nonzero, and the systems has support for it, change the ownership
72    of symbolic links rather than any files they point to.  */
73 static int change_symlinks;
74
75 /* If nonzero, change the ownership of directories recursively. */
76 static int recurse;
77
78 /* If nonzero, force silence (no error messages). */
79 static int force_silent;
80
81 /* If nonzero, describe the files we process. */
82 static int verbose;
83
84 /* If nonzero, describe only owners or groups that change. */
85 static int changes_only;
86
87 /* The name of the group to which ownership of the files is being given. */
88 static char *groupname;
89
90 /* If nonzero, display usage information and exit.  */
91 static int show_help;
92
93 /* If nonzero, print the version on standard output and exit.  */
94 static int show_version;
95
96 static struct option const long_options[] =
97 {
98   {"recursive", no_argument, 0, 'R'},
99   {"changes", no_argument, 0, 'c'},
100   {"no-dereference", no_argument, 0, 'h'},
101   {"silent", no_argument, 0, 'f'},
102   {"quiet", no_argument, 0, 'f'},
103   {"verbose", no_argument, 0, 'v'},
104   {"help", no_argument, &show_help, 1},
105   {"version", no_argument, &show_version, 1},
106   {0, 0, 0, 0}
107 };
108
109 /* Tell the user the group name to which ownership of FILE
110    has been given; if CHANGED is zero, FILE was that group already. */
111
112 static void
113 describe_change (char *file, int changed)
114 {
115   if (changed)
116     printf (_("group of %s changed to %s\n"), file, groupname);
117   else
118     printf (_("group of %s retained as %s\n"), file, groupname);
119 }
120
121 /* Set *G according to NAME. */
122
123 static void
124 parse_group (char *name, int *g)
125 {
126   struct group *grp;
127
128   groupname = name;
129   if (*name == '\0')
130     error (1, 0, _("can not change to null group"));
131
132   grp = getgrnam (name);
133   if (grp == NULL)
134     {
135       strtol_error s_err;
136       unsigned long int tmp_long;
137
138       s_err = xstrtoul (name, NULL, 0, &tmp_long, NULL);
139       *g = tmp_long;
140       if (s_err == LONGINT_OVERFLOW || tmp_long > INT_MAX)
141         {
142           STRTOL_FATAL_ERROR (name, _("group number"), s_err);
143         }
144     }
145   else
146     *g = grp->gr_gid;
147   endgrent ();          /* Save a file descriptor. */
148 }
149
150 /* Change the ownership of FILE to GID GROUP.
151    If it is a directory and -R is given, recurse.
152    Return 0 if successful, 1 if errors occurred. */
153
154 static int
155 change_file_group (char *file, int group)
156 {
157   struct stat file_stats;
158   int errors = 0;
159
160   if (lstat (file, &file_stats))
161     {
162       if (force_silent == 0)
163         error (0, errno, "%s", file);
164       return 1;
165     }
166
167   if (group != file_stats.st_gid)
168     {
169       int fail;
170
171       if (verbose)
172         describe_change (file, 1);
173
174       if (change_symlinks)
175         fail = LCHOWN (file, file_stats.st_uid, group);
176       else
177         fail = chown (file, file_stats.st_uid, group);
178
179       if (fail)
180         {
181           errors = 1;
182           if (force_silent == 0)
183             {
184               /* Give a more specific message.  Some systems set errno
185                  to EPERM for both `inaccessible file' and `user not a member
186                  of the specified group' errors.  */
187               if (errno == EPERM && !group_member (group))
188                 {
189                   error (0, errno, _("you are not a member of group `%s'"),
190                          groupname);
191                 }
192               else if (errno == EINVAL && group > MAXUID)
193                 {
194                   error (0, 0, _("%s: invalid group number"), groupname);
195                 }
196               else
197                 {
198                   error (0, errno, "%s", file);
199                 }
200             }
201         }
202     }
203   else if (verbose && changes_only == 0)
204     describe_change (file, 0);
205
206   if (recurse && S_ISDIR (file_stats.st_mode))
207     errors |= change_dir_group (file, group, &file_stats);
208   return errors;
209 }
210
211 /* Recursively change the ownership of the files in directory DIR
212    to GID GROUP.
213    STATP points to the results of lstat on DIR.
214    Return 0 if successful, 1 if errors occurred. */
215
216 static int
217 change_dir_group (char *dir, int group, struct stat *statp)
218 {
219   char *name_space, *namep;
220   char *path;                   /* Full path of each entry to process. */
221   unsigned dirlength;           /* Length of `dir' and '\0'. */
222   unsigned filelength;          /* Length of each pathname to process. */
223   unsigned pathlength;          /* Bytes allocated for `path'. */
224   int errors = 0;
225
226   errno = 0;
227   name_space = savedir (dir, statp->st_size);
228   if (name_space == NULL)
229     {
230       if (errno)
231         {
232           if (force_silent == 0)
233             error (0, errno, "%s", dir);
234           return 1;
235         }
236       else
237         error (1, 0, _("virtual memory exhausted"));
238     }
239
240   dirlength = strlen (dir) + 1; /* + 1 is for the trailing '/'. */
241   pathlength = dirlength + 1;
242   /* Give `path' a dummy value; it will be reallocated before first use. */
243   path = xmalloc (pathlength);
244   strcpy (path, dir);
245   path[dirlength - 1] = '/';
246
247   for (namep = name_space; *namep; namep += filelength - dirlength)
248     {
249       filelength = dirlength + strlen (namep) + 1;
250       if (filelength > pathlength)
251         {
252           pathlength = filelength * 2;
253           path = xrealloc (path, pathlength);
254         }
255       strcpy (path + dirlength, namep);
256       errors |= change_file_group (path, group);
257     }
258   free (path);
259   free (name_space);
260   return errors;
261 }
262
263 static void
264 usage (int status)
265 {
266   if (status != 0)
267     fprintf (stderr, _("Try `%s --help' for more information.\n"),
268              program_name);
269   else
270     {
271       printf (_("Usage: %s [OPTION]... GROUP FILE...\n"), program_name);
272       printf (_("\
273 Change the group membership of each FILE to GROUP.\n\
274 \n\
275   -c, --changes           like verbose but report only when a change is made\n\
276   -h, --no-dereference    affect symbolic links instead of any referenced file\n\
277                           (available only on systems with lchown system call)\n\
278   -f, --silent, --quiet   suppress most error messages\n\
279   -v, --verbose           output a diagnostic for every file processed\n\
280   -R, --recursive         change files and directories recursively\n\
281       --help              display this help and exit\n\
282       --version           output version information and exit\n"));
283     }
284   exit (status);
285 }
286
287 int
288 main (int argc, char **argv)
289 {
290   int group;
291   int errors = 0;
292   int optc;
293
294   program_name = argv[0];
295   recurse = force_silent = verbose = changes_only = 0;
296
297   while ((optc = getopt_long (argc, argv, "Rcfnv", long_options, (int *) 0))
298          != EOF)
299     {
300       switch (optc)
301         {
302         case 0:
303           break;
304         case 'R':
305           recurse = 1;
306           break;
307         case 'c':
308           verbose = 1;
309           changes_only = 1;
310           break;
311         case 'f':
312           force_silent = 1;
313           break;
314         case 'h':
315           change_symlinks = 1;
316           break;
317         case 'v':
318           verbose = 1;
319           break;
320         default:
321           usage (1);
322         }
323     }
324
325   if (show_version)
326     {
327       printf ("chgrp - %s\n", PACKAGE_VERSION);
328       exit (0);
329     }
330
331   if (show_help)
332     usage (0);
333
334   if (argc - optind <= 1)
335     {
336       error (0, 0, _("too few arguments"));
337       usage (1);
338     }
339
340 #ifndef HAVE_LCHOWN
341   if (change_symlinks)
342     {
343       error (1, 0, _("--no-dereference (-h) is not supported on this system"));
344     }
345 #endif
346
347   parse_group (argv[optind++], &group);
348
349   for (; optind < argc; ++optind)
350     errors |= change_file_group (argv[optind], group);
351
352   exit (errors);
353 }