(usage): Include one- or two-line synopsis in --help output.
[platform/upstream/coreutils.git] / src / chown.c
1 /* chown -- change user and group ownership of files
2    Copyright (C) 1989, 1990, 1991, 1995 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
16    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.  */
17
18 /*
19               |                       user
20               | unchanged                 explicit
21  -------------|-------------------------+-------------------------|
22  g unchanged  | ---                     | chown u                 |
23  r            |-------------------------+-------------------------|
24  o explicit   | chgrp g or chown .g     | chown u.g               |
25  u            |-------------------------+-------------------------|
26  p from passwd| ---                     | chown u.                |
27               |-------------------------+-------------------------|
28
29    Written by David MacKenzie <djm@gnu.ai.mit.edu>. */
30
31 #include <config.h>
32 #include <stdio.h>
33 #include <sys/types.h>
34 #include <pwd.h>
35 #include <grp.h>
36 #include <getopt.h>
37
38 #include "system.h"
39 #include "version.h"
40 #include "error.h"
41
42 #ifndef _POSIX_VERSION
43 struct passwd *getpwnam ();
44 struct group *getgrnam ();
45 struct group *getgrgid ();
46 #endif
47
48 #ifdef _POSIX_SOURCE
49 #define endgrent()
50 #define endpwent()
51 #endif
52
53 char *savedir ();
54 char *parse_user_spec ();
55 void strip_trailing_slashes ();
56 char *xmalloc ();
57 char *xrealloc ();
58 int isnumber ();
59
60 static int change_file_owner ();
61 static int change_dir_owner ();
62 static void describe_change ();
63 static void usage ();
64
65 /* The name the program was run with. */
66 char *program_name;
67
68 /* If nonzero, change the ownership of directories recursively. */
69 static int recurse;
70
71 /* If nonzero, force silence (no error messages). */
72 static int force_silent;
73
74 /* If nonzero, describe the files we process. */
75 static int verbose;
76
77 /* If nonzero, describe only owners or groups that change. */
78 static int changes_only;
79
80 /* The name of the user to which ownership of the files is being given. */
81 static char *username;
82
83 /* The name of the group to which ownership of the files is being given. */
84 static char *groupname;
85
86 /* If non-zero, display usage information and exit.  */
87 static int show_help;
88
89 /* If non-zero, print the version on standard output and exit.  */
90 static int show_version;
91
92 static struct option const long_options[] =
93 {
94   {"recursive", no_argument, 0, 'R'},
95   {"changes", no_argument, 0, 'c'},
96   {"silent", no_argument, 0, 'f'},
97   {"quiet", no_argument, 0, 'f'},
98   {"verbose", no_argument, 0, 'v'},
99   {"help", no_argument, &show_help, 1},
100   {"version", no_argument, &show_version, 1},
101   {0, 0, 0, 0}
102 };
103
104 void
105 main (argc, argv)
106      int argc;
107      char **argv;
108 {
109   uid_t user = (uid_t) -1;      /* New uid; -1 if not to be changed. */
110   gid_t group = (uid_t) -1;     /* New gid; -1 if not to be changed. */
111   int errors = 0;
112   int optc;
113   char *e;
114
115   program_name = argv[0];
116   recurse = force_silent = verbose = changes_only = 0;
117
118   while ((optc = getopt_long (argc, argv, "Rcfv", long_options, (int *) 0))
119          != EOF)
120     {
121       switch (optc)
122         {
123         case 0:
124           break;
125         case 'R':
126           recurse = 1;
127           break;
128         case 'c':
129           verbose = 1;
130           changes_only = 1;
131           break;
132         case 'f':
133           force_silent = 1;
134           break;
135         case 'v':
136           verbose = 1;
137           break;
138         default:
139           usage (1);
140         }
141     }
142
143   if (show_version)
144     {
145       printf ("%s\n", version_string);
146       exit (0);
147     }
148
149   if (show_help)
150     usage (0);
151
152   if (optind >= argc - 1)
153     {
154       error (0, 0, "too few arguments");
155       usage (1);
156     }
157
158   e = parse_user_spec (argv[optind], &user, &group, &username, &groupname);
159   if (e)
160     error (1, 0, "%s: %s", argv[optind], e);
161   if (username == NULL)
162     username = "";
163
164   for (++optind; optind < argc; ++optind)
165     {
166       strip_trailing_slashes (argv[optind]);
167       errors |= change_file_owner (argv[optind], user, group);
168     }
169
170   exit (errors);
171 }
172
173 /* Change the ownership of FILE to UID USER and GID GROUP.
174    If it is a directory and -R is given, recurse.
175    Return 0 if successful, 1 if errors occurred. */
176
177 static int
178 change_file_owner (file, user, group)
179      char *file;
180      uid_t user;
181      gid_t group;
182 {
183   struct stat file_stats;
184   uid_t newuser;
185   gid_t newgroup;
186   int errors = 0;
187
188   if (lstat (file, &file_stats))
189     {
190       if (force_silent == 0)
191         error (0, errno, "%s", file);
192       return 1;
193     }
194
195   newuser = user == (uid_t) -1 ? file_stats.st_uid : user;
196   newgroup = group == (gid_t) -1 ? file_stats.st_gid : group;
197   if (newuser != file_stats.st_uid || newgroup != file_stats.st_gid)
198     {
199       if (verbose)
200         describe_change (file, 1);
201       if (chown (file, newuser, newgroup))
202         {
203           if (force_silent == 0)
204             error (0, errno, "%s", file);
205           errors = 1;
206         }
207     }
208   else if (verbose && changes_only == 0)
209     describe_change (file, 0);
210
211   if (recurse && S_ISDIR (file_stats.st_mode))
212     errors |= change_dir_owner (file, user, group, &file_stats);
213   return errors;
214 }
215
216 /* Recursively change the ownership of the files in directory DIR
217    to UID USER and GID GROUP.
218    STATP points to the results of lstat on DIR.
219    Return 0 if successful, 1 if errors occurred. */
220
221 static int
222 change_dir_owner (dir, user, group, statp)
223      char *dir;
224      uid_t user;
225      gid_t group;
226      struct stat *statp;
227 {
228   char *name_space, *namep;
229   char *path;                   /* Full path of each entry to process. */
230   unsigned dirlength;           /* Length of `dir' and '\0'. */
231   unsigned filelength;          /* Length of each pathname to process. */
232   unsigned pathlength;          /* Bytes allocated for `path'. */
233   int errors = 0;
234
235   errno = 0;
236   name_space = savedir (dir, statp->st_size);
237   if (name_space == NULL)
238     {
239       if (errno)
240         {
241           if (force_silent == 0)
242             error (0, errno, "%s", dir);
243           return 1;
244         }
245       else
246         error (1, 0, "virtual memory exhausted");
247     }
248
249   dirlength = strlen (dir) + 1; /* + 1 is for the trailing '/'. */
250   pathlength = dirlength + 1;
251   /* Give `path' a dummy value; it will be reallocated before first use. */
252   path = xmalloc (pathlength);
253   strcpy (path, dir);
254   path[dirlength - 1] = '/';
255
256   for (namep = name_space; *namep; namep += filelength - dirlength)
257     {
258       filelength = dirlength + strlen (namep) + 1;
259       if (filelength > pathlength)
260         {
261           pathlength = filelength * 2;
262           path = xrealloc (path, pathlength);
263         }
264       strcpy (path + dirlength, namep);
265       errors |= change_file_owner (path, user, group);
266     }
267   free (path);
268   free (name_space);
269   return errors;
270 }
271
272 /* Tell the user the user and group names to which ownership of FILE
273    has been given; if CHANGED is zero, FILE had those owners already. */
274
275 static void
276 describe_change (file, changed)
277      char *file;
278      int changed;
279 {
280   if (changed)
281     printf ("owner of %s changed to ", file);
282   else
283     printf ("owner of %s retained as ", file);
284   if (groupname)
285     printf ("%s.%s\n", username, groupname);
286   else
287     printf ("%s\n", username);
288 }
289
290 static void
291 usage (status)
292      int status;
293 {
294   if (status != 0)
295     fprintf (stderr, "Try `%s --help' for more information.\n",
296              program_name);
297   else
298     {
299       printf ("\
300 Usage: %s [OPTION]... OWNER[.[GROUP]] FILE...\n\
301   or:  %s [OPTION]... .[GROUP] FILE...\n\
302 ",
303               program_name, program_name);
304       printf ("\
305 \n\
306   -c, --changes           be verbose whenever change occurs\n\
307   -f, --silent, --quiet   suppress most error messages\n\
308   -v, --verbose           explain what is being done\n\
309   -R, --recursive         change files and directories recursively\n\
310       --help              display this help and exit\n\
311       --version           output version information and exit\n\
312 \n\
313 Owner is unchanged if missing.  Group is unchanged if missing, but changed\n\
314 to login group if implied by a period.  A colon may replace the period.\n");
315     }
316   exit (status);
317 }