/*
 * util.c --- miscellaneous utilities
 */

#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <ctype.h>
#include <termios.h>
#include <sys/resource.h>

#include "e2fsck.h"

/*
 * Volatile to let gcc know that this doesn't return.
 */
volatile void fatal_error (const char *msg)
{
	if (msg) 
		fprintf (stderr, "%s: %s\n", program_name, msg);
	exit(FSCK_ERROR);
}

void *allocate_memory(int size, char *description)
{
	void *ret;
	char buf[256];

#ifdef DEBUG_ALLOCATE_MEMORY
	printf("Allocating %d bytes for %s...\n", size, description);
#endif
	ret = malloc(size);
	if (!ret) {
		sprintf(buf, "%%s: Can't allocate %s\n", description);
		fatal_error(buf);
	}
	memset(ret, 0, size);
	return ret;
}


int ask_yn(const char * string, int def)
{
	int		c;
	struct termios	termios, tmp;
	char		*defstr;

	tcgetattr (0, &termios);
	tmp = termios;
	tmp.c_lflag &= ~(ICANON | ECHO);
	tcsetattr (0, TCSANOW, &tmp);

	if (def == 1)
		defstr = "<y>";
	else if (def == 0)
		defstr = "<n>";
	else
		defstr = " (y/n)";
	printf("%s%s? ", string, defstr);
	while (1) {
		fflush (stdout);
		if ((c = getchar()) == EOF)
			break;
		c = toupper(c);
		if (c == 'Y') {
			def = 1;
			break;
		}
		else if (c == 'N') {
			def = 0;
			break;
		}
		else if ((c == ' ' || c == '\n') && (def != -1))
			break;
	}
	if (def)
		printf ("yes\n\n");
	else
		printf ("no\n\n");
	tcsetattr (0, TCSANOW, &termios);
	return def;
}

int ask (const char * string, int def)
{
	if (nflag) {
		printf ("%s? no\n\n", string);
		return 0;
	}
	if (yflag) {
		printf ("%s? yes\n\n", string);
		return 1;
	}
	if (preen) {
		printf ("%s? %s\n\n", string, def ? "yes" : "no");
		return def;
	}
	return ask_yn(string, def);
}

void read_bitmaps(ext2_filsys fs)
{
	errcode_t	retval;

	if (!fs->inode_map) {
		retval = ext2fs_read_inode_bitmap(fs);
		if (retval) {
			com_err(program_name, retval,
				"while retrying to read inode bitmaps for %s",
				device_name);
			fatal_error(0);
		}
	}
	
	if (!fs->block_map) {
		retval = ext2fs_read_block_bitmap(fs);
		if (retval) {
			com_err(program_name, retval,
				"while retrying to read block bitmaps for %s",
				device_name);
			fatal_error(0);
		}
	}
}	

void preenhalt()
{
	if (!preen)
		return;
	fprintf(stderr, "\n\n%s: UNEXPECTED INCONSISTENCY; RUN fsck MANUALLY.\n",
	       device_name);
	exit(FSCK_UNCORRECTED);
}

void init_resource_track(struct resource_track *track)
{
	struct rusage r;
	
	track->brk_start = sbrk(0);
	gettimeofday(&track->time_start, 0);
	getrusage(RUSAGE_SELF, &r);
	track->user_start = r.ru_utime;
	track->system_start = r.ru_stime;
}

static inline float timeval_subtract(struct timeval *tv1, struct timeval *tv2)
{
	return ((tv1->tv_sec - tv2->tv_sec) +
		((float) (tv1->tv_usec - tv2->tv_usec)) / 1000000);
}

void print_resource_track(struct resource_track *track)
{
	struct rusage r;
	struct timeval time_end;

	gettimeofday(&time_end, 0);
	getrusage(RUSAGE_SELF, &r);

	printf("Memory used: %d, elapsed time: %6.3f/%6.3f/%6.3f\n",
	       (int) (sbrk(0) - track->brk_start),
	       timeval_subtract(&time_end, &track->time_start),
	       timeval_subtract(&r.ru_utime, &track->user_start),
	       timeval_subtract(&r.ru_stime, &track->system_start));
}

/*
 * This function returns 1 if the inode's block entries actually
 * contain block entries.
 */
int inode_has_valid_blocks(struct ext2_inode *inode)
{
	/*
	 * Only directories, regular files, and some symbolic links
	 * have valid block entries.
	 */
	if (!S_ISDIR(inode->i_mode) && !S_ISREG(inode->i_mode) &&
	    !S_ISLNK(inode->i_mode))
		return 0;
	
	/*
	 * If the symbolic link is a "fast symlink", then the symlink
	 * target is stored in the block entries.
	 */
	if (S_ISLNK (inode->i_mode) && inode->i_blocks == 0 &&
	    inode->i_size < EXT2_N_BLOCKS * sizeof (unsigned long))
		return 0;

	return 1;
}

#ifdef MTRACE
void mtrace_print(char *mesg)
{
	FILE	*malloc_get_mallstream();
	FILE	*f = malloc_get_mallstream();

	if (f)
		fprintf(f, "============= %s\n", mesg);
}
#endif


