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

#include "mars.h"
#include "mars_client_config.h"

int pproc_initialise(int argc, char** argv);

globals mars;

#define THEQUOTE(x) #x
#define QUOTE(x) THEQUOTE(x)

static option mars_opts[] = {

    {NULL,
     "MARS_HOME",
     NULL,
     ".",
     t_str,
     sizeof(char*),
     OFFSET(globals, mars_home)},

    {"debug",
     "MARS_DEBUG",
     "-debug",
     "0",
     t_boolean,
     sizeof(boolean),
     OFFSET(globals, debug)},

    {"nofork",
     "MARS_NOFORK",
     "-nofork",
     "0",
     t_boolean,
     sizeof(boolean),
     OFFSET(globals, nofork)},

    {"privileged",
     "MARS_PRIVILEGED",
     NULL,
     "0",
     t_boolean,
     sizeof(boolean),
     OFFSET(globals, privileged)},

    {"maxforks",
     "MARS_MAXFORKS",
     "-maxforks",
     "20",
     t_int,
     sizeof(int),
     OFFSET(globals, maxforks)},

    {"echo",
     "MARS_ECHO",
     "-echo",
     "0",
     t_boolean,
     sizeof(boolean),
     OFFSET(globals, echo)},

    {"dont_trap_signals",
     "MARS_DONT_TRAP_SIGNALS",
     "-dont_trap_signals",
     "0",
     t_boolean,
     sizeof(boolean),
     OFFSET(globals, dont_trap_signals)},

    {"quiet",
     NULL,
     "-quiet",
     "0",
     t_boolean,
     sizeof(boolean),
     OFFSET(globals, quiet)},

    {"config",
     "MARS_CONFIG",
     "-config",
     NULL,
     t_str,
     sizeof(char*),
     OFFSET(globals, config)},

    {"config_dev",
     "MARS_CONFIG_DEVELOPMENT",
     NULL,
     NULL,
     t_str,
     sizeof(char*),
     OFFSET(globals, config_dev)},

    {"emsfile",
     "MARS_EMS_FILE",
     "-emsfile",
     NULL,
     t_str,
     sizeof(char*),
     OFFSET(globals, emsfile)},

    {"authfile",
     "MARS_AUTH_FILE",
     "-authfile",
     "etc/mars.authentication",
     t_str,
     sizeof(char*),
     OFFSET(globals, authfile)},

    {"authmail",
     "MARS_AUTH_MAIL",
     "-authmail",
     "usu",
     t_str,
     sizeof(char*),
     OFFSET(globals, authmail)},

    {"mailer",
     "MARS_MAILER",
     "-mailer",
     "/usr/bin/mailx",
     t_str,
     sizeof(char*),
     OFFSET(globals, mailer)},

    {"monhost",
     "MARS_MONITOR_HOST",
     "-monhost",
     "aramis",
     t_str,
     sizeof(char*),
     OFFSET(globals, monhost)},

    {"monport",
     "MARS_MONITOR_PORT",
     "-monport",
     "8901",
     t_int,
     sizeof(int),
     OFFSET(globals, monport)},

    {"langfile",
     "MARS_LANGUAGE_FILE",
     "-langfile",
     "etc/mars.def",
     t_str,
     sizeof(char*),
     OFFSET(globals, langfile)},

    {"testfile",
     "MARS_TEST_FILE",
     "-testfile",
     "chk/mars.chk",
     t_str,
     sizeof(char*),
     OFFSET(globals, testfile)},

    {"logreqfile",
     "MARS_LOG_REQUEST_FILE",
     "-logreqfile",
     NULL,
     t_str,
     sizeof(char*),
     OFFSET(globals, logreqfile)},

    {"computeflg",
     "MARS_COMPUTE_FLAG",
     NULL,
     "255",
     t_int,
     sizeof(int),
     OFFSET(globals, computeflg)},

    {"certify",
     NULL,
     "-certify",
     "0",
     t_boolean,
     sizeof(boolean),
     OFFSET(globals, certify)},

    {"infomissing",
     "MARS_INFORM_MISSING",
     "-infomissing",
     "0",
     t_boolean,
     sizeof(boolean),
     OFFSET(globals, infomissing)},

    {"dhsmail",
     "MARS_DHS_MAIL",
     "-dhsmail",
     "max@ecmwf.int",
     t_str,
     sizeof(char*),
     OFFSET(globals, dhsmail)},
    {"y2k",
     "MARS_Y2K",
     "-y2k",
     "1",
     t_boolean,
     sizeof(boolean),
     OFFSET(globals, y2k)},

    {"y2k_problem",
     "MARS_Y2K_PROBLEM",
     "-y2k_problem",
     "0",
     t_long,
     sizeof(long),
     OFFSET(globals, y2k_problem)},

    {"y2k_fail",
     "MARS_Y2K_FAIL",
     "-y2k_fail",
     "1",
     t_boolean,
     sizeof(boolean),
     OFFSET(globals, y2k_fail)},

    {"crc",
     "MARS_CRC",
     "-crc",
     "0",
     t_boolean,
     sizeof(boolean),
     OFFSET(globals, crc)},

    {"ignore_7777",
     "MARS_IGNORE_7777",
     "-ignore_7777",
     "0",
     t_boolean,
     sizeof(boolean),
     OFFSET(globals, ignore_7777)},

    {"autoresol",
     "MARS_AUTO_RESOL",
     "-autoresol",
     "1",
     t_boolean,
     sizeof(boolean),
     OFFSET(globals, autoresol)},

    {"autoarch",
     "MARS_AUTO_ARCH",
     "-autoarch",
     "0",
     t_long,
     sizeof(long),
     OFFSET(globals, autoarch)},

    {"sms_label",
     "MARS_SMS_LABEL",
     "-sms_label",
     NULL,
     t_str,
     sizeof(char*),
     OFFSET(globals, sms_label)},

    {"patch_sst",
     "MARS_PATCH_SST",
     "-patch_sst",
     "0",
     t_boolean,
     sizeof(boolean),
     OFFSET(globals, patch_sst)},

    "accuracy",
    "MARS_PACK_BITS",
    "-accuracy",
    "0",
    t_int,
    sizeof(int),
    OFFSET(globals, accuracy),

    {"grib_missing_value",
     "MARS_MISSING_VALUE",
     "-missing_value",
#ifdef __linux__
     "3.40282347E+38F",
#else
     QUOTE(FLOAT_MISSING_VALUE),
#endif
     t_fortfloat,
     sizeof(fortfloat),
     OFFSET(globals, grib_missing_value)},

    {"warning",
     "MARS_WARNING",
     "-warning",
     "1",
     t_boolean,
     sizeof(boolean),
     OFFSET(globals, warning)},

    {"info",
     "MARS_INFO",
     "-info",
     "1",
     t_boolean,
     sizeof(boolean),
     OFFSET(globals, info)},

    {"so_sndbuf",
     "MARS_SO_SNDBUF",
     NULL,
     "0",
     t_int,
     sizeof(int),
     OFFSET(globals, so_sndbuf)},

    {"so_rcvbuf",
     "MARS_SO_RCVBUF",
     NULL,
     "0",
     t_int,
     sizeof(int),
     OFFSET(globals, so_rcvbuf)},

    {"tcp_maxseg",
     "MARS_TCP_MAXSEG",
     NULL,
     "0",
     t_int,
     sizeof(int),
     OFFSET(globals, tcp_maxseg)},

    {"tcp_window_clamp",
     "MARS_TCP_WINDOW_CLAMP",
     NULL,
     "0",
     t_int,
     sizeof(int),
     OFFSET(globals, tcp_window_clamp)},

    {"tcp_congestion",
     "MARS_TCP_CONGESTION",
     NULL,
     NULL,
     t_str,
     sizeof(char*),
     OFFSET(globals, tcp_congestion)},

    {"validate",
     "MARS_VALIDATE",
     "-validate",
#ifdef ECMWF
     "ecmwf",
#else
     "null",
#endif
     t_str,
     sizeof(char*),
     OFFSET(globals, validate)},

    {"clients",
     "MARS_CLIENTS",
     "-clients",
     "0",
     t_int,
     sizeof(int),
     OFFSET(globals, clients)},

    {"show_pid",
     "MARS_SHOW_PID",
     "-show_pid",
     "0",
     t_int,
     sizeof(int),
     OFFSET(globals, show_pid)},

    {"statfile",
     "MARS_STATISTICS_FILE",
     "-statfile",
     NULL,
     t_str,
     sizeof(char*),
     OFFSET(globals, statfile)},

    {"udp_server",
     "MARS_UDP_SERVER",
     "-udp_server",
     NULL, /* hostanme1:port1:hostname2:port2 */
     t_str,
     sizeof(char*),
     OFFSET(globals, udp_server)},

    {"mail_frequency",
     "MARS_MAIL_FREQUENCY",
     "-mail_frequency",
     "0",
     t_long,
     sizeof(long),
     OFFSET(globals, mail_frequency)},

    {"home",
     "MARS_USER_HOME",
     "-home",
     NULL,
     t_str,
     sizeof(char*),
     OFFSET(globals, home)},

    {"max_filesize",
     "MARS_MAX_FILESIZE",
     "-max_filesize",
     "0",
     t_long64,
     sizeof(long64),
     OFFSET(globals, max_filesize)},

    {"maxretries",
     "MARS_MAX_RETRIEVE_RETRY",
     "-maxretries",
     "5",
     t_int,
     sizeof(int),
     OFFSET(globals, maxretries)},

    {"paramtable",
     "MARS_PARAM_TABLE",
     "-paramtable",
     "1",
     t_boolean,
     sizeof(boolean),
     OFFSET(globals, paramtable)},

    {"grib_postproc",
     "MARS_GRIB_POSTPROC",
     "-grib_postproc",
     "1",
     t_boolean,
     sizeof(boolean),
     OFFSET(globals, grib_postproc)},

    {"dont_check_pseudogrib",
     "MARS_DONT_CHECK_PSEUDOGRIB",
     "-dont_check_pseudogrib",
     "0",
     t_boolean,
     sizeof(boolean),
     OFFSET(globals, dont_check_pseudogrib)},

    {"bufr_empty_target",
     "MARS_BUFR_EMPTY_TARGET",
     "-bufr_empty_target",
     "1",
     t_boolean,
     sizeof(boolean),
     OFFSET(globals, bufr_empty_target)},

    {"mm_firstofmonth",
     "MARS_MM_FIRSTOFMONTH",
     "-mm_firstofmonth",
     "0",
     t_boolean,
     sizeof(boolean),
     OFFSET(globals, mm_firstofmonth)},

    {"use_intuvp",
     "MARS_USE_INTUVP",
     "-use_intuvp",
     "1",
     t_boolean,
     sizeof(boolean),
     OFFSET(globals, use_intuvp)},

    {"debug_ems",
     "MARS_DEBUG_EMS",
     "-debug_ems",
     "0",
     t_boolean,
     sizeof(boolean),
     OFFSET(globals, debug_ems)},

    {"can_do_vector_postproc",
     "MARS_CAN_DO_VECTOR_POSTPROC",
     "-can_do_vector_postproc",
     "1",
     t_boolean,
     sizeof(boolean),
     OFFSET(globals, can_do_vector_postproc)},

    {"gridded_observations_postproc",
     "MARS_GRIDDED_OBSERVATIONS_INTERP",
     "-gridded_observations_postproc",
     "0",
     t_boolean,
     sizeof(boolean),
     OFFSET(globals, gridded_observations_postproc)},


    {"proxies",
     "MARS_PROXIES",
     "-proxies",
     NULL,
     t_str,
     sizeof(char*),
     OFFSET(globals, proxies)},

    /*
    #define SCHEDULE_FAIL   0x01
    #define SCHEDULE_INFORM 0x02
    #define SCHEDULE_LOG    0x04
    #define SCHEDULE_INFORM_FUTURE_CHANGE    0x08
    #define SCHEDULE_MAIL   0x10

    *** After 1st of February ***
    SCHEDULE_FAIL | SCHEDULE_INFORM | SCHEDULE_LOG = 7
    SCHEDULE_FAIL | SCHEDULE_INFORM | SCHEDULE_LOG | SCHEDULE_MAIL = 23


    *** Before 1st of February ***
    SCHEDULE_INFORM_FUTURE_CHANGE | SCHEDULE_LOG = 12
    SCHEDULE_INFORM_FUTURE_CHANGE | SCHEDULE_LOG | SCHEDULE_MAIL = 28

    *** Other tests ****
    SCHEDULE_FAIL | SCHEDULE_INFORM_FUTURE_CHANGE | SCHEDULE_LOG = 13
    */

    {"dissemination_schedule",
     "MARS_DISSEMINATION_SCHEDULE",
     "-dissemination_schedule",
     "30",
     t_int,
     sizeof(int),
     OFFSET(globals, dissemination_schedule)},

    {"dissemination_schedule_file",
     "MARS_DISSEMINATION_SCHEDULE_FILE",
     "-dissemination_schedule_file",
     "etc/mars.dissemination.schedule.file",
     t_str,
     sizeof(char*),
     OFFSET(globals, dissemination_schedule_file)},

    {"daily_climatology",
     NULL,
     NULL,
     "0",
     t_boolean,
     sizeof(boolean),
     OFFSET(globals, daily_climatology)},

    {"no_special_fp",
     "MARS_FDB_NO_SPECIAL_FP",
     "-no_special_fp",
     "0",
     t_boolean,
     sizeof(boolean),
     OFFSET(globals, no_special_fp)},

    {"year_for_daily_climatology",
     "MARS_YEAR_DAILY_CLIMATOLOGY",
     "-year_for_daily_climatology",
     "2004",
     t_int,
     sizeof(int),
     OFFSET(globals, year_for_daily_climatology)},

    {"notimers",
     "MARS_NO_TIMERS",
     "-notimers",
     "0",
     t_boolean,
     sizeof(boolean),
     OFFSET(globals, notimers)},
    {"valid_data_includes_fcmonth",
     "MARS_VALID_DATA_INCLUDES_FCMONTH",
     NULL,
     "0",
     t_boolean,
     sizeof(boolean),
     OFFSET(globals, valid_data_includes_fcmonth)},

    {"valid_data_includes_fcmonth",
     "MARS_VALID_DATA_INCLUDES_FCMONTH",
     NULL,
     "0",
     t_boolean,
     sizeof(boolean),
     OFFSET(globals, valid_data_includes_fcmonth)},

    {"exit_on_failed_expand",
     "MARS_EXIT_ON_FAILED_EXPAND",
     NULL,
     "0",
     t_boolean,
     sizeof(boolean),
     OFFSET(globals, exit_on_failed_expand)},

    {"emsaccountsfile",
     "MARS_EMS_ACCOUNTS_FILE",
     "-emsaccountsfile",
     NULL,
     t_str,
     sizeof(char*),
     OFFSET(globals, emsaccountsfile)},

    {"force_setting_ecaccount",
     "MARS_FORCE_SETTING_ECACCOUNT",
     NULL,
     "0",
     t_boolean,
     sizeof(boolean),
     OFFSET(globals, force_setting_ecaccount)},

    {"enable_ecaccount_email",
     "MARS_ENABLE_ECACCOUNT_EMAIL",
     NULL,
     "1",
     t_boolean,
     sizeof(boolean),
     OFFSET(globals, enable_ecaccount_email)},

    {"timers_file",
     "MARS_TIMERS_FILE",
     NULL,
     NULL,
     t_str,
     sizeof(char*),
     OFFSET(globals, timers_file)},

    {"readdisk_buffer",
     "MARS_READDISK_BUFFER",
     NULL,
     "0",
     t_int,
     sizeof(int),
     OFFSET(globals, readdisk_buffer)},

    {"ecflow_label",
     "MARS_ECFLOW_LABEL",
     "-ecflow_label",
     NULL,
     t_str,
     sizeof(char*),
     OFFSET(globals, ecflow_label)},

    {"private_key",
     "MARS_PRIVATE_KEY",
     NULL,
     NULL,
     t_str,
     sizeof(char*),
     OFFSET(globals, private_key)},

    {"build_grib_index",
     "MARS_BUILD_GRIB_INDEX",
     NULL,
     "0",
     t_boolean,
     sizeof(boolean),
     OFFSET(globals, build_grib_index)},

    {"readany_buffer_size",
     "MARS_READANY_BUFFER_SIZE",
     NULL,
     "67108864",
     t_long64,
     sizeof(long64),
     OFFSET(globals, readany_buffer_size)},

    {"webmars_target",
     "WEBMARS_TARGET",
     "-webmars_target",
     NULL,
     t_str,
     sizeof(char*),
     OFFSET(globals, webmars_target)},

    {"show_hosts",
     "MARS_SHOW_HOSTS",
     NULL,
     "1",
     t_boolean,
     sizeof(boolean),
     OFFSET(globals, show_hosts)},

    {"marslite_mode",
     "MARS_MARSLITE_MODE",
     NULL,
     "0",
     t_boolean,
     sizeof(boolean),
     OFFSET(globals, marslite_mode)},

    {"enforce_ems_category",
     "MARS_ENFORCE_EMS_CATEGORY",
     NULL,
     "1",
     t_boolean,
     sizeof(boolean),
     OFFSET(globals, enforce_ems_category)},

    {NULL,
     "MARS_EXPAND_TIMEOUT",
     NULL,
     "0",
     t_int,
     sizeof(int),
     OFFSET(globals, expand_timeout)},

    {NULL,
     "MARS_AUTO_SPLIT_BY_DATES",
     NULL,
     "0",
     t_boolean,
     sizeof(boolean),
     OFFSET(globals, auto_split_by_dates)},

    {NULL,
     "MARS_AUTO_SPLIT_BY_DAY",
     NULL,
     "0",
     t_boolean,
     sizeof(boolean),
     OFFSET(globals, auto_split_by_day)},

    {NULL,
     "WEBMARS_KEEP_DATABASE",
     NULL,
     "0",
     t_boolean,
     sizeof(boolean),
     OFFSET(globals, keep_database)},

    {"webmars_request_id",
     "WEBMARS_REQUEST_ID",
     NULL,
     NULL,
     t_str,
     sizeof(char*),
     OFFSET(globals, webmars_request_id)},

    {NULL,
     "MARS_MAX_RETRIEVE_SIZE",
     NULL,
     "0",
     t_long64,
     sizeof(long64),
     OFFSET(globals, max_retrieve_size)},


};

