Files
seta75D d6fe8fe829 Init
2021-10-11 22:19:34 -03:00

499 lines
12 KiB
C

static char sccsid[] = "@(#)95 1.24.1.9 src/bos/usr/bin/rm/rm.c, cmdfiles, bos41J, 9508A 2/16/95 13:25:44";
/*
* COMPONENT_NAME: (CMDFILES) commands that manipulate files
*
* FUNCTIONS: rm, rmdir
*
* ORIGINS: 3, 18, 26, 27
*
* This module contains IBM CONFIDENTIAL code. -- (IBM
* Confidential Restricted when combined with the aggregated
* modules for this product)
* SOURCE MATERIALS
* (C) COPYRIGHT International Business Machines Corp. 1985, 1995
* All Rights Reserved
*
* US Government Users Restricted Rights - Use, duplication or
* disclosure restricted by GSA ADP Schedule Contract with IBM Corp.
*
* OSF/1 Release 1.0
* (c) Copyright 1990, OPEN SOFTWARE FOUNDATION, INC.
* ALL RIGHTS RESERVED
*
* static char rcsid[] = "RCSfile: rm.c,v Revision: 2.10 (OSF) Date: 90/10/07 16:53:20
*/
#include <stdlib.h>
#include <stdio.h>
#include <sys/types.h>
#include <sys/limits.h> /* for PATH_MAX */
#include <sys/stat.h>
#include <dirent.h>
#include <sys/access.h>
#include <sys/mode.h>
#include <sys/errno.h>
#include <ctype.h>
#include <sys/types.h>
#include <locale.h>
#include "rm_msg.h"
static nl_catd catd;
#define MSGSTR(Num,Str) catgets(catd,MS_RM,Num,Str)
static int errcode;
static char *cmdname;
static dev_t cwd_st_dev;
static ino_t cwd_st_ino;
static int is_cwd_called = 0;
static int fflg, iflg, rflg, eflg = 0; /* options for rm command */
/*
* NAME: writable
*
* FUNCTION: Test if file is writable. Symbolic links are always writable.
*
* RETURNS: 1 - yes
* 0 - no
*/
/* use a macro for performance */
#define writable(name,buf) ((((buf)->st_mode&S_IFMT) == S_IFLNK)?1:(access(name,W_OK) == 0))
#if 0
static writable(name, buf)
char *name;
struct stat *buf;
{
if ((buf->st_mode&S_IFMT) == S_IFLNK)
return 1;
return (access(name,W_OK) == 0);
}
#endif
/*
* NAME: dotname
*
* FUNCTION: Test whether s is "." or ".."
*
* RETURNS: 1 - yes, s is "." or ".."
* 0 - no
*/
/* use a macro for performance */
#define dotname(s) (s[0] == '.' && (!s[1] || s[1] == '.' && !s[2]))
#if 0
static dotname(s)
char *s;
{
return(s[0] == '.' && (!s[1] || s[1] == '.' && !s[2]));
}
#endif
/*
* NAME: {rm | delete} [-fiRre] File ...
* rmdir [-p] Directory ...
*
* FUNCTION: Removes file and directory entries.
*
* NOTES: -f do not prompt for confirmation
* -i prompt for confirmation
* -R remove file hierarchies
* -r same as -R
* -e displays a message after deleting each file
*
* -p remove all directories in a pathname
*/
main(argc,argv)
int argc;
char **argv;
{
int rv = 2;
(void) setlocale (LC_ALL,"");
catd = catopen((char *)MF_RM, NL_CAT_LOCALE);
/* find our cmdname */
cmdname = (char *) strrchr(argv[0],'/');
if (cmdname) cmdname++; else cmdname = argv[0];
if (!strcmp(cmdname,"rmdir")) rv = rmdir_main(argc,argv);
else rv = rm_main(argc, argv); /* assume rm */
exit(rv);
}
/*
* NAME: rm_main
*
* FUNCTION: main rm routine -- checks options and calls rm
*/
static rm_main(argc, argv)
int argc;
char *argv[];
{
register char *arg;
int c;
int cwdfis = 0; /* Current Working Directory found in Subdirectory
* Used by recursive rm function to detect a call of
* the form "rm -r <ancestor of current working directory>"
*/
while ((c=getopt(argc, argv, "fiRre")) != EOF)
switch(c) {
case 'f':
fflg++;
iflg = 0;
break;
case 'i':
iflg++;
fflg = 0;
break;
case 'R':
case 'r':
rflg++;
break;
case 'e':
eflg++;
break;
default:
fprintf(stderr,
MSGSTR(USAGE,"Usage: %s [-firRe] [--] File ...\n"), cmdname);
return(2);
}
argc -= optind;
argv=&argv[optind];
if (argc < 1 && !fflg) {
fprintf(stderr, MSGSTR(USAGE,"Usage: %s [-firRe] [--] File ...\n"), cmdname);
return(2);
}
while (argc-- > 0) {
arg = (char *) strrchr(*argv,'/');
if (arg) arg++; else arg = *argv;
if (dotname(arg)) {
fprintf(stderr,MSGSTR(CANTREMOVE,
"%s: cannot remove '.' or '..'\n"),cmdname);
++errcode;
}
else rm(*argv, *argv, &cwdfis);
argv++;
}
return(errcode?2:0);
}
/*
* NAME: rm
*
* FUNCTION: (recursive) removes files and directories
*/
static rm(arg, short_arg, cwdic)
char arg[]; /* the actual path, as seen by user. May be greater than PATH_MAX */
char short_arg[];/* if arg is greater than PATH_MAX, then this will be a shorter */
/* relative path name. Otherwise, it will be the same as arg */
int *cwdic; /* current working directory is a child */
{
struct stat buf;
struct dirent *direct;
char *name;
int malloc_len = PATH_MAX;/* current ammount of malloced space in name */
DIR *d;
int cwdfis; /* current working directory found in subdirectory */
*cwdic = 0; /* until proven 1 by finding it directly or by having a
* recursive call to rm find it in a subdirectory .
*/
/* The following "Steps" are specified by XPG4 */
/* Step 1 -- If file does not exist */
if(lstat(short_arg, &buf)) {
/* Step 1.a */
/* if '-f', don't print diagnostic msg for non-existent file */
if (fflg==0) {
fprintf(stderr, "%s: ", cmdname);
perror(arg);
++errcode;
}
/* Step 1.b */
return;
}
/* Step 2 -- If file is a directory... */
if ((buf.st_mode&S_IFMT) == S_IFDIR) {
/* Step 2.a */
if (!rflg) { /* directory and rflg not set */
fprintf(stderr, MSGSTR(ISDIRECT,"%s: cannot remove directory %s\n"), cmdname, arg);
++errcode;
return;
}
/* Step 2.b */
if (!fflg && ((!writable(short_arg, &buf) && isatty(fileno(stdin)))
|| iflg)) {
fprintf(stderr,MSGSTR(DIRECTORY,"%s: remove files in directory %s? Enter y for yes. "),cmdname,arg);
if(!yes())
return;
}
/* Step 2.c */
if((d=opendir(short_arg)) == NULL) {
fprintf(stderr,MSGSTR(CANTREAD,"%s: cannot read %s\n"), cmdname, arg);
++errcode;
return;
}
if ((name=(char *)malloc(malloc_len))==NULL) { /* get initial memory */
perror(cmdname);
exit(1); /* fatal error */
}
while ((direct = readdir(d)) != NULL) {
static int tmp_length;
if(!dotname(direct->d_name)) {
tmp_length=strlen(arg)+strlen(direct->d_name)+2;
while (tmp_length > malloc_len) {
malloc_len *= 2;
if ((name=(char *)realloc(name,malloc_len))==NULL) {
perror(cmdname);
exit(1); /* fatal error */
}
}
sprintf(name, "%s/%s", arg, direct->d_name);
cwdfis = 0;
if (arg==short_arg) { /* arg is <= PATH_MAX */
if (tmp_length <= PATH_MAX) /* normal path */
rm(name, name, &cwdfis);
else { /* boundry case */
/* need to jump all the way down and back */
char original[PATH_MAX];
if (getcwd(original,PATH_MAX)==NULL) {
perror(cmdname);
exit(1); /* fatal error */
}
if (chdir(arg)!=0) {
perror(cmdname);
++errcode;
}
rm(name, direct->d_name, &cwdfis);
if (chdir(original)!=0) {
perror(cmdname);
++errcode;
}
}
}
else { /* deep case */
/* need to go down and up a single directory */
if (chdir(short_arg)!=0) {
perror(cmdname);
++errcode;
}
rm(name, direct->d_name, &cwdfis);
if (chdir("..")!=0) {
perror(cmdname);
++errcode;
}
}
if (cwdfis) (*cwdic)++;
}
}
free(name);
closedir(d);
if (*cwdic)
return;
/* Step 2.d */
if (iflg) {
fprintf(stderr,MSGSTR(REMPROMPT,"%s: remove %s? "),cmdname,arg);
if(!yes())
return;
}
/* Step 3 */
/* Nothing to do, this is a directory. */
/* Step 4 (directory) */
/* if rmdir fails, print diagnostic message. */
if (rmdir(short_arg)) {
if (rm_msg(cmdname,arg,short_arg,1) == 1) (*cwdic)++; /* current working directory is a child */
errcode++;
return;
}
else if (eflg)
fprintf(stderr, MSGSTR(REMOVEDDIR, "%s: removing directory %s\n"), cmdname, arg);
return;
} /* end of directory logic */
/* Step 3 */
if (!fflg && ((!writable(short_arg, &buf) && isatty(fileno(stdin)))
|| iflg)) {
fprintf(stderr,MSGSTR(REMPROMPT,"%s: remove %s? "),cmdname,arg);
if(!yes())
return;
}
/* Step 4 (not directory) */
/* if unlink fails, print diagnostic message. */
if(unlink(short_arg)) {
if (rm_msg(cmdname,arg,short_arg,0) == 1) (*cwdic)++;
++errcode;
return;
}
else if (eflg)
fprintf(stderr, MSGSTR(FILEREMOVED, "%s: removing %s\n"), cmdname, arg);
return;
}
/*
* NAME: rmdir_main
*
* FUNCTION: main routine for rmdir command
*/
static rmdir_main(argc,argv)
char **argv;
int argc;
{
char rc = 0;
int Errors = 0;
int c;
int pflg = 0;
while ((c = getopt(argc, argv, "p")) != EOF)
switch(c) {
case 'p':
pflg++;
break;
default:
fprintf(stderr, MSGSTR(USAGE_RMDIR,"Usage: rmdir [-p] Directory ...\n"));
return(2);
/* break; line warning: statement not reached */
}
argc -= optind;
argv = &argv[optind];
if(argc < 1) {
fprintf(stderr, MSGSTR(USAGE_RMDIR,"Usage: rmdir [-p] Directory ...\n"));
return(2);
}
while(argc--) {
char *cp = *argv++;
char *p;
int i;
i = strlen(cp) - 1;
if (cp[i] == '/')
cp[i] = '\0'; /* remove '/' at end of pathname if any */
do {
if ((rc = rmdir(cp)) != 0) {
rm_msg("rmdir", cp, cp, 1);
++Errors;
break;
}
if (p=(char *)strrchr(cp,'/'))
*p = '\0'; /* remove last dirname*/
else
break;
} while(pflg && *cp);
}
return(Errors? 2: 0);
}
/*
* NAME: yes
*
* FUNCTION: gets interactive response from user
*/
static yes()
{
#define RESP_LEN 100
char response[RESP_LEN];
fgets(response,RESP_LEN,stdin);
return(rpmatch(response) == 1);
}
/*
* NAME: rm_msg
*
* FUNCTION: determine appropriate message to print from bad return
* from rmdir or unlink subroutine call
* RETURNS:
* if (errno = EEXIST or (errno = EBUSY and path is a known directory)
* then if (path is the current directory path)
* then write "cannot remove current directory", return 1
* else write "directory is not empty", return 2
* else write (whatever perror writes), return 0
*
* path is the path name that should be echoed to the user. It may be longer than
* PATH_MAX. short_path should be used for actual path access, it will always be
* less than PATH_MAX. These values may be the same if the path name is shorter
* than PATH_MAX.
*/
static int rm_msg(cmd,path,short_path,is_dir)
char *cmd, *path, *short_path;
int is_dir;
{
int rc=0;
if ((errno == EEXIST) || ((errno == EBUSY) && (is_dir == 1))) {
if (is_cwd(short_path)) {
fprintf(stderr, MSGSTR(CANTRMSELF,"%1$s: Cannot remove the current directory %2$s.\n"),cmd,path);
rc = 1;
}
else {
fprintf(stderr, MSGSTR(CANTRMFULL,"%1$s: Directory %2$s is not empty.\n"),cmd,path);
rc = 2;
}
}
else {
fprintf(stderr, MSGSTR(NOTREMOVED,"%1$s: %2$s not removed.\n"),cmd,path);
perror("");
rc = 3;
}
return(rc);
}
/*
* NAME: is_cwd
*
* FUNCTION: determine whether s is current directory path.
* this routine is called by rm_msg.
*
* RETURNS: 1 if s is the current directory path.
*/
static int is_cwd(s)
char *s;
{
struct stat statbuf;
/* If this is first call, find device and file serial of current directory.*/
if (!is_cwd_called++) {
char pwdbuf[PATH_MAX];
/* Get current directory path.*/
if ((char *)getcwd(pwdbuf,sizeof(pwdbuf))==NULL)
return(0); /* Can't prove it is cwd.*/
if (stat(pwdbuf,&statbuf) != 0)
return(0); /* Can't prove it is cwd.*/
cwd_st_dev = statbuf.st_dev;
cwd_st_ino = statbuf.st_ino;
}
/* Find device and file serial of candidate path.*/
if (stat(s,&statbuf) != 0)
return(0); /* No valid status, can't be current path.*/
/* Candidate is current directory if it has same device and file serial.*/
return( (cwd_st_dev == statbuf.st_dev) && (cwd_st_ino == statbuf.st_ino) );
}