/*
 * © Copyright 1996-2012 ECMWF.
 * 
 * This software is licensed under the terms of the Apache Licence Version 2.0
 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
 * In applying this licence, ECMWF does not waive the privileges and immunities 
 * granted to it by virtue of its status as an intergovernmental organisation nor
 * does it submit to any jurisdiction.
 */

#include "mars.h"
#include "grib_api.h"
#include <unistd.h>
#include <math.h>
#include <errno.h>
#include <fcntl.h>
#include <limits.h>
#include <pwd.h>
#include <ctype.h>
#ifdef sgi
#include <bstring.h>
#endif
#include <string.h>

#include <sys/param.h>

#include <dirent.h>
#include <sys/types.h>
#include <time.h>
#include <sys/time.h>

#include <grib_api.h>

#if defined(sgi)
#define _MNT_ENT_ 
#endif


#ifdef _MNT_ENT_
#include <mntent.h>
#endif

/* #include <sys/dir.h> */
#ifndef S_IAMB
#define S_IAMB 0777
#endif

#ifndef S_ISLNK
#define S_ISLNK(a) ((a)&S_IFLNK != 0)
#endif


#define MAX_TIMER 10
#ifndef NGROUPS
#define NGROUPS 20
#endif

#include <sys/resource.h>

#if 0

#include <sys/procfs.h>

typedef struct procfs {
	int        fd;
	prpsinfo_t ps;
	prstatus_t status;
	char       fname[1024];
} procfs_t;

static procfs_t process;

boolean init_process()
{
	pid_t pid;

	static boolean inited = false;	
	if(inited)
		return inited;

	pid = getpid();
	sprintf(process.fname,"/proc/%d",pid);

	if( (process.fd = open(process.fname,O_RDONLY | O_EXCL)) != -1)
		inited = true;
	else
		marslog(LOG_WARN|LOG_PERR,"Failed to open process information file (%s)",process.fname);

	return inited;
}

long64 proc_mem()
{
	if(init_process())
	{
		if(ioctl(process.fd,PIOCPSINFO,&(process.ps)) == -1)
			marslog(LOG_WARN|LOG_PERR,"ioctl(...,PIOCPSINFO,...) failed on %s [fd=%d]",process.fname,process.fd);
		else
			return (process.ps.pr_size * getpagesize());
	}
	return 0;
}

#elif defined(AIX)

#define PROCS_SIZE 1
#include <procinfo.h>
long64 proc_mem()
{
	pid_t p = getpid();
	struct procsinfo  pinfos[PROCS_SIZE];
	int n = getprocs(pinfos,sizeof(struct procsinfo),0,sizeof(struct fdsinfo),&p,PROCS_SIZE);
	if(n)
	{
		/* return (pinfos[0].pi_dvm + pinfos[0].pi_tsize + pinfos[0].pi_sdsize ); */
		return pinfos[0].pi_size * getpagesize();
	}
	return 0;
}

#else

long64 proc_mem()
{
    struct rusage rup;

    if(getrusage(RUSAGE_SELF,&rup) != -1)
        return rup.ru_maxrss*1024;

    return 0;
}

#endif


double proc_cpu()
{
	struct rusage rup;

	if(getrusage(RUSAGE_SELF,&rup) != -1)
	{
		return (rup.ru_utime.tv_sec + rup.ru_utime.tv_usec / 1000000.0 +
			   rup.ru_stime.tv_sec + rup.ru_stime.tv_usec / 1000000.0);
	}
	return clock()/(double)CLOCKS_PER_SEC;
}


const char *user(const char *s)
{
	static char *u;
	struct passwd *pw;

	if(s)
		u = strcache(s);

	if(u) return u;

	setpwent();
	if((pw = getpwuid(getuid())) == NULL)
		marslog(LOG_EXIT|LOG_PERR,"Cannot get user name");

	endpwent();

	return u = strcache(pw->pw_name);
}


