/* parse_rc.c--rcfile parser for niftyclean
 *
 * Written by Jay Laefer and Mike Darweesh
 * (C) Copyright 1989 by Jay Laefer and Mike Darweesh
 * All Rights Reserved.
 * Permission is granted to copy, modify, and use this as long
 * as this notice remains intact.  This is a nifty program.
 * (C) Copyright 1991- by Charles Swiger
 */

#include "niftyclean.h"
#include <stdlib.h>

static struct globtype *globlist = NULL;
static struct batchtype *batchlist = NULL;
static struct excl_dirtype *excl_dirlist = NULL;

/* Here is the default list of regex's to terminate; should move this to a 
 * system-wide config file and not hardcode it within the binary.
 */

char *defaults[] = { 
    "core",
    "*~",
    ".*~",
    "*.BAK",
    ".*.BAK",
    "*.CKP",
    ".*.CKP",
    "*.NEW",
    ".*.NEW",
    "#*#",
    ".emacs_[0-9]*",
    "dead.letter",
    "*.otl",
    ".*.otl",
    "*.backup",
    ".*.backup",
    NULL
};

char *def_excl_dirs[] = {
    ".MESSAGES",
    NULL
};

char *objects[] = {
    "*.o",
    "*.pyc",
    "*.pyo",
    NULL
};

/* do_default_rc() goes through the array of default globs and adds each item
 * to the global list of patterns.  It then goes through the array of default
 * directories and adds them to the list of directories to be excluded.
 */
static void 
do_default_rc(void)
{
    char **def_ptr;
    
    if (!(flag & NOGLOB)) {
	def_ptr = defaults;
        while (*def_ptr) {
            add_glob(*def_ptr++);
        }
    }

    if (!(flag & NOEXCL)) {
	def_ptr = def_excl_dirs;
        while (*def_ptr) {
            add_glob(*def_ptr++);
        }
    }
}

void 
do_rc(void)
{
    FILE *fp;
    char *home, rcfile[MAXPATHLEN], line[1024], **def_ptr;
    int noglob, noexcl;

    noglob = flag & NOGLOB;
    noexcl = flag & NOEXCL;

    if (flag & OBJECTS) {
	def_ptr = objects;
        printf("in objects, def_ptr = %s\n", *def_ptr);
        while (*def_ptr) {
            add_glob(*def_ptr++);
        }
    }
       
    /* return if we don't want the defaults */
    if(noglob && noexcl)
        return;

    /* get home directory */
    home = getenv("HOME");
    if (home == NULL) {
	do_default_rc();
	return;
    }
    
    if ((strlen(home) + strlen(RCFILE)) > MAXPATHLEN)
        errorh(FATAL,"Pathlength to ~/.cleanrc too long");
    
    strcpy(rcfile, home);
    strcat(rcfile, "/");
    strcat(rcfile, RCFILE);
    
    /* open .cleanrc */
    if ((fp = fopen(rcfile, "r")) == NULL) {
	do_default_rc();
	return;
    }
    
    /* read patterns from file */
    while (fgets(line, MAXPATHLEN, fp) != NULL) {
	int len, c;
	
	len = strlen(line) - 1;
	if (line[len] == '\n')
            line[len] = '\0';
	else
            while (((c = getc(fp)) != EOF) && (c != '\n'));

	/* check for comment or blank line */
	if ((line[0] != '#') && (line[0] != '\0')) {
	    if (line[0] == '!') {
		if (!noexcl)
                    add_excl_dir(line + 1);
	    } else if (!noglob) {
		if ((line[0] == '\\') &&
		    ((line[1] == '#') || (line[1] == '!') || (line[1] == '\\')))
                    add_glob(line + 1);
		else
                    add_glob(line);
	    }
	}
    }		   
    
    if (fclose(fp))
        errorh(WARNING,"Problem closing ~/.cleanrc");
}

/* find_match() takes a string and goes through the list of patterns until it
 * either finds a match or runs out of patterns.
 */
static int
find_match (char *file)
{
    struct globtype *temp;
    
    temp = globlist;
    while (temp) {
	if (match(file, temp->glob))
            return 1;
	temp = temp->next;
    }
    return 0;
}

/* dofile() takes a directory and a filename and acts based on whether FORCE,
 * BATCH, or INTERACTIVE mode is on.
 */
