/*
 * Copyright 2002 Red Hat Inc., Durham, North Carolina.
 *
 * All Rights Reserved.
 *
 * Permission is hereby granted, free of charge, to any person obtaining
 * a copy of this software and associated documentation files (the
 * "Software"), to deal in the Software without restriction, including
 * without limitation on the rights to use, copy, modify, merge,
 * publish, distribute, sublicense, and/or sell copies of the Software,
 * and to permit persons to whom the Software is furnished to do so,
 * subject to the following conditions:
 *
 * The above copyright notice and this permission notice (including the
 * next paragraph) shall be included in all copies or substantial
 * portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
 * NON-INFRINGEMENT.  IN NO EVENT SHALL RED HAT AND/OR THEIR SUPPLIERS
 * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
 * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
 * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 * SOFTWARE.
 */

/*
 * Authors:
 *   Rickard E. (Rik) Faith <faith@redhat.com>
 *
 */

/** \file
 *
 * This file provides support routines and helper functions to be used
 * by the DMX configuration file parser.
 *
 * Because the DMX configuration file parsing should be capable of being
 * used in a stand-alone fashion (i.e., independent from the DMX server
 * source tree), no dependencies on other DMX routines are made. */

#ifdef HAVE_DMX_CONFIG_H
#include <dmx-config.h>
#endif

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdarg.h>
#include "dmxparse.h"

/** A general error logging routine that does not depend on the dmxLog
 * functions. */
void
dmxConfigLog(const char *format, ...)
{
    va_list args;

    va_start(args, format);
    vprintf(format, args);      /* RATS: All calls to dmxConfigLog from
                                 * dmxparse.c and dmxprint.c use a
                                 * trusted format. */
    va_end(args);
}

void *
dmxConfigAlloc(unsigned long bytes)
{
    void *area = calloc(1, bytes);

    if (!area) {
        dmxConfigLog("dmxConfigAlloc: out of memory\n");
        return NULL;
    }
    return area;
}

void *
dmxConfigRealloc(void *orig, unsigned long orig_bytes, unsigned long bytes)
{
    unsigned char *area = realloc(orig, bytes);

    if (!area) {
        dmxConfigLog("dmxConfigRealloc: out of memory\n");
        return NULL;
    }
    memset(area + orig_bytes, 0, bytes - orig_bytes);
    return area;
}

const char *
dmxConfigCopyString(const char *string, int length)
{
    char *copy;

    if (!length)
        length = strlen(string);
    copy = dmxConfigAlloc(length + 1);
    if (length)
        strncpy(copy, string, length);
    copy[length] = '\0';
    return copy;
}

void
dmxConfigFree(void *area)
{
    free(area);
}

DMXConfigTokenPtr
dmxConfigCreateToken(int token, int line, const char *comment)
{
    DMXConfigTokenPtr pToken = dmxConfigAlloc(sizeof(*pToken));

    pToken->token = token;
    pToken->line = line;
    pToken->comment = comment;
    return pToken;
}

void
dmxConfigFreeToken(DMXConfigTokenPtr p)
{
    if (!p)
        return;
    dmxConfigFree((void *) p->comment);
    dmxConfigFree(p);
}

DMXConfigStringPtr
dmxConfigCreateString(int token, int line,
                      const char *comment, const char *string)
{
    DMXConfigStringPtr pString = dmxConfigAlloc(sizeof(*pString));

    pString->token = token;
    pString->line = line;
    pString->comment = comment;
    pString->string = string;
    return pString;
}

void
dmxConfigFreeString(DMXConfigStringPtr p)
{
    DMXConfigStringPtr next;

    if (!p)
        return;
    do {
        next = p->next;
        dmxConfigFree((void *) p->comment);
        dmxConfigFree((void *) p->string);
        dmxConfigFree(p);
    } while ((p = next));
}

DMXConfigNumberPtr
dmxConfigCreateNumber(int token, int line, const char *comment, int number)
{
    DMXConfigNumberPtr pNumber = dmxConfigAlloc(sizeof(*pNumber));

    pNumber->token = token;
    pNumber->line = line;
    pNumber->comment = comment;
    pNumber->number = number;
    return pNumber;
}