static err z(char *cmd,void *inbuf,void *outbuf,long inlen,long *outlen,long max)
{
	int toc[2];
	int top[2];
	int toz,frz;
	fd_set fdr,fdw;
	int nfd;
	char *p,*q;
	int  cnt;
	boolean eof = false;
	long sav = inlen;

	if(pipe(top) < 0)
	{
		marslog(LOG_EROR | LOG_PERR,"(un)compress pipe (top)");
		return -2;
	}

	if(pipe(toc) < 0)
	{
		marslog(LOG_EROR | LOG_PERR,"(un)compress pipe (toc)");
		return -2;
	}


	switch(fork())
	{
	case 0:
		if(dup2(toc[0],0)<0)
			marslog(LOG_EXIT|LOG_PERR,"(un)compress dup2 (toc)");

		if(dup2(top[1],1)<0)
			marslog(LOG_EXIT|LOG_PERR,"(un)compress dup2 (top)");

		close(toc[0]);
		close(toc[1]);
		close(top[0]);
		close(top[1]);

		execlp(cmd,cmd,"-c","-v",NULL);
		marslog(LOG_EXIT|LOG_PERR,"(un)compress execlp");

		break;

	case -1:
		marslog(LOG_EROR | LOG_PERR,"(un)compress fork");
		return -2;
		/*NOTREACHED*/
		break;

	default:

		toz = toc[1];
		frz = top[0];
		close(toc[0]);
		close(top[1]);

		nfd = toz;
		if(frz>nfd) nfd = frz;
		nfd++;

		p = inbuf;
		q = outbuf;
		cnt = 0;

		/*
		if(fcntl(toz,F_GETFL,&flags) < 0)
		{
			marslog(LOG_EROR | LOG_PERR,"(un)compress fcntl-F_GETFL(toz)");
			close(toz);
			close(frz);
			return -2;
		}
*/

		if( fcntl(toz,F_SETFL,O_NONBLOCK) <0 )
		{
			marslog(LOG_EROR | LOG_PERR,"(un)compress fcntl-F_SETFL(toz)");
			close(toz);
			close(frz);
			return -2;
		}

		/*
		if(fcntl(frz,F_GETFL,&flags) < 0 )
		{
			marslog(LOG_EROR | LOG_PERR,"(un)compress fcntl-F_GETFL(frz)");
			close(toz);
			close(frz);
			return -2;
		}
*/
		if( fcntl(frz,F_SETFL, O_NONBLOCK) <0 )
		{
			marslog(LOG_EROR | LOG_PERR,"(un)compress fcntl-F_SETFL(frz)");
			close(toz);
			close(frz);
			return -2;
		}

		while(!eof)
		{
			FD_ZERO(&fdr);
			FD_ZERO(&fdw);
			if(toz >= 0) FD_SET(toz,&fdw);
			if(frz >= 0) FD_SET(frz,&fdr);

#if defined(hpux) && !defined(R64)
			if(select(nfd,(int *) &fdr,(int *) &fdw,NULL,NULL) < 0)
#else
			if(select(nfd,&fdr,&fdw,NULL,NULL) < 0)
#endif
			{
				marslog(LOG_EROR | LOG_PERR,"(un)compress select");
				close(toz);
				close(frz);
				return -2;
			}

			if(toz >= 0  && FD_ISSET(toz,&fdw))
			{
				int len = write(toz,p,inlen);

				if(len == -1 && errno != EAGAIN)
				{
					marslog(LOG_EROR | LOG_PERR,"(un)compress write");
					close(toz);
					close(frz);
					return -2;
				}

				if(len > 0)
				{
					inlen -= len;
					p      += len;

					if(inlen <= 0)
					{
						close(toz);
						toz = -1;
					}
				}
			}

			if(FD_ISSET(frz,&fdr))
			{

				/* long bytes = MIN(PIPE_BUF,max); */
				long bytes = MIN(fpathconf(frz,_PC_PIPE_BUF),max);
				long len;

				len = read(frz,q,bytes);

				if(len == -1 && errno != EAGAIN)
				{
					marslog(LOG_EROR | LOG_PERR,"(un)compress read");
					close(toz);
					close(frz);
					return -2;
				}


				if(len == 0)
					eof = true;

				if(len > 0)
				{
					q  += len;
					cnt+= len;
					max-=len;

					if(max<=0)
					{
						*outlen = sav;
						close(toz);
						close(frz);
						marslog(LOG_DBUG,"Cannot %s",cmd);
						return -1;
					}
				}
			}

		}

		*outlen = cnt;

		close(toz);
		close(frz);

		marslog(LOG_DBUG,"%s : %d -> %d (%5.2f%%)",cmd,
		    sav,cnt,
		    ABS((double)sav-(double)cnt)/
		    (double)sav*100.0);

	}
	return NOERR;
}


err mars_compress(void *in,void *out,long inlen,long *outlen)
{
	return z("compress",in,out,inlen,outlen,inlen);
}

err mars_uncompress(void *in,void *out,long inlen,long *outlen)
{
	return z("uncompress",in,out,inlen,outlen,LONG_MAX);
}

/* s_stop() */
/* { */
/* } */

const char *no_quotes(const char *in)
{
	static char buf[10240];
	char *p = buf;


	if(!in) return NULL;

	strcpy(buf,in);

	if(*buf == '"') /* || *buf == '\'') */
	{
		p = buf+1;
		buf[strlen(buf)-1] = 0;
	}
	return p;
}

#ifdef VMS
/* void bzero(void *a,int b) { memset(a,0,b); } */
/* char *strdup(char *a) { char *p = MALLOC(strlen(a)+1);strcpy(p,a);return p;} */
#endif

#ifdef _AIX
int casecompare(const char *a,const char *b)
{
	char x,y;

	while(*a && *b)
	{
		x = islower(*a) ? toupper(*a) : *a;
		y = islower(*b) ? toupper(*b) : *b;

		if(x != y) return x-y;

		a++;
		b++;
	}
	x = islower(*a) ? toupper(*a) : *a;
	y = islower(*b) ? toupper(*b) : *b;

	return x-y;
}
#endif


static struct timeval start[MAX_TIMER];
static double startcpu[MAX_TIMER];
static int top = 0;

void start_timer(void)
{
	if(mars.notimers)
		return;
	gettimeofday(&start[top],NULL);
	startcpu[top] = proc_cpu();
	top++;
}