int 
dofile (char *dir, char *file)
{
    char    unlinkerr[MAXPATHLEN + 100], c;
    
    if (find_match(file)) {	/* if the file matches a pattern */
	char fullpath[MAXPATHLEN];
	
	strcpy(fullpath, dir);
	strcat(fullpath, "/");
	strcat(fullpath, file);
	
	if (flag & FORCE)
            (void)unlink(fullpath);
	else if (flag & BATCH)
            add_batch(fullpath);
	else {
	    fputs("Remove ", stdout);
	    fputs(fullpath, stdout);
	    fputs(" y/n/s [y]: ", stdout);
	    c = getfirstchar(stdin);
	    /* kill file if user wants it killed */
	    switch (c) {
              case 'y':
              case 'Y':
              case '\n':
		if (unlink(fullpath)) {
		    strcpy(unlinkerr, "Could not remove: ");
		    strcat(unlinkerr, fullpath);
		    errorh(WARNING, unlinkerr);
		} else {
		    fputs("Removed: ", stdout);
		    puts(fullpath);
		}
		break;
              case 's':
              case 'S':
		skip = 1;
		puts("Skipping this directory...");
		break;
	    }
	}
	return 1;
    } else
        return 0;
}

/* add_batch() takes the full pathname to a file that is slated for deletion.
 * It then adds that to the globab linked list of such files.
 */
void 
add_batch (char *path)
{
    struct batchtype *batchptr;
    batchptr = (struct batchtype *) malloc((sizeof (struct batchtype)
                                            + strlen(path)));
    if (batchptr == NULL)
        errorh(FATAL, "Malloc failed in add_batch()");
    
    strcpy(batchptr->path, path);    
    batchptr->next = batchlist;    
    batchlist = batchptr;
}

/* dobatch() runs through the list of files slated for deletion and asks the
 * user if he wants to delete them.
 */
void 
dobatch (void)
{
    struct batchtype *temp;
    char unlinkerr[MAXPATHLEN + 100];
    int c;
    
    if (!batchlist) {
	puts("No non-nifty files found.");
	return;
    }
    
    puts("\nThe following are non-nifty files:");
    for (temp = batchlist; temp; temp = temp->next)
        puts(temp->path);
    
    fputs("Delete them y/n [y]: ", stdout);
    c = getfirstchar(stdin);
    
    if ((c = ((c == 'y') || (c == 'Y') || (c == '\n'))))
        puts("Deleting files...");
    else
        puts("Files not deleted.");
    
    /* delete the files and free the memory */
    while (batchlist) {
	if (c && unlink(batchlist->path)) {
	    strcpy(unlinkerr, "Could not remove: ");
	    strcat(unlinkerr, batchlist->path);
	    errorh(WARNING,unlinkerr);
	}
	temp = batchlist;
	batchlist = batchlist->next;
	free(temp);
    }
}

/* add_glob() takes a word, malloc's space for a structure, and adds it to the
 * front of the list of patterns to be matched.
 */
void 
add_glob (char *word)
{
    struct globtype *globptr;

    if (0)
        printf("add_glob(): word = %s\n", word);
    
    globptr = (struct globtype *)malloc((sizeof (struct globtype)) + strlen(word));
    if (globptr == NULL)
        errorh(FATAL, "Malloc failed in add_glob()");
    
    strcpy(globptr->glob, word);
    
    globptr->next = globlist;
    
    globlist = globptr;
}

/* add_excl_dir() takes a word, malloc's space for a structure, and adds it to
 * the front of the list of directories to be excluded from the traversal.
 */
void 
add_excl_dir (char *word)
{
    struct excl_dirtype *excl_dirptr;
    if ((excl_dirptr = (struct excl_dirtype *)malloc((sizeof (struct excl_dirtype)) + strlen(word))) == NULL)
        errorh(FATAL, "Malloc failed in add_excl_dir()");

    strcpy(excl_dirptr->excl_dir, word);

    excl_dirptr->next = excl_dirlist;

    excl_dirlist = excl_dirptr;
}

/* check_excl_list() takes a directory name and checks to see if it is on the
 * list of directories to be excluded.  If so, a 0 is returned.  Otherwise,
 * return 1.
 */
int 
check_excl_list (char *dir)
{
    struct excl_dirtype *temp;

    /* Move down the linked list that starts with excl_dirlist */
    for (temp = excl_dirlist; temp; temp = temp->next)
        if (!strcmp(temp->excl_dir, dir))
            return 0;		/* Found */

    return 1;
}


syntax highlighted by Code2HTML, v. 0.9.1