mirror of
git://git.code.sf.net/p/cdesktopenv/code
synced 2025-02-15 04:32:24 +00:00
Support for the long-dead 3DFS userland versioning file system was already removed from ksh93 (although I'd overlooked some minor things), but libast still supported it. This removes that too. src/lib/libast/include/fs3d.h, src/lib/libast/man/fs3d.3, src/lib/libast/misc/fs3d.c: - Removed. bin/package, src/cmd/INIT/package.sh: - Remove attempted use of removed vpath builtin. src/cmd/ksh93/*: - Remove minor 3dfs vestiges. src/lib/lib{ast,cmd,coshell}/*: - Remove code supporting 3dfs.
401 lines
11 KiB
C
401 lines
11 KiB
C
/***********************************************************************
|
|
* *
|
|
* This software is part of the ast package *
|
|
* Copyright (c) 1992-2012 AT&T Intellectual Property *
|
|
* and is licensed under the *
|
|
* Eclipse Public License, Version 1.0 *
|
|
* by AT&T Intellectual Property *
|
|
* *
|
|
* A copy of the License is available at *
|
|
* http://www.eclipse.org/org/documents/epl-v10.html *
|
|
* (with md5 checksum b35adb5213ca9657e911e9befb180842) *
|
|
* *
|
|
* Information and Software Systems Research *
|
|
* AT&T Research *
|
|
* Florham Park NJ *
|
|
* *
|
|
* Glenn Fowler <gsf@research.att.com> *
|
|
* David Korn <dgk@research.att.com> *
|
|
* *
|
|
***********************************************************************/
|
|
#pragma prototyped
|
|
/*
|
|
* Glenn Fowler
|
|
* AT&T Research
|
|
*
|
|
* rm [-fir] [file ...]
|
|
*/
|
|
|
|
static const char usage[] =
|
|
"[-?\n@(#)$Id: rm (AT&T Research) 2012-02-14 $\n]"
|
|
USAGE_LICENSE
|
|
"[+NAME?rm - remove files]"
|
|
"[+DESCRIPTION?\brm\b removes the named \afile\a arguments. By default it"
|
|
" does not remove directories. If a file is unwritable, the"
|
|
" standard input is a terminal, and the \b--force\b option is not"
|
|
" given, \brm\b prompts the user for whether to remove the file."
|
|
" An affirmative response (\by\b or \bY\b) removes the file, a quit"
|
|
" response (\bq\b or \bQ\b) causes \brm\b to exit immediately, and"
|
|
" all other responses skip the current file.]"
|
|
|
|
"[c|F:clear|clobber?Clear the contents of each file before removing by"
|
|
" writing a 0 filled buffer the same size as the file, executing"
|
|
" \bfsync\b(2) and closing before attempting to remove. Implemented"
|
|
" only on systems that support \bfsync\b(2).]"
|
|
"[d:directory?\bremove\b(3) (or \bunlink\b(2)) directories rather than"
|
|
" \brmdir\b(2), and don't require that they be empty before removal."
|
|
" The caller requires sufficient privilege, not to mention a strong"
|
|
" constitution, to use this option. Even though the directory must"
|
|
" not be empty, \brm\b still attempts to empty it before removal.]"
|
|
"[f:force?Ignore nonexistent files, ignore no file operands specified,"
|
|
" and never prompt the user.]"
|
|
"[i:interactive|prompt?Prompt whether to remove each file."
|
|
" An affirmative response (\by\b or \bY\b) removes the file, a quit"
|
|
" response (\bq\b or \bQ\b) causes \brm\b to exit immediately, and"
|
|
" all other responses skip the current file.]"
|
|
"[r|R:recursive?Remove the contents of directories recursively.]"
|
|
"[u:unconditional?If \b--recursive\b and \b--force\b are also enabled then"
|
|
" the owner read, write and execute modes are enabled (if not already"
|
|
" enabled) for each directory before attempting to remove directory"
|
|
" contents.]"
|
|
"[v:verbose?Print the name of each file before removing it.]"
|
|
|
|
"\n"
|
|
"\nfile ...\n"
|
|
"\n"
|
|
|
|
"[+SEE ALSO?\bmv\b(1), \brmdir\b(2), \bunlink\b(2), \bremove\b(3)]"
|
|
;
|
|
|
|
#include <cmd.h>
|
|
#include <ls.h>
|
|
#include <fts_fix.h>
|
|
|
|
#define RM_ENTRY 1
|
|
|
|
#define beenhere(f) (((f)->fts_number>>1)==(f)->fts_statp->st_nlink)
|
|
#define isempty(f) (!((f)->fts_number&RM_ENTRY))
|
|
#define nonempty(f) ((f)->fts_parent->fts_number|=RM_ENTRY)
|
|
#define pathchunk(n) roundof(n,1024)
|
|
#define retry(f) ((f)->fts_number=((f)->fts_statp->st_nlink<<1))
|
|
|
|
typedef struct State_s /* program state */
|
|
{
|
|
Shbltin_t* context; /* builtin context */
|
|
int clobber; /* clear out file data first */
|
|
int directory; /* remove(dir) not rmdir(dir) */
|
|
int force; /* force actions */
|
|
int interactive; /* prompt for approval */
|
|
int recursive; /* remove subtrees too */
|
|
int terminal; /* attached to terminal */
|
|
int uid; /* caller uid */
|
|
int unconditional; /* enable dir rwx on preorder */
|
|
int verbose; /* display each file */
|
|
#if _lib_fsync
|
|
char buf[SF_BUFSIZE];/* clobber buffer */
|
|
#endif
|
|
} State_t;
|
|
|
|
/*
|
|
* remove a single file
|
|
*/
|
|
|
|
static int
|
|
rm(State_t* state, register FTSENT* ent)
|
|
{
|
|
register char* path;
|
|
register int n;
|
|
int v;
|
|
struct stat st;
|
|
|
|
if (ent->fts_info == FTS_NS || ent->fts_info == FTS_ERR || ent->fts_info == FTS_SLNONE)
|
|
{
|
|
if (!state->force)
|
|
error(2, "%s: not found", ent->fts_path);
|
|
}
|
|
else switch (ent->fts_info)
|
|
{
|
|
case FTS_DNR:
|
|
case FTS_DNX:
|
|
if (state->unconditional)
|
|
{
|
|
if (!beenhere(ent))
|
|
break;
|
|
if (!chmod(ent->fts_name, (ent->fts_statp->st_mode & S_IPERM)|S_IRWXU))
|
|
{
|
|
fts_set(NiL, ent, FTS_AGAIN);
|
|
break;
|
|
}
|
|
error_info.errors++;
|
|
}
|
|
else if (!state->force)
|
|
error(2, "%s: cannot %s directory", ent->fts_path, (ent->fts_info & FTS_NR) ? "read" : "search");
|
|
else
|
|
error_info.errors++;
|
|
fts_set(NiL, ent, FTS_SKIP);
|
|
nonempty(ent);
|
|
break;
|
|
case FTS_D:
|
|
case FTS_DC:
|
|
path = ent->fts_name;
|
|
if (path[0] == '.' && (!path[1] || path[1] == '.' && !path[2]) && (ent->fts_level > 0 || path[1]))
|
|
{
|
|
fts_set(NiL, ent, FTS_SKIP);
|
|
if (!state->force)
|
|
error(2, "%s: cannot remove", ent->fts_path);
|
|
else
|
|
error_info.errors++;
|
|
break;
|
|
}
|
|
if (!state->recursive)
|
|
{
|
|
fts_set(NiL, ent, FTS_SKIP);
|
|
error(2, "%s: directory", ent->fts_path);
|
|
break;
|
|
}
|
|
if (!beenhere(ent))
|
|
{
|
|
if (state->unconditional && (ent->fts_statp->st_mode & S_IRWXU) != S_IRWXU)
|
|
chmod(path, (ent->fts_statp->st_mode & S_IPERM)|S_IRWXU);
|
|
if (ent->fts_level > 0)
|
|
{
|
|
char* s;
|
|
|
|
if (ent->fts_accpath == ent->fts_name || !(s = strrchr(ent->fts_accpath, '/')))
|
|
v = !stat(".", &st);
|
|
else
|
|
{
|
|
path = ent->fts_accpath;
|
|
*s = 0;
|
|
v = !stat(path, &st);
|
|
*s = '/';
|
|
}
|
|
if (v)
|
|
v = st.st_nlink <= 2 || st.st_ino == ent->fts_parent->fts_statp->st_ino && st.st_dev == ent->fts_parent->fts_statp->st_dev || strchr(astconf("PATH_ATTRIBUTES", path, NiL), 'l');
|
|
}
|
|
else
|
|
v = 1;
|
|
if (v)
|
|
{
|
|
if (state->interactive)
|
|
{
|
|
if ((v = astquery(-1, "remove directory %s? ", ent->fts_path)) < 0 || sh_checksig(state->context))
|
|
return -1;
|
|
if (v > 0)
|
|
{
|
|
fts_set(NiL, ent, FTS_SKIP);
|
|
nonempty(ent);
|
|
}
|
|
}
|
|
if (ent->fts_info == FTS_D)
|
|
break;
|
|
}
|
|
else
|
|
{
|
|
ent->fts_info = FTS_DC;
|
|
error(1, "%s: hard link to directory", ent->fts_path);
|
|
}
|
|
}
|
|
else if (ent->fts_info == FTS_D)
|
|
break;
|
|
/*FALLTHROUGH*/
|
|
case FTS_DP:
|
|
if (isempty(ent) || state->directory)
|
|
{
|
|
path = ent->fts_name;
|
|
if (path[0] != '.' || path[1])
|
|
{
|
|
path = ent->fts_accpath;
|
|
if (state->verbose)
|
|
sfputr(sfstdout, ent->fts_path, '\n');
|
|
if ((ent->fts_info == FTS_DC || state->directory) ? remove(path) : rmdir(path))
|
|
switch (errno)
|
|
{
|
|
case ENOENT:
|
|
break;
|
|
case EEXIST:
|
|
#if defined(ENOTEMPTY) && (ENOTEMPTY) != (EEXIST)
|
|
case ENOTEMPTY:
|
|
#endif
|
|
if (ent->fts_info == FTS_DP && !beenhere(ent))
|
|
{
|
|
retry(ent);
|
|
fts_set(NiL, ent, FTS_AGAIN);
|
|
break;
|
|
}
|
|
/*FALLTHROUGH*/
|
|
default:
|
|
nonempty(ent);
|
|
if (!state->force)
|
|
error(ERROR_SYSTEM|2, "%s: directory not removed", ent->fts_path);
|
|
else
|
|
error_info.errors++;
|
|
break;
|
|
}
|
|
}
|
|
else if (!state->force)
|
|
error(2, "%s: cannot remove", ent->fts_path);
|
|
else
|
|
error_info.errors++;
|
|
}
|
|
else
|
|
{
|
|
nonempty(ent);
|
|
if (!state->force)
|
|
error(2, "%s: directory not removed", ent->fts_path);
|
|
else
|
|
error_info.errors++;
|
|
}
|
|
break;
|
|
default:
|
|
path = ent->fts_accpath;
|
|
if (state->verbose)
|
|
sfputr(sfstdout, ent->fts_path, '\n');
|
|
if (state->interactive)
|
|
{
|
|
if ((v = astquery(-1, "remove %s? ", ent->fts_path)) < 0 || sh_checksig(state->context))
|
|
return -1;
|
|
if (v > 0)
|
|
{
|
|
nonempty(ent);
|
|
break;
|
|
}
|
|
}
|
|
else if (!(ent->fts_info & FTS_SL) && !state->force && state->terminal && eaccess(path, W_OK))
|
|
{
|
|
if ((v = astquery(-1, "override protection %s for %s? ",
|
|
#ifdef ETXTBSY
|
|
errno == ETXTBSY ? "``running program''" :
|
|
#endif
|
|
ent->fts_statp->st_uid != state->uid ? "``not owner''" :
|
|
fmtmode(ent->fts_statp->st_mode & S_IPERM, 0) + 1, ent->fts_path)) < 0 ||
|
|
sh_checksig(state->context))
|
|
return -1;
|
|
if (v > 0)
|
|
{
|
|
nonempty(ent);
|
|
break;
|
|
}
|
|
}
|
|
#if _lib_fsync
|
|
if (state->clobber && S_ISREG(ent->fts_statp->st_mode) && ent->fts_statp->st_size > 0)
|
|
{
|
|
if ((n = open(path, O_WRONLY|O_cloexec)) < 0)
|
|
error(ERROR_SYSTEM|2, "%s: cannot clear data", ent->fts_path);
|
|
else
|
|
{
|
|
off_t c = ent->fts_statp->st_size;
|
|
|
|
for (;;)
|
|
{
|
|
if (write(n, state->buf, sizeof(state->buf)) != sizeof(state->buf))
|
|
{
|
|
error(ERROR_SYSTEM|2, "%s: data clear error", ent->fts_path);
|
|
break;
|
|
}
|
|
if (c <= sizeof(state->buf))
|
|
break;
|
|
c -= sizeof(state->buf);
|
|
}
|
|
fsync(n);
|
|
close(n);
|
|
}
|
|
}
|
|
#endif
|
|
if (remove(path))
|
|
{
|
|
nonempty(ent);
|
|
switch (errno)
|
|
{
|
|
case ENOENT:
|
|
break;
|
|
default:
|
|
if (!state->force || state->interactive)
|
|
error(ERROR_SYSTEM|2, "%s: not removed", ent->fts_path);
|
|
else
|
|
error_info.errors++;
|
|
break;
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
int
|
|
b_rm(int argc, register char** argv, Shbltin_t* context)
|
|
{
|
|
State_t state;
|
|
FTS* fts;
|
|
FTSENT* ent;
|
|
|
|
cmdinit(argc, argv, context, ERROR_CATALOG, ERROR_NOTIFY);
|
|
memset(&state, 0, sizeof(state));
|
|
state.context = context;
|
|
state.terminal = isatty(0);
|
|
for (;;)
|
|
{
|
|
switch (optget(argv, usage))
|
|
{
|
|
case 'd':
|
|
state.directory = 1;
|
|
continue;
|
|
case 'f':
|
|
state.force = 1;
|
|
state.interactive = 0;
|
|
continue;
|
|
case 'i':
|
|
state.interactive = 1;
|
|
state.force = 0;
|
|
continue;
|
|
case 'r':
|
|
case 'R':
|
|
state.recursive = 1;
|
|
continue;
|
|
case 'F':
|
|
#if _lib_fsync
|
|
state.clobber = 1;
|
|
#else
|
|
error(1, "%s not implemented on this system", opt_info.name);
|
|
#endif
|
|
continue;
|
|
case 'u':
|
|
state.unconditional = 1;
|
|
continue;
|
|
case 'v':
|
|
state.verbose = 1;
|
|
continue;
|
|
case '?':
|
|
error(ERROR_USAGE|4, "%s", opt_info.arg);
|
|
break;
|
|
case ':':
|
|
error(2, "%s", opt_info.arg);
|
|
break;
|
|
}
|
|
break;
|
|
}
|
|
argv += opt_info.index;
|
|
if (*argv && streq(*argv, "-") && !streq(*(argv - 1), "--"))
|
|
argv++;
|
|
if (error_info.errors || !*argv && !state.force)
|
|
error(ERROR_USAGE|4, "%s", optusage(NiL));
|
|
if (!*argv)
|
|
return 0;
|
|
|
|
/*
|
|
* do it
|
|
*/
|
|
|
|
if (state.interactive)
|
|
state.verbose = 0;
|
|
state.uid = geteuid();
|
|
state.unconditional = state.unconditional && state.recursive && state.force;
|
|
if (fts = fts_open(argv, FTS_PHYSICAL, NiL))
|
|
{
|
|
while (!sh_checksig(context) && (ent = fts_read(fts)) && !rm(&state, ent));
|
|
fts_close(fts);
|
|
}
|
|
else if (!state.force)
|
|
error(ERROR_SYSTEM|2, "%s: cannot remove", argv[0]);
|
|
return error_info.errors != 0;
|
|
}
|