static char *timetext(char *pfx,double ds,char *text)
{
	long s = ROUND(ds);
	long x = s;
	long n;
	char sec[20];
	char min[20];
	char hou[20];
	char day[20];

	*text = *sec = *min = *hou = *day = 0;
	if(s)
	{

		if((n = x % 60) != 0) sprintf(sec,"%ld sec ",n);
		x /= 60;
		if((n = x % 60) != 0) sprintf(min,"%ld min ",n);
		x /= 60;
		if((n = x % 24) != 0) sprintf(hou,"%ld hour ",n);
		x /= 24;
		if((n = x) != 0) sprintf(day,"%ld day ",n);

		sprintf(text,"%s%s%s%s%s",pfx,day,hou,min,sec);
	}

	return text;

}

char *timename(double t)
{
	static char buf[80];
	return timetext("",t,buf);
}

double stop_timer(char *text)
{
	if(mars.notimers)
	{
		*text = 0;
		return 0.0;
	}

	{
	struct timeval stop;
	struct timeval diff;
	double   s;
	clock_t cpu =  proc_cpu();

	top--;


	gettimeofday(&stop,NULL);


	diff.tv_sec  = stop.tv_sec  - start[top].tv_sec;
	diff.tv_usec = stop.tv_usec - start[top].tv_usec;

	if (diff.tv_usec < 0)
	{
		diff.tv_sec--;
		diff.tv_usec += 1000000;
	}

	s = (double)diff.tv_sec + ((double)diff.tv_usec / 1000000.);

	if(text)
	{
		char t1[80];
		char t2[80];

		sprintf(text,"%s%s",
		    timetext(" wall: ",s,t1),
			timetext(" cpu: ",(cpu-startcpu[top]),t2));
	}

	return s;
	}
}

clock_t timer_cpu()
{
	if(mars.notimers)
		return 0;

	return proc_cpu()-startcpu[top];
}

const char *bytename(double bytes)
{
	static char *names[] = { "","K","M","G","T","P","E","Z","Y" }; 
    double x = bytes; 
    int    n = 0;
	static char buf[20];

	while(x>=1024.0 && n < NUMBER(names))
	{
		x /= 1024.0;
		n++;
	}

	sprintf(buf,"%.2f %s",x,names[n]);

	return buf;
}

int mkdirp(const char *path,int mode)
{
	const char *p = path + 1;

	while(*p)
	{
		if(*p == '/')
		{
			char *q = (char*)p;
			*q = 0;
			mkdir(path,mode);
			*q = '/';
		}
		p++;
	}
	return mkdir(path,mode);
}

const char *makepath(const char *dname,const char *fname)
{
	static char buf[1024];
	char        tmp[1024];
	char *p;
	char *q;
	char *r = NULL;
	boolean more = true;

	*buf = 0;

	if(fname && *fname == '/') return fname;

	if(dname[0] == '/' || dname[0] == '.' || dname[0] == '\0')
		sprintf(tmp,"%s/%s",dname,fname);
	else
		sprintf(tmp,"./%s/%s",dname,fname);

	while(more)
	{
		more = false;
		p = tmp;
		r = NULL;
		*buf = 0;


		while((q = strtok(p,"/")) && !more)
		{
			if(strcmp(q,".") == 0)
			{
				if(r == NULL) strcat(buf,q);
			}
			else if((strcmp(q,"..") == 0)) {
				if(r == NULL) strcat(buf,q);
				else
				{
					more = true;
					*r   = 0;
					strcat(buf,"/");
					strcat(buf,q+3);
				}
			}
			else {
				r = buf + strlen(buf);
				strcat(buf,"/");
				strcat(buf,q);
			}

			p = NULL;
		}
		strcpy(tmp,buf);
	}

	return buf;
}

const char *mbasename(const char *fname)
{
	static char buf[1024];

	*buf = 0;

	if(fname)
	{
		int n = -1;
		int i = 0;
		const char *q = fname;

		while(*q) {
			if(*q == '/') n = i;
			q++;
			i++;
		}
		strcat(buf,fname+(n+1));
	}


	return buf;
}

const char *mdirname(const char *fname)
{
	static char buf[1024];

	strcpy(buf,".");

	if(fname)
	{
		int n = -1;
		int i = 0;
		char *q = buf;
		strcpy(buf,fname);
		while(*q) {
			if(*q == '/') n = i;
			q++;
			i++;
		}

		switch(n)
		{
		case -1:
			strcpy(buf,".");
			break;

		case 0:
			strcpy(buf,"/");
			break;

		default:
			buf[n] = 0;
		}
	}


	return buf;
}

int deletefile(const char *name)
{
	int e = 0;
	struct stat st;

	if((e = lstat(name,&st)) != 0) /*  Don't follow link */
	{
		marslog(LOG_EROR|LOG_PERR,"Cannot stat %s",name);
		return e;
	}

	if(S_ISDIR(st.st_mode) && !S_ISLNK(st.st_mode))
	{
		struct dirent *s;
		DIR *d;

		d = opendir(name);
		if(!d) {
			marslog(LOG_EROR|LOG_PERR,"opendir %s",name);
			return -1;
		}

		while((s = readdir(d)) && (e == 0))
		{
			if(strcmp(s->d_name,".") != 0 && strcmp(s->d_name,"..") != 0)
			{
				char buf[1024];
				sprintf(buf,"%s/%s",name,s->d_name);
				e = deletefile(buf);
			}
		}
		closedir(d);
		if(e == 0)
		{
			e = rmdir(name);
			if(e) marslog(LOG_EROR|LOG_PERR,"Cannot rmdir %s",name);
		}
	}
	else
	{
		e = unlink(name);
		if(e) marslog(LOG_EROR|LOG_PERR,"Cannot unlink %s",name);
	}
	return e;
}

