root/src/servers/launch/SettingsParser.cpp
/*
 * Copyright 2015, Axel Dörfler, axeld@pinc-software.de.
 * Distributed under the terms of the MIT License.
 */


#include "SettingsParser.h"

#include <string.h>

#include <DriverSettingsMessageAdapter.h>


class AbstractArgsConverter : public DriverSettingsConverter {
public:
        status_t ConvertEmptyFromDriverSettings(
                const driver_parameter& parameter, const char* name, uint32 type,
                BMessage& target)
        {
                if (parameter.parameter_count != 0 || strcmp(name, Name()) == 0)
                        return B_OK;

                BMessage message;
                return target.AddMessage(name, &message);
        }

protected:
        status_t AddSubMessage(const driver_parameter& parameter, int32 index,
                BMessage& target)
        {
                const char* condition = parameter.values[index];
                BMessage args;
                for (index++; index < parameter.value_count; index++) {
                        status_t status = args.AddString("args",
                                parameter.values[index]);
                        if (status != B_OK)
                                return status;
                }
                return target.AddMessage(condition, &args);
        }

        virtual const char* Name() = 0;
};


class ConditionConverter : public AbstractArgsConverter {
public:
        status_t ConvertFromDriverSettings(const driver_parameter& parameter,
                const char* name, int32 index, uint32 type, BMessage& target)
        {
                BMessage message;
                if (strcmp(parameter.name, "if") == 0) {
                        // Parse values directly following "if", with special
                        // handling for the "not" operator.
                        if (index != 0)
                                return B_OK;

                        BMessage* add = &target;
                        bool notOperator = parameter.value_count > 1
                                && strcmp(parameter.values[0], "not") == 0;
                        if (notOperator) {
                                add = &message;
                                index++;
                        }

                        status_t status = AddSubMessage(parameter, index, *add);
                        if (status == B_OK && notOperator)
                                status = target.AddMessage("not", &message);

                        return status;
                }
                if (strcmp(parameter.name, "not") == 0) {
                        if (index != 0)
                                return B_OK;

                        return AddSubMessage(parameter, index, target);
                }

                message.AddString("args", parameter.values[index]);
                return target.AddMessage(parameter.name, &message);
        }

        const char* Name()
        {
                return "if";
        }
};


class EventConverter : public AbstractArgsConverter {
public:
        status_t ConvertFromDriverSettings(const driver_parameter& parameter,
                const char* name, int32 index, uint32 type, BMessage& target)
        {
                BMessage message;
                if (strcmp(parameter.name, "on") == 0) {
                        // Parse values directly following "on"
                        if (index != 0)
                                return B_OK;

                        return AddSubMessage(parameter, index, target);
                }

                message.AddString("args", parameter.values[index]);
                return target.AddMessage(parameter.name, &message);
        }

        const char* Name()
        {
                return "on";
        }
};


class RunConverter : public DriverSettingsConverter {
public:
        status_t ConvertFromDriverSettings(const driver_parameter& parameter,
                const char* name, int32 index, uint32 type, BMessage& target)
        {
                if (parameter.parameter_count == 0)
                        return target.AddString("target", parameter.values[index]);

                return B_NOT_SUPPORTED;
        }

        status_t ConvertEmptyFromDriverSettings(
                const driver_parameter& parameter, const char* name, uint32 type,
                BMessage& target)
        {
                if (parameter.parameter_count != 0)
                        return B_OK;

                return target.AddString("target", name);
        }
};


const static settings_template kConditionTemplate[] = {
        {B_STRING_TYPE, NULL, NULL, true, new ConditionConverter()},
        {B_MESSAGE_TYPE, "not", kConditionTemplate},
        {B_MESSAGE_TYPE, "and", kConditionTemplate},
        {B_MESSAGE_TYPE, "or", kConditionTemplate},
        {0, NULL, NULL}
};

const static settings_template kEventTemplate[] = {
        {B_STRING_TYPE, NULL, NULL, true, new EventConverter()},
        {B_MESSAGE_TYPE, "and", kEventTemplate},
        {B_MESSAGE_TYPE, "or", kEventTemplate},
        {0, NULL, NULL}
};

const static settings_template kPortTemplate[] = {
        {B_STRING_TYPE, "name", NULL, true},
        {B_INT32_TYPE, "capacity", NULL},
};

const static settings_template kEnvTemplate[] = {
        {B_STRING_TYPE, "from_script", NULL, true},
        {B_STRING_TYPE, NULL, NULL},
};

const static settings_template kJobTemplate[] = {
        {B_STRING_TYPE, "name", NULL, true},
        {B_BOOL_TYPE, "disabled", NULL},
        {B_STRING_TYPE, "launch", NULL},
        {B_STRING_TYPE, "requires", NULL},
        {B_BOOL_TYPE, "legacy", NULL},
        {B_MESSAGE_TYPE, "port", kPortTemplate},
        {B_MESSAGE_TYPE, "on", kEventTemplate},
        {B_MESSAGE_TYPE, "if", kConditionTemplate},
        {B_BOOL_TYPE, "no_safemode", NULL},
        {B_BOOL_TYPE, "on_demand", NULL},
        {B_MESSAGE_TYPE, "env", kEnvTemplate},
        {0, NULL, NULL}
};

const static settings_template kTargetTemplate[] = {
        {B_STRING_TYPE, "name", NULL, true},
        {B_BOOL_TYPE, "reset", NULL},
        {B_MESSAGE_TYPE, "on", kEventTemplate},
        {B_MESSAGE_TYPE, "if", kConditionTemplate},
        {B_BOOL_TYPE, "no_safemode", NULL},
        {B_MESSAGE_TYPE, "env", kEnvTemplate},
        {B_MESSAGE_TYPE, "job", kJobTemplate},
        {B_MESSAGE_TYPE, "service", kJobTemplate},
        {0, NULL, NULL}
};

const static settings_template kRunConditionalTemplate[] = {
        {B_STRING_TYPE, NULL, NULL, true, new RunConverter()},
        {0, NULL, NULL}
};

const static settings_template kRunTemplate[] = {
        {B_STRING_TYPE, NULL, NULL, true, new RunConverter()},
        {B_MESSAGE_TYPE, "if", kConditionTemplate},
        {B_MESSAGE_TYPE, "then", kRunConditionalTemplate},
        {B_MESSAGE_TYPE, "else", kRunConditionalTemplate},
        {0, NULL, NULL}
};

const static settings_template kSettingsTemplate[] = {
        {B_MESSAGE_TYPE, "target", kTargetTemplate},
        {B_MESSAGE_TYPE, "job", kJobTemplate},
        {B_MESSAGE_TYPE, "service", kJobTemplate},
        {B_MESSAGE_TYPE, "run", kRunTemplate},
        {0, NULL, NULL}
};


SettingsParser::SettingsParser()
{
}


status_t
SettingsParser::ParseFile(const char* path, BMessage& settings)
{
        DriverSettingsMessageAdapter adapter;
        return adapter.ConvertFromDriverSettings(path, kSettingsTemplate, settings);
}


#ifdef TEST_HAIKU


status_t
SettingsParser::Parse(const char* text, BMessage& settings)
{
        void* driverSettings = parse_driver_settings_string(text);
        if (driverSettings == NULL)
                return B_BAD_VALUE;

        DriverSettingsMessageAdapter adapter;
        status_t status = adapter.ConvertFromDriverSettings(
                *get_driver_settings(driverSettings), kSettingsTemplate, settings);

        unload_driver_settings(driverSettings);
        return status;
}


#endif  // TEST_HAIKU