void
dmxConfigFreeNumber(DMXConfigNumberPtr p)
{
    if (!p)
        return;
    dmxConfigFree((void *) p->comment);
    dmxConfigFree(p);
}

DMXConfigPairPtr
dmxConfigCreatePair(int token, int line,
                    const char *comment, int x, int y, int xsign, int ysign)
{
    DMXConfigPairPtr pPair = dmxConfigAlloc(sizeof(*pPair));

    pPair->token = token;
    pPair->line = line;
    pPair->comment = comment;
    pPair->x = x;
    pPair->y = y;
    pPair->xsign = (xsign < 0) ? -1 : 1;
    pPair->ysign = (ysign < 0) ? -1 : 1;
    return pPair;
}

void
dmxConfigFreePair(DMXConfigPairPtr p)
{
    if (!p)
        return;
    dmxConfigFree((void *) p->comment);
    dmxConfigFree(p);
}

DMXConfigCommentPtr
dmxConfigCreateComment(int token, int line, const char *comment)
{
    DMXConfigCommentPtr pComment = dmxConfigAlloc(sizeof(*pComment));

    pComment->token = token;
    pComment->line = line;
    pComment->comment = comment;
    return pComment;
}

void
dmxConfigFreeComment(DMXConfigCommentPtr p)
{
    if (!p)
        return;
    dmxConfigFree((void *) p->comment);
    dmxConfigFree(p);
}

DMXConfigPartDimPtr
dmxConfigCreatePartDim(DMXConfigPairPtr pDim, DMXConfigPairPtr pOffset)
{
    DMXConfigPartDimPtr pPart = dmxConfigAlloc(sizeof(*pPart));

    pPart->dim = pDim;
    pPart->offset = pOffset;
    return pPart;
}

void
dmxConfigFreePartDim(DMXConfigPartDimPtr p)
{
    if (!p)
        return;
    dmxConfigFreePair(p->dim);
    dmxConfigFreePair(p->offset);
    dmxConfigFree(p);
}

DMXConfigFullDimPtr
dmxConfigCreateFullDim(DMXConfigPartDimPtr pScrn, DMXConfigPartDimPtr pRoot)
{
    DMXConfigFullDimPtr pFull = dmxConfigAlloc(sizeof(*pFull));

    pFull->scrn = pScrn;
    pFull->root = pRoot;
    return pFull;
}

void
dmxConfigFreeFullDim(DMXConfigFullDimPtr p)
{
    if (!p)
        return;
    dmxConfigFreePartDim(p->scrn);
    dmxConfigFreePartDim(p->root);
    dmxConfigFree(p);
}

DMXConfigDisplayPtr
dmxConfigCreateDisplay(DMXConfigTokenPtr pStart,
                       DMXConfigStringPtr pName,
                       DMXConfigFullDimPtr pDim,
                       DMXConfigPairPtr pOrigin, DMXConfigTokenPtr pEnd)
{
    DMXConfigDisplayPtr pDisplay = dmxConfigAlloc(sizeof(*pDisplay));

    pDisplay->start = pStart;
    pDisplay->dname = pName;
    pDisplay->dim = pDim;
    pDisplay->origin = pOrigin;
    pDisplay->end = pEnd;

    pDisplay->name = pName ? pName->string : NULL;
    pDisplay->rootXOrigin = pOrigin ? pOrigin->x : 0;
    pDisplay->rootYOrigin = pOrigin ? pOrigin->y : 0;

    if (pDim && pDim->scrn && pDim->scrn->dim) {
        pDisplay->scrnWidth = pDim->scrn->dim->x;
        pDisplay->scrnHeight = pDim->scrn->dim->y;
    }
    if (pDim && pDim->scrn && pDim->scrn->offset) {
        pDisplay->scrnX = pDim->scrn->offset->x;
        pDisplay->scrnY = pDim->scrn->offset->y;
        pDisplay->scrnXSign = pDim->scrn->offset->xsign;
        pDisplay->scrnYSign = pDim->scrn->offset->ysign;
    }

    if (pDim && pDim->root) {
        if (pDim->root->dim) {
            pDisplay->rootWidth = pDim->root->dim->x;
            pDisplay->rootHeight = pDim->root->dim->y;
        }
        if (pDim->root->offset) {
            pDisplay->rootX = pDim->root->offset->x;
            pDisplay->rootY = pDim->root->offset->y;
            pDisplay->rootXSign = pDim->root->offset->xsign;
            pDisplay->rootYSign = pDim->root->offset->ysign;
        }
    }
    else {                      /* If no root specification, copy width
                                 * and height from scrn -- leave offset
                                 * as zero, since it is relative to
                                 * scrn. */
        pDisplay->rootWidth = pDisplay->scrnWidth;
        pDisplay->rootHeight = pDisplay->scrnHeight;
    }

    return pDisplay;
}