int movefile(const char *from,const char *to)
{
	int e = rename(from,to);
	if(e)
	{
		if(errno == EXDEV) /* different file systems */
		{
			e = copyfile(from,to);
			if(e == 0) e = deletefile(from);
		}
		else marslog(LOG_EROR|LOG_PERR,"Cannot rename %s to %s",from,to);
	}
	return e;
}

int copylink(const char *from,const char *to)
{
	int e = 0;
	int x;
	char buf[1024];

	if(access(to,0) == 0)
	{
		marslog(LOG_EROR,"copy: %s exists",to);
		return -1;
	}

	if((x = readlink(from,buf,sizeof(buf)-1))<0)
	{
		marslog(LOG_EROR|LOG_PERR,"readlink(%s)",from);
		return e;
	}

	buf[x] = 0;

	if( (e = symlink(buf,to)) )
	{
		marslog(LOG_EROR|LOG_PERR,"symlink(%s,%s)",buf,to);
		return e;
	}
	return 0;

}

int copydata(const char *from,const char *to)
{
	FILE *f,*g;
	char buf[10240];
	int   e = 0;
	int n;

	if(faccess(to,0) == 0)
	{
		marslog(LOG_EROR,"copy: %s exists",to);
		return -1;
	}

	f = fopen(from,"r");
	if(f == NULL)
	{
		marslog(LOG_EROR|LOG_PERR,"fopen(%s)",from);
		return -1;
	}

	g = fopen(to,"w");
	if(g == NULL)
	{
		marslog(LOG_EROR|LOG_PERR,"fopen(%s)",to);
		return -1;
	}

	while( (n = fread(buf,1,sizeof(buf),f)) > 0)
	{
		if(ferror(f)) {
			marslog(LOG_EROR|LOG_PERR,"Read error file %s",from);
			e=1;
		}
		fwrite(buf,1,n,g);
		if(ferror(g)) {
			marslog(LOG_EROR|LOG_PERR,"Write error file %s",to);
			e=1;
		}
	}

	if(fflush(g))
	{
		marslog(LOG_EROR|LOG_PERR,"Write error file %s",to);
		e=1;
	}

	fclose(f);
	fclose(g);

	return e;

}

int copydir(const char *from,const char *to)
{
	struct dirent *s;
	DIR *d;
	int e = 0;

	if(faccess(to,0) == 0)
	{
		marslog(LOG_EROR,"copy: %s exists",to);
		return -1;
	}
	if( (e = mkdir(to,0777)) )
	{
		marslog(LOG_EROR|LOG_PERR,"mkdir %s",to);
		return e;
	}

	d = opendir(from);
	if(!d) {
		marslog(LOG_EROR|LOG_PERR,"opendir %s",from);
		return -1;
	}

	while((s = readdir(d)) && (e == 0))
	{
		if(strcmp(s->d_name,".") != 0 && strcmp(s->d_name,"..") != 0)
		{
			char buf1[1024];
			char buf2[1024];
			sprintf(buf1,"%s/%s",from,s->d_name);
			sprintf(buf2,"%s/%s",to  ,s->d_name);
			e = copyfile(buf1,buf2);
		}
	}
	closedir(d);
	return e;
}

int copyfile(const char *from,const char *to)
{
	int e = 0;
	struct stat stf,stt;
	boolean newfile = false;
	boolean dirt = false,dirf = false;

	mode_t mask = umask(0);
	umask(mask);

	if( (e = lstat(from,&stf)) ) /*  Don't follow link */
	{
		marslog(LOG_EROR|LOG_PERR,"Cannot stat %s",from);
		return e;
	}
	/* from is a directory */
	dirf = (S_ISDIR(stf.st_mode) && !S_ISLNK(stf.st_mode));

	if( (e = lstat(to,&stt)) ) /*  Don't follow link */
	{
		if(errno != ENOENT) /* File not found */
		{
			marslog(LOG_EROR|LOG_PERR,"Cannot stat %s",to);
			return e;
		}
		newfile = true;
		/* to is a directory */
	} else dirt = (S_ISDIR(stt.st_mode) && !S_ISLNK(stt.st_mode));

	/* There already is a file */
	if(!newfile)
	{
		if(stt.st_dev == stf.st_dev && stt.st_ino == stf.st_ino)
		{
			marslog(LOG_INFO,"Cannot copy file %s and %s are identical",
			    from,to);
			return -1;
		}
		if(dirf && !dirt)
		{
			marslog(LOG_INFO,"Cannot copy directory %s onto file %s",from,to);
			return -1;
		}

		/* The target is a directory */
		if(dirt)
		{
			char buf[1024];
			const char *p = mbasename(from);
			sprintf(buf,"%s/%s",to,p);
			return copyfile(from,buf);
		}
		/* The target is a file */
		else
		{
			if( (e = unlink(to)) )
			{
				marslog(LOG_EROR|LOG_PERR,"Cannot unlink %s",to);
				return e;
			}
			if(S_ISLNK(stf.st_mode)) return copylink(from,to);
			if(S_ISDIR(stf.st_mode)) return copydir(from,to);
			e = copydata(from,to);

			if(e == 0)
			{
				mode_t mode = 0777 & ~mask;
				if( (e = chmod(to,mode)) )
					marslog(LOG_EROR|LOG_PERR,"chmod(%s,%o) failed",to,mode);
			}
			return e;
		}

	}
	else /* There is no target file */
	{
		if(S_ISLNK(stf.st_mode)) return copylink(from,to);
		if(S_ISDIR(stf.st_mode)) return copydir(from,to);
		e = copydata(from,to);
		if(e == 0)
		{
			mode_t mode = 0777 & ~mask;
			if( (e = chmod(to,stf.st_mode&S_IAMB)) )
				marslog(LOG_EROR|LOG_PERR,"chmod(%s,%o) failed",to,mode);
		}
		return e;
	}
}

/* find a relative path */

const char *relpath(const char *from,const char *to)
{
	static char buf[1024];
	const char *p1,*p2,*p,*q;

	if(*from != '/' && *to != '/') return to;

	p = p1 = from;
	q = p2 = to;

	/* If exactly same pathname, stop at the end of string */
	while((*p1 != '\0') && (*p2  != '\0') && (*p1 == *p2)) {
		if (*p1 == '/') {
			p = p1+1;
			q = p2+1;
		}
		p1++;
		p2++;
	}

	buf[0] = 0;
	while(*p)
	{
		if(*p == '/') strcat(buf,"../");
		p++;
	}

	strcat(buf,q);
	return buf;


}

void dumpenv(void)
{
	/*
	extern char **environ;
	int n = 0;

	while(environ[n])
		marslog(LOG_INFO,"Env : '%s'",environ[n++]);
		*/

}

static int saccess(struct stat *s,int flags)
{
	static gid_t groups[NGROUPS];
	static int first = 1;
	static uid_t user;
	static int ngrp;
	int u,g,o;
	int i;

	if(first)
	{
		first = 0;
		user  = getuid();
		ngrp  = getgroups(NGROUPS,groups);
	}


	if(flags & R_OK) 
	{
		flags &= ~R_OK;
		u = s->st_mode & S_IRUSR;
		g = s->st_mode & S_IRGRP;
		o = s->st_mode & S_IROTH;

		if(user  == s->st_uid) return u?0:-1;
		for(i=0;i<ngrp;i++)
			if(groups[i] == s->st_gid) return g?0:-1;
		return o?0:-1;
	}

	if(flags & W_OK) 
	{
		flags &= ~W_OK;
		u = s->st_mode & S_IWUSR;
		g = s->st_mode & S_IWGRP;
		o = s->st_mode & S_IWOTH;

		if(user  == s->st_uid) return u?0:-1;
		for(i=0;i<ngrp;i++)
			if(groups[i] == s->st_gid) return g?0:-1;
		return o?0:-1;
	}

	if(flags & X_OK) 
	{
		flags &= ~X_OK;
		u = s->st_mode & S_IXUSR;
		g = s->st_mode & S_IXGRP;
		o = s->st_mode & S_IXOTH;

		if(user  == s->st_uid) return u?0:-1;
		for(i=0;i<ngrp;i++)
			if(groups[i] == s->st_gid) return g?0:-1;
		return o?0:-1;
	}

	if(flags != 0) 
		marslog(LOG_WARN,"saccess: bad flags");
	return 0;
}

int faccess(const char *fname,int flags)
{
	struct stat s;
	if(stat(fname,&s)) return -1;
	return saccess(&s,flags);
}

const char *lowcase(const char *p)
{
	static char buf[10240];
	int i = 0;
	while(*p)
	{
		if(isupper(*p)) 
			buf[i] = tolower(*p);
		else 
			buf[i] = *p;
		i++;
		p++;
	}
	buf[i] = 0;
	return buf;
}

const char *upcase(const char *p)
{
	static char buf[10240];
	int i = 0;
	while(*p)
	{
		if(islower(*p)) 
			buf[i] = toupper(*p);
		else 
			buf[i] = *p;
		i++;
		p++;
	}
	buf[i] = 0;
	return buf;
}

static int cmpstringp(const void *p1, const void *p2)
{
	return strcmp(* (char * const *) p1, * (char * const *) p2);
}

void print_environment(FILE *f)
{

#if defined(__STDC__)
#if !defined( __alpha ) && !defined( __APPLE__ )
#  define environ _environ
#endif
#endif

	extern char **environ;
	int n = 0;
	int s = 0;
	while(environ[s]) s++;

	qsort(environ, s, sizeof(char *), cmpstringp);

	while(environ[n])
		fprintf(f,"%s\n",environ[n++]);
}

