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