/******************************************************************************
 * Steve Rider - stever@transmission23.com
 * QuickPics
 *   Image galleries for the web made easy
 *
 * utility.c - miscellaneous routines
 *
 *****************************************************************************/
/* 17 February 98 */

#include "qp.h"
#include "utility.h"



/*******************************************************************************
 *
 * File I/O Stuff.
 * 
 ******************************************************************************/

/* Return 0 if file cannot be opened */
char IsAccessible(const char *filename)
{
	FILE *file;

	assert(filename != (char *) NULL);
	file = fopen(filename,ReadBinaryType);
	if(file == (FILE *) NULL)
		return 0;
	fclose(file);
	return 1;
}

char IsDirectory(const char *filename)
{
	int status;

	struct stat file_info;

	status = stat(filename,&file_info);
	if(status != 0)
		return 0;

	return S_ISDIR(file_info.st_mode);
}

static int FileCompare(const void *x,const void *y)
{
	register char **p, **q;

	p = (char **) x;
	q = (char **) y;
	return strcmp(*p,*q);
}

char **ListFiles(const char *dir_name, int *number_entries)
{
	char	**filelist, dirnamebuffer[MAXFILENAMELEN];
	char	cwd[MAXFILENAMELEN];
	DIR	*current_directory;
	int	status;
	struct dirent *entry;
	unsigned int max_entries = 5096;

	assert(dir_name != (char *) NULL);
	assert(number_entries != (int *) NULL);
	*number_entries = 0;

	/* Use our own buffer for dir_name since getcwd reassigns it. */
	strcpy(dirnamebuffer,dir_name);

	/* Save current working directory and change to specified dir.*/
	getcwd(cwd,MAXFILENAMELEN-1);
	status = chdir(dirnamebuffer);
	if(status != 0)
	{
		printf("Unable to chdir!!!!\n");
		return NULL;
	}

	/* Get full pathname of new cwd and open for reading.*/
	getcwd(dirnamebuffer,MAXFILENAMELEN-1);
	current_directory = opendir(dirnamebuffer);
	if(current_directory == NULL)
	{
		printf("Unable to opendir!!!!\n");
		/* Change back to start directory */
		chdir(cwd);

		return NULL;
	}


	/* Directory is legitimate.  Allocate filelist. */
	filelist = (char **)nmalloc(max_entries*sizeof(char *));
	if(filelist == NULL)
	{
		(void) closedir(current_directory);

		printf("Unable to malloc!!!!\n");
		/* Change back to start directory */
		chdir(cwd);

		return NULL;
	}

	/* Read directory */
	entry = readdir(current_directory);
	while(entry != NULL)
	{
		if(*entry->d_name == '.')
		{
			entry = readdir(current_directory);
			continue;
		}

		/* If directory or pattern */
		if(IsDirectory(entry->d_name) || 1)
		{
			if(*number_entries >= max_entries)
			{
				/* Extend the file list. */
				max_entries <<= 1;
				filelist = (char **)realloc((char **) filelist,max_entries*sizeof(char *));
				if(filelist == NULL)
				{
					(void) closedir(current_directory);

					printf("Max entries exceeded.\n");
					/* Change back to start directory */
					chdir(cwd);

					return NULL;
				}
			}
			filelist[*number_entries] = (char *)nmalloc(strlen(entry->d_name)+2);
			if(filelist[*number_entries] == NULL)
				break;
			(void) strcpy(filelist[*number_entries],entry->d_name);

			/* Add separator symbol to end of directory name */
/*
			if(IsDirectory(entry->d_name))
				(void) strcat(filelist[*number_entries],DirectorySeparator);
*/

			(*number_entries)++;
		}
		entry = readdir(current_directory);
	}
	(void) closedir(current_directory);

	/* Change back to start directory */
	chdir(cwd);

	/* Sort filelist in ascending order. */
	qsort((void *) filelist,*number_entries,sizeof(char **), (int (*)(const void *, const void *)) FileCompare);

	return(filelist);
}

u_long GetFilesize(const char *filename)
{
	struct stat st;

	stat(filename,&st);

	if(!(st.st_mode & S_IFREG))
		return 0L;

	return st.st_size;
}

	