/* Creates an e-mail to 'to' with 'subject' and a timestamp */
FILE* mail_open(const char* to, char *fmt,...)
{
	FILE  *f = 0;
	char  buf[1024];
	char subject[512];
	va_list list;

	va_start(list,fmt);
	vsprintf(subject, fmt, list);
	va_end(list);

	if(to)
		sprintf(buf,"%s -s '%s' %s",mars.mailer,subject,to);
	else
		marslog(LOG_EROR|LOG_EXIT,"MARS internal error. Mail recipient not specified");

	marslog(LOG_DBUG,"Seding email with command '%s'",buf);
	if( (f = popen(buf,"w")) ) {
		char timestamp[80];
		time_t now;
		char host[1024];

		if(gethostname(host,sizeof(host)) != 0)
			strcpy(host,"unknown");

		/* Timestamp */
		time(&now);
		strftime(timestamp,sizeof(timestamp),"%Y-%m-%d %H:%M:%S",gmtime(&now));
		fprintf(f,"Mail sent on %s from %s\n\n",timestamp,host);

	} else {
		marslog(LOG_EROR|LOG_PERR,"popen: '%s'",buf);
		f = stdout;
	}

	return f;		
}

void notify_user(const char *what, const request *r)
{
#if 0
	char message[256];
	FILE* f;
	char *subject;

	sprintf(message,"%s/chk/%s.mail",mars.mars_home,what);

	f = mail_once(what, user,"%s",subject);
#endif
}

FILE* mail_once(const char *mark, const char* to, char *fmt,...)
{
	FILE *f = 0;
	char markfile[256];
	time_t last = 0;
	boolean send = true;
			

	if(!mars.home)
		return 0;

	sprintf(markfile,"%s/%s",mars.home,mark);

	last = age(markfile);	

	/* Send if mark does not exist or 
	   the user wants always an e-mail or
	   is old enough */
	send = (faccess(markfile,0) != 0) || 
		   (!mars.mail_frequency) || 
		   (last > mars.mail_frequency);
	if(send)
	{
		char subject[512];
		va_list list;
		

		va_start(list,fmt);
		vsprintf(subject, fmt, list);
		va_end(list);

		f = mail_open(to,subject);
		fprintf(f,"The frequency of this automatic message is controlled\n");
		fprintf(f,"with the environment variable MARS_MAIL_FREQUENCY.\n");
		fprintf(f,"Set it to 0 before calling MARS if you want to receive an\n"); 
		fprintf(f,"e-mail for each occurrence of such situation.\n");
		fprintf(f,"Otherwise, it is expressed in seconds\n");
		if(mars.mail_frequency > 0)
			fprintf(f,"Its current value is: %s \n\n",timename(mars.mail_frequency));
		else
			fprintf(f,"Its current value is: %ld\n\n",mars.mail_frequency);

		touch(markfile);
	}

	return f;
}

void mail_msg(FILE *f, char *fmt,...)
{
	if(f) {
		char buf[1024];
		va_list list;

		va_start(list,fmt);
		vsprintf(buf, fmt, list);
		va_end(list);

		fprintf(f,"%s\n",buf);
	}
}

void mail_request(FILE *f, const char *msg, const request *r)
{
	if(f) {	
		if(msg)
			fprintf(f,"\n%s\n",msg);
		save_all_requests(f,r);
	}
}

void mail_close(FILE *f)
{
	if(f) {
		fprintf(f,"\nUnix Environment:\n");
		print_environment(f);
		pclose(f);
	}
}

const char *real_name(const char* fname)
{
	char buf[1014];
	static char tmp[1024];
	char *c = buf;
	char *p;
	int last = 0,n;
	char dir[1024];
	char lasttmp[1024];

	if(*fname != '/')
	{
		getcwd(dir,1024);
		sprintf(tmp,"%s/%s",dir,fname);
	}
	else strcpy(tmp,fname);

	*lasttmp = 0;

	for(;;)
	{
		strcpy(buf,tmp);

		*tmp = 0;
		c = buf;
		last = 0;


		while( (p=strtok(c,"/")) )
		{

			c = NULL;
			if(strcmp(p,".") == 0) continue;

			if(strcmp(p,"..") == 0)
			{
				tmp[last] = 0;
				while(tmp[last] != '/') last--;
			}
			else
			{
				last = strlen(tmp);
				strcat(tmp,"/");
				strcat(tmp,p);
			}

			/* sun specific */

			while((n=readlink(tmp,dir,1024))>0)
			{
				dir[n] = 0;


				if(*dir != '/')
				{
					tmp[last] = 0;
					strcat(tmp,"/");
					strcat(tmp,dir);
				}
				else strcpy(tmp,dir);

				last = strlen(tmp);

				while(tmp[last] != '/') last--;
			}
			/* end sun specific */


		}

		if(strcmp(lasttmp,tmp)==0) break;
		strcpy(lasttmp,tmp);
	}

	return tmp;

}


void nfs_lookup(const char* fname, char *host, char *path)
{
	
#ifdef _MNT_ENT_
	FILE *f;
	char dir[1024];
	struct mntent *ent;
#endif
	char buf[1024];
	const char *rname;
	int max = 0;
	char *q;

	rname = real_name(fname);

#ifndef _MNT_ENT_

	strcpy(host,"");

#else
	f = setmntent("/etc/mtab","r");
	while(ent = getmntent(f))
	{

		int len = strlen(ent->mnt_dir);

		if(strcmp(ent->mnt_type,"ignore") != 0 &&
		    strncmp(ent->mnt_dir,rname,len)==0)
		{
			if(len>=max)
			{
				max = len;
				strcpy(host,ent->mnt_fsname);
				strcpy(dir,ent->mnt_dir);
			}
		}
	}
	endmntent(f);
#endif

	if((strtok(host,":")) && (q = strtok(NULL,":")))
	{
		if(*q == '/')
		{
			sprintf(buf,"%s%s",q,rname+max);
			rname = buf;
		}
	}
	else gethostname(host,80);

	strcpy(path,rname);
}

