/*  Copyright 1996-2002,2005,2007,2009,2011 Alain Knaff.
 *  This file is part of mtools.
 *
 *  Mtools 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 3 of the License, or
 *  (at your option) any later version.
 *
 *  Mtools 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 Mtools.  If not, see <http://www.gnu.org/licenses/>.
 *
 * Miscellaneous routines.
 */

#include "sysincludes.h"
#include "msdos.h"
#include "stream.h"
#include "vfat.h"
#include "mtools.h"


void printOom(void)
{
	fprintf(stderr, "Out of memory error");
}

char *get_homedir(void)
{
#ifndef OS_mingw32msvc
	struct passwd *pw;
	uid_t uid;
	char *homedir;
	char *username;

	homedir = getenv ("HOME");
	/*
	 * first we call getlogin.
	 * There might be several accounts sharing one uid
	 */
	if ( homedir )
		return homedir;

	pw = 0;

	username = getenv("LOGNAME");
	if ( !username )
		username = getlogin();
	if ( username )
		pw = getpwnam( username);

	if ( pw == 0 ){
		/* if we can't getlogin, look up the pwent by uid */
		uid = geteuid();
		pw = getpwuid(uid);
	}

	/* we might still get no entry */
	if ( pw )
		return pw->pw_dir;
	return 0;
#else
	return getenv("HOME");
#endif
}


static void get_mcwd_file_name(char *file)
{
	char *mcwd_path;
	const char *homedir;

	mcwd_path = getenv("MCWD");
	if (mcwd_path == NULL || *mcwd_path == '\0'){
		homedir= get_homedir();
		if(!homedir)
			homedir="/tmp";
		strncpy(file, homedir, MAXPATHLEN-6);
		file[MAXPATHLEN-6]='\0';
		strcat( file, "/.mcwd");
	} else {
		strncpy(file, mcwd_path, MAXPATHLEN);
		file[MAXPATHLEN]='\0';
	}
}

void unlink_mcwd(void)
{
	char file[MAXPATHLEN+1];
	get_mcwd_file_name(file);
	unlink(file);
}

FILE *open_mcwd(const char *mode)
{
	struct MT_STAT sbuf;
	char file[MAXPATHLEN+1];
	time_t now;

	get_mcwd_file_name(file);
	if (*mode == 'r'){
		if (MT_STAT(file, &sbuf) < 0)
			return NULL;
		/*
		 * Ignore the info, if the file is more than 6 hours old
		 */
		getTimeNow(&now);
		if (now - sbuf.st_mtime > 6 * 60 * 60) {
			fprintf(stderr,
				"Warning: \"%s\" is out of date, removing it\n",
				file);
			unlink(file);
			return NULL;
		}
	}

	return  fopen(file, mode);
}



void *safe_malloc(size_t size)
{
	void *p;

	p = malloc(size);
	if(!p){
		printOom();
		exit(1);
	}
	return p;
}

void print_sector(const char *message, unsigned char *data, int size)
{
	int col;
	int row;

	printf("%s:\n", message);

	for(row = 0; row * 16 < size; row++){
		printf("%03x  ", row * 16);
		for(col = 0; col < 16; col++)
			printf("%02x ", data [row*16+col]);
		for(col = 0; col < 16; col++) {
			if(isprint(data [row*16+col]))
				printf("%c", data [row*16+col]);
			else
				printf(".");
		}
		printf("\n");
	}
}

#if (SIZEOF_TIME_T > SIZEOF_LONG) && defined (HAVE_STRTOLL)
# define STRTOTIME strtoll
#else
# define STRTOTIME strtol
#endif