void
dmxConfigFreeDisplay(DMXConfigDisplayPtr p)
{
    if (!p)
        return;
    dmxConfigFreeToken(p->start);
    dmxConfigFreeString(p->dname);
    dmxConfigFreeFullDim(p->dim);
    dmxConfigFreeToken(p->end);
    dmxConfigFree(p);
}

DMXConfigWallPtr
dmxConfigCreateWall(DMXConfigTokenPtr pStart,
                    DMXConfigPairPtr pWallDim,
                    DMXConfigPairPtr pDisplayDim,
                    DMXConfigStringPtr pNameList, DMXConfigTokenPtr pEnd)
{
    DMXConfigWallPtr pWall = dmxConfigAlloc(sizeof(*pWall));

    pWall->start = pStart;
    pWall->wallDim = pWallDim;
    pWall->displayDim = pDisplayDim;
    pWall->nameList = pNameList;
    pWall->end = pEnd;

    pWall->width = pDisplayDim ? pDisplayDim->x : 0;
    pWall->height = pDisplayDim ? pDisplayDim->y : 0;
    pWall->xwall = pWallDim ? pWallDim->x : 0;
    pWall->ywall = pWallDim ? pWallDim->y : 0;

    return pWall;
}

void
dmxConfigFreeWall(DMXConfigWallPtr p)
{
    if (!p)
        return;
    dmxConfigFreeToken(p->start);
    dmxConfigFreePair(p->wallDim);
    dmxConfigFreePair(p->displayDim);
    dmxConfigFreeString(p->nameList);
    dmxConfigFreeToken(p->end);
    dmxConfigFree(p);
}

DMXConfigOptionPtr
dmxConfigCreateOption(DMXConfigTokenPtr pStart,
                      DMXConfigStringPtr pOption, DMXConfigTokenPtr pEnd)
{
    int length = 0;
    int offset = 0;
    DMXConfigStringPtr p;
    DMXConfigOptionPtr option = dmxConfigAlloc(sizeof(*option));

    for (p = pOption; p; p = p->next) {
        if (p->string)
            length += strlen(p->string) + 1;
    }

    option->string = dmxConfigAlloc(length + 1);

    for (p = pOption; p; p = p->next) {
        if (p->string) {
            int len = strlen(p->string);

            strncpy(option->string + offset, p->string, len);
            offset += len;
            if (p->next)
                option->string[offset++] = ' ';
        }
    }
    option->string[offset] = '\0';

    option->start = pStart;
    option->option = pOption;
    option->end = pEnd;

    return option;
}

void
dmxConfigFreeOption(DMXConfigOptionPtr p)
{
    if (!p)
        return;
    free(p->string);
    dmxConfigFreeToken(p->start);
    dmxConfigFreeString(p->option);
    dmxConfigFreeToken(p->end);
    dmxConfigFree(p);
}

const char **
dmxConfigLookupParam(DMXConfigParamPtr p, const char *key, int *argc)
{
    DMXConfigParamPtr pt;

    for (pt = p; pt; pt = pt->next) {
        if (pt->argv && !strcasecmp(pt->argv[0], key)) {
            *argc = pt->argc;
            return pt->argv;
        }
    }
    *argc = 0;
    return NULL;
}