#ifdef sgi 

#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <netdb.h>
#include <sys/ioctl.h>
#include <net/if.h>


boolean if_check(const char* host)
{
	struct hostent *h = gethostbyname(host);  /* Incoming host */
	struct ifconf i;                          /* Interfaces configuration */
	int s = socket(AF_INET, SOCK_STREAM, 0);  /* Socket to get interfaces */

	int  buflen     = (sizeof(struct ifreq)*32); /* Maximum of 32 interfaces */
	char *ptr, *buf = reserve_mem(buflen);       /* Buffer to hold interfaces */
	boolean found   = false;
	err     ioctlerr = false;

	/* Initialize buf to avoid 'UMR: Uninitialized memory read'*/
	bzero(buf,buflen);

	/* Get list of all interfaces */
	i.ifc_len = buflen;
	i.ifc_req = (struct ifreq*)buf;
	if(ioctl(s,SIOCGIFCONF,&i) < 0)
	{
		marslog(LOG_WARN|LOG_PERR,"ioctl on ifconf request: ");
		return found;
	}

	/* Process inet address of all interfaces */
	for(ptr = buf; ptr < (buf + i.ifc_len); ptr += sizeof(struct ifreq))
	{
		struct ifreq flags;
		struct ifreq *ireq = (struct ifreq *)ptr;
		int len = 0;

		/* Check if interface is UP or DOWN */
		memcpy(flags.ifr_name,ireq->ifr_name,IFNAMSIZ);
		if(ioctl(s,SIOCGIFFLAGS,&flags) <0)
		{
			marslog(LOG_WARN|LOG_PERR,"ioctl on interface flags request: ");
			continue;
		}

		if( (flags.ifr_flags & IFF_UP) == 0)
		{
			marslog(LOG_DBUG,"Interface '%s' is down",flags.ifr_name);
			continue;
		}


#ifdef _HAVE_SA_LEN
		len = max(sizeof(struct sockaddr),ireq->ifr_addr.sa_len);
#else
		marslog(LOG_DBUG,"Cheking interface family %d for '%s'", ireq->ifr_addr.sa_family, ireq->ifr_name);
		switch(ireq->ifr_addr.sa_family)
		{
#ifdef IPV6
			case AF_INET6:
				len = sizeof(struct sockaddr_in6);
				break;
#endif
			case AF_INET:
				len = sizeof(struct sockaddr);
				break;

			default:
				marslog(LOG_DBUG,"Unknown interface family %d for '%s'", ireq->ifr_addr.sa_family, ireq->ifr_name);
				len = sizeof(struct sockaddr);
				break;
		}
#endif

		/* Get the flags for the interface to check if it is UP */
		memcpy(flags.ifr_name,ireq->ifr_name,IFNAMSIZ);
		memcpy(&flags.ifr_addr,&ireq->ifr_addr,len);
		if(ioctl(s,SIOCGIFFLAGS,&flags) <0)
		{
			marslog(LOG_WARN|LOG_PERR,"ioctl on interface flags request: ");
			ioctlerr = true;
		}

		if(!ioctlerr && (flags.ifr_flags & IFF_UP))
		{
			struct ifaliasreq alias;
			alias.cookie = 1;

			/* Process all aliases for this interface */
			while(alias.cookie != -1 && !found && !ioctlerr)
			{
				char **addr;
				struct in_addr *ain_addr = 0;

				/* Get inet address of alias for this interface */
				memcpy(alias.ifra_name,ireq->ifr_name,IFNAMSIZ);
				memcpy(&alias.ifra_addr,&ireq->ifr_addr,len);
				if(ioctl(s,SIOCLIFADDR,&alias) <0)
				{
					marslog(LOG_WARN|LOG_PERR,"ioctl on iterface alias request: ");
					ioctlerr = true;
				}
				else
				{
					/* Compare alias inet address with host's address list */
					ain_addr = &((struct sockaddr_in *)&alias.ifra_addr)->sin_addr;
					for(addr = h->h_addr_list; *addr; addr++)
					{
						if(memcmp(*addr,ain_addr,h->h_length) == 0) 
							found = true;
					}
				}
			}
		}
	}

	release_mem(buf);
	close(s);
	return found;
}

#else
boolean if_check(const char *p)
{
	return false;
}
#endif

void check_nfs_target(const char* path)
{
	static int done = 0;
	char remote[80]	;
	char me[80]	;
	char file[1024];

	gethostname(me,80);

	nfs_lookup(path,remote,file);

	if(!(EQ(me,remote) || if_check(remote))) 
	{
		log_statistics("target","nfs:%s",remote);
		if(!done && !getenv("MARS_DONT_DISPLAY_NFS"))
		{
			marslog(LOG_WARN,"");
			marslog(LOG_WARN,"The file '%s' is on the NFS server %s and",path,remote);
			marslog(LOG_WARN,"the data will be transfered twice over the network.");
			marslog(LOG_WARN,"First from the MARS server to %s, then from %s to %s.",me,me,remote);
			marslog(LOG_WARN,"NFS trafic is very, very slow, so if you retrieve a lot of data,");
			marslog(LOG_WARN,"you can either run your request on %s, or have a local target.",remote);
			marslog(LOG_WARN,"");
			done = 1;
		}
	}

}