time_t getTimeNow(time_t *now)
{
	static int haveTime = 0;
	static time_t sharedNow;

	if(!haveTime) {
		const char *source_date_epoch = getenv("SOURCE_DATE_EPOCH");
		if (source_date_epoch) {
			char *endptr;
			time_t epoch =
				STRTOTIME(source_date_epoch, &endptr, 10);
			errno = 0;

			if (endptr == source_date_epoch)
				fprintf(stderr,
					"SOURCE_DATE_EPOCH \"%s\" invalid\n",
					source_date_epoch);
			else if (errno != 0)
				fprintf(stderr,
					"SOURCE_DATE_EPOCH: strtoll: %s: %s\n",
					strerror(errno), source_date_epoch);
			else if (*endptr != '\0')
				fprintf(stderr,
					"SOURCE_DATE_EPOCH has trailing garbage \"%s\"\n",
					endptr);
			else {
				sharedNow = epoch;
				haveTime = 1;
			}
		}
	}

	if(!haveTime) {
		time(&sharedNow);
		haveTime = 1;
	}
	if(now)
		*now = sharedNow;
	return sharedNow;
}

/* Convert a string to an offset. The string should be a number,
   optionally followed by S (sectors), K (K-Bytes), M (Megabytes), G
   (Gigabytes) */
off_t str_to_offset_with_end(const char *str, char **endp) {
	char s;
	off_t ofs;

	*endp = NULL;
	ofs = strtol(str, endp, 0);
	s = **endp;
	/* trailing char, see if it is a size specifier */
	if (s == 's' || s == 'S')       /* sector */
		ofs <<= 9;
	else if (s == 'k' || s == 'K')  /* kb */
		ofs <<= 10;
	else if (s == 'm' || s == 'M')  /* Mb */
		ofs <<= 20;
	else if (s == 'g' || s == 'G')  /* Gb */
		ofs <<= 30;
	else
		return ofs;      /* invalid character */
	(*endp)++;
	return ofs;
}

/* Convert a string to a size. The string should be a number,
   optionally followed by S (sectors), K (K-Bytes), M (Megabytes), G
   (Gigabytes) */
mt_off_t str_to_off_with_end(const char *str, char **endp) {
	char s;
	mt_off_t siz;

	*endp = NULL;
	siz = strtol(str, endp, 0);
	s = **endp;
	/* trailing char, see if it is a size specifier */
	if (s == 's' || s == 'S')       /* sector */
		siz <<= 9;
	else if (s == 'k' || s == 'K')  /* kb */
		siz <<= 10;
	else if (s == 'm' || s == 'M')  /* Mb */
		siz <<= 20;
	else if (s == 'g' || s == 'G')  /* Gb */
		siz <<= 30;
	else
		return siz;      /* invalid character */
	(*endp)++;
	return siz;
}

off_t str_to_offset(char *str) {
	char *end;
	off_t ofs = str_to_offset_with_end(str, &end);
	if (ofs <= 0)
		return 0; /* invalid or missing offset */
	if (*end)
		return 0; /* extra char, invalid */
	return ofs;
}



#if 0

#undef free
#undef malloc

static int total=0;

void myfree(void *ptr)
{
	int *size = ((int *) ptr)-1;
	total -= *size;
	fprintf(stderr, "freeing %d bytes at %p total allocated=%d\n",
		*size, ptr, total);
	free(size);
}

void *mymalloc(size_t size)
{
	int *ptr;
	ptr = (int *)malloc(size+sizeof(int));
	if(!ptr)
		return 0;
	*ptr = size;
	ptr++;
	total += size;
	fprintf(stderr, "allocating %d bytes at %p total allocated=%d\n",
		size, ptr, total);
	return (void *) ptr;
}

void *mycalloc(size_t nmemb, size_t size)
{
	void *ptr = mymalloc(nmemb * size);
	if(!ptr)
		return 0;
	memset(ptr, 0, size);
	return ptr;
}

void *myrealloc(void *ptr, size_t size)
{
	int oldsize = ((int *)ptr) [-1];
	void *new = mymalloc(size);
	if(!new)
		return 0;
	memcpy(new, ptr, oldsize);
	myfree(ptr);
	return new;
}

char *mystrdup(char *src)
{
	char *dest;
	dest = mymalloc(strlen(src)+1);
	if(!dest)
		return 0;
	strcpy(dest, src);
	return dest;
}

#endif