static int ac    = 0;
static char** av = NULL;
static char me[20];

static char* cmdopt(const char* clss, const char* name, const char* opt, boolean isbool) {
    int i;

    if (!av)
        return NULL;

    for (i = 0; i < ac; i++)
        if (EQ(opt, av[i])) {
            if (isbool)
                return "1";
            else
                return av[i + 1];
        }

    return NULL;
}

static const char* config(const char* clss, const char* name, const char* opt, boolean random) {
    request* r = mars.setup;

    while (r) {
        const char* c = get_value(r, "class", 0);
        const char* n = get_value(r, "name", 0);

        if (clss && c && (EQ(clss, c)))
            if ((name == NULL) || (n != NULL && EQ(name, n))) {
                int i = 0;
                int n = count_values(r, opt);
                if (n == 0) {
                    return NULL;
                }
                if (random) {
                    i = rand() % n;
                }
                return no_quotes(get_value(r, opt, i));
            }
        r = r->next;
    }
    return NULL;
}

static const char* get_opt_value(const char* clss, const char* name, option* opt, boolean random) {
    const char* val = opt->def; /* default is default, of course */
    const char* p;


    /* then, from config file  */

    if (opt->name && (p = config(clss, name, opt->name, random)))
        val = p;


    /* then from env */

    if (opt->env && (p = getenv(opt->env)))
        val = p;


    /* then from command options  */

    if (opt->opt && (p = cmdopt(clss, name, opt->opt, (boolean)(opt->kind == t_boolean))))
        val = p;


    return val;
}

