611 lines
18 KiB
C
611 lines
18 KiB
C
/*
|
|
* 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 = malloc(bytes);
|
|
if (!area) {
|
|
dmxConfigLog("dmxConfigAlloc: out of memory\n");
|
|
return NULL;
|
|
}
|
|
memset(area, 0, bytes);
|
|
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)
|
|
{
|
|
if (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));
|
|
|
|
memset(pDisplay, 0, 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;
|
|
if (p->string) 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);
|
|
}
|