source: trunk/src/kmk/kmkbuiltin/rm.c@ 2995

Last change on this file since 2995 was 2995, checked in by bird, 9 years ago

rm.c: Use fts_dirfd on windows.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 20.0 KB
Line 
1/*-
2 * Copyright (c) 1990, 1993, 1994
3 * The Regents of the University of California. All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * 1. Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 * notice, this list of conditions and the following disclaimer in the
12 * documentation and/or other materials provided with the distribution.
13 * 4. Neither the name of the University nor the names of its contributors
14 * may be used to endorse or promote products derived from this software
15 * without specific prior written permission.
16 *
17 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
18 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
21 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
23 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27 * SUCH DAMAGE.
28 */
29
30#if 0
31#ifndef lint
32static const char copyright[] =
33"@(#) Copyright (c) 1990, 1993, 1994\n\
34 The Regents of the University of California. All rights reserved.\n";
35#endif /* not lint */
36
37#ifndef lint
38static char sccsid[] = "@(#)rm.c 8.5 (Berkeley) 4/18/94";
39#endif /* not lint */
40#include <sys/cdefs.h>
41/*__FBSDID("$FreeBSD: src/bin/rm/rm.c,v 1.47 2004/04/06 20:06:50 markm Exp $");*/
42#endif
43
44#include "config.h"
45#include <sys/stat.h>
46#if !defined(_MSC_VER) && !defined(__HAIKU__)
47# include <sys/param.h>
48# include <sys/mount.h>
49#endif
50
51#include "err.h"
52#include <errno.h>
53#include <fcntl.h>
54#include <fts.h>
55#include <grp.h>
56#include <pwd.h>
57#include <stdio.h>
58#include <stdlib.h>
59#include <string.h>
60#ifndef __HAIKU__
61# include <sysexits.h>
62#endif
63#include <unistd.h>
64#include <ctype.h>
65#include "getopt.h"
66#ifdef __HAIKU__
67# include "haikufakes.h"
68#endif
69#ifdef KBUILD_OS_WINDOWS
70# ifdef _MSC_VER
71# include "mscfakes.h"
72# endif
73# include "nt/ntunlink.h"
74 /* Use the special unlink implementation to do rmdir too. */
75# undef rmdir
76# define rmdir(a_pszPath) birdUnlinkForced(a_pszPath)
77#endif
78#if defined(__OS2__) || defined(_MSC_VER)
79# include <direct.h>
80# include <limits.h>
81#endif
82#include "kmkbuiltin.h"
83#include "kbuild_protection.h"
84
85#if defined(__EMX__) || defined(KBUILD_OS_WINDOWS)
86# define IS_SLASH(ch) ( (ch) == '/' || (ch) == '\\' )
87# define HAVE_DOS_PATHS 1
88# define DEFAULT_PROTECTION_DEPTH 1
89#else
90# define IS_SLASH(ch) ( (ch) == '/' )
91# undef HAVE_DOS_PATHS
92# define DEFAULT_PROTECTION_DEPTH 2
93#endif
94
95#ifdef __EMX__
96#undef S_IFWHT
97#undef S_ISWHT
98#endif
99#ifndef S_IFWHT
100#define S_IFWHT 0
101#define S_ISWHT(s) 0
102#define undelete(s) (-1)
103#endif
104
105extern void bsd_strmode(mode_t mode, char *p);
106
107static int dflag, eval, fflag, iflag, Pflag, vflag, Wflag, stdin_ok;
108#ifdef KBUILD_OS_WINDOWS
109static int fUseNtDeleteFile;
110#endif
111static uid_t uid;
112
113static char *argv0;
114static KBUILDPROTECTION g_ProtData;
115
116static struct option long_options[] =
117{
118 { "help", no_argument, 0, 261 },
119 { "version", no_argument, 0, 262 },
120 { "disable-protection", no_argument, 0, 263 },
121 { "enable-protection", no_argument, 0, 264 },
122 { "enable-full-protection", no_argument, 0, 265 },
123 { "disable-full-protection", no_argument, 0, 266 },
124 { "protection-depth", required_argument, 0, 267 },
125#ifdef KBUILD_OS_WINDOWS
126 { "nt-delete-file", no_argument, 0, 268 },
127#endif
128 { 0, 0, 0, 0 },
129};
130
131
132static int check(char *, char *, struct stat *);
133static void checkdot(char **);
134static int rm_file(char **);
135static int rm_overwrite(char *, struct stat *);
136static int rm_tree(char **);
137static int usage(FILE *);
138
139#if 1
140#define CUR_LINE_H2(x) "[line " #x "]"
141#define CUR_LINE_H1(x) CUR_LINE_H2(x)
142#define CUR_LINE() CUR_LINE_H1(__LINE__)
143#else
144# define CUR_LINE()
145#endif
146
147
148/*
149 * rm --
150 * This rm is different from historic rm's, but is expected to match
151 * POSIX 1003.2 behavior. The most visible difference is that -f
152 * has two specific effects now, ignore non-existent files and force
153 * file removal.
154 */
155int
156kmk_builtin_rm(int argc, char *argv[], char **envp)
157{
158 int ch, rflag;
159
160 /* reinitialize globals */
161 argv0 = argv[0];
162 dflag = eval = fflag = iflag = Pflag = vflag = Wflag = stdin_ok = 0;
163#ifdef KBUILD_OS_WINDOWS
164 fUseNtDeleteFile = 0;
165#endif
166 uid = 0;
167 kBuildProtectionInit(&g_ProtData);
168
169 /* kmk: reset getopt and set program name. */
170 g_progname = argv[0];
171 opterr = 1;
172 optarg = NULL;
173 optopt = 0;
174 optind = 0; /* init */
175
176 Pflag = rflag = 0;
177 while ((ch = getopt_long(argc, argv, "dfiPRvW", long_options, NULL)) != -1)
178 switch(ch) {
179 case 'd':
180 dflag = 1;
181 break;
182 case 'f':
183 fflag = 1;
184 iflag = 0;
185 break;
186 case 'i':
187 fflag = 0;
188 iflag = 1;
189 break;
190 case 'P':
191 Pflag = 1;
192 break;
193 case 'R':
194#if 0
195 case 'r': /* Compatibility. */
196#endif
197 rflag = 1;
198 break;
199 case 'v':
200 vflag = 1;
201 break;
202#ifdef FTS_WHITEOUT
203 case 'W':
204 Wflag = 1;
205 break;
206#endif
207 case 261:
208 kBuildProtectionTerm(&g_ProtData);
209 usage(stdout);
210 return 0;
211 case 262:
212 kBuildProtectionTerm(&g_ProtData);
213 return kbuild_version(argv[0]);
214 case 263:
215 kBuildProtectionDisable(&g_ProtData, KBUILDPROTECTIONTYPE_RECURSIVE);
216 break;
217 case 264:
218 kBuildProtectionEnable(&g_ProtData, KBUILDPROTECTIONTYPE_RECURSIVE);
219 break;
220 case 265:
221 kBuildProtectionEnable(&g_ProtData, KBUILDPROTECTIONTYPE_FULL);
222 break;
223 case 266:
224 kBuildProtectionDisable(&g_ProtData, KBUILDPROTECTIONTYPE_FULL);
225 break;
226 case 267:
227 if (kBuildProtectionSetDepth(&g_ProtData, optarg)) {
228 kBuildProtectionTerm(&g_ProtData);
229 return 1;
230 }
231 break;
232#ifdef KBUILD_OS_WINDOWS
233 case 268:
234 fUseNtDeleteFile = 1;
235 break;
236#endif
237 case '?':
238 default:
239 kBuildProtectionTerm(&g_ProtData);
240 return usage(stderr);
241 }
242 argc -= optind;
243 argv += optind;
244
245 if (argc < 1) {
246 kBuildProtectionTerm(&g_ProtData);
247 if (fflag)
248 return (0);
249 return usage(stderr);
250 }
251
252 if (!kBuildProtectionScanEnv(&g_ProtData, envp, "KMK_RM_")) {
253 checkdot(argv);
254 uid = geteuid();
255
256 if (*argv) {
257 stdin_ok = isatty(STDIN_FILENO);
258 if (rflag)
259 eval |= rm_tree(argv);
260 else
261 eval |= rm_file(argv);
262 }
263 } else {
264 eval = 1;
265 }
266
267 kBuildProtectionTerm(&g_ProtData);
268 return eval;
269}
270
271static int
272rm_tree(char **argv)
273{
274 FTS *fts;
275 FTSENT *p;
276 int needstat;
277 int flags;
278 int rval;
279
280 /*
281 * Check up front before anything is deleted. This will not catch
282 * everything, but we'll check the individual items later.
283 */
284 int i;
285 for (i = 0; argv[i]; i++) {
286 if (kBuildProtectionEnforce(&g_ProtData, KBUILDPROTECTIONTYPE_RECURSIVE, argv[i])) {
287 return 1;
288 }
289 }
290
291 /*
292 * Remove a file hierarchy. If forcing removal (-f), or interactive
293 * (-i) or can't ask anyway (stdin_ok), don't stat the file.
294 */
295 needstat = !uid || (!fflag && !iflag && stdin_ok);
296
297 /*
298 * If the -i option is specified, the user can skip on the pre-order
299 * visit. The fts_number field flags skipped directories.
300 */
301#define SKIPPED 1
302
303 flags = FTS_PHYSICAL;
304 if (!needstat)
305 flags |= FTS_NOSTAT;
306#ifdef FTS_WHITEOUT
307 if (Wflag)
308 flags |= FTS_WHITEOUT;
309#endif
310 if (!(fts = fts_open(argv, flags, NULL))) {
311 return err(1, "fts_open");
312 }
313 while ((p = fts_read(fts)) != NULL) {
314 const char *operation = "chflags";
315 switch (p->fts_info) {
316 case FTS_DNR:
317 if (!fflag || p->fts_errno != ENOENT) {
318 fprintf(stderr, "fts: %s: %s: %s" CUR_LINE() "\n",
319 argv0, p->fts_path, strerror(p->fts_errno));
320 eval = 1;
321 }
322 continue;
323 case FTS_ERR:
324 fts_close(fts);
325 return errx(1, "fts: %s: %s " CUR_LINE(), p->fts_path, strerror(p->fts_errno));
326 case FTS_NS:
327 /*
328 * Assume that since fts_read() couldn't stat the
329 * file, it can't be unlinked.
330 */
331 if (!needstat)
332 break;
333 if (!fflag || p->fts_errno != ENOENT) {
334 fprintf(stderr, "fts: %s: %s: %s " CUR_LINE() "\n",
335 argv0, p->fts_path, strerror(p->fts_errno));
336 eval = 1;
337 }
338 continue;
339 case FTS_D:
340 /* Pre-order: give user chance to skip. */
341 if (!fflag && !check(p->fts_path, p->fts_accpath,
342 p->fts_statp)) {
343 (void)fts_set(fts, p, FTS_SKIP);
344 p->fts_number = SKIPPED;
345 }
346#ifdef UF_APPEND
347 else if (!uid &&
348 (p->fts_statp->st_flags & (UF_APPEND|UF_IMMUTABLE)) &&
349 !(p->fts_statp->st_flags & (SF_APPEND|SF_IMMUTABLE)) &&
350 chflags(p->fts_accpath,
351 p->fts_statp->st_flags &= ~(UF_APPEND|UF_IMMUTABLE)) < 0)
352 goto err;
353#endif
354 continue;
355 case FTS_DP:
356 /* Post-order: see if user skipped. */
357 if (p->fts_number == SKIPPED)
358 continue;
359 break;
360 default:
361 if (!fflag &&
362 !check(p->fts_path, p->fts_accpath, p->fts_statp))
363 continue;
364 }
365
366 /*
367 * Protect against deleting root files and directories.
368 */
369 if (kBuildProtectionEnforce(&g_ProtData, KBUILDPROTECTIONTYPE_RECURSIVE, p->fts_accpath)) {
370 fts_close(fts);
371 return 1;
372 }
373
374 rval = 0;
375#ifdef UF_APPEND
376 if (!uid &&
377 (p->fts_statp->st_flags & (UF_APPEND|UF_IMMUTABLE)) &&
378 !(p->fts_statp->st_flags & (SF_APPEND|SF_IMMUTABLE)))
379 rval = chflags(p->fts_accpath,
380 p->fts_statp->st_flags &= ~(UF_APPEND|UF_IMMUTABLE));
381#endif
382 if (rval == 0) {
383 /*
384 * If we can't read or search the directory, may still be
385 * able to remove it. Don't print out the un{read,search}able
386 * message unless the remove fails.
387 */
388 switch (p->fts_info) {
389 case FTS_DP:
390 case FTS_DNR:
391#ifdef KBUILD_OS_WINDOWS
392 if (p->fts_parent->fts_dirfd != NT_FTS_INVALID_HANDLE_VALUE) {
393 rval = birdUnlinkForcedEx(p->fts_parent->fts_dirfd, p->fts_name);
394 } else {
395 rval = birdUnlinkForced(p->fts_accpath);
396 }
397#else
398 rval = rmdir(p->fts_accpath);
399#endif
400 if (rval == 0 || (fflag && errno == ENOENT)) {
401 if (rval == 0 && vflag)
402 (void)printf("%s\n",
403 p->fts_path);
404#if defined(KMK) && defined(KBUILD_OS_WINDOWS)
405 if (rval == 0) {
406 extern int dir_cache_deleted_directory(const char *pszDir);
407 dir_cache_deleted_directory(p->fts_accpath);
408 }
409#endif
410 continue;
411 }
412 operation = "rmdir";
413 break;
414
415#ifdef FTS_W
416 case FTS_W:
417 rval = undelete(p->fts_accpath);
418 if (rval == 0 && (fflag && errno == ENOENT)) {
419 if (vflag)
420 (void)printf("%s\n",
421 p->fts_path);
422 continue;
423 }
424 operation = "undelete";
425 break;
426#endif
427
428 case FTS_NS:
429 /*
430 * Assume that since fts_read() couldn't stat
431 * the file, it can't be unlinked.
432 */
433 if (fflag)
434 continue;
435 /* FALLTHROUGH */
436 default:
437 if (Pflag)
438 if (!rm_overwrite(p->fts_accpath, NULL))
439 continue;
440#ifdef KBUILD_OS_WINDOWS
441 if (p->fts_parent->fts_dirfd != NT_FTS_INVALID_HANDLE_VALUE) {
442 rval = birdUnlinkForcedFastEx(p->fts_parent->fts_dirfd, p->fts_name);
443 } else {
444 rval = birdUnlinkForcedFast(p->fts_accpath);
445 }
446#else
447 rval = unlink(p->fts_accpath);
448#endif
449
450 if (rval == 0 || (fflag && errno == ENOENT)) {
451 if (rval == 0 && vflag)
452 (void)printf("%s\n",
453 p->fts_path);
454 continue;
455 }
456 operation = "unlink";
457 break;
458 }
459 }
460#ifdef UF_APPEND
461err:
462#endif
463 fprintf(stderr, "%s: %s: %s: %s " CUR_LINE() "\n", operation, argv0, p->fts_path, strerror(errno));
464 eval = 1;
465 }
466 if (errno) {
467 fprintf(stderr, "%s: fts_read: %s " CUR_LINE() "\n", argv0, strerror(errno));
468 eval = 1;
469 }
470 fts_close(fts);
471 return eval;
472}
473
474static int
475rm_file(char **argv)
476{
477 struct stat sb;
478 int rval;
479 char *f;
480
481 /*
482 * Check up front before anything is deleted.
483 */
484 int i;
485 for (i = 0; argv[i]; i++) {
486 if (kBuildProtectionEnforce(&g_ProtData, KBUILDPROTECTIONTYPE_FULL, argv[i]))
487 return 1;
488 }
489
490 /*
491 * Remove a file. POSIX 1003.2 states that, by default, attempting
492 * to remove a directory is an error, so must always stat the file.
493 */
494 while ((f = *argv++) != NULL) {
495 const char *operation = "?";
496 /* Assume if can't stat the file, can't unlink it. */
497 if (lstat(f, &sb)) {
498#ifdef FTS_WHITEOUT
499 if (Wflag) {
500 sb.st_mode = S_IFWHT|S_IWUSR|S_IRUSR;
501 } else {
502#else
503 {
504#endif
505 if (!fflag || errno != ENOENT) {
506 fprintf(stderr, "lstat: %s: %s: %s " CUR_LINE() "\n", argv0, f, strerror(errno));
507 eval = 1;
508 }
509 continue;
510 }
511#ifdef FTS_WHITEOUT
512 } else if (Wflag) {
513 fprintf(stderr, "%s: %s: %s\n", argv0, f, strerror(EEXIST));
514 eval = 1;
515 continue;
516#endif
517 }
518
519 if (S_ISDIR(sb.st_mode) && !dflag) {
520 fprintf(stderr, "%s: %s: is a directory\n", argv0, f);
521 eval = 1;
522 continue;
523 }
524 if (!fflag && !S_ISWHT(sb.st_mode) && !check(f, f, &sb))
525 continue;
526 rval = 0;
527#ifdef UF_APPEND
528 if (!uid &&
529 (sb.st_flags & (UF_APPEND|UF_IMMUTABLE)) &&
530 !(sb.st_flags & (SF_APPEND|SF_IMMUTABLE)))
531 rval = chflags(f, sb.st_flags & ~(UF_APPEND|UF_IMMUTABLE));
532#endif
533 if (rval == 0) {
534 if (S_ISWHT(sb.st_mode)) {
535 rval = undelete(f);
536 operation = "undelete";
537 } else if (S_ISDIR(sb.st_mode)) {
538 rval = rmdir(f);
539 operation = "rmdir";
540 } else {
541 if (Pflag)
542 if (!rm_overwrite(f, &sb))
543 continue;
544#ifndef KBUILD_OS_WINDOWS
545 rval = unlink(f);
546 operation = "unlink";
547#else
548 if (fUseNtDeleteFile) {
549 rval = birdUnlinkForcedFast(f);
550 operation = "NtDeleteFile";
551 } else {
552 rval = birdUnlinkForced(f);
553 operation = "unlink";
554 }
555#endif
556 }
557 }
558 if (rval && (!fflag || errno != ENOENT)) {
559 fprintf(stderr, "%s: %s: %s: %s" CUR_LINE() "\n", operation, argv0, f, strerror(errno));
560 eval = 1;
561 }
562 if (vflag && rval == 0)
563 (void)printf("%s\n", f);
564 }
565 return eval;
566}
567
568/*
569 * rm_overwrite --
570 * Overwrite the file 3 times with varying bit patterns.
571 *
572 * XXX
573 * This is a cheap way to *really* delete files. Note that only regular
574 * files are deleted, directories (and therefore names) will remain.
575 * Also, this assumes a fixed-block file system (like FFS, or a V7 or a
576 * System V file system). In a logging file system, you'll have to have
577 * kernel support.
578 */
579static int
580rm_overwrite(char *file, struct stat *sbp)
581{
582 struct stat sb;
583#ifdef HAVE_FSTATFS
584 struct statfs fsb;
585#endif
586 off_t len;
587 int bsize, fd, wlen;
588 char *buf = NULL;
589 const char *operation = "lstat";
590
591 fd = -1;
592 if (sbp == NULL) {
593 if (lstat(file, &sb))
594 goto err;
595 sbp = &sb;
596 }
597 if (!S_ISREG(sbp->st_mode))
598 return (1);
599 operation = "open";
600 if ((fd = open(file, O_WRONLY, 0)) == -1)
601 goto err;
602#ifdef HAVE_FSTATFS
603 if (fstatfs(fd, &fsb) == -1)
604 goto err;
605 bsize = MAX(fsb.f_iosize, 1024);
606#elif defined(HAVE_ST_BLKSIZE)
607 bsize = MAX(sb.st_blksize, 1024);
608#else
609 bsize = 1024;
610#endif
611 if ((buf = malloc(bsize)) == NULL)
612 exit(err(1, "%s: malloc", file));
613
614#define PASS(byte) { \
615 operation = "write"; \
616 memset(buf, byte, bsize); \
617 for (len = sbp->st_size; len > 0; len -= wlen) { \
618 wlen = len < bsize ? len : bsize; \
619 if (write(fd, buf, wlen) != wlen) \
620 goto err; \
621 } \
622}
623 PASS(0xff);
624 operation = "fsync/lseek";
625 if (fsync(fd) || lseek(fd, (off_t)0, SEEK_SET))
626 goto err;
627 PASS(0x00);
628 operation = "fsync/lseek";
629 if (fsync(fd) || lseek(fd, (off_t)0, SEEK_SET))
630 goto err;
631 PASS(0xff);
632 if (!fsync(fd) && !close(fd)) {
633 free(buf);
634 return (1);
635 }
636 operation = "fsync/close";
637
638err: eval = 1;
639 if (buf)
640 free(buf);
641 if (fd != -1)
642 close(fd);
643 fprintf(stderr, "%s: %s: %s: %s" CUR_LINE() "\n", operation, argv0, file, strerror(errno));
644 return (0);
645}
646
647
648static int
649check(char *path, char *name, struct stat *sp)
650{
651 int ch, first;
652 char modep[15], *flagsp;
653
654 /* Check -i first. */
655 if (iflag)
656 (void)fprintf(stderr, "remove %s? ", path);
657 else {
658 /*
659 * If it's not a symbolic link and it's unwritable and we're
660 * talking to a terminal, ask. Symbolic links are excluded
661 * because their permissions are meaningless. Check stdin_ok
662 * first because we may not have stat'ed the file.
663 * Also skip this check if the -P option was specified because
664 * we will not be able to overwrite file contents and will
665 * barf later.
666 */
667 if (!stdin_ok || S_ISLNK(sp->st_mode) || Pflag ||
668 (!access(name, W_OK) &&
669#ifdef SF_APPEND
670 !(sp->st_flags & (SF_APPEND|SF_IMMUTABLE)) &&
671 (!(sp->st_flags & (UF_APPEND|UF_IMMUTABLE)) || !uid))
672#else
673 1)
674#endif
675 )
676 return (1);
677 bsd_strmode(sp->st_mode, modep);
678#ifdef SF_APPEND
679 if ((flagsp = fflagstostr(sp->st_flags)) == NULL)
680 exit(err(1, "fflagstostr"));
681 (void)fprintf(stderr, "override %s%s%s/%s %s%sfor %s? ",
682 modep + 1, modep[9] == ' ' ? "" : " ",
683 user_from_uid(sp->st_uid, 0),
684 group_from_gid(sp->st_gid, 0),
685 *flagsp ? flagsp : "", *flagsp ? " " : "",
686 path);
687 free(flagsp);
688#else
689 (void)flagsp;
690 (void)fprintf(stderr, "override %s%s %d/%d for %s? ",
691 modep + 1, modep[9] == ' ' ? "" : " ",
692 sp->st_uid, sp->st_gid, path);
693#endif
694 }
695 (void)fflush(stderr);
696
697 first = ch = getchar();
698 while (ch != '\n' && ch != EOF)
699 ch = getchar();
700 return (first == 'y' || first == 'Y');
701}
702
703#define ISDOT(a) ((a)[0] == '.' && (!(a)[1] || ((a)[1] == '.' && !(a)[2])))
704static void
705checkdot(char **argv)
706{
707 char *p, **save, **t;
708 int complained;
709
710 complained = 0;
711 for (t = argv; *t;) {
712#ifdef HAVE_DOS_PATHS
713 const char *tmp = p = *t;
714 while (*tmp) {
715 switch (*tmp) {
716 case '/':
717 case '\\':
718 case ':':
719 p = (char *)tmp + 1;
720 break;
721 }
722 tmp++;
723 }
724#else
725 if ((p = strrchr(*t, '/')) != NULL)
726 ++p;
727 else
728 p = *t;
729#endif
730 if (ISDOT(p)) {
731 if (!complained++)
732 fprintf(stderr, "%s: \".\" and \"..\" may not be removed\n", argv0);
733 eval = 1;
734 for (save = t; (t[0] = t[1]) != NULL; ++t)
735 continue;
736 t = save;
737 } else
738 ++t;
739 }
740}
741
742static int
743usage(FILE *pf)
744{
745 fprintf(pf,
746 "usage: %s [options] file ...\n"
747 " or: %s --help\n"
748 " or: %s --version\n"
749 "\n"
750 "Options:\n"
751 " -f\n"
752 " Attempt to remove files without prompting, regardless of the file\n"
753 " permission. Ignore non-existing files. Overrides previous -i's.\n"
754 " -i\n"
755 " Prompt for each file. Always.\n"
756 " -d\n"
757 " Attempt to remove directories as well as other kinds of files.\n"
758 " -P\n"
759 " Overwrite regular files before deleting; three passes: ff,0,ff\n"
760 " -R\n"
761 " Attempt to remove the file hierachy rooted in each file argument.\n"
762 " This option implies -d and file protection.\n"
763 " -v\n"
764 " Be verbose, show files as they are removed.\n"
765 " -W\n"
766 " Undelete without files.\n"
767 " --disable-protection\n"
768 " Will disable the protection file protection applied with -R.\n"
769 " --enable-protection\n"
770 " Will enable the protection file protection applied with -R.\n"
771 " --enable-full-protection\n"
772 " Will enable the protection file protection for all operations.\n"
773 " --disable-full-protection\n"
774 " Will disable the protection file protection for all operations.\n"
775 " --protection-depth\n"
776 " Number or path indicating the file protection depth. Default: %d\n"
777 "\n"
778 "Environment:\n"
779 " KMK_RM_DISABLE_PROTECTION\n"
780 " Same as --disable-protection. Overrides command line.\n"
781 " KMK_RM_ENABLE_PROTECTION\n"
782 " Same as --enable-protection. Overrides everyone else.\n"
783 " KMK_RM_ENABLE_FULL_PROTECTION\n"
784 " Same as --enable-full-protection. Overrides everyone else.\n"
785 " KMK_RM_DISABLE_FULL_PROTECTION\n"
786 " Same as --disable-full-protection. Overrides command line.\n"
787 " KMK_RM_PROTECTION_DEPTH\n"
788 " Same as --protection-depth. Overrides command line.\n"
789 "\n"
790 "The file protection of the top %d layers of the file hierarchy is there\n"
791 "to try prevent makefiles from doing bad things to your system. This\n"
792 "protection is not bulletproof, but should help prevent you from shooting\n"
793 "yourself in the foot.\n"
794 ,
795 g_progname, g_progname, g_progname,
796 kBuildProtectionDefaultDepth(), kBuildProtectionDefaultDepth());
797 return EX_USAGE;
798}
Note: See TracBrowser for help on using the repository browser.