(main): Initialize for internationalized message support: call setlocale,
[platform/upstream/coreutils.git] / src / rm.c
1 /* `rm' file deletion utility for GNU.
2    Copyright (C) 1988, 1990, 1991, 1994, 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 /* Written by Paul Rubin, David MacKenzie, and Richard Stallman.  */
19
20 #include <config.h>
21 #include <stdio.h>
22 #include <getopt.h>
23 #include <sys/types.h>
24
25 #include "system.h"
26 #include "version.h"
27 #include "error.h"
28
29 #ifdef D_INO_IN_DIRENT
30 # define D_INO(dp) ((dp)->d_ino)
31 #else
32 /* Some systems don't have inodes, so fake them to avoid lots of ifdefs.  */
33 # define D_INO(dp) 1
34 #endif
35
36 /* An element in a stack of pointers into `pathname'.
37    `pathp' points to where in `pathname' the terminating '\0' goes
38    for this level's directory name.  */
39 struct pathstack
40 {
41   struct pathstack *next;
42   char *pathp;
43   ino_t inum;
44 };
45
46 char *basename ();
47 char *stpcpy ();
48 char *xmalloc ();
49 char *xrealloc ();
50 int euidaccess ();
51 int yesno ();
52 void strip_trailing_slashes ();
53
54 static int clear_directory __P ((struct stat *statp));
55 static int duplicate_entry __P ((struct pathstack *stack, ino_t inum));
56 static int remove_dir __P ((struct stat *statp));
57 static int remove_file __P ((struct stat *statp));
58 static int rm __P ((void));
59 static void usage __P ((int status));
60
61 /* Name this program was run with.  */
62 char *program_name;
63
64 /* Linked list of pathnames of directories in progress in recursive rm.
65    The entries actually contain pointers into `pathname'.
66    `pathstack' is the current deepest level.  */
67 static struct pathstack *pathstack = NULL;
68
69 /* Path of file now being processed; extended as necessary.  */
70 static char *pathname;
71
72 /* Number of bytes currently allocated for `pathname';
73    made larger when necessary, but never smaller.  */
74 static int pnsize;
75
76 /* If nonzero, display the name of each file removed.  */
77 static int verbose;
78
79 /* If nonzero, ignore nonexistant files.  */
80 static int ignore_missing_files;
81
82 /* If nonzero, recursively remove directories.  */
83 static int recursive;
84
85 /* If nonzero, query the user about whether to remove each file.  */
86 static int interactive;
87
88 /* If nonzero, remove directories with unlink instead of rmdir, and don't
89    require a directory to be empty before trying to unlink it.
90    Only works for the super-user.  */
91 static int unlink_dirs;
92
93 /* If nonzero, stdin is a tty.  */
94 static int stdin_tty;
95
96 /* If nonzero, display usage information and exit.  */
97 static int show_help;
98
99 /* If nonzero, print the version on standard output and exit.  */
100 static int show_version;
101
102 static struct option const long_opts[] =
103 {
104   {"directory", no_argument, &unlink_dirs, 1},
105   {"force", no_argument, NULL, 'f'},
106   {"interactive", no_argument, NULL, 'i'},
107   {"recursive", no_argument, &recursive, 1},
108   {"verbose", no_argument, &verbose, 1},
109   {"help", no_argument, &show_help, 1},
110   {"version", no_argument, &show_version, 1},
111   {NULL, 0, NULL, 0}
112 };
113
114 void
115 main (int argc, char **argv)
116 {
117   int err = 0;
118   int c;
119
120   program_name = argv[0];
121   setlocale (LC_ALL, "");
122   bindtextdomain (PACKAGE, LOCALEDIR);
123   textdomain (PACKAGE);
124
125   verbose = ignore_missing_files = recursive = interactive
126     = unlink_dirs = 0;
127   pnsize = 256;
128   pathname = xmalloc (pnsize);
129
130   while ((c = getopt_long (argc, argv, "dfirvR", long_opts, (int *) 0)) != EOF)
131     {
132       switch (c)
133         {
134         case 0:         /* Long option.  */
135           break;
136         case 'd':
137           unlink_dirs = 1;
138           break;
139         case 'f':
140           interactive = 0;
141           ignore_missing_files = 1;
142           break;
143         case 'i':
144           interactive = 1;
145           ignore_missing_files = 0;
146           break;
147         case 'r':
148         case 'R':
149           recursive = 1;
150           break;
151         case 'v':
152           verbose = 1;
153           break;
154         default:
155           usage (1);
156         }
157     }
158
159   if (show_version)
160     {
161       printf ("rm - %s\n", version_string);
162       exit (0);
163     }
164
165   if (show_help)
166     usage (0);
167
168   if (optind == argc)
169     {
170       if (ignore_missing_files)
171         exit (0);
172       else
173         {
174           error (0, 0, _("too few arguments"));
175           usage (1);
176         }
177     }
178
179   stdin_tty = isatty (STDIN_FILENO);
180
181   for (; optind < argc; optind++)
182     {
183       int len;
184
185       /* Stripping slashes is harmless for rmdir;
186          if the arg is not a directory, it will fail with ENOTDIR.  */
187       strip_trailing_slashes (argv[optind]);
188       len = strlen (argv[optind]);
189       if (len + 1 > pnsize)
190         {
191           free (pathname);
192           pnsize = 2 * (len + 1);
193           pathname = xmalloc (pnsize);
194         }
195       strcpy (pathname, argv[optind]);
196       err += rm ();
197     }
198
199   exit (err > 0);
200 }
201
202 /* Remove file or directory `pathname' after checking appropriate things.
203    Return 0 if `pathname' is removed, 1 if not.  */
204
205 static int
206 rm (void)
207 {
208   struct stat path_stats;
209   char *base = basename (pathname);
210
211   if (base[0] == '.' && (base[1] == '\0'
212                              || (base[1] == '.' && base[2] == '\0')))
213     {
214       error (0, 0, _("cannot remove `.' or `..'"));
215       return 1;
216     }
217
218   if (lstat (pathname, &path_stats)
219       /* The following or-clause is solely for systems like SunOS 4.1.3
220          with (broken) lstat that interpret a zero-length file name
221          argument as something meaningful.  For such systems, manually
222          set errno to ENOENT.  */
223       || (pathname[0] == '\0' && (errno = ENOENT)))
224     {
225       if (errno == ENOENT && ignore_missing_files)
226         return 0;
227       error (0, errno, "%s", pathname);
228       return 1;
229     }
230
231   if (S_ISDIR (path_stats.st_mode) && !unlink_dirs)
232     return remove_dir (&path_stats);
233   else
234     return remove_file (&path_stats);
235 }
236
237 /* Query the user if appropriate, and if ok try to remove the
238    non-directory `pathname', which STATP contains info about.
239    Return 0 if `pathname' is removed, 1 if not.  */
240
241 static int
242 remove_file (struct stat *statp)
243 {
244   if (!ignore_missing_files && (interactive || stdin_tty)
245       && euidaccess (pathname, W_OK)
246 #ifdef S_ISLNK
247       && !S_ISLNK (statp->st_mode)
248 #endif
249       )
250     {
251       fprintf (stderr, _("%s: remove %s`%s', overriding mode %04o? "),
252                program_name,
253                S_ISDIR (statp->st_mode) ? _("directory ") : "",
254                pathname,
255                (unsigned int) (statp->st_mode & 07777));
256       if (!yesno ())
257         return 1;
258     }
259   else if (interactive)
260     {
261       fprintf (stderr, _("%s: remove %s`%s'? "), program_name,
262                S_ISDIR (statp->st_mode) ? _("directory ") : "",
263                pathname);
264       if (!yesno ())
265         return 1;
266     }
267
268   if (verbose)
269     printf ("%s\n", pathname);
270
271   if (unlink (pathname) && (errno != ENOENT || !ignore_missing_files))
272     {
273       error (0, errno, "%s", pathname);
274       return 1;
275     }
276   return 0;
277 }
278
279 /* If not in recursive mode, print an error message and return 1.
280    Otherwise, query the user if appropriate, then try to recursively
281    remove directory `pathname', which STATP contains info about.
282    Return 0 if `pathname' is removed, 1 if not.  */
283
284 static int
285 remove_dir (struct stat *statp)
286 {
287   int err;
288
289   if (!recursive)
290     {
291       error (0, 0, _("%s: is a directory"), pathname);
292       return 1;
293     }
294
295   if (!ignore_missing_files && (interactive || stdin_tty)
296       && euidaccess (pathname, W_OK))
297     {
298       fprintf (stderr,
299                _("%s: descend directory `%s', overriding mode %04o? "),
300                program_name, pathname,
301                (unsigned int) (statp->st_mode & 07777));
302       if (!yesno ())
303         return 1;
304     }
305   else if (interactive)
306     {
307       fprintf (stderr, _("%s: descend directory `%s'? "),
308                program_name, pathname);
309       if (!yesno ())
310         return 1;
311     }
312
313   if (verbose)
314     printf ("%s\n", pathname);
315
316   err = clear_directory (statp);
317
318   if (interactive)
319     {
320       if (err)
321         fprintf (stderr, _("%s: remove directory `%s' (might be nonempty)? "),
322                  program_name, pathname);
323       else
324         fprintf (stderr, _("%s: remove directory `%s'? "),
325                  program_name, pathname);
326       if (!yesno ())
327         return 1;
328     }
329
330   if (rmdir (pathname) && (errno != ENOENT || !ignore_missing_files))
331     {
332       error (0, errno, "%s", pathname);
333       return 1;
334     }
335   return 0;
336 }
337
338 /* Read directory `pathname' and remove all of its entries,
339    avoiding use of chdir.
340    On entry, STATP points to the results of stat on `pathname'.
341    Return 0 for success, error count for failure.
342    Upon return, `pathname' will have the same contents as before,
343    but its address might be different; in that case, `pnsize' will
344    be larger, as well.  */
345
346 static int
347 clear_directory (struct stat *statp)
348 {
349   DIR *dirp;
350   struct dirent *dp;
351   char *name_space;             /* Copy of directory's filenames.  */
352   char *namep;                  /* Current entry in `name_space'.  */
353   unsigned name_size;           /* Bytes allocated for `name_space'.  */
354   int name_length;              /* Length of filename in `namep' plus '\0'.  */
355   int pathname_length;          /* Length of `pathname'.  */
356   ino_t *inode_space;           /* Copy of directory's inodes.  */
357   ino_t *inodep;                /* Current entry in `inode_space'.  */
358   unsigned n_inodes_allocated;  /* There is space for this many inodes
359                                           in `inode_space'.  */
360   int err = 0;                  /* Return status.  */
361   struct pathstack pathframe;   /* New top of stack.  */
362   struct pathstack *pp;         /* Temporary.  */
363
364   name_size = statp->st_size;
365   name_space = (char *) xmalloc (name_size);
366
367   n_inodes_allocated = (statp->st_size + sizeof (ino_t) - 1) / sizeof (ino_t);
368   inode_space = (ino_t *) xmalloc (n_inodes_allocated * sizeof (ino_t));
369
370   do
371     {
372       namep = name_space;
373       inodep = inode_space;
374
375       errno = 0;
376       dirp = opendir (pathname);
377       if (dirp == NULL)
378         {
379           if (errno != ENOENT || !ignore_missing_files)
380             {
381               error (0, errno, "%s", pathname);
382               err = 1;
383             }
384           free (name_space);
385           free (inode_space);
386           return err;
387         }
388
389       while ((dp = readdir (dirp)) != NULL)
390         {
391           /* Skip "." and "..".  */
392           if (dp->d_name[0] != '.'
393               || (dp->d_name[1] != '\0'
394                   && (dp->d_name[1] != '.' || dp->d_name[2] != '\0')))
395             {
396               unsigned size_needed = (namep - name_space) + NLENGTH (dp) + 2;
397
398               if (size_needed > name_size)
399                 {
400                   char *new_name_space;
401
402                   while (size_needed > name_size)
403                     name_size += 1024;
404
405                   new_name_space = xrealloc (name_space, name_size);
406                   namep += new_name_space - name_space;
407                   name_space = new_name_space;
408                 }
409               namep = stpcpy (namep, dp->d_name) + 1;
410
411               if (inodep == inode_space + n_inodes_allocated)
412                 {
413                   ino_t *new_inode_space;
414
415                   n_inodes_allocated += 1024;
416                   new_inode_space = (ino_t *) xrealloc (inode_space,
417                                         n_inodes_allocated * sizeof (ino_t));
418                   inodep += new_inode_space - inode_space;
419                   inode_space = new_inode_space;
420                 }
421               *inodep++ = D_INO (dp);
422             }
423         }
424       *namep = '\0';
425       if (CLOSEDIR (dirp))
426         {
427           error (0, errno, "%s", pathname);
428           err = 1;
429         }
430
431       pathname_length = strlen (pathname);
432
433       for (namep = name_space, inodep = inode_space; *namep != '\0';
434            namep += name_length, inodep++)
435         {
436           name_length = strlen (namep) + 1;
437
438           /* Handle arbitrarily long filenames.  */
439           if (pathname_length + 1 + name_length > pnsize)
440             {
441               char *new_pathname;
442
443               pnsize = (pathname_length + 1 + name_length) * 2;
444               new_pathname = xrealloc (pathname, pnsize);
445               /* Update all pointers in the stack to use the new area.  */
446               for (pp = pathstack; pp != NULL; pp = pp->next)
447                 pp->pathp += new_pathname - pathname;
448               pathname = new_pathname;
449             }
450
451           /* Add a new frame to the top of the path stack.  */
452           pathframe.pathp = pathname + pathname_length;
453           pathframe.inum = *inodep;
454           pathframe.next = pathstack;
455           pathstack = &pathframe;
456
457           /* Append '/' and the filename to current pathname, take care of
458              the file (which could result in recursive calls), and take
459              the filename back off.  */
460
461           *pathstack->pathp = '/';
462           strcpy (pathstack->pathp + 1, namep);
463
464           /* If the i-number has already appeared, there's an error.  */
465           if (duplicate_entry (pathstack->next, pathstack->inum))
466             err++;
467           else if (rm ())
468             err++;
469
470           *pathstack->pathp = '\0';
471           pathstack = pathstack->next;  /* Pop the stack.  */
472         }
473     }
474   /* Keep trying while there are still files to remove.  */
475   while (namep > name_space && err == 0);
476
477   free (name_space);
478   free (inode_space);
479   return err;
480 }
481
482 /* If STACK does not already have an entry with the same i-number as INUM,
483    return 0. Otherwise, ask the user whether to continue;
484    if yes, return 1, and if no, exit.
485    This assumes that no one tries to remove filesystem mount points;
486    doing so could cause duplication of i-numbers that would not indicate
487    a corrupted file system.  */
488
489 static int
490 duplicate_entry (struct pathstack *stack, ino_t inum)
491 {
492 #ifdef D_INO_IN_DIRENT
493   struct pathstack *p;
494
495   for (p = stack; p != NULL; p = p->next)
496     {
497       if (p->inum == inum)
498         {
499           fprintf (stderr, _("\
500 %s: WARNING: Circular directory structure.\n\
501 This almost certainly means that you have a corrupted file system.\n\
502 NOTIFY YOUR SYSTEM MANAGER.\n\
503 Cycle detected:\n\
504 %s\n\
505 is the same file as\n"), program_name, pathname);
506           *p->pathp = '\0';     /* Truncate pathname.  */
507           fprintf (stderr, "%s\n", pathname);
508           *p->pathp = '/';      /* Put it back.  */
509           if (interactive)
510             {
511               fprintf (stderr, _("%s: continue? "), program_name);
512               if (!yesno ())
513                 exit (1);
514               return 1;
515             }
516           else
517             exit (1);
518         }
519     }
520 #endif /* D_INO_IN_DIRENT */
521   return 0;
522 }
523
524 static void
525 usage (int status)
526 {
527   if (status != 0)
528     fprintf (stderr, _("Try `%s --help' for more information.\n"),
529              program_name);
530   else
531     {
532       printf (_("Usage: %s [OPTION]... FILE...\n"), program_name);
533       printf (_("\
534 Remove (unlink) the FILE(s).\n\
535 \n\
536   -d, --directory       unlink directory, even if non-empty (super-user only)\n\
537   -f, --force           ignore nonexistent files, never prompt\n\
538   -i, --interactive     prompt before any removal\n\
539   -v, --verbose         explain what is being done\n\
540   -r, -R, --recursive   remove the contents of directories recursively\n\
541       --help            display this help and exit\n\
542       --version         output version information and exit\n"));
543     }
544   exit (status);
545 }