/* copy a file from one place to another (possibly erasing old copy) */
/* returns 0 if OK, 1 if can't open original file, 2 if can't open new */
/* file, 3 if original file isn't normal, 4 if ran out of disk space */
int copyfile(char *oldpath, char *newpath)
{
	int	fi,fo,x;
	char	buf[512];
	struct	stat st;

	fi = open(oldpath,O_RDONLY,0);
	if(fi < 0)
		return 1;

	fstat(fi,&st);
	if(!(st.st_mode & S_IFREG))
		return 3;

	fo = creat(newpath,(int)(st.st_mode & 0777));
	if(fo < 0)
	{
		close(fi);
		return 2;
	}
	for(x=1; x>0; )
	{
		x = read(fi,buf,512);
		if(x > 0)
		{
			if(write(fo,buf,x) < x)
			{
				close(fo);
				close(fi);
				unlink(newpath);
				return 4;
			}
		}
	}
	close(fo);
	close(fi);

	return 0;
}


int movefile(char *oldpath, char *newpath)
{
	int x = copyfile(oldpath,newpath);
	if (x == 0)
		unlink(oldpath);

	return x;
}


int isfile(char *filename)
{
	struct	stat ss;
	int	i = stat(filename,&ss);

	if(i < 0)
		return 0;

	if((ss.st_mode & S_IFREG) || (ss.st_mode & S_IFLNK))
		return 1;

	return 0;
}

char *readline(FILE *f, char *s)
{
	if(fgets(s,BUFSIZ,f))
	{
		s[strlen(s)-1] = '\0';
		return s;
	}
	return NULL;
}



/*******************************************************************************
 *
 * Date and Time stuff
 * 
 ******************************************************************************/
void timet_to_time_short(time_t tt, char *time)
{
	strcpy(time,ctime(&tt));
	strcpy(time,&time[11]);
	time[5] = 0;
}
	
void timet_to_date_short(time_t time, char *date)
{
	struct tm *btime;

	btime = localtime(&time);
	sprintf(date,"%-2.2d/%-2.2d/%-2.2d %-2.2d:%-2.2d",btime->tm_mon+1,btime->tm_mday,btime->tm_year,btime->tm_hour,btime->tm_min);
}

void timet_to_dateonly_short(time_t time, char *date)
{
	struct tm *btime;

	btime = localtime(&time);
	sprintf(date,"%-2.2d/%-2.2d/%-2.2d",btime->tm_mon+1,btime->tm_mday,btime->tm_year);
}

void timet_to_ago_short(time_t when, char *ago)
{
	time_to_elap_short(time(NULL) - when,ago);
}

void time_to_elap_short(time_t dur, char *elap)
{
	int days=0,hrs=0,mins=0,secs=0;

	elap[0] = 0;

	days = (int)(dur / 86400);
	dur -= (int)(dur / 86400) * 86400;

	hrs = (int)(dur / 3600);
	dur -= (int)(dur / 3600) * 3600;

	mins = (int)(dur / 60);
	secs = dur - (int)(dur / 60) * 60;

	if(days)
		sprintf(elap,"%dd",days);
	else if(hrs)
		sprintf(elap,"%dh",hrs);
	else if(mins)
		sprintf(elap,"%dm",mins);
	else
		sprintf(elap,"%ds",secs);
}

void timet_to_ago_long(time_t when, char *ago)
{
	time_t dur;
	int days=0,hrs=0,mins=0,secs=0;

	dur=time(NULL)-when;
		
	days=(int)(dur/86400);
	dur-=(((int)(dur/86400))*86400);

	hrs=((int)(dur/3600));
	dur-=(((int)(dur/3600))*3600);

	mins=((int)(dur/60));
	secs=dur-(((int)(dur/60))*60);

	if(days)
		sprintf(ago,"%d day%s, %d hr%s",days,EXTRA_CHAR(days),hrs,EXTRA_CHAR(hrs));
	else if(hrs)
		sprintf(ago,"%d hr%s, %d min%s",hrs,EXTRA_CHAR(hrs),mins,EXTRA_CHAR(mins));
	else if(mins)
		sprintf(ago,"%d min%s, %d sec%s",mins,EXTRA_CHAR(mins),secs,EXTRA_CHAR(secs));
	else
		sprintf(ago,"%d second%s",secs,EXTRA_CHAR(secs));

	return;
}

