(usage): Use the PACKAGE_BUGREPORT e-mail address, rather than hard-coding it.
[platform/upstream/coreutils.git] / src / chgrp.c
1 /* chgrp -- change group ownership of files
2    Copyright (C) 89, 90, 91, 1995-2001 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 #include "system.h"
27 #include "error.h"
28 #include "lchown.h"
29 #include "group-member.h"
30 #include "quote.h"
31 #include "xstrtol.h"
32 #include "chown-core.h"
33
34 /* The official name of this program (e.g., no `g' prefix).  */
35 #define PROGRAM_NAME "chgrp"
36
37 #define AUTHORS "David MacKenzie"
38
39 /* MAXUID may come from limits.h *or* sys/params.h (via system.h) above. */
40 #ifndef MAXUID
41 # define MAXUID UID_T_MAX
42 #endif
43 #ifndef MAXGID
44 # define MAXGID GID_T_MAX
45 #endif
46
47 #ifndef _POSIX_VERSION
48 struct group *getgrnam ();
49 #endif
50
51 #if ! HAVE_ENDGRENT
52 # define endgrent() ((void) 0)
53 #endif
54
55 /* The name the program was run with. */
56 char *program_name;
57
58 /* The argument to the --reference option.  Use the group ID of this file.
59    This file must exist.  */
60 static char *reference_file;
61
62 /* For long options that have no equivalent short option, use a
63    non-character as a pseudo short option, starting with CHAR_MAX + 1.  */
64 enum
65 {
66   REFERENCE_FILE_OPTION = CHAR_MAX + 1,
67   DEREFERENCE_OPTION
68 };
69
70 static struct option const long_options[] =
71 {
72   {"recursive", no_argument, 0, 'R'},
73   {"changes", no_argument, 0, 'c'},
74   {"dereference", no_argument, 0, DEREFERENCE_OPTION},
75   {"no-dereference", no_argument, 0, 'h'},
76   {"quiet", no_argument, 0, 'f'},
77   {"silent", no_argument, 0, 'f'},
78   {"reference", required_argument, 0, REFERENCE_FILE_OPTION},
79   {"verbose", no_argument, 0, 'v'},
80   {GETOPT_HELP_OPTION_DECL},
81   {GETOPT_VERSION_OPTION_DECL},
82   {0, 0, 0, 0}
83 };
84
85 /* Set *G according to NAME. */
86
87 static void
88 parse_group (const char *name, gid_t *g)
89 {
90   struct group *grp;
91
92   if (*name == '\0')
93     error (1, 0, _("cannot change to null group"));
94
95   grp = getgrnam (name);
96   if (grp == NULL)
97     {
98       strtol_error s_err;
99       unsigned long int tmp_long;
100
101       if (!ISDIGIT (*name))
102         error (1, 0, _("invalid group name %s"), quote (name));
103
104       s_err = xstrtoul (name, NULL, 0, &tmp_long, NULL);
105       if (s_err != LONGINT_OK)
106         STRTOL_FATAL_ERROR (name, _("group number"), s_err);
107
108       if (tmp_long > MAXGID)
109         error (1, 0, _("invalid group number %s"), quote (name));
110
111       *g = tmp_long;
112     }
113   else
114     *g = grp->gr_gid;
115   endgrent ();          /* Save a file descriptor. */
116 }
117
118 void
119 usage (int status)
120 {
121   if (status != 0)
122     fprintf (stderr, _("Try `%s --help' for more information.\n"),
123              program_name);
124   else
125     {
126       printf (_("\
127 Usage: %s [OPTION]... GROUP FILE...\n\
128   or:  %s [OPTION]... --reference=RFILE FILE...\n\
129 "),
130               program_name, program_name);
131       fputs (_("\
132 Change the group membership of each FILE to GROUP.\n\
133 \n\
134   -c, --changes          like verbose but report only when a change is made\n\
135       --dereference      affect the referent of each symbolic link, rather\n\
136                          than the symbolic link itself\n\
137 "), stdout);
138       fputs (_("\
139   -h, --no-dereference   affect symbolic links instead of any referenced file\n\
140                          (available only on systems that can change the\n\
141                          ownership of a symlink)\n\
142 "), stdout);
143       fputs (_("\
144   -f, --silent, --quiet  suppress most error messages\n\
145       --reference=RFILE  use RFILE's group rather than the specified\n\
146                          GROUP value\n\
147   -R, --recursive        operate on files and directories recursively\n\
148   -v, --verbose          output a diagnostic for every file processed\n\
149 "), stdout);
150       fputs (HELP_OPTION_DESCRIPTION, stdout);
151       fputs (VERSION_OPTION_DESCRIPTION, stdout);
152       printf (_("\nReport bugs to <%s>.\n"), PACKAGE_BUGREPORT);
153     }
154   exit (status);
155 }
156
157 int
158 main (int argc, char **argv)
159 {
160   gid_t gid;
161   int errors = 0;
162   int optc;
163   struct Chown_option chopt;
164
165   program_name = argv[0];
166   setlocale (LC_ALL, "");
167   bindtextdomain (PACKAGE, LOCALEDIR);
168   textdomain (PACKAGE);
169
170   atexit (close_stdout);
171
172   chopt_init (&chopt);
173
174   while ((optc = getopt_long (argc, argv, "Rcfhv", long_options, NULL)) != -1)
175     {
176       switch (optc)
177         {
178         case 0:
179           break;
180         case REFERENCE_FILE_OPTION:
181           reference_file = optarg;
182           break;
183         case DEREFERENCE_OPTION:
184           chopt.dereference = DEREF_ALWAYS;
185           break;
186         case 'R':
187           chopt.recurse = 1;
188           break;
189         case 'c':
190           chopt.verbosity = V_changes_only;
191           break;
192         case 'f':
193           chopt.force_silent = 1;
194           break;
195         case 'h':
196           chopt.dereference = DEREF_NEVER;
197           break;
198         case 'v':
199           chopt.verbosity = V_high;
200           break;
201         case_GETOPT_HELP_CHAR;
202         case_GETOPT_VERSION_CHAR (PROGRAM_NAME, AUTHORS);
203         default:
204           usage (1);
205         }
206     }
207
208   if (argc - optind + (reference_file ? 1 : 0) <= 1)
209     {
210       error (0, 0, _("too few arguments"));
211       usage (1);
212     }
213
214   if (reference_file)
215     {
216       struct stat ref_stats;
217       if (stat (reference_file, &ref_stats))
218         error (1, errno, _("failed to get attributes of %s"),
219                quote (reference_file));
220
221       chopt.group_name = gid_to_name (ref_stats.st_gid);
222       gid = ref_stats.st_gid;
223     }
224   else
225     {
226       chopt.group_name = argv[optind++];
227       parse_group (chopt.group_name, &gid);
228     }
229
230   for (; optind < argc; ++optind)
231     errors |= change_file_owner (1, argv[optind], (uid_t) -1, gid,
232                                  (uid_t) -1, (gid_t) -1, &chopt);
233
234   chopt_free (&chopt);
235
236   exit (errors);
237 }