1 /* chgrp -- change group ownership of files
2 Copyright (C) 89, 90, 91, 95, 96, 97, 1998 Free Software Foundation, Inc.
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)
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.
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. */
18 /* Written by David MacKenzie <djm@gnu.ai.mit.edu>. */
22 #include <sys/types.h>
31 #include "group-member.h"
33 /* MAXUID may come from limits.h *or* sys/params.h (via system.h) above. */
35 # define MAXUID INT_MAX
38 #ifndef _POSIX_VERSION
39 struct group
*getgrnam ();
43 # define endgrent() ((void) 0)
47 # define LCHOWN(FILE, OWNER, GROUP) lchown (FILE, OWNER, GROUP)
49 # define LCHOWN(FILE, OWNER, GROUP) 1
56 CH_NO_CHANGE_REQUESTED
61 /* Print a message for each file that is processed. */
64 /* Print a message for each file whose attributes we change. */
67 /* Do not be verbose. This is the default. */
71 static int change_dir_group
PARAMS ((const char *dir
, int group
,
72 const struct stat
*statp
));
74 /* The name the program was run with. */
77 /* If nonzero, and the systems has support for it, change the ownership
78 of symbolic links rather than any files they point to. */
79 static int change_symlinks
;
81 /* If nonzero, change the ownership of directories recursively. */
84 /* If nonzero, force silence (no error messages). */
85 static int force_silent
;
87 /* Level of verbosity. */
88 static enum Verbosity verbosity
= V_off
;
90 /* The name of the group to which ownership of the files is being given. */
91 static const char *groupname
;
93 /* The argument to the --reference option. Use the group ID of this file.
94 This file must exist. */
95 static char *reference_file
;
97 /* If nonzero, display usage information and exit. */
100 /* If nonzero, print the version on standard output and exit. */
101 static int show_version
;
103 static struct option
const long_options
[] =
105 {"recursive", no_argument
, 0, 'R'},
106 {"changes", no_argument
, 0, 'c'},
107 {"no-dereference", no_argument
, 0, 'h'},
108 {"silent", no_argument
, 0, 'f'},
109 {"quiet", no_argument
, 0, 'f'},
110 {"reference", required_argument
, 0, CHAR_MAX
+ 1},
111 {"verbose", no_argument
, 0, 'v'},
112 {"help", no_argument
, &show_help
, 1},
113 {"version", no_argument
, &show_version
, 1},
117 /* Tell the user how/if the group of FILE has been changed.
118 CHANGED describes what (if anything) has happened. */
121 describe_change (const char *file
, enum Change_status changed
)
127 fmt
= _("group of %s changed to %s\n");
130 fmt
= _("failed to change group of %s to %s\n");
132 case CH_NO_CHANGE_REQUESTED
:
133 fmt
= _("group of %s retained as %s\n");
138 printf (fmt
, file
, groupname
);
141 /* Set *G according to NAME. */
144 parse_group (const char *name
, int *g
)
150 error (1, 0, _("can not change to null group"));
152 grp
= getgrnam (name
);
156 unsigned long int tmp_long
;
158 if (!ISDIGIT (*name
))
159 error (1, 0, _("invalid group name `%s'"), name
);
161 s_err
= xstrtoul (name
, NULL
, 0, &tmp_long
, NULL
);
162 if (s_err
!= LONGINT_OK
)
163 STRTOL_FATAL_ERROR (name
, _("group number"), s_err
);
165 if (tmp_long
> INT_MAX
)
166 error (1, 0, _("invalid group number `%s'"), name
);
172 endgrent (); /* Save a file descriptor. */
175 /* Change the ownership of FILE to GID GROUP.
176 If it is a directory and -R is given, recurse.
177 Return 0 if successful, 1 if errors occurred. */
180 change_file_group (const char *file
, int group
)
182 struct stat file_stats
;
185 if (lstat (file
, &file_stats
))
187 if (force_silent
== 0)
188 error (0, errno
, "%s", file
);
192 if (group
!= file_stats
.st_gid
)
197 fail
= LCHOWN (file
, (uid_t
) -1, group
);
199 fail
= chown (file
, (uid_t
) -1, group
);
201 if (verbosity
== V_high
|| (verbosity
== V_changes_only
&& !fail
))
202 describe_change (file
, (fail
? CH_FAILED
: CH_SUCCEEDED
));
207 if (force_silent
== 0)
209 /* Give a more specific message. Some systems set errno
210 to EPERM for both `inaccessible file' and `user not a member
211 of the specified group' errors. */
212 if (errno
== EPERM
&& !group_member (group
))
214 error (0, errno
, _("you are not a member of group `%s'"),
217 else if (errno
== EINVAL
&& group
> MAXUID
)
219 error (0, 0, _("%s: invalid group number"), groupname
);
223 error (0, errno
, "%s", file
);
228 else if (verbosity
== V_high
)
230 describe_change (file
, CH_NO_CHANGE_REQUESTED
);
233 if (recurse
&& S_ISDIR (file_stats
.st_mode
))
234 errors
|= change_dir_group (file
, group
, &file_stats
);
239 /* Recursively change the ownership of the files in directory DIR
241 STATP points to the results of lstat on DIR.
242 Return 0 if successful, 1 if errors occurred. */
245 change_dir_group (const char *dir
, int group
, const struct stat
*statp
)
247 char *name_space
, *namep
;
248 char *path
; /* Full path of each entry to process. */
249 unsigned dirlength
; /* Length of `dir' and '\0'. */
250 unsigned filelength
; /* Length of each pathname to process. */
251 unsigned pathlength
; /* Bytes allocated for `path'. */
255 name_space
= savedir (dir
, (unsigned int) statp
->st_size
);
256 if (name_space
== NULL
)
260 if (force_silent
== 0)
261 error (0, errno
, "%s", dir
);
265 error (1, 0, _("virtual memory exhausted"));
268 dirlength
= strlen (dir
) + 1; /* + 1 is for the trailing '/'. */
269 pathlength
= dirlength
+ 1;
270 /* Give `path' a dummy value; it will be reallocated before first use. */
271 path
= xmalloc (pathlength
);
273 path
[dirlength
- 1] = '/';
275 for (namep
= name_space
; *namep
; namep
+= filelength
- dirlength
)
277 filelength
= dirlength
+ strlen (namep
) + 1;
278 if (filelength
> pathlength
)
280 pathlength
= filelength
* 2;
281 path
= xrealloc (path
, pathlength
);
283 strcpy (path
+ dirlength
, namep
);
284 errors
|= change_file_group (path
, group
);
295 fprintf (stderr
, _("Try `%s --help' for more information.\n"),
300 Usage: %s [OPTION]... GROUP FILE...\n\
301 or: %s [OPTION]... --reference=RFILE FILE...\n\
303 program_name
, program_name
);
305 Change the group membership of each FILE to GROUP.\n\
307 -c, --changes like verbose but report only when a change is made\n\
308 -h, --no-dereference affect symbolic links instead of any referenced file\n\
309 (available only on systems with lchown system call)\n\
310 -f, --silent, --quiet suppress most error messages\n\
311 --reference=RFILE use RFILE's group instead of using a GROUP value\n\
312 -R, --recursive change files and directories recursively\n\
313 -v, --verbose output a diagnostic for every file processed\n\
314 --help display this help and exit\n\
315 --version output version information and exit\n\
317 puts (_("\nReport bugs to <bug-fileutils@gnu.org>."));
324 main (int argc
, char **argv
)
330 program_name
= argv
[0];
331 setlocale (LC_ALL
, "");
332 bindtextdomain (PACKAGE
, LOCALEDIR
);
333 textdomain (PACKAGE
);
335 recurse
= force_silent
= 0;
337 while ((optc
= getopt_long (argc
, argv
, "Rcfhv", long_options
, NULL
)) != -1)
344 reference_file
= optarg
;
350 verbosity
= V_changes_only
;
368 printf ("chgrp (%s) %s\n", GNU_PACKAGE
, VERSION
);
376 if (argc
- optind
+ (reference_file
? 1 : 0) <= 1)
378 error (0, 0, _("too few arguments"));
385 error (1, 0, _("--no-dereference (-h) is not supported on this system"));
391 struct stat ref_stats
;
392 if (stat (reference_file
, &ref_stats
))
393 error (1, errno
, "%s", reference_file
);
395 group
= ref_stats
.st_gid
;
398 parse_group (argv
[optind
++], &group
);
400 for (; optind
< argc
; ++optind
)
401 errors
|= change_file_group (argv
[optind
], group
);
403 if (verbosity
!= V_off
)