DMXConfigParamPtr
dmxConfigCreateParam(DMXConfigTokenPtr pStart,
                     DMXConfigTokenPtr pOpen,
                     DMXConfigStringPtr pParam,
                     DMXConfigTokenPtr pClose, DMXConfigTokenPtr pEnd)
{
    DMXConfigParamPtr param = dmxConfigAlloc(sizeof(*param));
    DMXConfigStringPtr pt;

    param->argc = 0;
    param->argv = NULL;
    for (pt = pParam; pt; pt = pt->next) {
        if (pt->string) {
            param->argv = realloc(param->argv,
                                  (param->argc + 2) * sizeof(*param->argv));
            param->argv[param->argc] = pt->string;
            ++param->argc;
        }
    }
    if (param->argv)
        param->argv[param->argc] = NULL;

    param->start = pStart;
    param->open = pOpen;
    param->param = pParam;
    param->close = pClose;
    param->end = pEnd;

    return param;
}

void
dmxConfigFreeParam(DMXConfigParamPtr p)
{
    DMXConfigParamPtr next;

    if (!p)
        return;
    do {
        next = p->next;
        dmxConfigFreeToken(p->start);
        dmxConfigFreeToken(p->open);
        dmxConfigFreeString(p->param);
        dmxConfigFreeToken(p->close);
        dmxConfigFreeToken(p->end);
        dmxConfigFree(p->argv);
        dmxConfigFree(p);
    } while ((p = next));
}

DMXConfigSubPtr
dmxConfigCreateSub(DMXConfigType type,
                   DMXConfigCommentPtr comment,
                   DMXConfigDisplayPtr display,
                   DMXConfigWallPtr wall,
                   DMXConfigOptionPtr option, DMXConfigParamPtr param)
{
    DMXConfigSubPtr pSub = dmxConfigAlloc(sizeof(*pSub));

    pSub->type = type;
    switch (type) {
    case dmxConfigComment:
        pSub->comment = comment;
        break;
    case dmxConfigDisplay:
        pSub->display = display;
        break;
    case dmxConfigWall:
        pSub->wall = wall;
        break;
    case dmxConfigOption:
        pSub->option = option;
        break;
    case dmxConfigParam:
        pSub->param = param;
        break;
    default:
        dmxConfigLog("Type %d not supported in subentry\n", type);
        break;
    }
    return pSub;
}

void
dmxConfigFreeSub(DMXConfigSubPtr sub)
{
    DMXConfigSubPtr pt;

    for (pt = sub; pt; pt = pt->next) {
        switch (pt->type) {
        case dmxConfigComment:
            dmxConfigFreeComment(pt->comment);
            break;
        case dmxConfigDisplay:
            dmxConfigFreeDisplay(pt->display);
            break;
        case dmxConfigWall:
            dmxConfigFreeWall(pt->wall);
            break;
        case dmxConfigOption:
            dmxConfigFreeOption(pt->option);
            break;
        case dmxConfigParam:
            dmxConfigFreeParam(pt->param);
            break;
        default:
            dmxConfigLog("Type %d not supported in subentry\n", pt->type);
            break;
        }
    }
    dmxConfigFree(sub);
}

DMXConfigSubPtr
dmxConfigSubComment(DMXConfigCommentPtr comment)
{
    return dmxConfigCreateSub(dmxConfigComment, comment, NULL, NULL, NULL,
                              NULL);
}

DMXConfigSubPtr
dmxConfigSubDisplay(DMXConfigDisplayPtr display)
{
    return dmxConfigCreateSub(dmxConfigDisplay, NULL, display, NULL, NULL,
                              NULL);
}

DMXConfigSubPtr
dmxConfigSubWall(DMXConfigWallPtr wall)
{
    return dmxConfigCreateSub(dmxConfigWall, NULL, NULL, wall, NULL, NULL);
}

