VirtualBox

source: kBuild/trunk/src/kmk/kmkbuiltin/rm.c@ 1550

Last change on this file since 1550 was 1550, checked in by bird, 17 years ago

Enabled rm -R.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 20.8 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#ifdef __EMX__
44# define DO_RMTREE
45#endif
46
47#include <sys/stat.h>
48#ifndef _MSC_VER
49# include <sys/param.h>
50# include <sys/mount.h>
51#endif
52
53#include "err.h"
54#include <errno.h>
55#include <fcntl.h>
56#include <fts.h>
57#include <grp.h>
58#include <pwd.h>
59#include <stdio.h>
60#include <stdlib.h>
61#include <string.h>
62#include <sysexits.h>
63#include <unistd.h>
64#include "getopt.h"
65#ifdef _MSC_VER
66# include "mscfakes.h"
67#endif
68#include "kmkbuiltin.h"
69
70#if defined(__EMX__) || defined(_MSC_VER)
71# define IS_SLASH(ch) ( (ch) == '/' || (ch) == '\\' )
72# define HAVE_DOS_PATHS 1
73# define DEFAULT_REQUIRED_R_DEPTH 2
74#else
75# define IS_SLASH(ch) ( (ch) == '/' )
76# undef HAVE_DOS_PATHS
77# define DEFAULT_REQUIRED_R_DEPTH 3
78#endif
79
80#ifdef __EMX__
81#undef S_IFWHT
82#undef S_ISWHT
83#endif
84#ifndef S_IFWHT
85#define S_IFWHT 0
86#define S_ISWHT(s) 0
87#define undelete(s) (-1)
88#endif
89
90#if !defined(__FreeBSD__) && !defined(__APPLE__)
91extern void strmode(mode_t mode, char *p);
92#endif
93
94static int protectionflag;
95static int dflag, eval, fflag, iflag, Pflag, vflag, Wflag, stdin_ok;
96static uid_t uid;
97
98static char *argv0;
99
100static struct option long_options[] =
101{
102 { "help", no_argument, 0, 261 },
103 { "version", no_argument, 0, 262 },
104 { "disable-protection", no_argument, 0, 263 },
105 { 0, 0, 0, 0 },
106};
107
108
109static int check(char *, char *, struct stat *);
110static void checkdot(char **);
111static void rm_file(char **);
112static int rm_overwrite(char *, struct stat *);
113static void rm_tree(char **, int);
114static int count_path_components(const char *);
115static int usage(FILE *);
116
117
118
119/*
120 * rm --
121 * This rm is different from historic rm's, but is expected to match
122 * POSIX 1003.2 behavior. The most visible difference is that -f
123 * has two specific effects now, ignore non-existent files and force
124 * file removal.
125 */
126int
127kmk_builtin_rm(int argc, char *argv[], char **envp)
128{
129 int ch, rflag;
130
131 /* reinitialize globals */
132 argv0 = argv[0];
133 dflag = eval = fflag = iflag = Pflag = vflag = Wflag = stdin_ok = 0;
134 protectionflag = 1;
135 uid = 0;
136
137 /* kmk: reset getopt and set program name. */
138 g_progname = argv[0];
139 opterr = 1;
140 optarg = NULL;
141 optopt = 0;
142 optind = 0; /* init */
143
144 Pflag = rflag = 0;
145 while ((ch = getopt_long(argc, argv, "dfiPRrvW", long_options, NULL)) != -1)
146 switch(ch) {
147 case 'd':
148 dflag = 1;
149 break;
150 case 'f':
151 fflag = 1;
152 iflag = 0;
153 break;
154 case 'i':
155 fflag = 0;
156 iflag = 1;
157 break;
158 case 'P':
159 Pflag = 1;
160 break;
161 case 'R':
162#if 0
163 case 'r': /* Compatibility. */
164#endif
165 rflag = 1;
166 break;
167 case 'v':
168 vflag = 1;
169 break;
170#ifdef FTS_WHITEOUT
171 case 'W':
172 Wflag = 1;
173 break;
174#endif
175 case 261:
176 usage(stdout);
177 return 0;
178 case 262:
179 return kbuild_version(argv[0]);
180 case 263:
181 protectionflag = 0;
182 break;
183 case '?':
184 default:
185 return usage(stderr);
186 }
187 argc -= optind;
188 argv += optind;
189
190 if (argc < 1) {
191 if (fflag)
192 return (0);
193 return usage(stderr);
194 }
195
196 checkdot(argv);
197 uid = geteuid();
198
199 if (*argv) {
200 stdin_ok = isatty(STDIN_FILENO);
201 if (rflag) {
202 /*
203 * Get options from the environment before doing rm_tree().
204 */
205 int required_r_depth = DEFAULT_REQUIRED_R_DEPTH;
206 int i;
207 for (i = 0; envp[i]; i++) {
208 if (!strncmp(envp[i], "KMK_RM_=", sizeof("KMK_RM_=") - 1)) {
209 if (!strncmp(envp[i], "KMK_RM_PROTECTION_DEPTH=", sizeof("KMK_RM_PROTECTION_DEPTH=") - 1)) {
210 char *ignore;
211 const char *val = envp[i] + sizeof("KMK_RM_PROTECTION_DEPTH=") - 1;
212 required_r_depth = isdigit(*val) ? strtol(val, &ignore, 0) : count_path_components(val);
213 if (required_r_depth < 1)
214 required_r_depth = DEFAULT_REQUIRED_R_DEPTH;
215 } else if (!strcmp(envp[i], "KMK_RM_DISABLE_PROTECTION=1")) {
216 protectionflag = 0;
217 }
218 }
219 }
220
221 if (!eval)
222 rm_tree(argv, required_r_depth);
223 } else {
224 rm_file(argv);
225 }
226 }
227
228 return eval;
229}
230
231/**
232 * Counts the components in the specified sub path.
233 * This is a helper for count_path_components.
234 *
235 * etc = 1
236 * etc/ = 1
237 * etc/x11 = 2
238 * and so and and so forth.
239 */
240static int
241count_sub_path_components(const char *path, int depth)
242{
243 for (;;) {
244 const char *end;
245 size_t len;
246
247 /* skip slashes. */
248 while (IS_SLASH(*path))
249 path++;
250 if (!*path)
251 break;
252
253 /* find end of component. */
254 end = path;
255 while (!IS_SLASH(*end) && *end)
256 end++;
257
258 /* count it, checking for '..' and '.'. */
259 len = end - path;
260 if (len == 2 && path[0] == '.' && path[1] == '.') {
261 if (depth > 0)
262 depth--;
263 } else if (len != 1 || path[0] != '.') {
264 depth++;
265 }
266
267 /* advance */
268 if (!*end)
269 break;
270 path = end + 1;
271 }
272 return depth;
273}
274
275/**
276 * Parses the specified path counting the number of components
277 * relative to root.
278 *
279 * We don't check symbolic links and such, just some simple and cheap
280 * path parsing.
281 *
282 * @param path The path to process.
283 *
284 * @returns 0 or higher on success.
285 * On failure an error is printed, eval is set and -1 is returned.
286 */
287static int
288count_path_components(const char *path)
289{
290 int components = 0;
291
292 /*
293 * Deal with root, UNC, drive letter.
294 */
295#if defined(_MSC_VER) || defined(__OS2__)
296 if (IS_SLASH(path[0]) && IS_SLASH(path[1]) && !IS_SLASH(path[2])) {
297 /* skip the root - UNC */
298 path += 3;
299 while (!IS_SLASH(*path) && *path) /* server name */
300 path++;
301 while (IS_SLASH(*path))
302 path++;
303 while (!IS_SLASH(*path) && *path) /* share name */
304 path++;
305 while (IS_SLASH(*path))
306 path++;
307 } else {
308 unsigned drive_letter = (unsigned)toupper(path[0]) - (unsigned)'A';
309 if (drive_letter <= (unsigned)('Z' - 'A') && path[1] == ':') {
310 drive_letter++; /* A == 1 */
311 } else {
312 drive_letter = 0; /* 0 == default */
313 }
314
315 if (IS_SLASH(path[drive_letter ? 2 : 0])) {
316 /*
317 * Relative path, must count cwd depth first.
318 */
319 char *tmp = _getdcwd(drive_letter, NULL, 32);
320 if (!tmp) {
321 eval = err(1, "_getdcwd");
322 return -1;
323 }
324
325 if (IS_SLASH(cwd[0]) && IS_SLASH(cwd[1])) {
326 /* skip the root - UNC */
327 tmp = &cwd[2];
328 while (!IS_SLASH(*tmp) && *tmp) /* server name */
329 tmp++;
330 while (IS_SLASH(*tmp))
331 tmp++;
332 while (!IS_SLASH(*tmp) && *tmp) /* share name */
333 tmp++;
334 } else {
335 /* skip the drive letter and while we're at it, the root slash too. */
336 tmp = &cwd[1 + (cwd[1] == ':')];
337 }
338 components = count_sub_path_components(tmp, 0);
339 free(tmp);
340 } else {
341 /* skip the drive letter and while we're at it, the root slash too. */
342 path += drive_letter ? 3 : 1;
343 }
344 }
345#else
346 if (!IS_SLASH(path[0])) {
347 /*
348 * Relative path, must count cwd depth first.
349 */
350 char cwd[4096];
351 if (!getcwd(cwd, sizeof(cwd))) {
352 eval = err(1, "getcwd");
353 return -1;
354 }
355 components = count_sub_path_components(cwd, 0);
356 }
357#endif
358
359 /*
360 * We're now past any UNC or drive letter crap, possibly positioned
361 * at the root slash or at the start of a path component at the
362 * given depth. Count the remainder.
363 */
364 return count_sub_path_components(path, components);
365}
366
367
368/**
369 * Protect the upper layers of the file system against accidental
370 * or malicious deletetion attempt from within a makefile.
371 *
372 * @param path The path to check.
373 * @param required_depth The minimum number of components in the
374 * path counting from the root.
375 *
376 * @returns 0 on success.
377 * On failure an error is printed, eval is set and -1 is returned.
378 */
379static int
380enforce_protection(const char *path, unsigned required_depth)
381{
382 int components;
383
384 /*
385 * Count the path and compare it with the required depth.
386 */
387 components = count_path_components(path);
388 if (components < 0)
389 return -1;
390 if (components < required_depth) {
391 eval = errx(1, "%s: protected", path);
392 return -1;
393 }
394 return 0;
395}
396
397static void
398rm_tree(char **argv, int required_r_depth)
399{
400 FTS *fts;
401 FTSENT *p;
402 int needstat;
403 int flags;
404 int rval;
405
406 /*
407 * Check up front before anything is deleted. This will not catch
408 * everything, but we'll check the individual items later.
409 */
410 if (protectionflag) {
411 int i;
412 for (i = 0; argv[i]; i++) {
413 if (enforce_protection(argv[i], required_r_depth))
414 return;
415 }
416 }
417
418 /*
419 * Remove a file hierarchy. If forcing removal (-f), or interactive
420 * (-i) or can't ask anyway (stdin_ok), don't stat the file.
421 */
422 needstat = !uid || (!fflag && !iflag && stdin_ok);
423
424 /*
425 * If the -i option is specified, the user can skip on the pre-order
426 * visit. The fts_number field flags skipped directories.
427 */
428#define SKIPPED 1
429
430 flags = FTS_PHYSICAL;
431 if (!needstat)
432 flags |= FTS_NOSTAT;
433#ifdef FTS_WHITEOUT
434 if (Wflag)
435 flags |= FTS_WHITEOUT;
436#endif
437 if (!(fts = fts_open(argv, flags, NULL))) {
438 eval = err(1, "fts_open");
439 return;
440 }
441 while ((p = fts_read(fts)) != NULL) {
442 switch (p->fts_info) {
443 case FTS_DNR:
444 if (!fflag || p->fts_errno != ENOENT) {
445 fprintf(stderr, "%s: %s: %s\n",
446 argv0, p->fts_path, strerror(p->fts_errno));
447 eval = 1;
448 }
449 continue;
450 case FTS_ERR:
451 eval = errx(1, "%s: %s", p->fts_path, strerror(p->fts_errno));
452 fts_close(fts);
453 return;
454 case FTS_NS:
455 /*
456 * Assume that since fts_read() couldn't stat the
457 * file, it can't be unlinked.
458 */
459 if (!needstat)
460 break;
461 if (!fflag || p->fts_errno != ENOENT) {
462 fprintf(stderr, "%s: %s: %s\n",
463 argv0, p->fts_path, strerror(p->fts_errno));
464 eval = 1;
465 }
466 continue;
467 case FTS_D:
468 /* Pre-order: give user chance to skip. */
469 if (!fflag && !check(p->fts_path, p->fts_accpath,
470 p->fts_statp)) {
471 (void)fts_set(fts, p, FTS_SKIP);
472 p->fts_number = SKIPPED;
473 }
474#ifdef UF_APPEND
475 else if (!uid &&
476 (p->fts_statp->st_flags & (UF_APPEND|UF_IMMUTABLE)) &&
477 !(p->fts_statp->st_flags & (SF_APPEND|SF_IMMUTABLE)) &&
478 chflags(p->fts_accpath,
479 p->fts_statp->st_flags &= ~(UF_APPEND|UF_IMMUTABLE)) < 0)
480 goto err;
481#endif
482 continue;
483 case FTS_DP:
484 /* Post-order: see if user skipped. */
485 if (p->fts_number == SKIPPED)
486 continue;
487 break;
488 default:
489 if (!fflag &&
490 !check(p->fts_path, p->fts_accpath, p->fts_statp))
491 continue;
492 }
493
494 /*
495 * Protect against deleting root files and directories.
496 */
497 if (protectionflag && enforce_protection(p->fts_accpath, required_r_depth)) {
498 fts_close(fts);
499 return;
500 }
501
502 rval = 0;
503#ifdef UF_APPEND
504 if (!uid &&
505 (p->fts_statp->st_flags & (UF_APPEND|UF_IMMUTABLE)) &&
506 !(p->fts_statp->st_flags & (SF_APPEND|SF_IMMUTABLE)))
507 rval = chflags(p->fts_accpath,
508 p->fts_statp->st_flags &= ~(UF_APPEND|UF_IMMUTABLE));
509#endif
510 if (rval == 0) {
511 /*
512 * If we can't read or search the directory, may still be
513 * able to remove it. Don't print out the un{read,search}able
514 * message unless the remove fails.
515 */
516 switch (p->fts_info) {
517 case FTS_DP:
518 case FTS_DNR:
519 rval = rmdir(p->fts_accpath);
520 if (rval == 0 || (fflag && errno == ENOENT)) {
521 if (rval == 0 && vflag)
522 (void)printf("%s\n",
523 p->fts_path);
524 continue;
525 }
526 break;
527
528#ifdef FTS_W
529 case FTS_W:
530 rval = undelete(p->fts_accpath);
531 if (rval == 0 && (fflag && errno == ENOENT)) {
532 if (vflag)
533 (void)printf("%s\n",
534 p->fts_path);
535 continue;
536 }
537 break;
538#endif
539
540 case FTS_NS:
541 /*
542 * Assume that since fts_read() couldn't stat
543 * the file, it can't be unlinked.
544 */
545 if (fflag)
546 continue;
547 /* FALLTHROUGH */
548 default:
549 if (Pflag)
550 if (!rm_overwrite(p->fts_accpath, NULL))
551 continue;
552 rval = unlink(p->fts_accpath);
553#ifdef _MSC_VER
554 if (rval != 0) {
555 chmod(p->fts_accpath, 0777);
556 rval = unlink(p->fts_accpath);
557 }
558#endif
559
560 if (rval == 0 || (fflag && errno == ENOENT)) {
561 if (rval == 0 && vflag)
562 (void)printf("%s\n",
563 p->fts_path);
564 continue;
565 }
566 }
567 }
568err:
569 fprintf(stderr, "%s: %s: %s\n", argv0, p->fts_path, strerror(errno));
570 eval = 1;
571 }
572 if (errno) {
573 fprintf(stderr, "%s: fts_read: %s\n", argv0, strerror(errno));
574 eval = 1;
575 }
576 fts_close(fts);
577}
578
579static void
580rm_file(char **argv)
581{
582 struct stat sb;
583 int rval;
584 char *f;
585
586 /*
587 * Remove a file. POSIX 1003.2 states that, by default, attempting
588 * to remove a directory is an error, so must always stat the file.
589 */
590 while ((f = *argv++) != NULL) {
591 /* Assume if can't stat the file, can't unlink it. */
592 if (lstat(f, &sb)) {
593#ifdef FTS_WHITEOUT
594 if (Wflag) {
595 sb.st_mode = S_IFWHT|S_IWUSR|S_IRUSR;
596 } else {
597#else
598 {
599#endif
600 if (!fflag || errno != ENOENT) {
601 fprintf(stderr, "%s: %s: %s\n", argv0, f, strerror(errno));
602 eval = 1;
603 }
604 continue;
605 }
606#ifdef FTS_WHITEOUT
607 } else if (Wflag) {
608 fprintf(stderr, "%s: %s: %s\n", argv0, f, strerror(EEXIST));
609 eval = 1;
610 continue;
611#endif
612 }
613
614 if (S_ISDIR(sb.st_mode) && !dflag) {
615 fprintf(stderr, "%s: %s: is a directory\n", argv0, f);
616 eval = 1;
617 continue;
618 }
619 if (!fflag && !S_ISWHT(sb.st_mode) && !check(f, f, &sb))
620 continue;
621 rval = 0;
622#ifdef UF_APPEND
623 if (!uid &&
624 (sb.st_flags & (UF_APPEND|UF_IMMUTABLE)) &&
625 !(sb.st_flags & (SF_APPEND|SF_IMMUTABLE)))
626 rval = chflags(f, sb.st_flags & ~(UF_APPEND|UF_IMMUTABLE));
627#endif
628 if (rval == 0) {
629 if (S_ISWHT(sb.st_mode))
630 rval = undelete(f);
631 else if (S_ISDIR(sb.st_mode))
632 rval = rmdir(f);
633 else {
634 if (Pflag)
635 if (!rm_overwrite(f, &sb))
636 continue;
637 rval = unlink(f);
638#ifdef _MSC_VER
639 if (rval != 0) {
640 chmod(f, 0777);
641 rval = unlink(f);
642 }
643#endif
644 }
645 }
646 if (rval && (!fflag || errno != ENOENT)) {
647 fprintf(stderr, "%s: %s: %s\n", argv0, f, strerror(errno));
648 eval = 1;
649 }
650 if (vflag && rval == 0)
651 (void)printf("%s\n", f);
652 }
653}
654
655/*
656 * rm_overwrite --
657 * Overwrite the file 3 times with varying bit patterns.
658 *
659 * XXX
660 * This is a cheap way to *really* delete files. Note that only regular
661 * files are deleted, directories (and therefore names) will remain.
662 * Also, this assumes a fixed-block file system (like FFS, or a V7 or a
663 * System V file system). In a logging file system, you'll have to have
664 * kernel support.
665 */
666static int
667rm_overwrite(char *file, struct stat *sbp)
668{
669 struct stat sb;
670#ifdef HAVE_FSTATFS
671 struct statfs fsb;
672#endif
673 off_t len;
674 int bsize, fd, wlen;
675 char *buf = NULL;
676
677 fd = -1;
678 if (sbp == NULL) {
679 if (lstat(file, &sb))
680 goto err;
681 sbp = &sb;
682 }
683 if (!S_ISREG(sbp->st_mode))
684 return (1);
685 if ((fd = open(file, O_WRONLY, 0)) == -1)
686 goto err;
687#ifdef HAVE_FSTATFS
688 if (fstatfs(fd, &fsb) == -1)
689 goto err;
690 bsize = MAX(fsb.f_iosize, 1024);
691#elif defined(HAVE_ST_BLKSIZE)
692 bsize = MAX(sb.st_blksize, 1024);
693#else
694 bsize = 1024;
695#endif
696 if ((buf = malloc(bsize)) == NULL)
697 exit(err(1, "%s: malloc", file));
698
699#define PASS(byte) { \
700 memset(buf, byte, bsize); \
701 for (len = sbp->st_size; len > 0; len -= wlen) { \
702 wlen = len < bsize ? len : bsize; \
703 if (write(fd, buf, wlen) != wlen) \
704 goto err; \
705 } \
706}
707 PASS(0xff);
708 if (fsync(fd) || lseek(fd, (off_t)0, SEEK_SET))
709 goto err;
710 PASS(0x00);
711 if (fsync(fd) || lseek(fd, (off_t)0, SEEK_SET))
712 goto err;
713 PASS(0xff);
714 if (!fsync(fd) && !close(fd)) {
715 free(buf);
716 return (1);
717 }
718
719err: eval = 1;
720 if (buf)
721 free(buf);
722 if (fd != -1)
723 close(fd);
724 fprintf(stderr, "%s: %s: %s\n", argv0, file, strerror(errno));
725 return (0);
726}
727
728
729static int
730check(char *path, char *name, struct stat *sp)
731{
732 int ch, first;
733 char modep[15], *flagsp;
734
735 /* Check -i first. */
736 if (iflag)
737 (void)fprintf(stderr, "remove %s? ", path);
738 else {
739 /*
740 * If it's not a symbolic link and it's unwritable and we're
741 * talking to a terminal, ask. Symbolic links are excluded
742 * because their permissions are meaningless. Check stdin_ok
743 * first because we may not have stat'ed the file.
744 * Also skip this check if the -P option was specified because
745 * we will not be able to overwrite file contents and will
746 * barf later.
747 */
748 if (!stdin_ok || S_ISLNK(sp->st_mode) || Pflag ||
749 (!access(name, W_OK) &&
750#ifdef SF_APPEND
751 !(sp->st_flags & (SF_APPEND|SF_IMMUTABLE)) &&
752 (!(sp->st_flags & (UF_APPEND|UF_IMMUTABLE)) || !uid))
753#else
754 1)
755#endif
756 )
757 return (1);
758 strmode(sp->st_mode, modep);
759#ifdef SF_APPEND
760 if ((flagsp = fflagstostr(sp->st_flags)) == NULL)
761 exit(err(1, "fflagstostr"));
762 (void)fprintf(stderr, "override %s%s%s/%s %s%sfor %s? ",
763 modep + 1, modep[9] == ' ' ? "" : " ",
764 user_from_uid(sp->st_uid, 0),
765 group_from_gid(sp->st_gid, 0),
766 *flagsp ? flagsp : "", *flagsp ? " " : "",
767 path);
768 free(flagsp);
769#else
770 (void)flagsp;
771 (void)fprintf(stderr, "override %s%s %d/%d for %s? ",
772 modep + 1, modep[9] == ' ' ? "" : " ",
773 sp->st_uid, sp->st_gid, path);
774#endif
775 }
776 (void)fflush(stderr);
777
778 first = ch = getchar();
779 while (ch != '\n' && ch != EOF)
780 ch = getchar();
781 return (first == 'y' || first == 'Y');
782}
783
784#define ISDOT(a) ((a)[0] == '.' && (!(a)[1] || ((a)[1] == '.' && !(a)[2])))
785static void
786checkdot(char **argv)
787{
788 char *p, **save, **t;
789 int complained;
790
791 complained = 0;
792 for (t = argv; *t;) {
793#ifdef HAVE_DOS_PATHS
794 const char *tmp = p = *t;
795 while (*tmp) {
796 switch (*tmp) {
797 case '/':
798 case '\\':
799 case ':':
800 p = (char *)tmp + 1;
801 break;
802 }
803 tmp++;
804 }
805#else
806 if ((p = strrchr(*t, '/')) != NULL)
807 ++p;
808 else
809 p = *t;
810#endif
811 if (ISDOT(p)) {
812 if (!complained++)
813 fprintf(stderr, "%s: \".\" and \"..\" may not be removed\n", argv0);
814 eval = 1;
815 for (save = t; (t[0] = t[1]) != NULL; ++t)
816 continue;
817 t = save;
818 } else
819 ++t;
820 }
821}
822
823static int
824usage(FILE *pf)
825{
826 fprintf(pf,
827 "usage: %s [-f | -i] [-dPRvW] [--disable-protection] file ...\n"
828 " or: %s --help\n"
829 " or: %s --version\n"
830 "\n"
831 "Options:\n"
832 " -f\n"
833 " Attempt to remove files without prompting, regardless of the file\n"
834 " permission. Ignore non-existing files. Overrides previous -i's.\n"
835 " -i\n"
836 " Prompt for each file. Always.\n"
837 " -d\n"
838 " Attempt to remove directories as well as other kinds of files.\n"
839 " -P\n"
840 " Overwrite regular files before deleting; three passes: ff,0,ff\n"
841 " -R\n"
842 " Attempt to remove the file hierachy rooted in each file argument.\n"
843 " This option implies -d and file protection.\n"
844 " -v\n"
845 " Be verbose, show files as they are removed.\n"
846 " -W\n"
847 " Undelete without files.\n"
848 " --disable-protection\n"
849 " Will disable the protection file protection applied by -R.\n"
850 "\n"
851 "Environment:\n"
852 " KMK_RM_DISABLE_PROTECTION\n"
853 " Same as --disable-protection.\n"
854 " KMK_RM_PROTECTION_DEPTH\n"
855 " Changes the protection depth, path or number. Default: %d\n"
856 "\n"
857 "The file protection of the top %d layers of the file hierarchy is there\n"
858 "to try prevent makefiles from doing bad things to your system. This\n"
859 "protection is not bulletproof, but should help prevent you from shooting\n"
860 "yourself in the foot. Not that it does NOT apply to normal file removal.\n"
861 ,
862 g_progname, g_progname, g_progname,
863 DEFAULT_REQUIRED_R_DEPTH, DEFAULT_REQUIRED_R_DEPTH);
864 return EX_USAGE;
865}
Note: See TracBrowser for help on using the repository browser.

© 2024 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette