/*
 * Copyright 1993, 1994 by Ulrich Khn. All rights reserved.
 *
 * THIS PROGRAM COMES WITH ABSOLUTELY NO WARRANTY, NOT
 * EVEN THE IMPLIED WARRANTIES OF MERCHANTIBILITY OR
 * FITNESS FOR A PARTICULAR PURPOSE. USE AT YOUR OWN
 * RISK.
 */

/*
 * File : util.c
 *        utility functions for the nfs demon
 */


#include <unistd.h>
#include <stat.h>
#include <string.h>
#include <ctype.h>
#include <support.h>
#include <osbind.h>
#include <ostruct.h>
#include <time.h>
#include <limits.h>
#include "util.h"



#define _HZ_200  (0x4baL)



static long
read_hz_200()
{
	return *(long*)_HZ_200;
}

long
get_timestamp()
{
	return Supexec(read_hz_200);
}


/* Was time stamp u build after timestamp v? Make sure to watch for
 * wrap-arounds!
 */
int
after(u_long u, u_long v)
{
  return ((u-v < LONG_MAX) || (v-u > LONG_MAX));
}


enum ftype
nfs_type(int mode, int attrib)
{
	switch (mode & S_IFMT)
	{
		case S_IFCHR:
			return NFCHR;
		case S_IFDIR:
			return NFDIR;
		case S_IFREG:
			return NFREG;
		case S_IFIFO:
			/* Is a fifo the same as a socket? Or a block special file? */
 			return NFNON;
		case S_IMEM:
			/* What kind of file is this? A block special file? */
			return NFBLK;
		case S_IFLNK:
			return NFLNK;
	}
	return NFNON;
}


int
nfs_mode(int mode, int attrib)
{
	int newmode = mode & ~S_IFMT;

	switch (mode & S_IFMT)
	{
		case S_IFCHR:
			newmode |= N_IFCHR;
			break;
		case S_IFDIR:
			newmode |= N_IFDIR;
			break;
		case S_IFREG:
			newmode |= N_IFREG;
			break;
		case S_IFIFO:
			/* Is a fifo the same as a socket? Or a block special file? */
			newmode |= N_IFSCK;
			break;
		case S_IMEM:
			/* What kind of file is this? A block special file? */
			newmode |= N_IFBLK;
			break;
		case S_IFLNK:
			newmode |= N_IFLNK;
			break;
	}
	return newmode;
}


int
mint_mode(int mode, int type)
{
	int newmode = mode & ~N_IFMT;

	switch (mode & N_IFMT)
	{
		case N_IFDIR:
			newmode |= S_IFDIR;
			break;
		case N_IFCHR:
			newmode |= S_IFCHR;
			break;
		case N_IFBLK:
			if (type == NFNON)
				newmode |= S_IFIFO;
			else
				newmode |= S_IFBLK;
			break;
		case N_IFREG:
			newmode |= S_IFREG;
			break;
		case N_IFLNK:
			newmode |= S_IFLNK;
			break;
	}	
	return newmode;
}



/* convert an nfs fattr structure into an MiNT xattr structure */

extern long _unixtime(unsigned dostime, unsigned dosdate);
#define CONVERT2UNX(x)  ( _unixtime( ((unsigned*)&x)[0], ((unsigned*)&x)[1]) )

void
fattr2stat(fattr *fa, struct stat *xa)
{
	xa->res2[0] = xa->res2[1] = 0;
	xa->res1 = 0;
	xa->st_ctime = fa->ctime.seconds;
	xa->st_atime = fa->atime.seconds;
	xa->st_mtime = fa->mtime.seconds;
	xa->st_blocks = fa->blocks;
	xa->st_blksize = fa->blocksize;
	xa->st_size = fa->size;
	xa->st_gid = fa->gid;
	xa->st_uid = fa->uid;
	xa->st_nlink = fa->nlink;
	xa->st_rdev = 0;
	xa->st_dev = fa->fsid;
	xa->st_ino = fa->fileid;
	xa->st_mode = mint_mode(fa->mode, fa->type);

	xa->st_attr = 0;
	if ((xa->st_mode & S_IFMT) == S_IFDIR)
		xa->st_attr |= FA_DIR;
	if ((xa->st_mode & (S_IWUSR | S_IWGRP | S_IWOTH)) == 0)
		xa->st_attr |= FA_RDONLY;
}



void
stat2fattr(struct stat *xa, fattr *fa)
{
	fa->ctime.useconds = 0;    /* time measurement is not that fine */
	fa->mtime.useconds = 0;
	fa->atime.useconds = 0;
	fa->ctime.seconds = CONVERT2UNX(xa->st_ctime);
	fa->mtime.seconds = CONVERT2UNX(xa->st_mtime);
	fa->atime.seconds = CONVERT2UNX(xa->st_atime);
	fa->fileid = xa->st_ino;
	fa->fsid = xa->st_dev;
	fa->blocks = xa->st_blocks;
	fa->rdev = 0;    /* reserved for MiNT */
	fa->blocksize = xa->st_blksize;
	fa->size = xa->st_size;
	fa->gid = xa->st_gid;
	fa->uid = xa->st_uid;
	fa->nlink = xa->st_nlink;
	fa->mode = nfs_mode(xa->st_mode, xa->st_attr);
	fa->type = nfs_type(xa->st_mode, xa->st_attr);
}



/* Convert the path to one relative to u:\ which is defined to be \ and
 * therefore absolute :-)
 */
int
path2abs(char *path, char *dest)
{
	char buf[MAXPATHLEN+1];

	/* convert into the native representation of MiNT */
	unx2dos(path, buf);
	
	/* get rid of the leading u: */
	if (buf[1] == ':')
	{
		if (tolower(buf[0]) == 'u')
			strcpy(buf, buf+2);
		else
		{
			/* Here we have something like d:\foo, which we convert into
			 * \d\foo, so we do not need more space.
			 */
			buf[1] = tolower(buf[0]);
			buf[0] = '\\';
		}
	}
	else if (buf[0] != '\\')
	{
		/* There is no absolute path given, so we fail here, as the daemon
		 * might have some wierd current directory at this time.
		 *
		 * BUG: we might want to take that directory.
		 */
		return -1;
	}

	/* BUG: we should also get rid of symbolic links etc. which make paths
	 *      not being unique.
	 */

	strcpy(dest, buf);
	return 0;
}
