/*
 * rm - for ReMoving files, directories & trees.
 */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/param.h>
#include <sys/stat.h>
#include <sys/dir.h>
#include <sys/file.h>

int fflg;       /* -f force - supress error messages */
int iflg;       /* -i interrogate user on each file */
int rflg;       /* -r recurse */

int errcode;    /* true if errors occured */

main(argc, argv)
    char *argv[];
{
    register char *arg;

    fflg = !isatty(0);
    iflg = 0;
    rflg = 0;
    while (argc > 1 && argv[1][0] == '-') {
        arg = *++argv;
        argc--;

        /*
         *  all files following a null option are considered file names
         */
        if (arg[1] == '\0')
            break;

        while (*++arg != '\0')
            switch(*arg) {
            case 'f':
                fflg++;
                break;

            case 'i':
                iflg++;
                break;

            case 'R':
            case 'r':
                rflg++;
                break;

            default:
                fprintf(stderr, "usage: rm [-rif] file ...\n");
                exit(1);
            }
    }

    if (argc < 2 && !fflg) {
        fprintf(stderr, "usage: rm [-rif] file ...\n");
        exit(1);
    }

    while (--argc > 0)
        (void) rm(*++argv, 0);

    exit(errcode != 0);
}

char    *path;      /* pointer to malloc'ed buffer for path */
char    *pathp;     /* current pointer to end of path */
int pathsz;     /* size of path */

/*
 * Return TRUE if sucessful. Recursive with -r (rflg)
 */
rm(arg, level)
    char arg[];
{
    int ok;             /* true if recursive rm succeeded */
    struct stat buf;        /* for finding out what a file is */
    struct direct *dp;      /* for reading a directory */
    DIR *dirp;          /* for reading a directory */
    char prevname[MAXNAMLEN + 1];   /* previous name for -r */
    char *cp;

    if (dotname(arg)) {
        fprintf(stderr, "rm: cannot remove `.' or `..'\n");
        return (0);
    }
    if (lstat(arg, &buf)) {
        if (!fflg) {
            fprintf(stderr, "rm: %s nonexistent\n", arg);
            errcode++;
        }
        return (0);     /* error */
    }
    if ((buf.st_mode&S_IFMT) == S_IFDIR) {
        if (!rflg) {
            if (!fflg) {
                fprintf(stderr, "rm: %s directory\n", arg);
                errcode++;
            }
            return (0);
        }
        if (iflg && level != 0) {
            printf("rm: remove directory %s? ", arg);
            if (!yes())
                return (0); /* didn't remove everything */
        }
        if (access(arg, R_OK|W_OK|X_OK) != 0) {
            if (rmdir(arg) == 0)
                return (1); /* salvaged: removed empty dir */
            if (!fflg) {
                fprintf(stderr, "rm: %s not changed\n", arg);
                errcode++;
            }
            return (0);     /* error */
        }
        if ((dirp = opendir(arg)) == NULL) {
            if (!fflg) {
                fprintf(stderr, "rm: cannot read %s?\n", arg);
                errcode++;
            }
            return (0);
        }
        if (level == 0)
            append(arg);
        prevname[0] = '\0';
        while ((dp = readdir(dirp)) != NULL) {
            if (dotname(dp->d_name)) {
                strcpy(prevname, dp->d_name);
                continue;
            }
            append(dp->d_name);
            closedir(dirp);
            ok = rm(path, level + 1);
            for (cp = pathp; *--cp != '/' && cp > path; )
                ;
            pathp = cp;
            *cp++ = '\0';
            if ((dirp = opendir(arg)) == NULL) {
                if (!fflg) {
                    fprintf(stderr, "rm: cannot read %s?\n", arg);
                    errcode++;
                }
                break;
            }
            /* pick up where we left off */
            if (prevname[0] != '\0') {
                while ((dp = readdir(dirp)) != NULL &&
                    strcmp(prevname, dp->d_name) != 0)
                    ;
            }
            /* skip the one we just failed to delete */
            if (!ok) {
                dp = readdir(dirp);
                if (dp != NULL && strcmp(cp, dp->d_name)) {
                    fprintf(stderr,
            "rm: internal synchronization error: %s, %s, %s\n",
                        arg, cp, dp->d_name);
                }
                strcpy(prevname, dp->d_name);
            }
        }
        closedir(dirp);
        if (level == 0) {
            pathp = path;
            *pathp = '\0';
        }
        if (iflg) {
            printf("rm: remove %s? ", arg);
            if (!yes())
                return (0);
        }
        if (rmdir(arg) < 0) {
            if (!fflg || iflg) {
                fprintf(stderr, "rm: %s not removed\n", arg);
                errcode++;
            }
            return (0);
        }
        return (1);
    }

    if (iflg) {
        printf("rm: remove %s? ", arg);
        if (!yes())
            return (0);
    } else if (!fflg) {
        if ((buf.st_mode&S_IFMT) != S_IFLNK && access(arg, W_OK) < 0) {
            printf("rm: override protection %o for %s? ",
                buf.st_mode&0777, arg);
            if (!yes())
                return (0);
        }
    }
    if (unlink(arg) < 0) {
        if (!fflg || iflg) {
            fprintf(stderr, "rm: %s not removed\n", arg);
            errcode++;
        }
        return (0);
    }
    return (1);
}

/*
 * boolean: is it "." or ".." ?
 */
dotname(s)
    char *s;
{
    if (s[0] == '.')
        if (s[1] == '.')
            if (s[2] == '\0')
                return (1);
            else
                return (0);
        else if (s[1] == '\0')
            return (1);
    return (0);
}

/*
 * Get a yes/no answer from the user.
 */
yes()
{
    int i, b;

    i = b = getchar();
    while (b != '\n' && b != EOF)
        b = getchar();
    return (i == 'y');
}

/*
 * Append 'name' to 'path'.
 */
append(name)
    char *name;
{
    register int n;

    n = strlen(name);
    if (path == NULL) {
        pathsz = MAXNAMLEN + MAXPATHLEN + 2;
        if ((path = malloc(pathsz)) == NULL) {
            fprintf(stderr, "rm: ran out of memory\n");
            exit(1);
        }
        pathp = path;
    } else if (pathp + n + 2 > path + pathsz) {
        fprintf(stderr, "rm: path name too long: %s\n", path);
        exit(1);
    } else if (pathp != path && pathp[-1] != '/')
        *pathp++ = '/';
    strcpy(pathp, name);
    pathp += n;
}
