Declared lots of external functions and variables static.
[platform/upstream/coreutils.git] / src / chmod.c
1 /* chmod -- change permission modes of files
2    Copyright (C) 1989, 1990, 1991 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 /* Options:
19    -R   Recursively change modes of directory contents.
20    -c   Verbosely describe only files whose modes actually change.
21    -f   Do not print error messages about files.
22    -v   Verbosely describe changed modes.
23
24    David MacKenzie <djm@gnu.ai.mit.edu> */
25
26 #include <stdio.h>
27 #include <getopt.h>
28 #include <sys/types.h>
29 #include "modechange.h"
30 #include "system.h"
31
32 int lstat ();
33
34 char *savedir ();
35 char *xmalloc ();
36 char *xrealloc ();
37 void error ();
38 void mode_string ();
39
40 static int change_file_mode ();
41 static int change_dir_mode ();
42 static void describe_change ();
43 static void usage ();
44
45 /* The name the program was run with. */
46 char *program_name;
47
48 /* If nonzero, change the modes of directories recursively. */
49 static int recurse;
50
51 /* If nonzero, force silence (no error messages). */
52 static int force_silent;
53
54 /* If nonzero, describe the modes we set. */
55 static int verbose;
56
57 /* If nonzero, describe only modes that change. */
58 static int changes_only;
59
60 /* Parse the ASCII mode given on the command line into a linked list
61    of `struct mode_change' and apply that to each file argument. */
62
63 void
64 main (argc, argv)
65      int argc;
66      char **argv;
67 {
68   struct mode_change *changes;
69   int errors = 0;
70   int modeind = 0;              /* Index of the mode argument in `argv'. */
71   int thisind;
72   int c;
73
74   program_name = argv[0];
75   recurse = force_silent = verbose = changes_only = 0;
76
77   while (1)
78     {
79       thisind = optind ? optind : 1;
80
81       c = getopt (argc, argv, "RcfvrwxXstugoa,+-=");
82       if (c == EOF)
83         break;
84
85       switch (c)
86         {
87         case 'r':
88         case 'w':
89         case 'x':
90         case 'X':
91         case 's':
92         case 't':
93         case 'u':
94         case 'g':
95         case 'o':
96         case 'a':
97         case ',':
98         case '+':
99         case '-':
100         case '=':
101           if (modeind != 0 && modeind != thisind)
102             error (1, 0, "invalid mode");
103           modeind = thisind;
104           break;
105         case 'R':
106           recurse = 1;
107           break;
108         case 'c':
109           verbose = 1;
110           changes_only = 1;
111           break;
112         case 'f':
113           force_silent = 1;
114           break;
115         case 'v':
116           verbose = 1;
117           break;
118         default:
119           usage ();
120         }
121     }
122
123   if (modeind == 0)
124     modeind = optind++;
125   if (optind >= argc)
126     usage ();
127
128   changes = mode_compile (argv[modeind],
129                           MODE_MASK_EQUALS | MODE_MASK_PLUS | MODE_MASK_MINUS);
130   if (changes == MODE_INVALID)
131     error (1, 0, "invalid mode");
132   else if (changes == MODE_MEMORY_EXHAUSTED)
133     error (1, 0, "virtual memory exhausted");
134
135   for (; optind < argc; ++optind)
136     errors |= change_file_mode (argv[optind], changes);
137
138   exit (errors);
139 }
140
141 /* Change the mode of FILE according to the list of operations CHANGES.
142    Return 0 if successful, 1 if errors occurred. */
143
144 static int
145 change_file_mode (file, changes)
146      char *file;
147      struct mode_change *changes;
148 {
149   struct stat file_stats;
150   unsigned short newmode;
151   int errors = 0;
152
153   if (lstat (file, &file_stats))
154     {
155       if (force_silent == 0)
156         error (0, errno, "%s", file);
157       return 1;
158     }
159 #ifdef S_ISLNK
160   if (S_ISLNK (file_stats.st_mode))
161     return 0;
162 #endif
163
164   newmode = mode_adjust (file_stats.st_mode, changes);
165
166   if (newmode != (file_stats.st_mode & 07777))
167     {
168       if (verbose)
169         describe_change (file, newmode, 1);
170       if (chmod (file, (int) newmode))
171         {
172           if (force_silent == 0)
173             error (0, errno, "%s", file);
174           errors = 1;
175         }
176     }
177   else if (verbose && changes_only == 0)
178     describe_change (file, newmode, 0);
179
180   if (recurse && S_ISDIR (file_stats.st_mode))
181     errors |= change_dir_mode (file, changes, &file_stats);
182   return errors;
183 }
184
185 /* Recursively change the modes of the files in directory DIR
186    according to the list of operations CHANGES.
187    STATP points to the results of lstat on DIR.
188    Return 0 if successful, 1 if errors occurred. */
189
190 static int
191 change_dir_mode (dir, changes, statp)
192      char *dir;
193      struct mode_change *changes;
194      struct stat *statp;
195 {
196   char *name_space, *namep;
197   char *path;                   /* Full path of each entry to process. */
198   unsigned dirlength;           /* Length of DIR and '\0'. */
199   unsigned filelength;          /* Length of each pathname to process. */
200   unsigned pathlength;          /* Bytes allocated for `path'. */
201   int errors = 0;
202
203   errno = 0;
204   name_space = savedir (dir, statp->st_size);
205   if (name_space == NULL)
206     {
207       if (errno)
208         {
209           if (force_silent == 0)
210             error (0, errno, "%s", dir);
211           return 1;
212         }
213       else
214         error (1, 0, "virtual memory exhausted");
215     }
216
217   dirlength = strlen (dir) + 1; /* + 1 is for the trailing '/'. */
218   pathlength = dirlength + 1;
219   /* Give `path' a dummy value; it will be reallocated before first use. */
220   path = xmalloc (pathlength);
221   strcpy (path, dir);
222   path[dirlength - 1] = '/';
223
224   for (namep = name_space; *namep; namep += filelength - dirlength)
225     {
226       filelength = dirlength + strlen (namep) + 1;
227       if (filelength > pathlength)
228         {
229           pathlength = filelength * 2;
230           path = xrealloc (path, pathlength);
231         }
232       strcpy (path + dirlength, namep);
233       errors |= change_file_mode (path, changes);
234     }
235   free (path);
236   free (name_space);
237   return errors;
238 }
239
240 /* Tell the user the mode MODE that file FILE has been set to;
241    if CHANGED is zero, FILE had that mode already. */
242
243 static void
244 describe_change (file, mode, changed)
245      char *file;
246      unsigned short mode;
247      int changed;
248 {
249   char perms[11];               /* "-rwxrwxrwx" ls-style modes. */
250
251   mode_string (mode, perms);
252   perms[10] = '\0';             /* `mode_string' does not null terminate. */
253   if (changed)
254     printf ("mode of %s changed to %04o (%s)\n",
255             file, mode & 07777, &perms[1]);
256   else
257     printf ("mode of %s retained as %04o (%s)\n",
258             file, mode & 07777, &perms[1]);
259 }
260
261 static void
262 usage ()
263 {
264   fprintf (stderr, "\
265 Usage: %s [-Rcfv] mode file...\n\
266        mode is [ugoa...][[+-=][rwxXstugo...]...][,...] or octal number\n",
267            program_name);
268   exit (1);
269 }