/*
   Licensed to the Apache Software Foundation (ASF) under one or more
   contributor license agreements.  See the NOTICE file distributed with
   this work for additional information regarding copyright ownership.
   The ASF licenses this file to You under the Apache License, Version 2.0
   (the "License"); you may not use this file except in compliance with
   the License.  You may obtain a copy of the License at

       https://www.apache.org/licenses/LICENSE-2.0

   Unless required by applicable law or agreed to in writing, software
   distributed under the License is distributed on an "AS IS" BASIS,
   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   See the License for the specific language governing permissions and
   limitations under the License.
*/
#include "jsvc.h"

/* Check if a path is a directory */
static bool checkdir(char *path)
{
    struct stat home;

    if (path == NULL)
        return (false);
    if (stat(path, &home) != 0)
        return (false);
    if (S_ISDIR(home.st_mode))
        return (true);
    return (false);
}

/* Check if a path is a file */
static bool checkfile(char *path)
{
    struct stat home;

    if (path == NULL)
        return (false);
    if (stat(path, &home) != 0)
        return (false);
    if (S_ISREG(home.st_mode))
        return (true);
    return (false);
}

/* Parse a VM configuration file */
static bool parse(home_data *data)
{
    FILE *cfgf = fopen(data->cfgf, "r");
    char *ret = NULL, *sp;
    char buf[1024];

    if (cfgf == NULL) {
        log_debug("Can't open %s\n", data->cfgf);
        return (false);
    }

    data->jvms = (home_jvm **)malloc(256 * sizeof(home_jvm *));

    while ((ret = fgets(buf, 1024, cfgf)) != NULL) {
        char *tmp = strchr(ret, '#');
        int pos;

        /* Clear the string at the first occurrence of '#' */
        if (tmp != NULL)
            tmp[0] = '\0';

        /* Trim the string (including leading '-' chars */
        while ((ret[0] == ' ') || (ret[0] == '\t') || (ret[0] == '-'))
            ret++;
        pos = strlen(ret);
        while (pos >= 0) {
            if ((ret[pos] == '\r') || (ret[pos] == '\n') || (ret[pos] == '\t')
                || (ret[pos] == '\0') || (ret[pos] == ' ')) {
                ret[pos--] = '\0';
            }
            else
                break;
        }
        /* Format changed for 1.4 JVMs */
        sp = strchr(ret, ' ');
        if (sp != NULL)
            *sp = '\0';

        /* Did we find something significant? */
        if (strlen(ret) > 0) {
            char *libf = NULL;
            int x = 0;

            log_debug("Found VM %s definition in configuration", ret);
            while (location_jvm_configured[x] != NULL) {
                char *orig = location_jvm_configured[x];
                char temp[1024];
                char repl[1024];
                int k;

                k = replace(temp, 1024, orig, "$JAVA_HOME", data->path);
                if (k != 0) {
                    log_error("Can't replace home in VM library (%d)", k);
                    return (false);
                }
                k = replace(repl, 1024, temp, "$VM_NAME", ret);
                if (k != 0) {
                    log_error("Can't replace name in VM library (%d)", k);
                    return (false);
                }

                log_debug("Checking library %s", repl);
                if (checkfile(repl)) {
                    libf = strdup(repl);
                    break;
                }
                x++;
            }

            if (libf == NULL) {
                log_debug("Cannot locate library for VM %s (skipping)", ret);
            }
            else {
                data->jvms[data->jnum] = (home_jvm *)malloc(sizeof(home_jvm));
                data->jvms[data->jnum]->name = strdup(ret);
                data->jvms[data->jnum]->libr = libf;
                data->jnum++;
                data->jvms[data->jnum] = NULL;
            }
        }
    }
    fclose(cfgf);
    return (true);
}