#ifdef NO_ATOLL_SUPPORT
extern long64 atoll(const char*);
#endif

void get_options(const char* clss, const char* name, void* addr, int count, option opts[]) {
    int i;
    int size;

    union {
        char c;
        int i;
        long l;
        long64 ll;
        char* s;
        void* p;
        double d;
        boolean b;
        fortfloat f;
    } u;

    for (i = 0; i < count; i++) {
        switch (opts[i].kind) {
            case t_char:
                u.c  = *get_opt_value(clss, name, &opts[i], false);
                size = sizeof(char);
                break;

            case t_int:
                u.i  = atoi(get_opt_value(clss, name, &opts[i], false));
                size = sizeof(int);
                break;

            case t_long:
                u.l  = atol(get_opt_value(clss, name, &opts[i], false));
                size = sizeof(long);
                break;

            case t_long64:
                u.ll = atoll(get_opt_value(clss, name, &opts[i], false));
                size = sizeof(long64);
                break;

            case t_str:
            case t_str_random:
                u.s  = strcache(get_opt_value(clss, name, &opts[i], opts[i].kind == t_str_random));
                size = sizeof(char*);
                break;

            case t_ptr:
                u.p = NULL;
                marslog(LOG_WARN, "t_ptr options are not implemented.");
                size = sizeof(void*);
                break;

            case t_double:
                u.d  = atof(get_opt_value(clss, name, &opts[i], false));
                size = sizeof(double);
                break;

            case t_boolean:
                u.b  = (boolean)(atoi(get_opt_value(clss, name, &opts[i], false)) != 0);
                size = sizeof(boolean);
                break;

            case t_fortfloat:
                u.f  = (fortfloat)atof(get_opt_value(clss, name, &opts[i], false));
                size = sizeof(fortfloat);
                break;

            default:
                marslog(LOG_EXIT, "Error in var_options type=%d",
                        opts[i].kind);
                break;
        }

        if (size != opts[i].size)
            marslog(LOG_EXIT,
                    "Error in var_options size %d != %d (type=%d)",
                    size, opts[i].size, opts[i].kind);

        memcpy((char*)addr + opts[i].offset, (char*)&u, opts[i].size);
    }
}

