(WRITTEN_BY): Rename from AUTHORS.
[platform/upstream/coreutils.git] / src / chgrp.c
1 /* chgrp -- change group ownership of files
2    Copyright (C) 89, 90, 91, 1995-2002 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 WRITTEN_BY _("Written by 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 (EXIT_FAILURE, 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 (EXIT_FAILURE, 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 (EXIT_FAILURE, 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   initialize_main (&argc, &argv);
166   program_name = argv[0];
167   setlocale (LC_ALL, "");
168   bindtextdomain (PACKAGE, LOCALEDIR);
169   textdomain (PACKAGE);
170
171   atexit (close_stdout);
172
173   chopt_init (&chopt);
174
175   while ((optc = getopt_long (argc, argv, "Rcfhv", long_options, NULL)) != -1)
176     {
177       switch (optc)
178         {
179         case 0:
180           break;
181         case REFERENCE_FILE_OPTION:
182           reference_file = optarg;
183           break;
184         case DEREFERENCE_OPTION:
185           chopt.dereference = DEREF_ALWAYS;
186           break;
187         case 'R':
188           chopt.recurse = 1;
189           break;
190         case 'c':
191           chopt.verbosity = V_changes_only;
192           break;
193         case 'f':
194           chopt.force_silent = 1;
195           break;
196         case 'h':
197           chopt.dereference = DEREF_NEVER;
198           break;
199         case 'v':
200           chopt.verbosity = V_high;
201           break;
202         case_GETOPT_HELP_CHAR;
203         case_GETOPT_VERSION_CHAR (PROGRAM_NAME, WRITTEN_BY);
204         default:
205           usage (EXIT_FAILURE);
206         }
207     }
208
209   if (argc - optind + (reference_file ? 1 : 0) <= 1)
210     {
211       error (0, 0, _("too few arguments"));
212       usage (EXIT_FAILURE);
213     }
214
215   if (reference_file)
216     {
217       struct stat ref_stats;
218       if (stat (reference_file, &ref_stats))
219         error (EXIT_FAILURE, errno, _("failed to get attributes of %s"),
220                quote (reference_file));
221
222       chopt.group_name = gid_to_name (ref_stats.st_gid);
223       gid = ref_stats.st_gid;
224     }
225   else
226     {
227       chopt.group_name = argv[optind++];
228       parse_group (chopt.group_name, &gid);
229     }
230
231   for (; optind < argc; ++optind)
232     errors |= change_file_owner (1, argv[optind], (uid_t) -1, gid,
233                                  (uid_t) -1, (gid_t) -1, &chopt);
234
235   chopt_free (&chopt);
236
237   exit (errors);
238 }