/* Build a Java Home structure for a path */
static home_data *build(char *path)
{
    home_data *data = NULL;
    char *cfgf = NULL;
    char buf[1024];
    int x = 0;
    int k = 0;

    if (path == NULL)
        return (NULL);

    log_debug("Attempting to locate Java Home in %s", path);
    if (checkdir(path) == false) {
        log_debug("Path %s is not a directory", path);
        return (NULL);
    }

    while (location_jvm_cfg[x] != NULL) {
        if ((k = replace(buf, 1024, location_jvm_cfg[x], "$JAVA_HOME", path)) != 0) {
            log_error("Error replacing values for jvm.cfg (%d)", k);
            return (NULL);
        }
        log_debug("Attempting to locate VM configuration file %s", buf);
        if (checkfile(buf) == true) {
            log_debug("Found VM configuration file at %s", buf);
            cfgf = strdup(buf);
            break;
        }
        x++;
    }

    data = (home_data *)malloc(sizeof(home_data));
    data->path = strdup(path);
    data->cfgf = cfgf;
    data->jvms = NULL;
    data->jnum = 0;

    /* We don't have a jvm.cfg configuration file, so all we have to do is
       trying to locate the "default" Java Virtual Machine library */
    if (cfgf == NULL) {
        log_debug("VM configuration file not found");
        x = 0;
        while (location_jvm_default[x] != NULL) {
            char *libr = location_jvm_default[x];

            if ((k = replace(buf, 1024, libr, "$JAVA_HOME", path)) != 0) {
                log_error("Error replacing values for JVM library (%d)", k);
                return (NULL);
            }
            log_debug("Attempting to locate VM library %s", buf);
            if (checkfile(buf) == true) {
                data->jvms = (home_jvm **)malloc(2 * sizeof(home_jvm *));
                data->jvms[0] = (home_jvm *)malloc(sizeof(home_jvm));
                data->jvms[0]->name = NULL;
                data->jvms[0]->libr = strdup(buf);
                data->jvms[1] = NULL;
                data->jnum = 1;
                return (data);
            }
            x++;
        }

        return (data);
    }

    /* If we got here, we most definitely found a jvm.cfg file */
    if (parse(data) == false) {
        log_error("Cannot parse VM configuration file %s", data->cfgf);
    }

    return (data);
}

/* Find the Java Home */
static home_data *find(char *path)
{
    home_data *data = NULL;
    int x = 0;

    if (path == NULL || *path == '\0' || strcmp(path, "/") == 0) {
        log_debug("Home not specified on command line, using environment");
        path = getenv("JAVA_HOME");
        if (path == NULL || *path == '\0' || strcmp(path, "/") == 0) {
            /* guard against empty JAVA_HOME */
            path = NULL;
        }
    }

    if (path == NULL) {
        log_debug("Home not on command line or in environment, searching");
        while (location_home[x] != NULL) {
            if ((data = build(location_home[x])) != NULL) {
                log_debug("Java Home located in %s", data->path);
                return (data);
            }
            x++;
        }
    }
    else {
        if ((data = build(path)) != NULL) {
            log_debug("Java Home located in %s", data->path);
            return (data);
        }
    }

    return (NULL);
}

/* Main entry point: locate home and dump structure */
home_data *home(char *path)
{
    home_data *data = find(path);
    int x = 0;

    if (data == NULL) {
        log_error("Cannot locate Java Home");
        return (NULL);
    }

    if (log_debug_flag == true) {
        log_debug("+-- DUMPING JAVA HOME STRUCTURE ------------------------");
        log_debug("| Java Home:       \"%s\"", PRINT_NULL(data->path));
        log_debug("| Java VM Config.: \"%s\"", PRINT_NULL(data->cfgf));
        log_debug("| Found JVMs:      %d", data->jnum);
        for (x = 0; x < data->jnum; x++) {
            home_jvm *jvm = data->jvms[x];
            log_debug("| JVM Name:        \"%s\"", PRINT_NULL(jvm->name));
            log_debug("|                  \"%s\"", PRINT_NULL(jvm->libr));
        }
        log_debug("+-------------------------------------------------------");
    }

    return (data);
}