char* progname(void) {
    return me;
}

static void trap(int n
#if defined(__cplusplus) || defined(c_plusplus)
                 ,
                 ...
#endif

) {
    marslog(LOG_EXIT, "Signal %d received", n);
}

static void toggle_debug(int n
#if defined(__cplusplus) || defined(c_plusplus)
                         ,
                         ...
#endif
) {
    marslog(LOG_INFO, "Toggeling debugging");
    mars.debug = (boolean)!mars.debug;
    signal(n, toggle_debug);
}

void trap_all_signals() {
    signal(SIGINT, trap);
    signal(SIGHUP, trap);
    signal(SIGQUIT, trap);
    signal(SIGTERM, trap);
    signal(SIGALRM, trap);

#if 0
	signal(SIGBUS, trap);
	signal(SIGSEGV, trap);
#endif

#ifndef __i386__
    signal(SIGSYS, trap);
#endif

    signal(SIGUSR2, toggle_debug);

    /* don't trap SIGCHLD, otherwise pclose won't return a usable value */
    /* signal(SIGCHLD,SIG_IGN); */
}

void marsinit(int* argc, char** argv, void* addr, int count, option opts[]) {
    char* p;
    int e;
    static int done = 0;
    if (done)
        return;
    done = 1;

    srand(getpid());

    strncpy(me, mbasename(argv[0]), 19);
    me[19] = 0;
    p      = me;
    while (*p) {
        if (*p == '.')
            *p = 0;
        p++;
    }


    ac = *argc - 1;
    av = argv + 1;


    mars.expflags    = EXPAND_MARS;
    mars.restriction = false;

    get_options("application", me, &mars, NUMBER(mars_opts), mars_opts);

    if (mars.config_dev)
        mars.setup = read_request_file(mars.config_dev);
    else {
        if (mars.config)
            mars.setup = read_request_file(mars.config);
    }

    get_options("application", me, &mars, NUMBER(mars_opts), mars_opts);

    get_options("application", me, addr, count, opts);

    if (!mars.dont_trap_signals) {
        trap_all_signals();
    }

    /* read config */


    /* if(mars.debug) */
    /* print_all_requests(mars.setup); */

    mars.appl = strcache(mbasename(me));
#ifdef sun_bsd
    on_exit(_marsexit, NULL);
#else
    atexit(_marsexit);
#endif

    /* Initialise grib_api settings */
    mars_grib_api_init();

    /* Initialise fdb5 settings */
    mars_fdb5_init(argc, argv);

    e = pproc_initialise(*argc, argv);
    if (e != NOERR) {
        marsexit(e);
    }

#if mars_client_HAVE_UDP_STATS
    mars_eckit_init(argc, argv);
#endif
}

const char* config_file(const char* p) {
    static char path[1024];
    if (*p != '~')
        return p;
    sprintf(path, "%s/%s", mars.mars_home, p + 1);
    return path;
}