/* This should be in the GribAPI. COME BACK HERE! */
long grib_length(const char *buffer, long length)
{

	long len;
	err e = NOERR;
	grib_handle *h = grib_handle_new_from_message_copy(0,buffer,length);
	if( (e = grib_get_long(h,"totalLength",&len)) )
	{
		marslog(LOG_WARN,"Cannot get totalLength for message");
	}
	grib_handle_delete(h);
	return len;

}

/*
Trick to avoid re-declaring all long length to fortint in mars source code
*/


#if 0
#if defined(__cplusplus) || defined(c_plusplus)
extern "C" {
#endif

extern fortint readgrib(FILE *,char*,fortint*);
extern fortint readbufr(FILE *,char*,fortint*);
extern fortint readany(FILE *,char*,fortint*);

#if defined(__cplusplus) || defined(c_plusplus)
}
#endif
#endif

void mars_grib_api_init()
{
	/* Set grib_api debug logging with marslog */
	grib_context_set_logging_proc(NULL, mars_grib_api_log);

	/* grib_api compatibility mode with GRIBEX on */
	grib_gribex_mode_on(NULL);
}


long _readany(FILE *f, char *b, long *l)
{
	size_t len = *l;
	long e =  wmo_read_any_from_file(f,(unsigned char*)b,&len);

    if(e && e != GRIB_END_OF_FILE) {
        marslog(LOG_EROR|LOG_PERR,"wmo_read_any_from_file: error %d (%s) l=%ld, len=%ld", e,
        grib_get_error_message(e), l, len);
    }

    *l = len;

    return e;
}

long _readgrib(FILE *f, char *b, long *l)
{
	size_t len = *l;
	long e =  wmo_read_grib_from_file(f,(unsigned char*)b,&len);
	*l = len;
	return e;
}

long _readbufr(FILE *f, char *b, long *l)
{
	size_t len = *l;
	long e =  wmo_read_bufr_from_file(f,(unsigned char*)b,&len);
	*l = len;
	return e;
}

void touch(const char* path)
{
	FILE *f = fopen(path,"a");
	if(!f)
	{
		marslog(LOG_WARN|LOG_PERR,"Cannot open '%s'",path);
		return;
	}
	if(fclose(f) == -1)
	{
		marslog(LOG_WARN|LOG_PERR,"Cannot close '%s'",path);
		return;
	}
}


time_t age(const char* path)
{
	time_t now;
	struct stat st;
	err    e = NOERR;

	if( (e = stat(path,&st)) )
	{
		return e;
	}
	time(&now);

	return now-st.st_mtime;
}

/* base: depending on what the buffer is used for, base could
		 be different: 
         - For GRIB headers, good size could be 32 Kb. 
         - For data transfers, good size could be 1 Mb
		 - Set base=0 in order to accept whatever the filesystem prefers
*/


long preferred_IO_blocksize(const char *path, long base)
{
	struct stat buf;

	if(stat(path,&buf))
	{
		marslog(LOG_WARN|LOG_PERR,"Cannot stat '%s'",path);
		return 1024*1024;    /* 1Mb by default */
	}

	if(buf.st_blksize == 0)
	{
		marslog(LOG_WARN,"stat(%s) does not return a block size",path);
		return 1024*1024;    /* 1Mb by default */
	}

	/* Take rounding max of preferred filesystem size and base */
	return ((base + (buf.st_blksize - 1))/buf.st_blksize)*buf.st_blksize;
}

double round_decimal(double f)
{
	double precision;
	/* Warning, gcc wants two lines here */
	double x ;
	precision = 1000.0;
	x = floor(precision * f + 0.5) / precision; 
	return x;
}

#ifdef NO_ATOLL_SUPPORT
long64 atoll(const char *str)
{
	long64 ll = 0;
	sscanf(str,"%lld",&ll);
	return ll;
}
#endif


void print_memory_usage(char *s)
{
	long long bytes = proc_mem();
	if(bytes)
	{
		if(s)
			marslog(LOG_INFO,"(%s) Memory used: %sbyte(s)",s,bytename((double)bytes));
		else
			marslog(LOG_INFO,"Memory used: %sbyte(s)",bytename((double)bytes));
	}
}


#ifndef ECMWF
void check_emoscycle()
{
	fortint off    = 1;
	long    ecycle = (long)emosnum_(&off);
	long    mcycle = marsemoscycle();

	if(ecycle < mcycle)
	{
		marslog(LOG_WARN,"LIBEMOS linked with this MARS client is cycle '%ld'",ecycle);
		marslog(LOG_WARN,"Ideal LIBEMOS for this version is cycle '%ld'",mcycle);
	}
}

void check_precision()
{
	long mprecision  = sizeof(fortfloat) * 8;
	long eprecision = emosPrecision();

	if(eprecision != mprecision)
	{
		marslog(LOG_WARN,"MARS and LIBEMOS have different precisions for reals");
		marslog(LOG_WARN,"MARS precision    : %ld",mprecision);
		marslog(LOG_WARN,"LIBEMOS precision : %ld",eprecision);
		marslog(LOG_WARN,"Note both packages need the same precision.");
	}
}

#endif
