/*
 * © 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 "mars_client_config.h"

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

#include <sys/param.h>

#include <dirent.h>
#include <sys/time.h>
#include <sys/types.h>
#include <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    = NULL;
    struct passwd* pw = NULL;

    if (s) {
        u = strcache(s);
    }

    if (u) {
        marslog(LOG_DBUG, "Cached user is [%s]", u);
        return u;
    }

#if mars_client_HAVE_MARS_USER_ENVIRONMENT
    const char* p = getenv("MARS_USER");
    if (p) {
        u = strcache(p);
        marslog(LOG_DBUG, "$MARS_USER user is [%s]", u);
        return u;
    }
#endif

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

    endpwent();

    marslog(LOG_DBUG, "User is [%s]", pw->pw_name);

    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 = mars_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)) < 0) {
        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)) < 0) {
        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 = mars_copyfile(buf1, buf2);
        }
    }
    closedir(d);
    return e;
}

int mars_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 mars_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 <arpa/inet.h>
#include <net/if.h>
#include <netdb.h>
#include <netinet/in.h>
#include <sys/ioctl.h>
#include <sys/socket.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)) < 0) {
        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));
    }
}
