#ifdef	ournix
#include "ournix.h"
#endif
char sccsID[] =	"@(#) rm.c V1.2 Copyright Julian H. Stacey 1987.\n" ;

/* rm - Unix like rm, with -r option, written for MSDOS by Julian H. Stacey,
	directory searching component adopted from ls.c (by pjac).
BUGS
	- rm doesnt delete directories that were empty before rm was called.
	- mode of files & directories is not considered
FEATURES
	- cd \  ; rm -r \ doesnt delete top directories
		however these top directories are mounted with join.exe,
		so they are special.
		(carefull if you try it, it does delete everything else !
FUTURES
	- -i & -f options
*/

#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>

#ifdef	MSDOS
#include <direct.h>
#endif

#ifdef	MSDOS
#include "ndir.h"
#else
#include <dir.h>
#endif

#define ENOUGH	128
#define MAXDIR	1024

#define	strequ(a,b)	(strcmp(a,b) == 0)

struct	m_list	
	{
	char	m_name[12+2] ;
	long	m_size ;
	short	m_mode ;
	long	m_time ;
	} ;

struct	m_list	*m_tab[MAXDIR] ;
struct	m_list	**m_start ;
struct	m_list	**m_end ;

char	*malloc() ;
char	r_flag ;
char	f_flag ;
char	i_flag ;
char	no_mem_flag ;
char	**ARGV  ;

char	no_mem[] = "Out of memory.\n" ;

/* ??????????????????? 		0 = failed to stat */
	struct	m_list	*
our_stat(nam, nam_ptr, stat_ptr)
	char	*nam ;
	char	*nam_ptr ;
	struct stat *stat_ptr ;	/* maybe where structure should be returned */
	{
	struct	stat	stat_buf ;
	struct	m_list	*mem_ptr ;

	if (stat_ptr == (struct stat *)0)
		{
		stat_ptr = &stat_buf ;
		if (stat(nam, stat_ptr) < 0)
			{
#ifdef DEBUG
			fprintf(stderr, "Can't stat '%s'.\n", nam) ;
#endif
			return(0) ;
			}
		}
	mem_ptr = (struct m_list *)malloc(sizeof(struct m_list)) ;
	if (mem_ptr == 0) {
		if (!no_mem_flag) fprintf(stderr, no_mem ) ;
		no_mem_flag = 1 ;
		return(0) ;
		}
	if (nam_ptr != (char *)0) (void) strcpy(mem_ptr->m_name, nam_ptr) ;
	else mem_ptr->m_name[0] = '\0' ;
	mem_ptr->m_size = stat_ptr->st_size ;
	mem_ptr->m_mode = stat_ptr->st_mode ;
	mem_ptr->m_time = stat_ptr->st_mtime ;
	return(mem_ptr) ;
	}

main(argc, argv)
	char	**argv ;
	{
	char	*opt ;
	int	status = 0 ;

	ARGV = argv ;
#ifdef	VSL	/* { */
#include	"../../include/vsl.h"
#endif		/* } */
	for(argv++, argc-- ; argc  ; argc--, argv++)
		{
		if (*(opt = *argv) != '-') break ;
		while(*++opt) switch(*opt)
			{
			case 'P':		/* print release number */
				printf("%s",sccsID)  ;
				break  ;
			case 'r':		/* recursive delete */
				r_flag = 1 ;
				break ;
			case 'f':		/* delete with force ie 
							ignore protect mode */
				f_flag = 1 ;
				break ;
			case 'i':		/* interactive prompt */
				i_flag = 1  ;
				break ;
			default:
				fprintf(stderr,
				"%s: Error, -%c not implemented, so ignored.\n",
					*ARGV,*opt) ;
				fprintf(stderr, "%s",sccsID)  ;
				break ;
			}
		}

	if (argc <= 0)
		{
		fprintf(stderr,
			"%s: Error need at least one argument.\n",*ARGV) ;
		exit(1) ;
		}
	for ( ; argc  ; argc--, argv++)
		status |= doit(*argv) ;
	exit(status) ;
	}

doit(d)
	char	*d ;	/* name of element in current directory to analyse */
	{
	struct	m_list	*mp ;
	struct	stat	stat_buf ;
	int	status ;

	if ( strequ(d,"..") )
		{
		fprintf(stderr,
			"%s: User Error: wont delete parent directory %s.\n",
			*ARGV,(r_flag)?
			"(with -r it would have been very unpleasant.)\n" :
			"" )  ;
		return(1) ;
		}
	if (stat(d, &stat_buf) < 0)	/* */
		{
		status = root_chk(d) ;
		if (status >= 0) return(status) ;
		mp = our_stat(d, 0, (struct stat *)0) ;
		}	
	else	{			/* */
		mp = our_stat(d, 0, &stat_buf) ;
		}
	if (mp == NULL) return(1) ;
	if ((mp->m_mode & S_IFMT) != S_IFDIR )
		{
		free( (char *)mp) ;
		return(fzap("","",d)) ;
		}
	free( (char *)mp) ;
	if (!r_flag) 
		{
		fprintf(stderr,"%s: Error %s is a directory.\n",*ARGV,d) ;
		return(1) ;
		}
	m_start = m_tab ;	/* put start at start of table */
	if (status = recurse(d)) return(status) ;
	return(0) ;
	}