void timet_to_elap_short(time_t t, char *elap)
{
	char s1[50];

	if(t > 0)
	{
		strcpy(elap,ctime(&(t)));

		if((time(NULL)-t) > 86400)
		{
			strcpy(s1,&elap[4]);
			strcpy(elap,&elap[8]);
			strcpy(&elap[2],s1);
			elap[5] = 0;
		}
		else
		{
			strcpy(elap,&elap[11]);
			elap[5] = 0;
		}
	}
	else
		strcpy(elap,"?????");
}



/*******************************************************************************
 *
 * Formatting stuff
 * 
 ******************************************************************************/

void bytes_to_kb(u_long bytes, char *kb)
{
	if(bytes < 1048576)
		sprintf(kb,"%.1fk",(float)(bytes) / 1024.0);
	else
		sprintf(kb,"%.1fM",(float)(bytes) / 1048576.0);
}

void comma_number(char *string, u_long sum)
{
	u_long mod = 0;
	char o_bytes[64];

	strcpy(o_bytes,"");
	while(sum>999)
	{
		mod = sum%1000;
		sum = sum/1000;
		sprintf(string,",%-3.3lu%s",mod,o_bytes);
		strcpy(o_bytes,string);
	}
	sprintf(string,"%lu%s",sum,o_bytes);
	return;
}



/*******************************************************************************
 *
 * String stuff
 * 
 ******************************************************************************/
/* split first word off of rest and put it in first */
void splitc(char *first, char *rest, char divider)
{
       char *p;

       p = strchr(rest,divider);
       if(p == NULL)
       {
                if((first != rest) && (first != NULL))
		{
			strcpy(first,rest);
                        rest[0] = 0;
		}
                return;
        }
        *p = 0;
        if(first != NULL)
                strcpy(first,rest);
        if(first != rest)
                strcpy(rest,p+1);
}

void split(char *first, char *rest)
{
        splitc(first,rest,' ');
}

void rmspace(char *s)
{
#define whitespace(c) ( ((c)==32) || ((c)==9) || ((c)==13) || ((c)==10) )
	char *p;

	/* wipe end of string */
	for(p=s+strlen(s)-1; ((whitespace(*p)) && (p>=s)); p--);

	if(p != s+strlen(s)-1)
		*(p+1) = 0;
	for(p=s; ((whitespace(*p)) && (*p)); p++);

	if(p != s)
		strcpy(s,p);
}

int rmquotes(char *s)
{
	char *p, last = '\0';
	int numquotes = 0;

	rmspace(s);

	/* count number of quotes */
	for(p=s; *p!='\0'; p++)
	{
		if(*p == '"' && last != '\\')
			numquotes++;
		last = *p;
	}

	if(numquotes != 2)
		return 0;

	/* if last char is not the quote, and first char is not the quote */
	if(s[0] != '"' && s[strlen(s)-1] != '"')
		return 0;

	s[strlen(s)-1] = '\0';
	strcpy(s,s+1);

	return 1;
}

int isanumber(char *s)
{
	char *p;

	for(p=s; *p!='\0'; p++)
		if(!isdigit(*p))
			return 0;

	return 1;
}

void str_element(char *out, char *in, int item)
{
	char *p, *p1, *pbuf, buf[BUFSIZ];
	int on_token_last = 0, num_tokens = 0, done = 0;

	for(p=in; *p && !done; p++)
	{
		if(whitespace(*p))
			on_token_last = 0;
		else
		{
			if(!on_token_last)
				num_tokens++;
			on_token_last = 1;
			if(num_tokens == item)
			{
				pbuf = buf;
				for(p1=p; !(whitespace(*p1)) && *p1; p1++)
					*pbuf++ = *p1;
				*pbuf = 0;
				done = 1;
			}
		}
	}

	if(done)
		strcpy(out,buf);
	else
		strcpy(out,"");
}