DMXConfigSubPtr
dmxConfigSubOption(DMXConfigOptionPtr option)
{
    return dmxConfigCreateSub(dmxConfigOption, NULL, NULL, NULL, option, NULL);
}

DMXConfigSubPtr
dmxConfigSubParam(DMXConfigParamPtr param)
{
    return dmxConfigCreateSub(dmxConfigParam, NULL, NULL, NULL, NULL, param);
}

extern DMXConfigSubPtr
dmxConfigAddSub(DMXConfigSubPtr head, DMXConfigSubPtr sub)
{
    DMXConfigSubPtr pt;

    if (!head)
        return sub;
    for (pt = head; pt->next; pt = pt->next);
    pt->next = sub;
    return head;
}

DMXConfigVirtualPtr
dmxConfigCreateVirtual(DMXConfigTokenPtr pStart,
                       DMXConfigStringPtr pName,
                       DMXConfigPairPtr pDim,
                       DMXConfigTokenPtr pOpen,
                       DMXConfigSubPtr pSubentry, DMXConfigTokenPtr pClose)
{
    DMXConfigVirtualPtr pVirtual = dmxConfigAlloc(sizeof(*pVirtual));

    pVirtual->start = pStart;
    pVirtual->vname = pName;
    pVirtual->dim = pDim;
    pVirtual->open = pOpen;
    pVirtual->subentry = pSubentry;
    pVirtual->close = pClose;

    pVirtual->name = pName ? pName->string : NULL;
    pVirtual->width = pDim ? pDim->x : 0;
    pVirtual->height = pDim ? pDim->y : 0;

    return pVirtual;
}

void
dmxConfigFreeVirtual(DMXConfigVirtualPtr virtual)
{
    dmxConfigFreeToken(virtual->start);
    dmxConfigFreeString(virtual->vname);
    dmxConfigFreePair(virtual->dim);
    dmxConfigFreeToken(virtual->open);
    dmxConfigFreeSub(virtual->subentry);
    dmxConfigFreeToken(virtual->close);
    dmxConfigFree(virtual);
}

DMXConfigEntryPtr
dmxConfigCreateEntry(DMXConfigType type,
                     DMXConfigCommentPtr comment, DMXConfigVirtualPtr virtual)
{
    DMXConfigEntryPtr pEntry = dmxConfigAlloc(sizeof(*pEntry));

    pEntry->type = type;
    switch (type) {
    case dmxConfigComment:
        pEntry->comment = comment;
        break;
    case dmxConfigVirtual:
        pEntry->virtual = virtual;
        break;
    default:
        dmxConfigLog("Type %d not supported in entry\n", type);
        break;
    }
    return pEntry;
}

void
dmxConfigFreeEntry(DMXConfigEntryPtr entry)
{
    DMXConfigEntryPtr pt;

    for (pt = entry; pt; pt = pt->next) {
        switch (pt->type) {
        case dmxConfigComment:
            dmxConfigFreeComment(pt->comment);
            break;
        case dmxConfigVirtual:
            dmxConfigFreeVirtual(pt->virtual);
            break;
        default:
            dmxConfigLog("Type %d not supported in entry\n", pt->type);
            break;
        }
    }
    dmxConfigFree(entry);
}

DMXConfigEntryPtr
dmxConfigAddEntry(DMXConfigEntryPtr head,
                  DMXConfigType type,
                  DMXConfigCommentPtr comment, DMXConfigVirtualPtr virtual)
{
    DMXConfigEntryPtr child = dmxConfigCreateEntry(type, comment, virtual);
    DMXConfigEntryPtr pt;

    if (!head)
        return child;

    for (pt = head; pt->next; pt = pt->next);
    pt->next = child;

    return head;
}

DMXConfigEntryPtr
dmxConfigEntryComment(DMXConfigCommentPtr comment)
{
    return dmxConfigCreateEntry(dmxConfigComment, comment, NULL);
}

DMXConfigEntryPtr
dmxConfigEntryVirtual(DMXConfigVirtualPtr virtual)
{
    return dmxConfigCreateEntry(dmxConfigVirtual, NULL, virtual);
}