root_chk(d)		/* check to see if directory is root */
			/* return ............... */
	char	*d ;
	{
	DIR	*dir_ptr ;
	static	char	NULLS[] = "" ;
	char	**x_ptr ;
	static	char	*matches[] = { ".", "/", "\\", (char *)0, } ;

	for(x_ptr = matches  ; *x_ptr  ; x_ptr++)
	if (strcmp(d, *x_ptr) == 0)
		{
		dir_ptr = opendir(NULLS) ;	/* "" for ROOT ?? */
		if (dir_ptr == NULL) return(-1) ;
		closedir(dir_ptr) ;
		m_start = m_tab ;	/* get start right */
		return(recurse(NULLS)) ;
		}
	return(-1) ;
	}

/* Recursively evaluates directories, 
*  returns 1 on error, 0 on no entries or recursion flag not on,
*  returns 1 if a lower level returns 1, ?? on normal exit. 
*/
recurse(d)
	char	*d ;
	{
	int	nentries ;
	struct	m_list	*mp, **xmp ;
	struct	m_list	**Mp ;
	char	*tbuf ;

	nentries = dir_build(d) ;
	if (nentries < 0) return(1) ;
	else if (nentries == 0) return(0) ;
	for(Mp = m_start  ; Mp < m_end  ;)
		{
		mp = *Mp++ ;
		next(mp, mp->m_name, d) ;
		if (!r_flag) free( (char *)mp) ;
		}
	if (!r_flag) return(0) ;
	for(Mp = m_start  ; Mp < m_end  ;)
		{
		mp = *Mp++ ;
		if ((mp->m_mode & S_IFMT) == S_IFDIR && !skip_parent(mp->m_name))
			{
			tbuf = malloc(strlen(d) + strlen(mp->m_name) + 2) ;
			if (tbuf != 0)
				{
				(void) sprintf(tbuf, "%s\\%s", d, mp->m_name) ;
				xmp = m_start ;
				m_start = m_end ;
				if (recurse(tbuf))
					{
					m_end = m_start ;
					m_start = xmp ;
					free(tbuf) ;
					return(1) ;
					}
				m_end = m_start ;
				m_start = xmp ;
				free(tbuf) ;
				}
			}
		free( (char *)mp) ;
		}
	dir_rm(d) ;
	return(0) ;
	}

dir_build(d)
	char		*d ;
	{
	DIR		*dir_ptr ;
	struct	direct	*dp ;
	struct	m_list	*mp ;
	static	char	tbuf[ENOUGH] ;
	int		n = 0 ;

	m_end = m_start ;
	dir_ptr = opendir(d) ;
	if (dir_ptr == 0) {
		fprintf(stderr, "Cannot open '%s'.\n", d) ;
		return(-1) ;
		}
	(void) strcpy(tbuf, d) ;
	d = &tbuf[strlen(d)] ;
	*d++ = '\\' ;
	while((dp = readdir(dir_ptr)) != NULL)
		{
		if (skip_parent(dp->d_name)) continue ;
		(void) strcpy(d, dp->d_name) ;
		mp = our_stat(tbuf, dp->d_name, (struct stat *)0) ;
		if (mp == 0) {
			if (no_mem_flag) break ;
			continue ;
			}
		*m_end++ = mp ;
		if (m_end > &m_tab[MAXDIR-1])
			{
			fprintf(stderr, no_mem ) ;
			no_mem_flag = 1 ;
			break ;
			}
		}
	closedir(dir_ptr) ;
	n = m_end - m_start ;
	sortit(m_start, n) ;
	return(n) ;
	}

/* stops us recursing up .. & killing parent directories */
skip_parent(name)
	char	*name ;
	{
	if (*name != '.') return(0) ;
	if (name[1] == '\0' || (name[1] == '.' && name[2] == '\0')) return(1) ;
	return(0) ;
	}

asc_cmp(a1, a2)
	struct	m_list	**a1, **a2 ;
	{
	return(strcmp((*a1)->m_name, (*a2)->m_name)) ;
	}

/* sorting prior to deletion is done so if rm -rf is run by accident,
*  you can tell what is missing more easily, as it will have
*  removed alphabetically, not by directory entry order 
* note this is something standard unix doesnt do, but would be nice if it did.
*/
sortit(m_tab, n)
	struct	m_list	**m_tab ;
	{
	int	(*cmpfunc)() ;

	if (!n) return ;
	cmpfunc = asc_cmp ;
	qsort((char *)m_tab, n, sizeof(struct m_list *), cmpfunc) ;
	}

next(mp, nam, d)
	struct	m_list	*mp ;
	char	*nam, *d ;
	{
	if(nam == 0) nam = mp->m_name ;
	/*
	if(mp->m_mode & S_IWRITE)
	if(mp->m_mode & S_IEXEC)
	*/
	if((mp->m_mode & S_IFMT) != S_IFDIR) fzap(d,"/",nam) ;
	}

/* Removes Directory
*  returns 0 on success, 1 on failure */
dir_rm(d)
	char *d ;
	{
	if (strequ(d,".") || strequ(d,"./.") || strequ(d,".\\.") ||
		strequ(d,"\\") || strequ(d,"/") ) return(0) ;
		/* msdos wont allow rmdir . or rmdir \ */
	if (rmdir(d)) return(1)  ; else return(0)  ;
	}

/* Deletes File
*  returns 0 on success, 1 on failure 
*/
fzap(d,slash,nam)
	char *d,*slash,*nam ;
	{
	char gone[ENOUGH] ;
	(void) sprintf(gone,"%s%s%s",d,slash,nam) ;
	if (unlink(gone) < 0)
		{
		fprintf(stderr,"%s: Failed to unlink %s.\n", *ARGV, gone) ;
		return(1) ;
		}
	return(0) ;
	}