void quoted_str_element(char *out, char *in, int item)
{
	char *p, *p1, *pbuf, buf[BUFSIZ];
	int on_token_last = 0, num_tokens = 0, done = 0;
	int inquote = 0;

	for(p=in; *p && !done; p++)
	{
		if(*p == '"')
			inquote = inquote?0:1;
		if(whitespace(*p) && !inquote)
			on_token_last = 0;
		else
		{
			if(!on_token_last)
				num_tokens++;
			on_token_last = 1;
			if(num_tokens == item)
			{
				pbuf = buf;
				inquote = 0;
				for(p1=p; (!(whitespace(*p1)) || inquote) && *p1; p1++)
				{
					*pbuf++ = *p1;
					if(*p1 == '"')
						inquote = inquote?0:1;
				}
				*pbuf = 0;
				done = 1;
			}
		}
	}

	if(done)
		strcpy(out,buf);
	else
		strcpy(out,"");
}

int num_elements(char *s)
{
	char *p;
	int on_token_last = 0, num_tokens = 0;

	for(p=s; *p; p++)
	{
		if(whitespace(*p))
			on_token_last = 0;
		else
		{
			if(!on_token_last)
				num_tokens++;
			on_token_last = 1;
		}
	}
	return num_tokens;
}

void AppendImageFormat(const char *format,char *filename)
{
	char staging[MAXFILENAMELEN];
	register char *p;

	assert(format != (char *) NULL);
	assert(filename != (char *) NULL);
	if((*format == '\0') || (*filename == '\0'))
		return;
	if(strcmp(filename,"-") == 0)
	{
		(void) sprintf(staging,"%s:%s",format,filename);
		(void) strcpy(filename,staging);
		return;
	}

	p = filename+strlen(filename)-1;
	while((p > filename) && (*p != *BasenameSeparator))
	{
		if(*(p-1) == '.')
		{
			(void) strcpy(p,format);
			return;
		}
		p--;
	}
	(void) strcat(filename,".");
	(void) strcat(filename,format);
}

char *BaseFilename(const char *name)
{
	register char *p;
	static char basename[MAXFILENAMELEN];

	/* Get basename of client. */
	assert(name != (char *) NULL);
	(void) strcpy(basename,name);
	p = basename+(strlen(basename)-1);
	while(p > basename)
	{
		if (*p == *BasenameSeparator)
		{
			(void) strcpy(basename,p+1);
			break;
		}
		p--;
	}

	/* Delete any extension. */
	p = basename+(strlen(basename)-1);
	while(p > basename)
	{
		if (*p == '.')
		{
			*p='\0';
			break;
		}
		p--;
	}
	return(basename);
}

char *FilenameExtension(const char *name)
{
	register char *p;
	static char extension[MAXFILENAMELEN];

	/* Get filename extension */
	strcpy(extension,name);

	/* Remove path */
	p = extension + (strlen(extension) - 1);
	while(p > extension)
	{
		if(*p == *BasenameSeparator)
		{
			strcpy(extension,p+1);
			break;
		}
		p--;
	}

	/* Retrieve extension */
	p = extension + (strlen(extension) - 1);
	while(p > extension)
	{
		if(*p == '.')
			return p+1;

		p--;
	}

	return NULL;
}




/*******************************************************************************
 *
 * Memory stuff
 * 
 ******************************************************************************/
void mfree(void *ptr)
{
	if(ptr != NULL)
		nfree(ptr);
}

void *n_malloc(int size, char *file, int line)
{
	void *x;

	x = (void *)malloc(size);
	if (x == NULL)
	{
		printf("*** FAILED MALLOC %s (%d)\n",file,line);
		return NULL;
	}
	return x;
}

void n_free(void *ptr, char *file, int line)
{
	if (ptr == NULL)
	{
		printf("*** ATTEMPTING TO FREE NULL PTR: %s (%d)\n",file,line);
		return;
	}
	free(ptr);
}

void mstrcpy(char **dest, const char *src)
{
	mfree(*dest);

	if(src == NULL)
	{
		printf("*** ATTEMPTING TO MSTRCPY WITH NULL SRC\n");
		return;
	}

	*dest = (char *)nmalloc(strlen(src)+1);
	strcpy(*dest,src);
}



/*******************************************************************************
 *
 * Memory stuff
 * 
 ******************************************************************************/
int nearest(float actual, int low, int high)
{
	float diff1, diff2;

	diff1 = (float)high - actual;
	diff2 = actual - (float)low;

	if(diff1 <= diff2)
		return high;
	else
		return low;
}
