/*****************************************************************************
 *
 *	asrm - command line remove to astrash, Version 0.9
 *	(C) 1997 Carsten Weinholz <weinholz@hni.uni-paderborn.de>
 *
 *	Idea and some code borrowed from
 *  	Trash - the OffiX waste basket
 *  	Copyright (C) 1996  César Crusius
 *					                                    
 *  	This program is free software; you can redistribute it and/or modify
 *  	it under the terms of the GNU General Public License as published by
 *  	the Free Software Foundation; either version 2 of the License, or
 *  	(at your option) any later version.
 *
 *  	This program is distributed in the hope that it will be useful,
 *  	but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  	GNU General Public License for more details.
 *
 *  	You should have received a copy of the GNU General Public License
 *  	along with this program; if not, write to the Free Software
 *  	Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 *
 ****************************************************************************/
#include <sys/param.h>
#include <sys/stat.h>
#include <limits.h>
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <signal.h>

/* Version *******************************************************************/
#define VERSION "0.9"

/* Definitions ***************************************************************/
#define APPNAME	"AsTrash"
#define TRASHDIR ".trash"
#define LISTNAME "index.db"
#define PIDFILE "trash.pid"

/* List struct ***************************************************************/
typedef struct _List {
	struct _List *next;
	int    index;
	time_t time;
	char   entry;	/* muss letztes Element sein ! */
} List;

static List *TrashList = NULL;
static int ListEntries;

/* Functions *****************************************************************/
int  ProcessOneFile(char *);
int  TrashMove(char *,char *);
int  FreeIndex();
int  TrashDelete(char *);
int  InitTrashDir(char *);
void ReadList(char *);
void WriteList(char *);
void AddListEntry (int, char *);
int  readPId(char *);

/****************************************************************************/
void main
	(int argc, char **argv)
{
	int i, p;
	char buf[PATH_MAX];
	
	getcwd (buf, PATH_MAX);
	strcat (buf,"/");
	p = strlen(buf);
		
	if (argc < 1) 
		exit(0);
	else if (!InitTrashDir(TRASHDIR))
		exit (1);

	ReadList (LISTNAME);
		
	for (i = 1; i < argc; i++) {
		
		if (argv[i][0] == '/')
			ProcessOneFile (argv[i]);
		else {
		
			buf[p] = '\0';
			strncat (buf, argv[i], PATH_MAX-p); 
			ProcessOneFile (buf);
		}
	}
	
	WriteList (LISTNAME);
	readPId (PIDFILE);
}

/****************************************************************************/
int InitTrashDir
	(char *TrashDir)
{
	struct stat JunkStat;
	char DestDir[MAXPATHLEN];
	int ErrStatus;

	if(getenv("HOME") == NULL) {
		
		fprintf(stderr,APPNAME ": Could not get HOME environment.\n");
		return 0;
	}
	
	sprintf(DestDir,"%s/%s",getenv("HOME"),TrashDir);
	
	ErrStatus=lstat(DestDir,&JunkStat);
	if(ErrStatus) {
	
		fprintf(stderr,APPNAME ": Creating directory %s...\n",DestDir);

		ErrStatus=mkdir(DestDir, 0700);
		if(ErrStatus) {
		
			fprintf(stderr,APPNAME ": Could not create!\n");
			return 0;
		}
	}

	ErrStatus=chdir(DestDir);
	if(ErrStatus) {
	
		fprintf(stderr,"Trash: could not chdir to trash dir!\n");
		return 0;
	}
	

	return 1;
}

/****************************************************************************/
int ProcessOneFile
	(char *Path)
{
	char newPath[0xff];
	int newIndex;
	
	newIndex = FreeIndex();
	sprintf (newPath, "%ld", newIndex);
	
	if (TrashMove (Path, newPath)) {

		AddListEntry (newIndex, Path);
		return 1;
	}
	
	return 0;
}

/****************************************************************************/
void ReadList 
	(char *filename)
{
	FILE *f;
	List *next;
	char buf[PATH_MAX], *p;
	struct stat JunkStat;

	if ((f = fopen (filename, "r"))) {

		while (!feof(f)) {

			if (fgets (buf, PATH_MAX, f) &&
			    (next = malloc (sizeof(List) + PATH_MAX))) {
			
				buf[strlen(buf)-1] = '\0';
				next->index = strtol(buf, &p, 10); 
				strncpy (&(next->entry), p, PATH_MAX);

				next->next  = TrashList;
				TrashList   = next;
			
				ListEntries++;
				
				sprintf (buf, "%d", next->index);
				lstat (buf, &JunkStat);				
				next->time  = JunkStat.st_ctime;
			}
		}
		
		fclose (f);
	}				
}

/****************************************************************************/
void WriteListEntry (List *entry, FILE *f)
{
	static char buf[PATH_MAX];
	
	if (entry) { 
	
		WriteListEntry (entry->next, f);
		
		sprintf (buf, "%d%s\n", entry->index, &(entry->entry));	
		fputs (buf, f);
	}
}

/****************************************************************************/
void WriteList
	(char *filename)
{
	FILE *f;
	List *entry, *next, *prev;
		
	if ((f = fopen (filename, "w"))) {

		WriteListEntry (TrashList, f);
		fclose (f);
	}
}

/****************************************************************************/
void AddListEntry 
	(int index, char *entry)
{
	char buf[PATH_MAX];
	List *new;
	FILE *f;
		
	if ((new = malloc (sizeof(List) + PATH_MAX))) {

		new->index = index;
		strncpy (&(new->entry), entry, PATH_MAX);

		new->next = TrashList;
		TrashList = new;

		time (&new->time); 		
		ListEntries++;
	}		
}
 
/***************************************************************************/
int FreeIndex
	()
{
	int i, index;
	List *entry;
		
	do {
		index = random();
		for (i = 0, entry = TrashList; i < ListEntries; 
		     i++, entry = entry->next)
			if (atoi(&(entry->entry)) == index)
				break;
	} while (i < ListEntries);
	
	return index;			
}

/***************************************************************************/
int TrashMove
	(char *OldPath,char *NewPath)
{
	char CmdBuf[2*MAXPATHLEN+13];
	struct stat JunkStat;
	int ErrStatus;

	if(lstat(OldPath,&JunkStat)) return 1;
	if(!lstat(NewPath,&JunkStat)) return 0;
	
	sprintf(CmdBuf,"cp -r '%s' '%s'",OldPath,NewPath);
	if ((ErrStatus=system(CmdBuf))==0)
		if((ErrStatus=!TrashDelete(OldPath))!=0)
			TrashDelete(NewPath);

	return (ErrStatus==0);
}

/***************************************************************************/
int TrashDelete
	(char *OldPath)
{
	struct stat JunkStat;
	char Command[MAXPATHLEN];
	int ErrStatus;
	
	if(lstat(OldPath,&JunkStat)) return 1;
	
	sprintf(Command,"rm -rf %s",OldPath);
	ErrStatus=system(Command);
	
	return (!ErrStatus);
}

/****************************************************************************/
int readPId
	(char *filename)
{
	FILE *f;
	char buf[10];
	int pid;
	
	if ((f = fopen (filename, "r"))) {
	
		fgets (buf, 10, f);
		fclose (f);
		
		pid = atoi (buf);
		if (!kill (pid, SIGUSR1))
			return pid;
	}
	
	return 0;	
}