summaryrefslogtreecommitdiff
path: root/src/VBox/Storage/testcase
diff options
context:
space:
mode:
Diffstat (limited to 'src/VBox/Storage/testcase')
-rw-r--r--src/VBox/Storage/testcase/Makefile.kmk49
-rw-r--r--src/VBox/Storage/testcase/VDDefs.h38
-rw-r--r--src/VBox/Storage/testcase/VDIoBackend.cpp283
-rw-r--r--src/VBox/Storage/testcase/VDIoBackend.h90
-rw-r--r--src/VBox/Storage/testcase/VDIoBackendMem.cpp31
-rw-r--r--src/VBox/Storage/testcase/VDIoBackendMem.h20
-rw-r--r--src/VBox/Storage/testcase/VDScript.cpp2733
-rw-r--r--src/VBox/Storage/testcase/VDScript.h153
-rw-r--r--src/VBox/Storage/testcase/VDScriptAst.cpp343
-rw-r--r--src/VBox/Storage/testcase/VDScriptAst.h452
-rw-r--r--src/VBox/Storage/testcase/VDScriptChecker.cpp31
-rw-r--r--src/VBox/Storage/testcase/VDScriptInternal.h107
-rw-r--r--src/VBox/Storage/testcase/VDScriptInterp.cpp1054
-rw-r--r--src/VBox/Storage/testcase/VDScriptStack.h142
-rw-r--r--src/VBox/Storage/testcase/tstVD-2.cpp2
-rw-r--r--src/VBox/Storage/testcase/tstVD.cpp2
-rw-r--r--src/VBox/Storage/testcase/tstVDCompact.vd125
-rw-r--r--src/VBox/Storage/testcase/tstVDCopy.cpp2
-rw-r--r--src/VBox/Storage/testcase/tstVDCopy.vd175
-rw-r--r--src/VBox/Storage/testcase/tstVDDiscard.vd114
-rw-r--r--src/VBox/Storage/testcase/tstVDIo.cpp1860
-rw-r--r--src/VBox/Storage/testcase/tstVDIo.vd159
-rw-r--r--src/VBox/Storage/testcase/tstVDResize.vd32
-rw-r--r--src/VBox/Storage/testcase/tstVDShareable.cpp2
-rw-r--r--src/VBox/Storage/testcase/tstVDShareable.vd108
-rw-r--r--src/VBox/Storage/testcase/tstVDSnap.cpp39
-rw-r--r--src/VBox/Storage/testcase/vbox-img.cpp180
27 files changed, 6456 insertions, 1870 deletions
diff --git a/src/VBox/Storage/testcase/Makefile.kmk b/src/VBox/Storage/testcase/Makefile.kmk
index 3d0f2b31..9d44e45c 100644
--- a/src/VBox/Storage/testcase/Makefile.kmk
+++ b/src/VBox/Storage/testcase/Makefile.kmk
@@ -22,7 +22,7 @@ include $(KBUILD_PATH)/subheader.kmk
# vditool - useful too for manipulating VDIs, but now pretty obsolete and
# probably will go away soon. Testcase only now.
#
-ifdef VBOX_WITH_TESTCASES
+if defined(VBOX_WITH_TESTCASES) || defined(VBOX_WITH_VDITOOL)
PROGRAMS += vditool
vditool_TEMPLATE = VBOXR3TSTEXE
vditool_LIBS = $(LIB_DDU)
@@ -33,7 +33,7 @@ endif
# Basic testcases for the VD code.
#
ifdef VBOX_WITH_TESTCASES
- PROGRAMS += tstVD tstVD-2 tstVDCopy tstVDSnap tstVDShareable vbox-img
+ PROGRAMS += tstVD tstVD-2 tstVDCopy tstVDSnap tstVDShareable
tstVD_TEMPLATE = VBOXR3TSTEXE
tstVD_SOURCES = tstVD.cpp
@@ -52,9 +52,14 @@ ifdef VBOX_WITH_TESTCASES
tstVDIo_TEMPLATE = VBOXR3TSTEXE
tstVDIo_SOURCES = tstVDIo.cpp \
+ VDIoBackend.cpp \
VDIoBackendMem.cpp \
VDMemDisk.cpp \
- VDIoRnd.cpp
+ VDIoRnd.cpp \
+ VDScript.cpp \
+ VDScriptAst.cpp \
+ VDScriptChecker.cpp \
+ VDScriptInterp.cpp
tstVDIo_LIBS = \
$(LIB_DDU) \
$(PATH_STAGE_LIB)/StorageDbgLib$(VBOX_SUFF_LIB)
@@ -70,27 +75,32 @@ ifdef VBOX_WITH_TESTCASES
tstVDSnap_TEMPLATE = VBOXR3TSTEXE
tstVDSnap_LIBS = $(LIB_DDU)
tstVDSnap_SOURCES = tstVDSnap.cpp
+endif
+
+if defined(VBOX_WITH_TESTCASES) || defined(VBOX_WITH_VBOX_IMG)
+ PROGRAMS += vbox-img
#
# vbox-img - static because it migth be used as at standalone tool.
#
- vbox-img_TEMPLATE = VBOXR3STATIC
- vbox-img_DEFS += IN_VBOXDDU IN_VBOXDDU_STATIC VBOX_HDD_NO_DYNAMIC_BACKENDS IN_RT_R3
+ vbox-img_TEMPLATE = VBoxR3Static
+ vbox-img_DEFS += IN_VBOXDDU IN_VBOXDDU_STATIC VBOX_HDD_NO_DYNAMIC_BACKENDS
vbox-img_SOURCES = \
vbox-img.cpp \
- $(VBOX_PATH_STORAGE_SRC)/VD.cpp \
- $(VBOX_PATH_STORAGE_SRC)/VDVfs.cpp \
- $(VBOX_PATH_STORAGE_SRC)/VDI.cpp \
- $(VBOX_PATH_STORAGE_SRC)/VMDK.cpp \
- $(VBOX_PATH_STORAGE_SRC)/VHD.cpp \
- $(VBOX_PATH_STORAGE_SRC)/DMG.cpp \
- $(VBOX_PATH_STORAGE_SRC)/Parallels.cpp \
- $(VBOX_PATH_STORAGE_SRC)/ISCSI.cpp \
- $(VBOX_PATH_STORAGE_SRC)/RAW.cpp \
- $(VBOX_PATH_STORAGE_SRC)/QED.cpp \
- $(VBOX_PATH_STORAGE_SRC)/QCOW.cpp \
- $(VBOX_PATH_STORAGE_SRC)/VHDX.cpp \
- $(VBOX_PATH_STORAGE_SRC)/VCICache.cpp
+ ../VD.cpp \
+ ../VDVfs.cpp \
+ ../VDI.cpp \
+ ../VMDK.cpp \
+ ../VHD.cpp \
+ ../DMG.cpp \
+ ../Parallels.cpp \
+ ../ISCSI.cpp \
+ ../RAW.cpp \
+ ../QED.cpp \
+ ../QCOW.cpp \
+ ../VHDX.cpp \
+ ../VCICache.cpp \
+ ../VDIfVfs.cpp
vbox-img_LIBS = \
$(VBOX_LIB_RUNTIME_STATIC)
if1of ($(KBUILD_TARGET),os2 win)
@@ -104,6 +114,9 @@ ifdef VBOX_WITH_TESTCASES
endif
ifeq ($(KBUILD_TARGET),linux)
vbox-img_LIBS += crypt
+ ifdef SDK_VBOX_LIBXML2_LIBS
+ vbox-img_LIBS += xml2
+ endif
else if1of ($(KBUILD_TARGET),darwin freebsd)
vbox-img_LIBS += iconv
else ifeq ($(KBUILD_TARGET),win)
diff --git a/src/VBox/Storage/testcase/VDDefs.h b/src/VBox/Storage/testcase/VDDefs.h
new file mode 100644
index 00000000..729a8fff
--- /dev/null
+++ b/src/VBox/Storage/testcase/VDDefs.h
@@ -0,0 +1,38 @@
+/** $Id: VDDefs.h $ */
+/** @file
+ *
+ * VBox HDD container test utility, common definitions.
+ */
+
+/*
+ * Copyright (C) 2013 Oracle Corporation
+ *
+ * This file is part of VirtualBox Open Source Edition (OSE), as
+ * available from http://www.virtualbox.org. This file is free software;
+ * you can redistribute it and/or modify it under the terms of the GNU
+ * General Public License (GPL) as published by the Free Software
+ * Foundation, in version 2 as it comes in the "COPYING" file of the
+ * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
+ * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
+ */
+#ifndef __VDDefs_h__
+#define __VDDefs_h__
+
+#include <iprt/sg.h>
+
+/**
+ * I/O transfer direction.
+ */
+typedef enum VDIOTXDIR
+{
+ /** Read. */
+ VDIOTXDIR_READ = 0,
+ /** Write. */
+ VDIOTXDIR_WRITE,
+ /** Flush. */
+ VDIOTXDIR_FLUSH,
+ /** Invalid. */
+ VDIOTXDIR_INVALID
+} VDIOTXDIR;
+
+#endif /* __VDDefs_h__ */
diff --git a/src/VBox/Storage/testcase/VDIoBackend.cpp b/src/VBox/Storage/testcase/VDIoBackend.cpp
new file mode 100644
index 00000000..6458d7df
--- /dev/null
+++ b/src/VBox/Storage/testcase/VDIoBackend.cpp
@@ -0,0 +1,283 @@
+/** $Id: VDIoBackend.cpp $ */
+/** @file
+ *
+ * VBox HDD container test utility, I/O backend API
+ */
+
+/*
+ * Copyright (C) 2013 Oracle Corporation
+ *
+ * This file is part of VirtualBox Open Source Edition (OSE), as
+ * available from http://www.virtualbox.org. This file is free software;
+ * you can redistribute it and/or modify it under the terms of the GNU
+ * General Public License (GPL) as published by the Free Software
+ * Foundation, in version 2 as it comes in the "COPYING" file of the
+ * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
+ * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
+ */
+#define LOGGROUP LOGGROUP_DEFAULT /** @todo: Log group */
+#include <iprt/err.h>
+#include <iprt/log.h>
+#include <iprt/assert.h>
+#include <iprt/asm.h>
+#include <iprt/mem.h>
+#include <iprt/file.h>
+#include <iprt/aiomgr.h>
+#include <iprt/string.h>
+
+#include "VDIoBackend.h"
+#include "VDMemDisk.h"
+#include "VDIoBackendMem.h"
+
+typedef struct VDIOBACKEND
+{
+ /** Memory I/O backend handle. */
+ PVDIOBACKENDMEM pIoMem;
+ /** Async I/O manager. */
+ RTAIOMGR hAioMgr;
+ /** Users of the memory backend. */
+ volatile uint32_t cRefsIoMem;
+ /** Users of the file backend. */
+ volatile uint32_t cRefsFile;
+} VDIOBACKEND;
+
+typedef struct VDIOSTORAGE
+{
+ /** Pointer to the I/O backend parent. */
+ PVDIOBACKEND pIoBackend;
+ /** Completion callback. */
+ PFNVDIOCOMPLETE pfnComplete;
+ /** Flag whether this storage is backed by a file or memory.disk. */
+ bool fMemory;
+ /** Type dependent data. */
+ union
+ {
+ /** Memory disk handle. */
+ PVDMEMDISK pMemDisk;
+ struct
+ {
+ /** file handle. */
+ RTFILE hFile;
+ /** I/O manager file handle. */
+ RTAIOMGRFILE hAioMgrFile;
+ } File;
+ } u;
+} VDIOSTORAGE;
+
+static DECLCALLBACK(void) vdIoBackendFileIoComplete(RTAIOMGRFILE hAioMgrFile, int rcReq, void *pvUser)
+{
+ PVDIOSTORAGE pIoStorage = (PVDIOSTORAGE)RTAioMgrFileGetUser(hAioMgrFile);
+ pIoStorage->pfnComplete(pvUser, rcReq);
+}
+
+int VDIoBackendCreate(PPVDIOBACKEND ppIoBackend)
+{
+ int rc = VINF_SUCCESS;
+ PVDIOBACKEND pIoBackend;
+
+ pIoBackend = (PVDIOBACKEND)RTMemAllocZ(sizeof(VDIOBACKEND));
+ if (pIoBackend)
+ {
+ pIoBackend->hAioMgr = NIL_RTAIOMGR;
+ *ppIoBackend = pIoBackend;
+ }
+ else
+ rc = VERR_NO_MEMORY;
+
+ return rc;
+}
+
+void VDIoBackendDestroy(PVDIOBACKEND pIoBackend)
+{
+ if (pIoBackend->pIoMem)
+ VDIoBackendMemDestroy(pIoBackend->pIoMem);
+ if (pIoBackend->hAioMgr)
+ RTAioMgrRelease(pIoBackend->hAioMgr);
+ RTMemFree(pIoBackend);
+}
+
+int VDIoBackendStorageCreate(PVDIOBACKEND pIoBackend, const char *pszBackend,
+ const char *pszName, PFNVDIOCOMPLETE pfnComplete,
+ PPVDIOSTORAGE ppIoStorage)
+{
+ int rc = VINF_SUCCESS;
+ PVDIOSTORAGE pIoStorage = (PVDIOSTORAGE)RTMemAllocZ(sizeof(VDIOSTORAGE));
+
+ if (pIoStorage)
+ {
+ pIoStorage->pIoBackend = pIoBackend;
+ pIoStorage->pfnComplete = pfnComplete;
+ if (!strcmp(pszBackend, "memory"))
+ {
+ pIoStorage->fMemory = true;
+ rc = VDMemDiskCreate(&pIoStorage->u.pMemDisk, 0 /* Growing */);
+ if (RT_SUCCESS(rc))
+ {
+ uint32_t cRefs = ASMAtomicIncU32(&pIoBackend->cRefsIoMem);
+ if ( cRefs == 1
+ && !pIoBackend->pIoMem)
+ {
+ rc = VDIoBackendMemCreate(&pIoBackend->pIoMem);
+ if (RT_FAILURE(rc))
+ VDMemDiskDestroy(pIoStorage->u.pMemDisk);
+ }
+ }
+ }
+ else if (!strcmp(pszBackend, "file"))
+ {
+ pIoStorage->fMemory = false;
+ uint32_t cRefs = ASMAtomicIncU32(&pIoBackend->cRefsFile);
+ if ( cRefs == 1
+ && pIoBackend->hAioMgr == NIL_RTAIOMGR)
+ rc = RTAioMgrCreate(&pIoBackend->hAioMgr, 1024);
+
+ if (RT_SUCCESS(rc))
+ {
+ /* Create file. */
+ rc = RTFileOpen(&pIoStorage->u.File.hFile, pszName,
+ RTFILE_O_READWRITE | RTFILE_O_CREATE | RTFILE_O_ASYNC_IO | RTFILE_O_NO_CACHE | RTFILE_O_DENY_NONE);
+ if (RT_SUCCESS(rc))
+ {
+ /* Create file handle for I/O manager. */
+ rc = RTAioMgrFileCreate(pIoBackend->hAioMgr, pIoStorage->u.File.hFile,
+ vdIoBackendFileIoComplete, pIoStorage,
+ &pIoStorage->u.File.hAioMgrFile);
+ if (RT_FAILURE(rc))
+ RTFileClose(pIoStorage->u.File.hFile);
+ }
+ }
+
+ if (RT_FAILURE(rc))
+ ASMAtomicDecU32(&pIoBackend->cRefsFile);
+ }
+ else
+ rc = VERR_NOT_SUPPORTED;
+
+ if (RT_FAILURE(rc))
+ RTMemFree(pIoStorage);
+ else
+ *ppIoStorage = pIoStorage;
+ }
+ else
+ rc = VERR_NO_MEMORY;
+
+ return rc;
+}
+
+void VDIoBackendStorageDestroy(PVDIOSTORAGE pIoStorage)
+{
+ if (pIoStorage->fMemory)
+ {
+ VDMemDiskDestroy(pIoStorage->u.pMemDisk);
+ ASMAtomicDecU32(&pIoStorage->pIoBackend->cRefsIoMem);
+ }
+ else
+ {
+ RTAioMgrFileRelease(pIoStorage->u.File.hAioMgrFile);
+ RTFileClose(pIoStorage->u.File.hFile);
+ ASMAtomicDecU32(&pIoStorage->pIoBackend->cRefsFile);
+ }
+ RTMemFree(pIoStorage);
+}
+
+int VDIoBackendTransfer(PVDIOSTORAGE pIoStorage, VDIOTXDIR enmTxDir, uint64_t off,
+ size_t cbTransfer, PRTSGBUF pSgBuf, void *pvUser, bool fSync)
+{
+ int rc = VINF_SUCCESS;
+
+ if (pIoStorage->fMemory)
+ {
+ if (!fSync)
+ {
+ rc = VDIoBackendMemTransfer(pIoStorage->pIoBackend->pIoMem, pIoStorage->u.pMemDisk,
+ enmTxDir, off, cbTransfer, pSgBuf, pIoStorage->pfnComplete,
+ pvUser);
+ }
+ else
+ {
+ switch (enmTxDir)
+ {
+ case VDIOTXDIR_READ:
+ rc = VDMemDiskRead(pIoStorage->u.pMemDisk, off, cbTransfer, pSgBuf);
+ break;
+ case VDIOTXDIR_WRITE:
+ rc = VDMemDiskWrite(pIoStorage->u.pMemDisk, off, cbTransfer, pSgBuf);
+ break;
+ case VDIOTXDIR_FLUSH:
+ break;
+ default:
+ AssertMsgFailed(("Invalid transfer type %d\n", enmTxDir));
+ }
+ }
+ }
+ else
+ {
+ if (!fSync)
+ {
+ switch (enmTxDir)
+ {
+ case VDIOTXDIR_READ:
+ rc = RTAioMgrFileRead(pIoStorage->u.File.hAioMgrFile, off, pSgBuf, cbTransfer, pvUser);
+ break;
+ case VDIOTXDIR_WRITE:
+ rc = RTAioMgrFileWrite(pIoStorage->u.File.hAioMgrFile, off, pSgBuf, cbTransfer, pvUser);
+ break;
+ case VDIOTXDIR_FLUSH:
+ rc = RTAioMgrFileFlush(pIoStorage->u.File.hAioMgrFile, pvUser);
+ break;
+ default:
+ AssertMsgFailed(("Invalid transfer type %d\n", enmTxDir));
+ }
+ if (rc == VERR_FILE_AIO_IN_PROGRESS)
+ rc = VINF_SUCCESS;
+ }
+ else
+ {
+ switch (enmTxDir)
+ {
+ case VDIOTXDIR_READ:
+ rc = RTFileSgReadAt(pIoStorage->u.File.hFile, off, pSgBuf, cbTransfer, NULL);
+ break;
+ case VDIOTXDIR_WRITE:
+ rc = RTFileSgWriteAt(pIoStorage->u.File.hFile, off, pSgBuf, cbTransfer, NULL);
+ break;
+ case VDIOTXDIR_FLUSH:
+ rc = RTFileFlush(pIoStorage->u.File.hFile);
+ break;
+ default:
+ AssertMsgFailed(("Invalid transfer type %d\n", enmTxDir));
+ }
+ }
+ }
+
+ return rc;
+}
+
+int VDIoBackendStorageSetSize(PVDIOSTORAGE pIoStorage, uint64_t cbSize)
+{
+ int rc = VINF_SUCCESS;
+
+ if (pIoStorage->fMemory)
+ {
+ rc = VDMemDiskSetSize(pIoStorage->u.pMemDisk, cbSize);
+ }
+ else
+ rc = RTFileSetSize(pIoStorage->u.File.hFile, cbSize);
+
+ return rc;
+}
+
+int VDIoBackendStorageGetSize(PVDIOSTORAGE pIoStorage, uint64_t *pcbSize)
+{
+ int rc = VINF_SUCCESS;
+
+ if (pIoStorage->fMemory)
+ {
+ rc = VDMemDiskGetSize(pIoStorage->u.pMemDisk, pcbSize);
+ }
+ else
+ rc = RTFileGetSize(pIoStorage->u.File.hFile, pcbSize);
+
+ return rc;
+}
+
diff --git a/src/VBox/Storage/testcase/VDIoBackend.h b/src/VBox/Storage/testcase/VDIoBackend.h
new file mode 100644
index 00000000..bec1bb7d
--- /dev/null
+++ b/src/VBox/Storage/testcase/VDIoBackend.h
@@ -0,0 +1,90 @@
+/** $Id: VDIoBackend.h $ */
+/** @file
+ *
+ * VBox HDD container test utility, async I/O backend
+ */
+
+/*
+ * Copyright (C) 2013 Oracle Corporation
+ *
+ * This file is part of VirtualBox Open Source Edition (OSE), as
+ * available from http://www.virtualbox.org. This file is free software;
+ * you can redistribute it and/or modify it under the terms of the GNU
+ * General Public License (GPL) as published by the Free Software
+ * Foundation, in version 2 as it comes in the "COPYING" file of the
+ * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
+ * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
+ */
+#ifndef __VDIoBackend_h__
+#define __VDIoBackend_h__
+
+#include <iprt/sg.h>
+
+#include "VDDefs.h"
+
+/** I/O backend handle. */
+typedef struct VDIOBACKEND *PVDIOBACKEND;
+/** Pointer to a I/O backend handle. */
+typedef PVDIOBACKEND *PPVDIOBACKEND;
+
+/** Storage handle. */
+typedef struct VDIOSTORAGE *PVDIOSTORAGE;
+/** Pointer to a storage handle. */
+typedef PVDIOSTORAGE *PPVDIOSTORAGE;
+
+/**
+ * Completion handler.
+ *
+ * @returns nothing.
+ * @param pvUser Opaque user data.
+ * @param rcReq Completion code for the request.
+ */
+typedef DECLCALLBACK(int) FNVDIOCOMPLETE(void *pvUser, int rcReq);
+/** Pointer to a completion handler. */
+typedef FNVDIOCOMPLETE *PFNVDIOCOMPLETE;
+
+/**
+ * Creates a new memory I/O backend.
+ *
+ * @returns IPRT status code.
+ *
+ * @param ppIoBackend Where to store the handle on success.
+ */
+int VDIoBackendCreate(PPVDIOBACKEND ppIoBackend);
+
+/**
+ * Destroys a memory I/O backend.
+ *
+ * @returns nothing.
+ *
+ * @param pIoBackend The backend to destroy.
+ */
+void VDIoBackendDestroy(PVDIOBACKEND pIoBackend);
+
+int VDIoBackendStorageCreate(PVDIOBACKEND pIoBackend, const char *pszBackend,
+ const char *pszName, PFNVDIOCOMPLETE pfnComplete,
+ PPVDIOSTORAGE ppIoStorage);
+
+void VDIoBackendStorageDestroy(PVDIOSTORAGE pIoStorage);
+
+int VDIoBackendStorageSetSize(PVDIOSTORAGE pIoStorage, uint64_t cbSize);
+
+int VDIoBackendStorageGetSize(PVDIOSTORAGE pIoStorage, uint64_t *pcbSize);
+
+/**
+ * Enqueues a new I/O request.
+ *
+ * @returns IPRT status code.
+ *
+ * @param pIoStorage Storage handle.
+ * @param enmTxDir The transfer direction.
+ * @param off Start offset of the transfer.
+ * @param cbTransfer Size of the transfer.
+ * @param pSgBuf S/G buffer to use.
+ * @param pvUser Opaque user data.
+ * @param fSync Flag whether to wait for the operation to complete.
+ */
+int VDIoBackendTransfer(PVDIOSTORAGE pIoStorage, VDIOTXDIR enmTxDir, uint64_t off,
+ size_t cbTransfer, PRTSGBUF pSgBuf, void *pvUser, bool fSync);
+
+#endif /* __VDIoBackendMem_h__ */
diff --git a/src/VBox/Storage/testcase/VDIoBackendMem.cpp b/src/VBox/Storage/testcase/VDIoBackendMem.cpp
index a3ab1793..b572fe77 100644
--- a/src/VBox/Storage/testcase/VDIoBackendMem.cpp
+++ b/src/VBox/Storage/testcase/VDIoBackendMem.cpp
@@ -5,7 +5,7 @@
*/
/*
- * Copyright (C) 2011 Oracle Corporation
+ * Copyright (C) 2011-2012 Oracle Corporation
*
* This file is part of VirtualBox Open Source Edition (OSE), as
* available from http://www.virtualbox.org. This file is free software;
@@ -43,13 +43,13 @@ typedef struct VDIOBACKENDREQ
uint64_t off;
/** Size of the transfer. */
size_t cbTransfer;
- /** Number of segments in the array. */
- unsigned cSegs;
/** Completion handler to call. */
PFNVDIOCOMPLETE pfnComplete;
/** Opaque user data. */
void *pvUser;
- /** Segment array - variable in size */
+ /** S/G buffer. */
+ RTSGBUF SgBuf;
+ /** Segment array - variable size. */
RTSGSEG aSegs[1];
} VDIOBACKENDREQ, *PVDIOBACKENDREQ;
@@ -142,15 +142,19 @@ int VDIoBackendMemDestroy(PVDIOBACKENDMEM pIoBackend)
}
int VDIoBackendMemTransfer(PVDIOBACKENDMEM pIoBackend, PVDMEMDISK pMemDisk,
- VDIOTXDIR enmTxDir, uint64_t off, size_t cbTransfer, PCRTSGSEG paSegs,
- unsigned cSegs, PFNVDIOCOMPLETE pfnComplete, void *pvUser)
+ VDIOTXDIR enmTxDir, uint64_t off, size_t cbTransfer,
+ PRTSGBUF pSgBuf, PFNVDIOCOMPLETE pfnComplete, void *pvUser)
{
PVDIOBACKENDREQ pReq = NULL;
PPVDIOBACKENDREQ ppReq = NULL;
size_t cbData;
+ unsigned cSegs = 0;
LogFlowFunc(("Queuing request\n"));
+ if (enmTxDir != VDIOTXDIR_FLUSH)
+ RTSgBufSegArrayCreate(pSgBuf, NULL, &cSegs, cbTransfer);
+
pReq = (PVDIOBACKENDREQ)RTMemAlloc(RT_OFFSETOF(VDIOBACKENDREQ, aSegs[cSegs]));
if (!pReq)
return VERR_NO_MEMORY;
@@ -167,13 +171,12 @@ int VDIoBackendMemTransfer(PVDIOBACKENDMEM pIoBackend, PVDMEMDISK pMemDisk,
pReq->cbTransfer = cbTransfer;
pReq->off = off;
pReq->pMemDisk = pMemDisk;
- pReq->cSegs = cSegs;
pReq->pfnComplete = pfnComplete;
pReq->pvUser = pvUser;
- for (unsigned i = 0; i < cSegs; i++)
+ if (enmTxDir != VDIOTXDIR_FLUSH)
{
- pReq->aSegs[i].pvSeg = paSegs[i].pvSeg;
- pReq->aSegs[i].cbSeg = paSegs[i].cbSeg;
+ RTSgBufSegArrayCreate(pSgBuf, &pReq->aSegs[0], &cSegs, cbTransfer);
+ RTSgBufInit(&pReq->SgBuf, pReq->aSegs, cSegs);
}
*ppReq = pReq;
@@ -225,16 +228,12 @@ static int vdIoBackendMemThread(RTTHREAD hThread, void *pvUser)
{
case VDIOTXDIR_READ:
{
- RTSGBUF SgBuf;
- RTSgBufInit(&SgBuf, pReq->aSegs, pReq->cSegs);
- rcReq = VDMemDiskRead(pReq->pMemDisk, pReq->off, pReq->cbTransfer, &SgBuf);
+ rcReq = VDMemDiskRead(pReq->pMemDisk, pReq->off, pReq->cbTransfer, &pReq->SgBuf);
break;
}
case VDIOTXDIR_WRITE:
{
- RTSGBUF SgBuf;
- RTSgBufInit(&SgBuf, pReq->aSegs, pReq->cSegs);
- rcReq = VDMemDiskWrite(pReq->pMemDisk, pReq->off, pReq->cbTransfer, &SgBuf);
+ rcReq = VDMemDiskWrite(pReq->pMemDisk, pReq->off, pReq->cbTransfer, &pReq->SgBuf);
break;
}
case VDIOTXDIR_FLUSH:
diff --git a/src/VBox/Storage/testcase/VDIoBackendMem.h b/src/VBox/Storage/testcase/VDIoBackendMem.h
index 89b16dc1..3e1f5b84 100644
--- a/src/VBox/Storage/testcase/VDIoBackendMem.h
+++ b/src/VBox/Storage/testcase/VDIoBackendMem.h
@@ -20,20 +20,7 @@
#include <iprt/sg.h>
-/**
- * I/O transfer direction.
- */
-typedef enum VDIOTXDIR
-{
- /** Read. */
- VDIOTXDIR_READ = 0,
- /** Write. */
- VDIOTXDIR_WRITE,
- /** Flush. */
- VDIOTXDIR_FLUSH,
- /** Invalid. */
- VDIOTXDIR_INVALID
-} VDIOTXDIR;
+#include "VDDefs.h"
/** Memory backend handle. */
typedef struct VDIOBACKENDMEM *PVDIOBACKENDMEM;
@@ -85,8 +72,7 @@ int VDIoBackendMemDestroy(PVDIOBACKENDMEM pIoBackend);
* @param pvUser Opaque user data.
*/
int VDIoBackendMemTransfer(PVDIOBACKENDMEM pIoBackend, PVDMEMDISK pMemDisk,
- VDIOTXDIR enmTxDir, uint64_t off, size_t cbTransfer, PCRTSGSEG paSegs,
- unsigned cSegs,
- PFNVDIOCOMPLETE pfnComplete, void *pvUser);
+ VDIOTXDIR enmTxDir, uint64_t off, size_t cbTransfer,
+ PRTSGBUF pSgBuf, PFNVDIOCOMPLETE pfnComplete, void *pvUser);
#endif /* __VDIoBackendMem_h__ */
diff --git a/src/VBox/Storage/testcase/VDScript.cpp b/src/VBox/Storage/testcase/VDScript.cpp
new file mode 100644
index 00000000..63786311
--- /dev/null
+++ b/src/VBox/Storage/testcase/VDScript.cpp
@@ -0,0 +1,2733 @@
+/** $Id: VDScript.cpp $ */
+/** @file
+ *
+ * VBox HDD container test utility - scripting engine.
+ */
+
+/*
+ * Copyright (C) 2013 Oracle Corporation
+ *
+ * This file is part of VirtualBox Open Source Edition (OSE), as
+ * available from http://www.virtualbox.org. This file is free software;
+ * you can redistribute it and/or modify it under the terms of the GNU
+ * General Public License (GPL) as published by the Free Software
+ * Foundation, in version 2 as it comes in the "COPYING" file of the
+ * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
+ * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
+ */
+
+/** @page pg_vd_script VDScript - Simple scripting language for VD I/O testing.
+ *
+ * This component implements a very simple scripting language to make testing the VD
+ * library more flexible and testcases faster to implement without the need to recompile
+ * everything after it changed.
+ * The language is a small subset of the C language. It doesn't support unions, structs,
+ * global variables, typedefed types or pointers (yet). It also adds a boolean and a string type.
+ * Strings are immutable and only to print messages from the script.
+ * There are also not the default types like int or unsigned because theire ranges are architecture
+ * dependent. Instead VDScript uses uint8_t, int8_t, ... as primitive types.
+ *
+ * Why inventing a completely new language?
+ *
+ * Well it is not a completely new language to start with, it is a subset of C and the
+ * language can be extended later on to reach the full C language later on.
+ * Second, there is no static typed scripting language I like which could be implemented
+ * and finally because I can ;)
+ * The code implementing the scripting engine is designed to be easily incorporated into other
+ * code. Could be used as a scripting language for the VBox debugger for example or in the scm
+ * tool to automatically rewrite C code using the AST VDSCript generates...
+ *
+ * The syntax of VDSCript is derived from the C syntax. The syntax of C in BNF was taken
+ * from: http://www.csci.csusb.edu/dick/samples/c.syntax.html
+ * and: http://slps.github.com/zoo/c/iso-9899-tc3.html
+ * and: http://www.open-std.org/jtc1/sc22/WG14/www/docs/n1256.pdf
+ */
+#define LOGGROUP LOGGROUP_DEFAULT
+#include <iprt/string.h>
+#include <iprt/list.h>
+#include <iprt/mem.h>
+#include <iprt/ctype.h>
+#include <iprt/stream.h>
+
+#include <VBox/log.h>
+
+#include "VDScriptAst.h"
+#include "VDScriptInternal.h"
+
+/**
+ * VD script token class.
+ */
+typedef enum VDTOKENCLASS
+{
+ /** Invalid. */
+ VDTOKENCLASS_INVALID = 0,
+ /** Identifier class. */
+ VDTOKENCLASS_IDENTIFIER,
+ /** Numerical constant. */
+ VDTOKENCLASS_NUMCONST,
+ /** String constant. */
+ VDTOKENCLASS_STRINGCONST,
+ /** Operators */
+ VDTOKENCLASS_OPERATORS,
+ /** Reserved keyword */
+ VDTOKENCLASS_KEYWORD,
+ /** Punctuator */
+ VDTOKENCLASS_PUNCTUATOR,
+ /** End of stream */
+ VDTOKENCLASS_EOS,
+ /** 32bit hack. */
+ VDTOKENCLASS_32BIT_HACK = 0x7fffffff
+} VDTOKENCLASS;
+/** Pointer to a token class. */
+typedef VDTOKENCLASS *PVDTOKENCLASS;
+
+/**
+ * Keyword types.
+ */
+typedef enum VDSCRIPTTOKENKEYWORD
+{
+ VDSCRIPTTOKENKEYWORD_INVALID = 0,
+ VDSCRIPTTOKENKEYWORD_CONTINUE,
+ VDSCRIPTTOKENKEYWORD_DEFAULT,
+ VDSCRIPTTOKENKEYWORD_RETURN,
+ VDSCRIPTTOKENKEYWORD_SWITCH,
+ VDSCRIPTTOKENKEYWORD_WHILE,
+ VDSCRIPTTOKENKEYWORD_BREAK,
+ VDSCRIPTTOKENKEYWORD_FALSE,
+ VDSCRIPTTOKENKEYWORD_TRUE,
+ VDSCRIPTTOKENKEYWORD_ELSE,
+ VDSCRIPTTOKENKEYWORD_CASE,
+ VDSCRIPTTOKENKEYWORD_FOR,
+ VDSCRIPTTOKENKEYWORD_IF,
+ VDSCRIPTTOKENKEYWORD_DO,
+ VDSCRIPTTOKENKEYWORD_32BIT_HACK = 0x7fffffff
+} VDSCRIPTTOKENKEYWORD;
+/** Pointer to a keyword type. */
+typedef VDSCRIPTTOKENKEYWORD *PVDSCRIPTTOKENKEYWORD;
+
+/**
+ * VD script token.
+ */
+typedef struct VDSCRIPTTOKEN
+{
+ /** Token class. */
+ VDTOKENCLASS enmClass;
+ /** Token position in the source buffer. */
+ VDSRCPOS Pos;
+ /** Data based on the token class. */
+ union
+ {
+ /** Identifier. */
+ struct
+ {
+ /** Pointer to the start of the identifier. */
+ const char *pszIde;
+ /** Number of characters for the identifier excluding the null terminator. */
+ size_t cchIde;
+ } Ide;
+ /** Numerical constant. */
+ struct
+ {
+ uint64_t u64;
+ } NumConst;
+ /** String constant */
+ struct
+ {
+ /** Pointer to the start of the string constant. */
+ const char *pszString;
+ /** Number of characters of the string, including the null terminator. */
+ size_t cchString;
+ } StringConst;
+ /** Operator */
+ struct
+ {
+ /** The operator string. */
+ char aszOp[4]; /** Maximum of 3 for >>= + null terminator. */
+ } Operator;
+ /** Keyword. */
+ struct
+ {
+ /** The keyword type. */
+ VDSCRIPTTOKENKEYWORD enmKeyword;
+ } Keyword;
+ /** Punctuator. */
+ struct
+ {
+ /** The punctuator in question. */
+ char chPunctuator;
+ } Punctuator;
+ } Class;
+} VDSCRIPTTOKEN;
+/** Pointer to a script token. */
+typedef VDSCRIPTTOKEN *PVDSCRIPTTOKEN;
+/** Pointer to a const script token. */
+typedef const VDSCRIPTTOKEN *PCVDSCRIPTTOKEN;
+
+/**
+ * Tokenizer state.
+ */
+typedef struct VDTOKENIZER
+{
+ /** Char buffer to read from. */
+ const char *pszInput;
+ /** Current position ininput buffer. */
+ VDSRCPOS Pos;
+ /** Token 1. */
+ VDSCRIPTTOKEN Token1;
+ /** Token 2. */
+ VDSCRIPTTOKEN Token2;
+ /** Pointer to the current active token. */
+ PVDSCRIPTTOKEN pTokenCurr;
+ /** The next token in the input stream (used for peeking). */
+ PVDSCRIPTTOKEN pTokenNext;
+} VDTOKENIZER;
+
+/**
+ * Operators entry.
+ */
+typedef struct VDSCRIPTOP
+{
+ /** Operator string. */
+ const char *pszOp;
+ /** Size of the operator in characters without zero terminator. */
+ size_t cchOp;
+} VDSCRIPTOP;
+/** Pointer to a script operator. */
+typedef VDSCRIPTOP *PVDSCRIPTOP;
+
+/**
+ * Known operators array, sort from higest character count to lowest.
+ */
+static VDSCRIPTOP g_aScriptOps[] =
+{
+ {">>=", 3},
+ {"<<=", 3},
+ {"+=", 2},
+ {"-=", 2},
+ {"/=", 2},
+ {"%=", 2},
+ {"&=", 2},
+ {"|=", 2},
+ {"^=", 2},
+ {"&&", 2},
+ {"||", 2},
+ {"<<", 2},
+ {">>", 2},
+ {"++", 2},
+ {"--", 2},
+ {"==", 2},
+ {"!=", 2},
+ {">=", 2},
+ {"<=", 2},
+ {"=", 1},
+ {"+", 1},
+ {"-", 1},
+ {"*", 1},
+ {"/", 1},
+ {"%", 1},
+ {"|", 1},
+ {"&", 1},
+ {"^", 1},
+ {"<", 1},
+ {">", 1},
+ {"!", 1},
+ {"~", 1}
+};
+
+/**
+ * Known punctuators.
+ */
+static VDSCRIPTOP g_aScriptPunctuators[] =
+{
+ {"(", 1},
+ {")", 1},
+ {"{", 1},
+ {"}", 1},
+ {",", 1},
+ {";", 1},
+};
+
+/**
+ * Keyword entry.
+ */
+typedef struct VDSCRIPTKEYWORD
+{
+ /** Keyword string. */
+ const char *pszKeyword;
+ /** Size of the string in characters without zero terminator. */
+ size_t cchKeyword;
+ /** Keyword type. */
+ VDSCRIPTTOKENKEYWORD enmKeyword;
+} VDSCRIPTKEYWORD;
+/** */
+typedef VDSCRIPTKEYWORD *PVDSCRIPTKEYWORD;
+
+/**
+ * Known keywords.
+ */
+static VDSCRIPTKEYWORD g_aKeywords[] =
+{
+ {"continue", 8, VDSCRIPTTOKENKEYWORD_CONTINUE},
+ {"default", 7, VDSCRIPTTOKENKEYWORD_DEFAULT},
+ {"return", 6, VDSCRIPTTOKENKEYWORD_RETURN},
+ {"switch", 6, VDSCRIPTTOKENKEYWORD_SWITCH},
+ {"while", 5, VDSCRIPTTOKENKEYWORD_WHILE},
+ {"break", 5, VDSCRIPTTOKENKEYWORD_BREAK},
+ {"false", 5, VDSCRIPTTOKENKEYWORD_FALSE},
+ {"true", 4, VDSCRIPTTOKENKEYWORD_TRUE},
+ {"else", 4, VDSCRIPTTOKENKEYWORD_ELSE},
+ {"case", 4, VDSCRIPTTOKENKEYWORD_CASE},
+ {"for", 3, VDSCRIPTTOKENKEYWORD_FOR},
+ {"if", 2, VDSCRIPTTOKENKEYWORD_IF},
+ {"do", 2, VDSCRIPTTOKENKEYWORD_DO}
+};
+
+static int vdScriptParseCompoundStatement(PVDSCRIPTCTXINT pThis, PVDSCRIPTASTSTMT *ppAstNodeCompound);
+static int vdScriptParseStatement(PVDSCRIPTCTXINT pThis, PVDSCRIPTASTSTMT *ppAstNodeStmt);
+static int vdScriptParseExpression(PVDSCRIPTCTXINT pThis, PVDSCRIPTASTEXPR *ppAstNodeExpr);
+static int vdScriptParseAssignmentExpression(PVDSCRIPTCTXINT pThis, PVDSCRIPTASTEXPR *ppAstNodeExpr);
+
+/**
+ * Returns whether the tokenizer reached the end of the stream.
+ *
+ * @returns true if the tokenizer reached the end of stream marker
+ * false otherwise.
+ * @param pTokenizer The tokenizer state.
+ */
+DECLINLINE(bool) vdScriptTokenizerIsEos(PVDTOKENIZER pTokenizer)
+{
+ return *pTokenizer->pszInput == '\0';
+}
+
+/**
+ * Skip one character in the input stream.
+ *
+ * @returns nothing.
+ * @param pTokenizer The tokenizer state.
+ */
+DECLINLINE(void) vdScriptTokenizerSkipCh(PVDTOKENIZER pTokenizer)
+{
+ pTokenizer->pszInput++;
+ pTokenizer->Pos.iChStart++;
+ pTokenizer->Pos.iChEnd++;
+}
+
+/**
+ * Returns the next char in the input buffer without advancing it.
+ *
+ * @returns Next character in the input buffer.
+ * @param pTokenizer The tokenizer state.
+ */
+DECLINLINE(char) vdScriptTokenizerPeekCh(PVDTOKENIZER pTokenizer)
+{
+ return vdScriptTokenizerIsEos(pTokenizer)
+ ? '\0'
+ : *(pTokenizer->pszInput + 1);
+}
+
+/**
+ * Returns the next character in the input buffer advancing the internal
+ * position.
+ *
+ * @returns Next character in the stream.
+ * @param pTokenizer The tokenizer state.
+ */
+DECLINLINE(char) vdScriptTokenizerGetCh(PVDTOKENIZER pTokenizer)
+{
+ char ch;
+
+ if (vdScriptTokenizerIsEos(pTokenizer))
+ ch = '\0';
+ else
+ ch = *pTokenizer->pszInput;
+
+ return ch;
+}
+
+/**
+ * Sets a new line for the tokenizer.
+ *
+ * @returns nothing.
+ * @param pTokenizer The tokenizer state.
+ */
+DECLINLINE(void) vdScriptTokenizerNewLine(PVDTOKENIZER pTokenizer, unsigned cSkip)
+{
+ pTokenizer->pszInput += cSkip;
+ pTokenizer->Pos.iLine++;
+ pTokenizer->Pos.iChStart = 1;
+ pTokenizer->Pos.iChEnd = 1;
+}
+
+/**
+ * Checks whether the current position in the input stream is a new line
+ * and skips it.
+ *
+ * @returns Flag whether there was a new line at the current position
+ * in the input buffer.
+ * @param pTokenizer The tokenizer state.
+ */
+DECLINLINE(bool) vdScriptTokenizerIsSkipNewLine(PVDTOKENIZER pTokenizer)
+{
+ bool fNewline = true;
+
+ if ( vdScriptTokenizerGetCh(pTokenizer) == '\r'
+ && vdScriptTokenizerPeekCh(pTokenizer) == '\n')
+ vdScriptTokenizerNewLine(pTokenizer, 2);
+ else if (vdScriptTokenizerGetCh(pTokenizer) == '\n')
+ vdScriptTokenizerNewLine(pTokenizer, 1);
+ else
+ fNewline = false;
+
+ return fNewline;
+}
+
+/**
+ * Skips a multi line comment.
+ *
+ * @returns nothing.
+ * @param pTokenizer The tokenizer state.
+ */
+DECLINLINE(void) vdScriptTokenizerSkipComment(PVDTOKENIZER pTokenizer)
+{
+ while ( !vdScriptTokenizerIsEos(pTokenizer)
+ && ( vdScriptTokenizerGetCh(pTokenizer) != '*'
+ || vdScriptTokenizerPeekCh(pTokenizer) != '/'))
+ {
+ if (!vdScriptTokenizerIsSkipNewLine(pTokenizer))
+ vdScriptTokenizerSkipCh(pTokenizer);
+ }
+
+ if (!vdScriptTokenizerIsEos(pTokenizer))
+ vdScriptTokenizerSkipCh(pTokenizer);
+ if (!vdScriptTokenizerIsEos(pTokenizer))
+ vdScriptTokenizerSkipCh(pTokenizer);
+}
+
+/**
+ * Skip all whitespace starting from the current input buffer position.
+ * Skips all present comments too.
+ *
+ * @returns nothing.
+ * @param pTokenizer The tokenizer state.
+ */
+DECLINLINE(void) vdScriptTokenizerSkipWhitespace(PVDTOKENIZER pTokenizer)
+{
+ while (!vdScriptTokenizerIsEos(pTokenizer))
+ {
+ while ( vdScriptTokenizerGetCh(pTokenizer) == ' '
+ || vdScriptTokenizerGetCh(pTokenizer) == '\t')
+ vdScriptTokenizerSkipCh(pTokenizer);
+
+ if ( !vdScriptTokenizerIsEos(pTokenizer)
+ && !vdScriptTokenizerIsSkipNewLine(pTokenizer))
+ {
+ if ( vdScriptTokenizerGetCh(pTokenizer) == '/'
+ && vdScriptTokenizerPeekCh(pTokenizer) == '*')
+ {
+ vdScriptTokenizerSkipCh(pTokenizer);
+ vdScriptTokenizerSkipCh(pTokenizer);
+ vdScriptTokenizerSkipComment(pTokenizer);
+ }
+ else
+ break; /* Skipped everything, next is some real content. */
+ }
+ }
+}
+
+/**
+ * Get an identifier token from the tokenizer.
+ *
+ * @returns nothing.
+ * @param pTokenizer The tokenizer state.
+ * @param pToken The uninitialized token.
+ */
+static void vdScriptTokenizerGetIdeOrKeyword(PVDTOKENIZER pTokenizer, PVDSCRIPTTOKEN pToken)
+{
+ char ch;
+ size_t cchIde = 0;
+ bool fIsKeyword = false;
+ const char *pszIde = pTokenizer->pszInput;
+
+ pToken->Pos = pTokenizer->Pos;
+
+ Assert(RT_C_IS_ALPHA(*pszIde) || *pszIde == '_' );
+
+ do
+ {
+ cchIde++;
+ vdScriptTokenizerSkipCh(pTokenizer);
+ ch = vdScriptTokenizerGetCh(pTokenizer);
+ }
+ while (RT_C_IS_ALNUM(ch) || ch == '_');
+
+ /* Check whether we got an identifier or an reserved keyword. */
+ for (unsigned i = 0; i < RT_ELEMENTS(g_aKeywords); i++)
+ {
+ if (!RTStrNCmp(g_aKeywords[i].pszKeyword, pszIde, g_aKeywords[i].cchKeyword))
+ {
+ fIsKeyword = true;
+ pToken->enmClass = VDTOKENCLASS_KEYWORD;
+ pToken->Class.Keyword.enmKeyword = g_aKeywords[i].enmKeyword;
+ break;
+ }
+ }
+
+ if (!fIsKeyword)
+ {
+ pToken->enmClass = VDTOKENCLASS_IDENTIFIER;
+ pToken->Class.Ide.pszIde = pszIde;
+ pToken->Class.Ide.cchIde = cchIde;
+ }
+ pToken->Pos.iChEnd += cchIde;
+}
+
+/**
+ * Get a numerical constant from the tokenizer.
+ *
+ * @returns nothing.
+ * @param pTokenizer The tokenizer state.
+ * @param pToken The uninitialized token.
+ */
+static void vdScriptTokenizerGetNumberConst(PVDTOKENIZER pTokenizer, PVDSCRIPTTOKEN pToken)
+{
+ unsigned uBase = 10;
+ char *pszNext = NULL;
+
+ Assert(RT_C_IS_DIGIT(vdScriptTokenizerGetCh(pTokenizer)));
+
+ /* Let RTStrToUInt64Ex() do all the work, looks C compliant :). */
+ pToken->enmClass = VDTOKENCLASS_NUMCONST;
+ int rc = RTStrToUInt64Ex(pTokenizer->pszInput, &pszNext, 0, &pToken->Class.NumConst.u64);
+ Assert(RT_SUCCESS(rc) || rc == VWRN_TRAILING_CHARS || rc == VWRN_TRAILING_SPACES);
+ /** @todo: Handle number to big, throw a warning */
+
+ unsigned cchNumber = pszNext - pTokenizer->pszInput;
+ for (unsigned i = 0; i < cchNumber; i++)
+ vdScriptTokenizerSkipCh(pTokenizer);
+
+ /* Check for a supported suffix, supported are K|M|G. */
+ if (vdScriptTokenizerGetCh(pTokenizer) == 'K')
+ {
+ pToken->Class.NumConst.u64 *= _1K;
+ vdScriptTokenizerSkipCh(pTokenizer);
+ }
+ else if (vdScriptTokenizerGetCh(pTokenizer) == 'M')
+ {
+ pToken->Class.NumConst.u64 *= _1M;
+ vdScriptTokenizerSkipCh(pTokenizer);
+ }
+ else if (vdScriptTokenizerGetCh(pTokenizer) == 'G')
+ {
+ pToken->Class.NumConst.u64 *= _1G;
+ vdScriptTokenizerSkipCh(pTokenizer);
+ }
+}
+
+/**
+ * Parses a string constant.
+ *
+ * @returns nothing.
+ * @param pTokenizer The tokenizer state.
+ * @param pToken The uninitialized token.
+ *
+ * @remarks: No escape sequences allowed at this time.
+ */
+static void vdScriptTokenizerGetStringConst(PVDTOKENIZER pTokenizer, PVDSCRIPTTOKEN pToken)
+{
+ size_t cchStr = 0;
+
+ Assert(vdScriptTokenizerGetCh(pTokenizer) == '\"');
+ vdScriptTokenizerSkipCh(pTokenizer); /* Skip " */
+
+ pToken->enmClass = VDTOKENCLASS_STRINGCONST;
+ pToken->Pos = pTokenizer->Pos;
+ pToken->Class.StringConst.pszString = pTokenizer->pszInput;
+
+ while (vdScriptTokenizerGetCh(pTokenizer) != '\"')
+ {
+ cchStr++;
+ vdScriptTokenizerSkipCh(pTokenizer);
+ }
+
+ vdScriptTokenizerSkipCh(pTokenizer); /* Skip closing " */
+
+ pToken->Class.StringConst.cchString = cchStr;
+ pToken->Pos.iChEnd += cchStr;
+}
+
+/**
+ * Get the end of stream token.
+ *
+ * @returns nothing.
+ * @param pTokenizer The tokenizer state.
+ * @param pToken The uninitialized token.
+ */
+static void vdScriptTokenizerGetEos(PVDTOKENIZER pTokenizer, PVDSCRIPTTOKEN pToken)
+{
+ Assert(vdScriptTokenizerGetCh(pTokenizer) == '\0');
+
+ pToken->enmClass = VDTOKENCLASS_EOS;
+ pToken->Pos = pTokenizer->Pos;
+}
+
+/**
+ * Get operator or punctuator token.
+ *
+ * @returns nothing.
+ * @param pTokenizer The tokenizer state.
+ * @param pToken The uninitialized token.
+ */
+static void vdScriptTokenizerGetOperatorOrPunctuator(PVDTOKENIZER pTokenizer, PVDSCRIPTTOKEN pToken)
+{
+ bool fOpFound = false;
+
+ pToken->enmClass = VDTOKENCLASS_INVALID;
+ pToken->Pos = pTokenizer->Pos;
+
+ /*
+ * Use table based approach here, not the fastest solution but enough for our purpose
+ * for now.
+ */
+ for (unsigned i = 0; i < RT_ELEMENTS(g_aScriptOps); i++)
+ {
+ if (!RTStrNCmp(g_aScriptOps[i].pszOp, pTokenizer->pszInput, g_aScriptOps[i].cchOp))
+ {
+ memset(pToken->Class.Operator.aszOp, 0, sizeof(pToken->Class.Operator.aszOp));
+
+ int rc = RTStrCopy(pToken->Class.Operator.aszOp, sizeof(pToken->Class.Operator.aszOp), g_aScriptOps[i].pszOp);
+ AssertRC(rc);
+
+ pToken->enmClass = VDTOKENCLASS_OPERATORS;
+ pToken->Pos.iChEnd += g_aScriptOps[i].cchOp;
+
+ /** @todo: Make this prettier. */
+ for (unsigned j = 0; j < g_aScriptOps[i].cchOp; j++)
+ vdScriptTokenizerSkipCh(pTokenizer);
+ fOpFound = true;
+ break;
+ }
+ }
+
+ if (!fOpFound)
+ {
+ for (unsigned i = 0; i < RT_ELEMENTS(g_aScriptPunctuators); i++)
+ {
+ if (!RTStrNCmp(g_aScriptPunctuators[i].pszOp, pTokenizer->pszInput, g_aScriptPunctuators[i].cchOp))
+ {
+ pToken->Pos.iChEnd += g_aScriptPunctuators[i].cchOp;
+ pToken->enmClass = VDTOKENCLASS_PUNCTUATOR;
+ pToken->Class.Punctuator.chPunctuator = *g_aScriptPunctuators[i].pszOp;
+
+ vdScriptTokenizerSkipCh(pTokenizer);
+ fOpFound = true;
+ break;
+ }
+ }
+ }
+}
+
+/**
+ * Read the next token from the tokenizer stream.
+ *
+ * @returns nothing.
+ * @param pTokenizer The tokenizer to read from.
+ * @param pToken Uninitialized token to fill the token data into.
+ */
+static void vdScriptTokenizerReadNextToken(PVDTOKENIZER pTokenizer, PVDSCRIPTTOKEN pToken)
+{
+ /* Skip all eventually existing whitespace, newlines and comments first. */
+ vdScriptTokenizerSkipWhitespace(pTokenizer);
+
+ char ch = vdScriptTokenizerGetCh(pTokenizer);
+ if (RT_C_IS_ALPHA(ch) || ch == '_')
+ vdScriptTokenizerGetIdeOrKeyword(pTokenizer, pToken);
+ else if (RT_C_IS_DIGIT(ch))
+ vdScriptTokenizerGetNumberConst(pTokenizer, pToken);
+ else if (ch == '\"')
+ vdScriptTokenizerGetStringConst(pTokenizer, pToken);
+ else if (ch == '\0')
+ vdScriptTokenizerGetEos(pTokenizer, pToken);
+ else
+ vdScriptTokenizerGetOperatorOrPunctuator(pTokenizer, pToken);
+}
+
+/**
+ * Create a new tokenizer.
+ *
+ * @returns Pointer to the new tokenizer state on success.
+ * NULL if out of memory.
+ * @param pszInput The input to create the tokenizer for.
+ */
+static PVDTOKENIZER vdScriptTokenizerCreate(const char *pszInput)
+{
+ PVDTOKENIZER pTokenizer = (PVDTOKENIZER)RTMemAllocZ(sizeof(VDTOKENIZER));
+ if (pTokenizer)
+ {
+ pTokenizer->pszInput = pszInput;
+ pTokenizer->Pos.iLine = 1;
+ pTokenizer->Pos.iChStart = 1;
+ pTokenizer->Pos.iChEnd = 1;
+ pTokenizer->pTokenCurr = &pTokenizer->Token1;
+ pTokenizer->pTokenNext = &pTokenizer->Token2;
+ /* Fill the tokenizer with two first tokens. */
+ vdScriptTokenizerReadNextToken(pTokenizer, pTokenizer->pTokenCurr);
+ vdScriptTokenizerReadNextToken(pTokenizer, pTokenizer->pTokenNext);
+ }
+
+ return pTokenizer;
+}
+
+/**
+ * Destroys a given tokenizer state.
+ *
+ * @returns nothing.
+ * @param pTokenizer The tokenizer to destroy.
+ */
+static void vdScriptTokenizerDestroy(PVDTOKENIZER pTokenizer)
+{
+ RTMemFree(pTokenizer);
+}
+
+/**
+ * Get the current token in the input stream.
+ *
+ * @returns Pointer to the next token in the stream.
+ * @param pTokenizer The tokenizer to destroy.
+ */
+DECLINLINE(PCVDSCRIPTTOKEN) vdScriptTokenizerGetToken(PVDTOKENIZER pTokenizer)
+{
+ return pTokenizer->pTokenCurr;
+}
+
+/**
+ * Get the class of the current token.
+ *
+ * @returns Class of the current token.
+ * @param pTokenizer The tokenizer state.
+ */
+DECLINLINE(VDTOKENCLASS) vdScriptTokenizerGetTokenClass(PVDTOKENIZER pTokenizer)
+{
+ return pTokenizer->pTokenCurr->enmClass;
+}
+
+/**
+ * Returns the token class of the next token in the stream.
+ *
+ * @returns Token class of the next token.
+ * @param pTokenizer The tokenizer state.
+ */
+DECLINLINE(VDTOKENCLASS) vdScriptTokenizerPeekNextClass(PVDTOKENIZER pTokenizer)
+{
+ return pTokenizer->pTokenNext->enmClass;
+}
+
+/**
+ * Consume the current token advancing to the next in the stream.
+ *
+ * @returns nothing.
+ * @param pTokenizer The tokenizer state.
+ */
+static void vdScriptTokenizerConsume(PVDTOKENIZER pTokenizer)
+{
+ PVDSCRIPTTOKEN pTokenTmp = pTokenizer->pTokenCurr;
+
+ /* Switch next token to current token and read in the next token. */
+ pTokenizer->pTokenCurr = pTokenizer->pTokenNext;
+ pTokenizer->pTokenNext = pTokenTmp;
+ vdScriptTokenizerReadNextToken(pTokenizer, pTokenizer->pTokenNext);
+}
+
+/**
+ * Check whether the next token in the input stream is a punctuator and matches the given
+ * character.
+ *
+ * @returns true if the token matched.
+ * false otherwise.
+ * @param pTokenizer The tokenizer state.
+ * @param chCheck The punctuator to check against.
+ */
+static bool vdScriptTokenizerIsPunctuatorEqual(PVDTOKENIZER pTokenizer, char chCheck)
+{
+ PCVDSCRIPTTOKEN pToken = vdScriptTokenizerGetToken(pTokenizer);
+
+ if ( pToken->enmClass == VDTOKENCLASS_PUNCTUATOR
+ && pToken->Class.Punctuator.chPunctuator == chCheck)
+ return true;
+
+ return false;
+}
+
+/**
+ * Check whether the next token in the input stream is a punctuator and matches the given
+ * character and skips it.
+ *
+ * @returns true if the token matched and was skipped.
+ * false otherwise.
+ * @param pTokenizer The tokenizer state.
+ * @param chCheck The punctuator to check against.
+ */
+static bool vdScriptTokenizerSkipIfIsPunctuatorEqual(PVDTOKENIZER pTokenizer, char chCheck)
+{
+ bool fEqual = vdScriptTokenizerIsPunctuatorEqual(pTokenizer, chCheck);
+ if (fEqual)
+ vdScriptTokenizerConsume(pTokenizer);
+
+ return fEqual;
+}
+
+/**
+ * Check whether the next token in the input stream is a keyword and matches the given
+ * keyword.
+ *
+ * @returns true if the token matched.
+ * false otherwise.
+ * @param pTokenizer The tokenizer state.
+ * @param enmKey The keyword to check against.
+ */
+static bool vdScriptTokenizerIsKeywordEqual(PVDTOKENIZER pTokenizer, VDSCRIPTTOKENKEYWORD enmKeyword)
+{
+ PCVDSCRIPTTOKEN pToken = vdScriptTokenizerGetToken(pTokenizer);
+
+ if ( pToken->enmClass == VDTOKENCLASS_KEYWORD
+ && pToken->Class.Keyword.enmKeyword == enmKeyword)
+ return true;
+
+ return false;
+}
+
+/**
+ * Check whether the next token in the input stream is a keyword and matches the given
+ * keyword and skips it.
+ *
+ * @returns true if the token matched and was skipped.
+ * false otherwise.
+ * @param pTokenizer The tokenizer state.
+ * @param enmKey The keyword to check against.
+ */
+static bool vdScriptTokenizerSkipIfIsKeywordEqual(PVDTOKENIZER pTokenizer, VDSCRIPTTOKENKEYWORD enmKeyword)
+{
+ bool fEqual = vdScriptTokenizerIsKeywordEqual(pTokenizer, enmKeyword);
+ if (fEqual)
+ vdScriptTokenizerConsume(pTokenizer);
+
+ return fEqual;
+}
+
+/**
+ * Check whether the next token in the input stream is a keyword and matches the given
+ * keyword.
+ *
+ * @returns true if the token matched.
+ * false otherwise.
+ * @param pTokenizer The tokenizer state.
+ * @param pszOp The operation to check against.
+ */
+static bool vdScriptTokenizerIsOperatorEqual(PVDTOKENIZER pTokenizer, const char *pszOp)
+{
+ PCVDSCRIPTTOKEN pToken = vdScriptTokenizerGetToken(pTokenizer);
+
+ if ( pToken->enmClass == VDTOKENCLASS_OPERATORS
+ && !RTStrCmp(pToken->Class.Operator.aszOp, pszOp))
+ return true;
+
+ return false;
+}
+
+/**
+ * Check whether the next token in the input stream is an operator and matches the given
+ * keyword and skips it.
+ *
+ * @returns true if the token matched and was skipped.
+ * false otherwise.
+ * @param pTokenizer The tokenizer state.
+ * @param pszOp The operation to check against.
+ */
+static bool vdScriptTokenizerSkipIfIsOperatorEqual(PVDTOKENIZER pTokenizer, const char *pszOp)
+{
+ bool fEqual = vdScriptTokenizerIsOperatorEqual(pTokenizer, pszOp);
+ if (fEqual)
+ vdScriptTokenizerConsume(pTokenizer);
+
+ return fEqual;
+}
+
+/**
+ * Record an error while parsing.
+ *
+ * @returns VBox status code passed.
+ */
+static int vdScriptParserError(PVDSCRIPTCTXINT pThis, int rc, RT_SRC_POS_DECL, const char *pszFmt, ...)
+{
+ NOREF(pThis);
+ NOREF(pszFmt);
+ RTPrintf(pszFmt);
+ return rc;
+}
+
+/**
+ * Puts the next identifier AST node on the stack.
+ *
+ * @returns VBox status code.
+ * @param pThis The script context.
+ * @param ppAstNodeIde Where to store the identifier AST node on success.
+ */
+static int vdScriptParseIde(PVDSCRIPTCTXINT pThis, PVDSCRIPTASTIDE *ppAstNodeIde)
+{
+ int rc = VINF_SUCCESS;
+ PCVDSCRIPTTOKEN pToken;
+
+ LogFlowFunc(("pThis=%p ppAstNodeIde=%p\n", pThis, ppAstNodeIde));
+
+ pToken = vdScriptTokenizerGetToken(pThis->pTokenizer);
+ if (pToken->enmClass != VDTOKENCLASS_IDENTIFIER)
+ rc = vdScriptParserError(pThis, VERR_INVALID_PARAMETER, RT_SRC_POS, "Parser: Expected identifer got...\n");
+ else
+ {
+ /* Create new AST node and push onto stack. */
+ PVDSCRIPTASTIDE pAstNodeIde = vdScriptAstNodeIdeAlloc(pToken->Class.Ide.cchIde);
+ if (pAstNodeIde)
+ {
+ rc = RTStrCopyEx(pAstNodeIde->aszIde, pToken->Class.Ide.cchIde + 1, pToken->Class.Ide.pszIde, pToken->Class.Ide.cchIde);
+ AssertRC(rc);
+ pAstNodeIde->cchIde = pToken->Class.Ide.cchIde;
+
+ *ppAstNodeIde = pAstNodeIde;
+ vdScriptTokenizerConsume(pThis->pTokenizer);
+ }
+ else
+ rc = vdScriptParserError(pThis, VERR_NO_MEMORY, RT_SRC_POS, "Parser: Out of memory allocating identifier AST node\n");
+ }
+
+ LogFlowFunc(("returns %Rrc\n", rc));
+ return rc;
+}
+
+/**
+ * Parse a primary expression.
+ *
+ * @returns VBox status code.
+ * @param pThis The script context.
+ * @param ppAstNodeExpr Where to store the primary expression on success.
+ */
+static int vdScriptParsePrimaryExpression(PVDSCRIPTCTXINT pThis, PVDSCRIPTASTEXPR *ppAstNodeExpr)
+{
+ int rc = VINF_SUCCESS;
+
+ LogFlowFunc(("pThis=%p ppAstNodeExpr=%p\n"));
+
+ if (vdScriptTokenizerSkipIfIsPunctuatorEqual(pThis->pTokenizer, '('))
+ {
+ rc = vdScriptParseExpression(pThis, ppAstNodeExpr);
+ if (RT_SUCCESS(rc)
+ && !vdScriptTokenizerSkipIfIsPunctuatorEqual(pThis->pTokenizer, ')'))
+ rc = vdScriptParserError(pThis, VERR_INVALID_PARAMETER, RT_SRC_POS, "Parser: Expected \")\", got ...\n");
+ }
+ else
+ {
+ PVDSCRIPTASTEXPR pExpr = (PVDSCRIPTASTEXPR)vdScriptAstNodeAlloc(VDSCRIPTASTCLASS_EXPRESSION);
+ if (pExpr)
+ {
+ if (vdScriptTokenizerGetTokenClass(pThis->pTokenizer) == VDTOKENCLASS_IDENTIFIER)
+ {
+ PVDSCRIPTASTIDE pIde = NULL;
+ rc = vdScriptParseIde(pThis, &pIde);
+ if (RT_SUCCESS(rc))
+ {
+ pExpr->enmType = VDSCRIPTEXPRTYPE_PRIMARY_IDENTIFIER;
+ pExpr->pIde = pIde;
+ }
+ }
+ else if (vdScriptTokenizerGetTokenClass(pThis->pTokenizer) == VDTOKENCLASS_NUMCONST)
+ {
+ PCVDSCRIPTTOKEN pToken = vdScriptTokenizerGetToken(pThis->pTokenizer);
+ pExpr->enmType = VDSCRIPTEXPRTYPE_PRIMARY_NUMCONST;
+ pExpr->u64 = pToken->Class.NumConst.u64;
+ vdScriptTokenizerConsume(pThis->pTokenizer);
+ }
+ else if (vdScriptTokenizerGetTokenClass(pThis->pTokenizer) == VDTOKENCLASS_STRINGCONST)
+ {
+ PCVDSCRIPTTOKEN pToken = vdScriptTokenizerGetToken(pThis->pTokenizer);
+ pExpr->enmType = VDSCRIPTEXPRTYPE_PRIMARY_STRINGCONST;
+ pExpr->pszStr = RTStrDupN(pToken->Class.StringConst.pszString, pToken->Class.StringConst.cchString);
+ vdScriptTokenizerConsume(pThis->pTokenizer);
+
+ if (!pExpr->pszStr)
+ rc = vdScriptParserError(pThis, VERR_NO_MEMORY, RT_SRC_POS, "Parser: Out of memory allocating string\n");
+ }
+ else if (vdScriptTokenizerGetTokenClass(pThis->pTokenizer) == VDTOKENCLASS_KEYWORD)
+ {
+ PCVDSCRIPTTOKEN pToken = vdScriptTokenizerGetToken(pThis->pTokenizer);
+ pExpr->enmType = VDSCRIPTEXPRTYPE_PRIMARY_BOOLEAN;
+
+ if (pToken->Class.Keyword.enmKeyword == VDSCRIPTTOKENKEYWORD_TRUE)
+ pExpr->f = true;
+ else if (pToken->Class.Keyword.enmKeyword == VDSCRIPTTOKENKEYWORD_FALSE)
+ pExpr->f = false;
+ else
+ rc = vdScriptParserError(pThis, VERR_INVALID_PARAMETER, RT_SRC_POS, "Parser: Unexpected keyword, expected true or false\n");
+ vdScriptTokenizerConsume(pThis->pTokenizer);
+ }
+ else
+ rc = vdScriptParserError(pThis, VERR_INVALID_PARAMETER, RT_SRC_POS, "Parser: Expected \"(\" | identifier | constant | string, got ...\n");
+
+ if (RT_FAILURE(rc))
+ vdScriptAstNodeFree(&pExpr->Core);
+ else
+ *ppAstNodeExpr = pExpr;
+ }
+ else
+ rc = vdScriptParserError(pThis, VERR_NO_MEMORY, RT_SRC_POS, "Parser: Out of memory allocating expression AST node\n");
+ }
+
+ LogFlowFunc(("returns rc=%Rrc\n", rc));
+ return rc;
+}
+
+/**
+ * Parse an argument list for a function call.
+ *
+ * @returns VBox status code.
+ * @param pThis The script context.
+ * @param pFnCall The function call AST node.
+ */
+static int vdScriptParseFnCallArgumentList(PVDSCRIPTCTXINT pThis, PVDSCRIPTASTEXPR pFnCall)
+{
+ int rc = VINF_SUCCESS;
+ PVDSCRIPTASTEXPR pExpr = NULL;
+
+ LogFlowFunc(("pThis=%p ppAstNodeExpr=%p\n"));
+
+ rc = vdScriptParseAssignmentExpression(pThis, &pExpr);
+ if (RT_SUCCESS(rc))
+ {
+ RTListAppend(&pFnCall->FnCall.ListArgs, &pExpr->Core.ListNode);
+ while (vdScriptTokenizerSkipIfIsPunctuatorEqual(pThis->pTokenizer, ','))
+ {
+ rc = vdScriptParseAssignmentExpression(pThis, &pExpr);
+ if (RT_SUCCESS(rc))
+ RTListAppend(&pFnCall->FnCall.ListArgs, &pExpr->Core.ListNode);
+ else
+ break;
+ }
+ if ( RT_SUCCESS(rc)
+ && !vdScriptTokenizerSkipIfIsPunctuatorEqual(pThis->pTokenizer, ')'))
+ rc = vdScriptParserError(pThis, VERR_INVALID_PARAMETER, RT_SRC_POS, "Parser: Expected \")\", got ...\n");
+ }
+
+ LogFlowFunc(("returns rc=%Rrc\n", rc));
+ return rc;
+}
+
+/**
+ * Parse a postfix expression.
+ *
+ * @returns VBox status code.
+ * @param pThis The script context.
+ * @param ppAstNodeExpr Where to store the expression AST node on success.
+ *
+ * @note Syntax:
+ * postfix-expression:
+ * primary-expression
+ * postfix-expression ( argument-expression )
+ * postfix-expression ++
+ * postfix-expression --
+ */
+static int vdScriptParsePostfixExpression(PVDSCRIPTCTXINT pThis, PVDSCRIPTASTEXPR *ppAstNodeExpr)
+{
+ int rc = VINF_SUCCESS;
+ PVDSCRIPTASTEXPR pExpr = NULL;
+
+ LogFlowFunc(("pThis=%p ppAstNodeExpr=%p\n"));
+
+ rc = vdScriptParsePrimaryExpression(pThis, &pExpr);
+ if (RT_SUCCESS(rc))
+ {
+ while (true)
+ {
+ PVDSCRIPTASTEXPR pExprNew = NULL;
+
+ if (vdScriptTokenizerSkipIfIsOperatorEqual(pThis->pTokenizer, "++"))
+ {
+ pExprNew = (PVDSCRIPTASTEXPR)vdScriptAstNodeAlloc(VDSCRIPTASTCLASS_EXPRESSION);
+ if (pExprNew)
+ {
+ pExprNew->enmType = VDSCRIPTEXPRTYPE_POSTFIX_INCREMENT;
+ pExprNew->pExpr = pExpr;
+ pExpr = pExprNew;
+ }
+ else
+ rc = vdScriptParserError(pThis, VERR_NO_MEMORY, RT_SRC_POS, "Parser: Out of memory allocating expression AST node\n");
+ }
+ else if (vdScriptTokenizerSkipIfIsOperatorEqual(pThis->pTokenizer, "--"))
+ {
+ pExprNew = (PVDSCRIPTASTEXPR)vdScriptAstNodeAlloc(VDSCRIPTASTCLASS_EXPRESSION);
+ if (pExprNew)
+ {
+ pExprNew->enmType = VDSCRIPTEXPRTYPE_POSTFIX_DECREMENT;
+ pExprNew->pExpr = pExpr;
+ pExpr = pExprNew;
+ }
+ else
+ rc = vdScriptParserError(pThis, VERR_NO_MEMORY, RT_SRC_POS, "Parser: Out of memory allocating expression AST node\n");
+ }
+ else if (vdScriptTokenizerSkipIfIsPunctuatorEqual(pThis->pTokenizer, '('))
+ {
+ pExprNew = (PVDSCRIPTASTEXPR)vdScriptAstNodeAlloc(VDSCRIPTASTCLASS_EXPRESSION);
+ if (pExprNew)
+ {
+ pExprNew->enmType = VDSCRIPTEXPRTYPE_POSTFIX_FNCALL;
+ RTListInit(&pExprNew->FnCall.ListArgs);
+ if (!vdScriptTokenizerSkipIfIsPunctuatorEqual(pThis->pTokenizer, ')'))
+ rc = vdScriptParseFnCallArgumentList(pThis, pExprNew);
+ pExprNew->FnCall.pFnIde = pExpr;
+ pExpr = pExprNew;
+ }
+ else
+ rc = vdScriptParserError(pThis, VERR_NO_MEMORY, RT_SRC_POS, "Parser: Out of memory allocating expression AST node\n");
+ }
+ else
+ break;
+
+ if (RT_FAILURE(rc))
+ break;
+ }
+
+ if (RT_SUCCESS(rc))
+ *ppAstNodeExpr = pExpr;
+ else
+ vdScriptAstNodeFree(&pExpr->Core);
+ }
+
+ LogFlowFunc(("returns rc=%Rrc\n", rc));
+ return rc;
+}
+
+/**
+ * Parse an unary expression.
+ *
+ * @returns VBox status code.
+ * @param pThis The script context.
+ * @param ppAstNodeExpr Where to store the expression AST node on success.
+ *
+ * @note Syntax:
+ * unary-expression:
+ * postfix-expression
+ * ++ unary-expression
+ * -- unary-expression
+ * + unary-expression
+ * - unary-expression
+ * ~ unary-expression
+ * ! unary-expression
+ */
+static int vdScriptParseUnaryExpression(PVDSCRIPTCTXINT pThis, PVDSCRIPTASTEXPR *ppAstNodeExpr)
+{
+ int rc = VINF_SUCCESS;
+ PVDSCRIPTASTEXPR pExpr = NULL;
+ PVDSCRIPTASTEXPR pExprTop = NULL;
+
+ LogFlowFunc(("pThis=%p ppAstNodeExpr=%p\n"));
+
+ while (true)
+ {
+ bool fQuit = false;
+ PVDSCRIPTASTEXPR pExprNew = NULL;
+
+ if (vdScriptTokenizerSkipIfIsOperatorEqual(pThis->pTokenizer, "++"))
+ {
+ pExprNew = (PVDSCRIPTASTEXPR)vdScriptAstNodeAlloc(VDSCRIPTASTCLASS_EXPRESSION);
+ if (pExprNew)
+ pExprNew->enmType = VDSCRIPTEXPRTYPE_UNARY_INCREMENT;
+ else
+ rc = vdScriptParserError(pThis, VERR_NO_MEMORY, RT_SRC_POS, "Parser: Out of memory allocating expression AST node\n");
+ }
+ else if (vdScriptTokenizerSkipIfIsOperatorEqual(pThis->pTokenizer, "--"))
+ {
+ pExprNew = (PVDSCRIPTASTEXPR)vdScriptAstNodeAlloc(VDSCRIPTASTCLASS_EXPRESSION);
+ if (pExprNew)
+ pExprNew->enmType = VDSCRIPTEXPRTYPE_UNARY_DECREMENT;
+ else
+ rc = vdScriptParserError(pThis, VERR_NO_MEMORY, RT_SRC_POS, "Parser: Out of memory allocating expression AST node\n");
+ }
+ else if (vdScriptTokenizerSkipIfIsOperatorEqual(pThis->pTokenizer, "+"))
+ {
+ pExprNew = (PVDSCRIPTASTEXPR)vdScriptAstNodeAlloc(VDSCRIPTASTCLASS_EXPRESSION);
+ if (pExprNew)
+ pExprNew->enmType = VDSCRIPTEXPRTYPE_UNARY_POSSIGN;
+ else
+ rc = vdScriptParserError(pThis, VERR_NO_MEMORY, RT_SRC_POS, "Parser: Out of memory allocating expression AST node\n");
+ }
+ else if (vdScriptTokenizerSkipIfIsOperatorEqual(pThis->pTokenizer, "-"))
+ {
+ pExprNew = (PVDSCRIPTASTEXPR)vdScriptAstNodeAlloc(VDSCRIPTASTCLASS_EXPRESSION);
+ if (pExprNew)
+ pExprNew->enmType = VDSCRIPTEXPRTYPE_UNARY_NEGSIGN;
+ else
+ rc = vdScriptParserError(pThis, VERR_NO_MEMORY, RT_SRC_POS, "Parser: Out of memory allocating expression AST node\n");
+ }
+ else if (vdScriptTokenizerSkipIfIsOperatorEqual(pThis->pTokenizer, "~"))
+ {
+ pExprNew = (PVDSCRIPTASTEXPR)vdScriptAstNodeAlloc(VDSCRIPTASTCLASS_EXPRESSION);
+ if (pExprNew)
+ pExprNew->enmType = VDSCRIPTEXPRTYPE_UNARY_INVERT;
+ else
+ rc = vdScriptParserError(pThis, VERR_NO_MEMORY, RT_SRC_POS, "Parser: Out of memory allocating expression AST node\n");
+ }
+ else if (vdScriptTokenizerSkipIfIsOperatorEqual(pThis->pTokenizer, "!"))
+ {
+ pExprNew = (PVDSCRIPTASTEXPR)vdScriptAstNodeAlloc(VDSCRIPTASTCLASS_EXPRESSION);
+ if (pExprNew)
+ pExprNew->enmType = VDSCRIPTEXPRTYPE_UNARY_NEGATE;
+ else
+ rc = vdScriptParserError(pThis, VERR_NO_MEMORY, RT_SRC_POS, "Parser: Out of memory allocating expression AST node\n");
+ }
+ else
+ {
+ /* Must be a postfix expression. */
+ rc = vdScriptParsePostfixExpression(pThis, &pExprNew);
+ fQuit = true;
+ }
+
+ if (RT_SUCCESS(rc))
+ {
+ if (!pExprTop)
+ {
+ pExprTop = pExprNew;
+ pExpr = pExprNew;
+ }
+ else
+ {
+ pExpr->pExpr = pExprNew;
+ pExpr = pExprNew;
+ }
+ if (fQuit)
+ break;
+ }
+ else
+ break;
+ }
+
+ if (RT_SUCCESS(rc))
+ *ppAstNodeExpr = pExprTop;
+ else if (pExprTop)
+ vdScriptAstNodeFree(&pExprTop->Core);
+
+ LogFlowFunc(("returns rc=%Rrc\n", rc));
+ return rc;
+}
+
+/**
+ * Parse a multiplicative expression.
+ *
+ * @returns VBox status code.
+ * @param pThis The script context.
+ * @param ppAstNodeExpr Where to store the expression AST node on success.
+ *
+ * @note Syntax:
+ * multiplicative-expression:
+ * unary-expression
+ * multiplicative-expression * unary-expression
+ * multiplicative-expression / unary-expression
+ * multiplicative-expression % unary-expression
+ */
+static int vdScriptParseMultiplicativeExpression(PVDSCRIPTCTXINT pThis, PVDSCRIPTASTEXPR *ppAstNodeExpr)
+{
+ int rc = VINF_SUCCESS;
+ PVDSCRIPTASTEXPR pExpr = NULL;
+
+ LogFlowFunc(("pThis=%p ppAstNodeExpr=%p\n"));
+
+ rc = vdScriptParseUnaryExpression(pThis, &pExpr);
+ if (RT_SUCCESS(rc))
+ {
+ PVDSCRIPTASTEXPR pExprNew = NULL;
+ while (RT_SUCCESS(rc))
+ {
+ if (vdScriptTokenizerSkipIfIsOperatorEqual(pThis->pTokenizer, "*"))
+ {
+ pExprNew = (PVDSCRIPTASTEXPR)vdScriptAstNodeAlloc(VDSCRIPTASTCLASS_EXPRESSION);
+ if (pExprNew)
+ pExprNew->enmType = VDSCRIPTEXPRTYPE_MULTIPLICATION;
+ else
+ rc = vdScriptParserError(pThis, VERR_NO_MEMORY, RT_SRC_POS, "Parser: Out of memory allocating expression AST node\n");
+ }
+ else if (vdScriptTokenizerSkipIfIsOperatorEqual(pThis->pTokenizer, "/"))
+ {
+ pExprNew = (PVDSCRIPTASTEXPR)vdScriptAstNodeAlloc(VDSCRIPTASTCLASS_EXPRESSION);
+ if (pExprNew)
+ pExprNew->enmType = VDSCRIPTEXPRTYPE_DIVISION;
+ else
+ rc = vdScriptParserError(pThis, VERR_NO_MEMORY, RT_SRC_POS, "Parser: Out of memory allocating expression AST node\n");
+ }
+ else if (vdScriptTokenizerSkipIfIsOperatorEqual(pThis->pTokenizer, "%"))
+ {
+ pExprNew = (PVDSCRIPTASTEXPR)vdScriptAstNodeAlloc(VDSCRIPTASTCLASS_EXPRESSION);
+ if (pExprNew)
+ pExprNew->enmType = VDSCRIPTEXPRTYPE_MODULUS;
+ else
+ rc = vdScriptParserError(pThis, VERR_NO_MEMORY, RT_SRC_POS, "Parser: Out of memory allocating expression AST node\n");
+ }
+ else
+ break;
+
+ pExprNew->BinaryOp.pLeftExpr = pExpr;
+ pExpr = pExprNew;
+ rc = vdScriptParseUnaryExpression(pThis, &pExprNew);
+ if (RT_SUCCESS(rc))
+ pExpr->BinaryOp.pRightExpr = pExprNew;
+ }
+
+ if (RT_SUCCESS(rc))
+ *ppAstNodeExpr = pExpr;
+ else
+ vdScriptAstNodeFree(&pExpr->Core);
+ }
+
+ LogFlowFunc(("returns rc=%Rrc\n", rc));
+ return rc;
+}
+
+/**
+ * Parse a additive expression.
+ *
+ * @returns VBox status code.
+ * @param pThis The script context.
+ * @param ppAstNodeExpr Where to store the expression AST node on success.
+ *
+ * @note Syntax:
+ * additive-expression:
+ * multiplicative-expression
+ * additive-expression + multiplicative-expression
+ * additive-expression - multiplicative-expression
+ */
+static int vdScriptParseAdditiveExpression(PVDSCRIPTCTXINT pThis, PVDSCRIPTASTEXPR *ppAstNodeExpr)
+{
+ int rc = VINF_SUCCESS;
+ PVDSCRIPTASTEXPR pExpr = NULL;
+
+ LogFlowFunc(("pThis=%p ppAstNodeExpr=%p\n"));
+
+ rc = vdScriptParseMultiplicativeExpression(pThis, &pExpr);
+ if (RT_SUCCESS(rc))
+ {
+ PVDSCRIPTASTEXPR pExprNew = NULL;
+ while (RT_SUCCESS(rc))
+ {
+ if (vdScriptTokenizerSkipIfIsOperatorEqual(pThis->pTokenizer, "+"))
+ {
+ pExprNew = (PVDSCRIPTASTEXPR)vdScriptAstNodeAlloc(VDSCRIPTASTCLASS_EXPRESSION);
+ if (pExprNew)
+ pExprNew->enmType = VDSCRIPTEXPRTYPE_ADDITION;
+ else
+ rc = vdScriptParserError(pThis, VERR_NO_MEMORY, RT_SRC_POS, "Parser: Out of memory allocating expression AST node\n");
+ }
+ else if (vdScriptTokenizerSkipIfIsOperatorEqual(pThis->pTokenizer, "-"))
+ {
+ pExprNew = (PVDSCRIPTASTEXPR)vdScriptAstNodeAlloc(VDSCRIPTASTCLASS_EXPRESSION);
+ if (pExprNew)
+ pExprNew->enmType = VDSCRIPTEXPRTYPE_SUBTRACTION;
+ else
+ rc = vdScriptParserError(pThis, VERR_NO_MEMORY, RT_SRC_POS, "Parser: Out of memory allocating expression AST node\n");
+ }
+ else
+ break;
+
+ pExprNew->BinaryOp.pLeftExpr = pExpr;
+ pExpr = pExprNew;
+ rc = vdScriptParseMultiplicativeExpression(pThis, &pExprNew);
+ if (RT_SUCCESS(rc))
+ pExpr->BinaryOp.pRightExpr = pExprNew;
+ }
+
+ if (RT_SUCCESS(rc))
+ *ppAstNodeExpr = pExpr;
+ else
+ vdScriptAstNodeFree(&pExpr->Core);
+ }
+
+ LogFlowFunc(("returns rc=%Rrc\n", rc));
+ return rc;
+}
+
+/**
+ * Parse a shift expression.
+ *
+ * @returns VBox status code.
+ * @param pThis The script context.
+ * @param ppAstNodeExpr Where to store the expression AST node on success.
+ *
+ * @note Syntax:
+ * shift-expression:
+ * additive-expression
+ * shift-expression << additive-expression
+ * shift-expression >> additive-expression
+ */
+static int vdScriptParseShiftExpression(PVDSCRIPTCTXINT pThis, PVDSCRIPTASTEXPR *ppAstNodeExpr)
+{
+ int rc = VINF_SUCCESS;
+ PVDSCRIPTASTEXPR pExpr = NULL;
+
+ LogFlowFunc(("pThis=%p ppAstNodeExpr=%p\n"));
+
+ rc = vdScriptParseAdditiveExpression(pThis, &pExpr);
+ if (RT_SUCCESS(rc))
+ {
+ PVDSCRIPTASTEXPR pExprNew = NULL;
+ while (RT_SUCCESS(rc))
+ {
+ if (vdScriptTokenizerSkipIfIsOperatorEqual(pThis->pTokenizer, "<<"))
+ {
+ pExprNew = (PVDSCRIPTASTEXPR)vdScriptAstNodeAlloc(VDSCRIPTASTCLASS_EXPRESSION);
+ if (pExprNew)
+ pExprNew->enmType = VDSCRIPTEXPRTYPE_LSL;
+ else
+ rc = vdScriptParserError(pThis, VERR_NO_MEMORY, RT_SRC_POS, "Parser: Out of memory allocating expression AST node\n");
+ }
+ else if (vdScriptTokenizerSkipIfIsOperatorEqual(pThis->pTokenizer, ">>"))
+ {
+ pExprNew = (PVDSCRIPTASTEXPR)vdScriptAstNodeAlloc(VDSCRIPTASTCLASS_EXPRESSION);
+ if (pExprNew)
+ pExprNew->enmType = VDSCRIPTEXPRTYPE_LSR;
+ else
+ rc = vdScriptParserError(pThis, VERR_NO_MEMORY, RT_SRC_POS, "Parser: Out of memory allocating expression AST node\n");
+ }
+ else
+ break;
+
+ pExprNew->BinaryOp.pLeftExpr = pExpr;
+ pExpr = pExprNew;
+ rc = vdScriptParseAdditiveExpression(pThis, &pExprNew);
+ if (RT_SUCCESS(rc))
+ pExpr->BinaryOp.pRightExpr = pExprNew;
+ }
+
+ if (RT_SUCCESS(rc))
+ *ppAstNodeExpr = pExpr;
+ else
+ vdScriptAstNodeFree(&pExpr->Core);
+ }
+
+ LogFlowFunc(("returns rc=%Rrc\n", rc));
+ return rc;
+}
+
+/**
+ * Parse a relational expression.
+ *
+ * @returns VBox status code.
+ * @param pThis The script context.
+ * @param ppAstNodeExpr Where to store the expression AST node on success.
+ *
+ * @note Syntax:
+ * relational-expression:
+ * shift-expression
+ * relational-expression < shift-expression
+ * relational-expression > shift-expression
+ * relational-expression >= shift-expression
+ * relational-expression <= shift-expression
+ */
+static int vdScriptParseRelationalExpression(PVDSCRIPTCTXINT pThis, PVDSCRIPTASTEXPR *ppAstNodeExpr)
+{
+ int rc = VINF_SUCCESS;
+ PVDSCRIPTASTEXPR pExpr = NULL;
+
+ LogFlowFunc(("pThis=%p ppAstNodeExpr=%p\n"));
+
+ rc = vdScriptParseShiftExpression(pThis, &pExpr);
+ if (RT_SUCCESS(rc))
+ {
+ PVDSCRIPTASTEXPR pExprNew = NULL;
+ while (RT_SUCCESS(rc))
+ {
+ if (vdScriptTokenizerSkipIfIsOperatorEqual(pThis->pTokenizer, "<"))
+ {
+ pExprNew = (PVDSCRIPTASTEXPR)vdScriptAstNodeAlloc(VDSCRIPTASTCLASS_EXPRESSION);
+ if (pExprNew)
+ pExprNew->enmType = VDSCRIPTEXPRTYPE_LOWER;
+ else
+ rc = vdScriptParserError(pThis, VERR_NO_MEMORY, RT_SRC_POS, "Parser: Out of memory allocating expression AST node\n");
+ }
+ else if (vdScriptTokenizerSkipIfIsOperatorEqual(pThis->pTokenizer, ">"))
+ {
+ pExprNew = (PVDSCRIPTASTEXPR)vdScriptAstNodeAlloc(VDSCRIPTASTCLASS_EXPRESSION);
+ if (pExprNew)
+ pExprNew->enmType = VDSCRIPTEXPRTYPE_HIGHER;
+ else
+ rc = vdScriptParserError(pThis, VERR_NO_MEMORY, RT_SRC_POS, "Parser: Out of memory allocating expression AST node\n");
+ }
+ else if (vdScriptTokenizerSkipIfIsOperatorEqual(pThis->pTokenizer, ">="))
+ {
+ pExprNew = (PVDSCRIPTASTEXPR)vdScriptAstNodeAlloc(VDSCRIPTASTCLASS_EXPRESSION);
+ if (pExprNew)
+ pExprNew->enmType = VDSCRIPTEXPRTYPE_HIGHEREQUAL;
+ else
+ rc = vdScriptParserError(pThis, VERR_NO_MEMORY, RT_SRC_POS, "Parser: Out of memory allocating expression AST node\n");
+ }
+ else if (vdScriptTokenizerSkipIfIsOperatorEqual(pThis->pTokenizer, "<="))
+ {
+ pExprNew = (PVDSCRIPTASTEXPR)vdScriptAstNodeAlloc(VDSCRIPTASTCLASS_EXPRESSION);
+ if (pExprNew)
+ pExprNew->enmType = VDSCRIPTEXPRTYPE_LOWEREQUAL;
+ else
+ rc = vdScriptParserError(pThis, VERR_NO_MEMORY, RT_SRC_POS, "Parser: Out of memory allocating expression AST node\n");
+ }
+ else
+ break;
+
+ pExprNew->BinaryOp.pLeftExpr = pExpr;
+ pExpr = pExprNew;
+ rc = vdScriptParseShiftExpression(pThis, &pExprNew);
+ if (RT_SUCCESS(rc))
+ pExpr->BinaryOp.pRightExpr = pExprNew;
+ }
+
+ if (RT_SUCCESS(rc))
+ *ppAstNodeExpr = pExpr;
+ else
+ vdScriptAstNodeFree(&pExpr->Core);
+ }
+
+ LogFlowFunc(("returns rc=%Rrc\n", rc));
+ return rc;
+}
+
+/**
+ * Parse a equality expression.
+ *
+ * @returns VBox status code.
+ * @param pThis The script context.
+ * @param ppAstNodeExpr Where to store the expression AST node on success.
+ *
+ * @note Syntax:
+ * equality-expression:
+ * relational-expression
+ * equality-expression == relational-expression
+ * equality-expression != relational-expression
+ */
+static int vdScriptParseEqualityExpression(PVDSCRIPTCTXINT pThis, PVDSCRIPTASTEXPR *ppAstNodeExpr)
+{
+ int rc = VINF_SUCCESS;
+ PVDSCRIPTASTEXPR pExpr = NULL;
+
+ LogFlowFunc(("pThis=%p ppAstNodeExpr=%p\n"));
+
+ rc = vdScriptParseRelationalExpression(pThis, &pExpr);
+ if (RT_SUCCESS(rc))
+ {
+ PVDSCRIPTASTEXPR pExprNew = NULL;
+ while (RT_SUCCESS(rc))
+ {
+ if (vdScriptTokenizerSkipIfIsOperatorEqual(pThis->pTokenizer, "=="))
+ {
+ pExprNew = (PVDSCRIPTASTEXPR)vdScriptAstNodeAlloc(VDSCRIPTASTCLASS_EXPRESSION);
+ if (pExprNew)
+ pExprNew->enmType = VDSCRIPTEXPRTYPE_EQUAL;
+ else
+ rc = vdScriptParserError(pThis, VERR_NO_MEMORY, RT_SRC_POS, "Parser: Out of memory allocating expression AST node\n");
+ }
+ else if (vdScriptTokenizerSkipIfIsOperatorEqual(pThis->pTokenizer, "!="))
+ {
+ pExprNew = (PVDSCRIPTASTEXPR)vdScriptAstNodeAlloc(VDSCRIPTASTCLASS_EXPRESSION);
+ if (pExprNew)
+ pExprNew->enmType = VDSCRIPTEXPRTYPE_NOTEQUAL;
+ else
+ rc = vdScriptParserError(pThis, VERR_NO_MEMORY, RT_SRC_POS, "Parser: Out of memory allocating expression AST node\n");
+ }
+ else
+ break;
+
+ pExprNew->BinaryOp.pLeftExpr = pExpr;
+ pExpr = pExprNew;
+ rc = vdScriptParseRelationalExpression(pThis, &pExprNew);
+ if (RT_SUCCESS(rc))
+ pExpr->BinaryOp.pRightExpr = pExprNew;
+ }
+
+ if (RT_SUCCESS(rc))
+ *ppAstNodeExpr = pExpr;
+ else
+ vdScriptAstNodeFree(&pExpr->Core);
+ }
+
+ LogFlowFunc(("returns rc=%Rrc\n", rc));
+ return rc;
+}
+
+/**
+ * Parse a bitwise and expression.
+ *
+ * @returns VBox status code.
+ * @param pThis The script context.
+ * @param ppAstNodeExpr Where to store the expression AST node on success.
+ *
+ * @note Syntax:
+ * and-expression:
+ * equality-expression
+ * and-expression & equality-expression
+ */
+static int vdScriptParseBitwiseAndExpression(PVDSCRIPTCTXINT pThis, PVDSCRIPTASTEXPR *ppAstNodeExpr)
+{
+ int rc = VINF_SUCCESS;
+ PVDSCRIPTASTEXPR pExpr = NULL;
+
+ LogFlowFunc(("pThis=%p ppAstNodeExpr=%p\n"));
+
+ rc = vdScriptParseEqualityExpression(pThis, &pExpr);
+ if (RT_SUCCESS(rc))
+ {
+ PVDSCRIPTASTEXPR pExprNew = NULL;
+ while ( RT_SUCCESS(rc)
+ && vdScriptTokenizerSkipIfIsOperatorEqual(pThis->pTokenizer, "&"))
+ {
+ pExprNew = (PVDSCRIPTASTEXPR)vdScriptAstNodeAlloc(VDSCRIPTASTCLASS_EXPRESSION);
+ if (pExprNew)
+ {
+ pExprNew->enmType = VDSCRIPTEXPRTYPE_EQUAL;
+ pExprNew->BinaryOp.pLeftExpr = pExpr;
+ pExpr = pExprNew;
+ rc = vdScriptParseEqualityExpression(pThis, &pExprNew);
+ if (RT_SUCCESS(rc))
+ pExpr->BinaryOp.pRightExpr = pExprNew;
+ }
+ else
+ rc = vdScriptParserError(pThis, VERR_NO_MEMORY, RT_SRC_POS, "Parser: Out of memory allocating expression AST node\n");
+ }
+
+ if (RT_SUCCESS(rc))
+ *ppAstNodeExpr = pExpr;
+ else
+ vdScriptAstNodeFree(&pExpr->Core);
+ }
+
+ LogFlowFunc(("returns rc=%Rrc\n", rc));
+ return rc;
+}
+
+/**
+ * Parse a bitwise xor expression.
+ *
+ * @returns VBox status code.
+ * @param pThis The script context.
+ * @param ppAstNodeExpr Where to store the expression AST node on success.
+ *
+ * @note Syntax:
+ * xor-expression:
+ * and-expression
+ * xor-expression ^ equality-expression
+ */
+static int vdScriptParseBitwiseXorExpression(PVDSCRIPTCTXINT pThis, PVDSCRIPTASTEXPR *ppAstNodeExpr)
+{
+ int rc = VINF_SUCCESS;
+ PVDSCRIPTASTEXPR pExpr = NULL;
+
+ LogFlowFunc(("pThis=%p ppAstNodeExpr=%p\n"));
+
+ rc = vdScriptParseBitwiseAndExpression(pThis, &pExpr);
+ if (RT_SUCCESS(rc))
+ {
+ PVDSCRIPTASTEXPR pExprNew = NULL;
+ while ( RT_SUCCESS(rc)
+ && vdScriptTokenizerSkipIfIsOperatorEqual(pThis->pTokenizer, "^"))
+ {
+ pExprNew = (PVDSCRIPTASTEXPR)vdScriptAstNodeAlloc(VDSCRIPTASTCLASS_EXPRESSION);
+ if (pExprNew)
+ {
+ pExprNew->enmType = VDSCRIPTEXPRTYPE_BITWISE_XOR;
+ pExprNew->BinaryOp.pLeftExpr = pExpr;
+ pExpr = pExprNew;
+ rc = vdScriptParseBitwiseAndExpression(pThis, &pExprNew);
+ if (RT_SUCCESS(rc))
+ pExpr->BinaryOp.pRightExpr = pExprNew;
+ }
+ else
+ rc = vdScriptParserError(pThis, VERR_NO_MEMORY, RT_SRC_POS, "Parser: Out of memory allocating expression AST node\n");
+ }
+
+ if (RT_SUCCESS(rc))
+ *ppAstNodeExpr = pExpr;
+ else
+ vdScriptAstNodeFree(&pExpr->Core);
+ }
+
+ LogFlowFunc(("returns rc=%Rrc\n", rc));
+ return rc;
+}
+
+/**
+ * Parse a bitwise or expression.
+ *
+ * @returns VBox status code.
+ * @param pThis The script context.
+ * @param ppAstNodeExpr Where to store the expression AST node on success.
+ *
+ * @note Syntax:
+ * or-expression:
+ * xor-expression
+ * or-expression | xor-expression
+ */
+static int vdScriptParseBitwiseOrExpression(PVDSCRIPTCTXINT pThis, PVDSCRIPTASTEXPR *ppAstNodeExpr)
+{
+ int rc = VINF_SUCCESS;
+ PVDSCRIPTASTEXPR pExpr = NULL;
+
+ LogFlowFunc(("pThis=%p ppAstNodeExpr=%p\n"));
+
+ rc = vdScriptParseBitwiseXorExpression(pThis, &pExpr);
+ if (RT_SUCCESS(rc))
+ {
+ PVDSCRIPTASTEXPR pExprNew = NULL;
+ while ( RT_SUCCESS(rc)
+ && vdScriptTokenizerSkipIfIsOperatorEqual(pThis->pTokenizer, "|"))
+ {
+ pExprNew = (PVDSCRIPTASTEXPR)vdScriptAstNodeAlloc(VDSCRIPTASTCLASS_EXPRESSION);
+ if (pExprNew)
+ {
+ pExprNew->enmType = VDSCRIPTEXPRTYPE_BITWISE_OR;
+ pExprNew->BinaryOp.pLeftExpr = pExpr;
+ pExpr = pExprNew;
+ rc = vdScriptParseBitwiseXorExpression(pThis, &pExprNew);
+ if (RT_SUCCESS(rc))
+ pExpr->BinaryOp.pRightExpr = pExprNew;
+ }
+ else
+ rc = vdScriptParserError(pThis, VERR_NO_MEMORY, RT_SRC_POS, "Parser: Out of memory allocating expression AST node\n");
+ }
+
+ if (RT_SUCCESS(rc))
+ *ppAstNodeExpr = pExpr;
+ else
+ vdScriptAstNodeFree(&pExpr->Core);
+ }
+
+ LogFlowFunc(("returns rc=%Rrc\n", rc));
+ return rc;
+}
+
+/**
+ * Parse a logical and expression.
+ *
+ * @returns VBox status code.
+ * @param pThis The script context.
+ * @param ppAstNodeExpr Where to store the expression AST node on success.
+ *
+ * @note Syntax:
+ * logical-and-expression:
+ * or-expression
+ * logical-and-expression | or-expression
+ */
+static int vdScriptParseLogicalAndExpression(PVDSCRIPTCTXINT pThis, PVDSCRIPTASTEXPR *ppAstNodeExpr)
+{
+ int rc = VINF_SUCCESS;
+ PVDSCRIPTASTEXPR pExpr = NULL;
+
+ LogFlowFunc(("pThis=%p ppAstNodeExpr=%p\n"));
+
+ rc = vdScriptParseBitwiseOrExpression(pThis, &pExpr);
+ if (RT_SUCCESS(rc))
+ {
+ PVDSCRIPTASTEXPR pExprNew = NULL;
+ while ( RT_SUCCESS(rc)
+ && vdScriptTokenizerSkipIfIsOperatorEqual(pThis->pTokenizer, "&&"))
+ {
+ pExprNew = (PVDSCRIPTASTEXPR)vdScriptAstNodeAlloc(VDSCRIPTASTCLASS_EXPRESSION);
+ if (pExprNew)
+ {
+ pExprNew->enmType = VDSCRIPTEXPRTYPE_LOGICAL_AND;
+ pExprNew->BinaryOp.pLeftExpr = pExpr;
+ pExpr = pExprNew;
+ rc = vdScriptParseBitwiseOrExpression(pThis, &pExprNew);
+ if (RT_SUCCESS(rc))
+ pExpr->BinaryOp.pRightExpr = pExprNew;
+ }
+ else
+ rc = vdScriptParserError(pThis, VERR_NO_MEMORY, RT_SRC_POS, "Parser: Out of memory allocating expression AST node\n");
+ }
+
+ if (RT_SUCCESS(rc))
+ *ppAstNodeExpr = pExpr;
+ else
+ vdScriptAstNodeFree(&pExpr->Core);
+ }
+
+ LogFlowFunc(("returns rc=%Rrc\n", rc));
+ return rc;
+}
+
+/**
+ * Parse a logical or expression.
+ *
+ * @returns VBox status code.
+ * @param pThis The script context.
+ * @param ppAstNodeExpr Where to store the expression AST node on success.
+ *
+ * @note Syntax:
+ * logical-or-expression:
+ * logical-and-expression
+ * logical-or-expression | logical-and-expression
+ */
+static int vdScriptParseLogicalOrExpression(PVDSCRIPTCTXINT pThis, PVDSCRIPTASTEXPR *ppAstNodeExpr)
+{
+ int rc = VINF_SUCCESS;
+ PVDSCRIPTASTEXPR pExpr = NULL;
+
+ LogFlowFunc(("pThis=%p ppAstNodeExpr=%p\n"));
+
+ rc = vdScriptParseLogicalAndExpression(pThis, &pExpr);
+ if (RT_SUCCESS(rc))
+ {
+ PVDSCRIPTASTEXPR pExprNew = NULL;
+ while ( RT_SUCCESS(rc)
+ && vdScriptTokenizerSkipIfIsOperatorEqual(pThis->pTokenizer, "||"))
+ {
+ pExprNew = (PVDSCRIPTASTEXPR)vdScriptAstNodeAlloc(VDSCRIPTASTCLASS_EXPRESSION);
+ if (pExprNew)
+ {
+ pExprNew->enmType = VDSCRIPTEXPRTYPE_LOGICAL_OR;
+ pExprNew->BinaryOp.pLeftExpr = pExpr;
+ pExpr = pExprNew;
+ rc = vdScriptParseLogicalAndExpression(pThis, &pExprNew);
+ if (RT_SUCCESS(rc))
+ pExpr->BinaryOp.pRightExpr = pExprNew;
+ }
+ else
+ rc = vdScriptParserError(pThis, VERR_NO_MEMORY, RT_SRC_POS, "Parser: Out of memory allocating expression AST node\n");
+ }
+
+ if (RT_SUCCESS(rc))
+ *ppAstNodeExpr = pExpr;
+ else
+ vdScriptAstNodeFree(&pExpr->Core);
+ }
+
+ LogFlowFunc(("returns rc=%Rrc\n", rc));
+ return rc;
+}
+
+/**
+ * Parse a conditional expression.
+ *
+ * @returns VBox status code.
+ * @param pThis The script context.
+ * @param ppAstNodeExpr Where to store the expression AST node on success.
+ *
+ * @note: VDScript doesn't support logical-or-expression ? expression : conditional-expression
+ * so a conditional expression is equal to a logical-or-expression.
+ */
+static int vdScriptParseCondExpression(PVDSCRIPTCTXINT pThis, PVDSCRIPTASTEXPR *ppAstNodeExpr)
+{
+ return vdScriptParseLogicalOrExpression(pThis, ppAstNodeExpr);
+}
+
+/**
+ * Parse an assignment expression.
+ *
+ * @returns VBox status code.
+ * @param pThis The script context.
+ * @param ppAstNodeExpr Where to store the expression AST node on success.
+ *
+ * @note Syntax:
+ * assignment-expression:
+ * conditional-expression
+ * unary-expression assignment-operator assignment-expression
+ */
+static int vdScriptParseAssignmentExpression(PVDSCRIPTCTXINT pThis, PVDSCRIPTASTEXPR *ppAstNodeExpr)
+{
+ int rc = VINF_SUCCESS;
+ PVDSCRIPTASTEXPR pExpr;
+
+ LogFlowFunc(("pThis=%p ppAstNodeExpr=%p\n"));
+
+ rc = vdScriptParseLogicalOrExpression(pThis, &pExpr);
+ if (RT_SUCCESS(rc))
+ {
+ PVDSCRIPTASTEXPR pExprNew = NULL;
+ while (RT_SUCCESS(rc))
+ {
+ if (vdScriptTokenizerSkipIfIsOperatorEqual(pThis->pTokenizer, "="))
+ {
+ pExprNew = (PVDSCRIPTASTEXPR)vdScriptAstNodeAlloc(VDSCRIPTASTCLASS_EXPRESSION);
+ if (pExprNew)
+ pExprNew->enmType = VDSCRIPTEXPRTYPE_ASSIGN;
+ else
+ rc = vdScriptParserError(pThis, VERR_NO_MEMORY, RT_SRC_POS, "Parser: Out of memory allocating expression AST node\n");
+ }
+ else if (vdScriptTokenizerSkipIfIsOperatorEqual(pThis->pTokenizer, "*="))
+ {
+ pExprNew = (PVDSCRIPTASTEXPR)vdScriptAstNodeAlloc(VDSCRIPTASTCLASS_EXPRESSION);
+ if (pExprNew)
+ pExprNew->enmType = VDSCRIPTEXPRTYPE_ASSIGN_MULT;
+ else
+ rc = vdScriptParserError(pThis, VERR_NO_MEMORY, RT_SRC_POS, "Parser: Out of memory allocating expression AST node\n");
+ }
+ else if (vdScriptTokenizerSkipIfIsOperatorEqual(pThis->pTokenizer, "/="))
+ {
+ pExprNew = (PVDSCRIPTASTEXPR)vdScriptAstNodeAlloc(VDSCRIPTASTCLASS_EXPRESSION);
+ if (pExprNew)
+ pExprNew->enmType = VDSCRIPTEXPRTYPE_ASSIGN_DIV;
+ else
+ rc = vdScriptParserError(pThis, VERR_NO_MEMORY, RT_SRC_POS, "Parser: Out of memory allocating expression AST node\n");
+ }
+ else if (vdScriptTokenizerSkipIfIsOperatorEqual(pThis->pTokenizer, "%="))
+ {
+ pExprNew = (PVDSCRIPTASTEXPR)vdScriptAstNodeAlloc(VDSCRIPTASTCLASS_EXPRESSION);
+ if (pExprNew)
+ pExprNew->enmType = VDSCRIPTEXPRTYPE_ASSIGN_MOD;
+ else
+ rc = vdScriptParserError(pThis, VERR_NO_MEMORY, RT_SRC_POS, "Parser: Out of memory allocating expression AST node\n");
+ }
+ else if (vdScriptTokenizerSkipIfIsOperatorEqual(pThis->pTokenizer, "+="))
+ {
+ pExprNew = (PVDSCRIPTASTEXPR)vdScriptAstNodeAlloc(VDSCRIPTASTCLASS_EXPRESSION);
+ if (pExprNew)
+ pExprNew->enmType = VDSCRIPTEXPRTYPE_ASSIGN_ADD;
+ else
+ rc = vdScriptParserError(pThis, VERR_NO_MEMORY, RT_SRC_POS, "Parser: Out of memory allocating expression AST node\n");
+ }
+ else if (vdScriptTokenizerSkipIfIsOperatorEqual(pThis->pTokenizer, "-="))
+ {
+ pExprNew = (PVDSCRIPTASTEXPR)vdScriptAstNodeAlloc(VDSCRIPTASTCLASS_EXPRESSION);
+ if (pExprNew)
+ pExprNew->enmType = VDSCRIPTEXPRTYPE_ASSIGN_SUB;
+ else
+ rc = vdScriptParserError(pThis, VERR_NO_MEMORY, RT_SRC_POS, "Parser: Out of memory allocating expression AST node\n");
+ }
+ else if (vdScriptTokenizerSkipIfIsOperatorEqual(pThis->pTokenizer, "<<="))
+ {
+ pExprNew = (PVDSCRIPTASTEXPR)vdScriptAstNodeAlloc(VDSCRIPTASTCLASS_EXPRESSION);
+ if (pExprNew)
+ pExprNew->enmType = VDSCRIPTEXPRTYPE_ASSIGN_LSL;
+ else
+ rc = vdScriptParserError(pThis, VERR_NO_MEMORY, RT_SRC_POS, "Parser: Out of memory allocating expression AST node\n");
+ }
+ else if (vdScriptTokenizerSkipIfIsOperatorEqual(pThis->pTokenizer, ">>="))
+ {
+ pExprNew = (PVDSCRIPTASTEXPR)vdScriptAstNodeAlloc(VDSCRIPTASTCLASS_EXPRESSION);
+ if (pExprNew)
+ pExprNew->enmType = VDSCRIPTEXPRTYPE_ASSIGN_LSR;
+ else
+ rc = vdScriptParserError(pThis, VERR_NO_MEMORY, RT_SRC_POS, "Parser: Out of memory allocating expression AST node\n");
+ }
+ else if (vdScriptTokenizerSkipIfIsOperatorEqual(pThis->pTokenizer, "&="))
+ {
+ pExprNew = (PVDSCRIPTASTEXPR)vdScriptAstNodeAlloc(VDSCRIPTASTCLASS_EXPRESSION);
+ if (pExprNew)
+ pExprNew->enmType = VDSCRIPTEXPRTYPE_ASSIGN_AND;
+ else
+ rc = vdScriptParserError(pThis, VERR_NO_MEMORY, RT_SRC_POS, "Parser: Out of memory allocating expression AST node\n");
+ }
+ else if (vdScriptTokenizerSkipIfIsOperatorEqual(pThis->pTokenizer, "^="))
+ {
+ pExprNew = (PVDSCRIPTASTEXPR)vdScriptAstNodeAlloc(VDSCRIPTASTCLASS_EXPRESSION);
+ if (pExprNew)
+ pExprNew->enmType = VDSCRIPTEXPRTYPE_ASSIGN_XOR;
+ else
+ rc = vdScriptParserError(pThis, VERR_NO_MEMORY, RT_SRC_POS, "Parser: Out of memory allocating expression AST node\n");
+ }
+ else if (vdScriptTokenizerSkipIfIsOperatorEqual(pThis->pTokenizer, "|="))
+ {
+ pExprNew = (PVDSCRIPTASTEXPR)vdScriptAstNodeAlloc(VDSCRIPTASTCLASS_EXPRESSION);
+ if (pExprNew)
+ pExprNew->enmType = VDSCRIPTEXPRTYPE_ASSIGN_OR;
+ else
+ rc = vdScriptParserError(pThis, VERR_NO_MEMORY, RT_SRC_POS, "Parser: Out of memory allocating expression AST node\n");
+ }
+ else
+ break;
+
+ pExprNew->BinaryOp.pLeftExpr = pExpr;
+ pExpr = pExprNew;
+ rc = vdScriptParseLogicalOrExpression(pThis, &pExprNew);
+ if (RT_SUCCESS(rc))
+ pExpr->BinaryOp.pRightExpr = pExprNew;
+ }
+
+ if (RT_SUCCESS(rc))
+ *ppAstNodeExpr = pExpr;
+ else
+ vdScriptAstNodeFree(&pExpr->Core);
+ }
+
+ LogFlowFunc(("returns rc=%Rrc\n", rc));
+ return rc;
+}
+
+/**
+ * Parse an expression.
+ *
+ * @returns VBox status code.
+ * @param pThis The script context.
+ * @param ppAstNodeExpr Where to store the expression AST node on success.
+ *
+ * @note Syntax:
+ * expression:
+ * assignment-expression
+ * expression , assignment-expression
+ */
+static int vdScriptParseExpression(PVDSCRIPTCTXINT pThis, PVDSCRIPTASTEXPR *ppAstNodeExpr)
+{
+ int rc = VINF_SUCCESS;
+ PVDSCRIPTASTEXPR pAssignExpr = NULL;
+
+ LogFlowFunc(("pThis=%p ppAstNodeExpr=%p\n"));
+
+ rc = vdScriptParseAssignmentExpression(pThis, &pAssignExpr);
+ if ( RT_SUCCESS(rc)
+ && vdScriptTokenizerSkipIfIsPunctuatorEqual(pThis->pTokenizer, ','))
+ {
+ PVDSCRIPTASTEXPR pListAssignExpr = (PVDSCRIPTASTEXPR)vdScriptAstNodeAlloc(VDSCRIPTASTCLASS_EXPRESSION);
+ if (pListAssignExpr)
+ {
+ pListAssignExpr->enmType = VDSCRIPTEXPRTYPE_ASSIGNMENT_LIST;
+ RTListInit(&pListAssignExpr->ListExpr);
+ RTListAppend(&pListAssignExpr->ListExpr, &pAssignExpr->Core.ListNode);
+ do
+ {
+ rc = vdScriptParseAssignmentExpression(pThis, &pAssignExpr);
+ if (RT_SUCCESS(rc))
+ RTListAppend(&pListAssignExpr->ListExpr, &pAssignExpr->Core.ListNode);
+ } while ( RT_SUCCESS(rc)
+ && vdScriptTokenizerSkipIfIsPunctuatorEqual(pThis->pTokenizer, ','));
+
+ if (RT_FAILURE(rc))
+ vdScriptAstNodeFree(&pListAssignExpr->Core);
+ else
+ *ppAstNodeExpr = pListAssignExpr;
+ }
+ else
+ rc = vdScriptParserError(pThis, VERR_NO_MEMORY, RT_SRC_POS, "Parser: Out of memory allocating expression AST node\n");
+ }
+ else if (RT_SUCCESS(rc))
+ *ppAstNodeExpr = pAssignExpr;
+
+ LogFlowFunc(("returns rc=%Rrc\n", rc));
+ return rc;
+}
+
+/**
+ * Parse an if statement.
+ *
+ * @returns VBox status code.
+ * @param pThis The script context.
+ * @param pAstNodeIf Uninitialized if AST node.
+ *
+ * @note The caller skipped the "if" token already.
+ */
+static int vdScriptParseIf(PVDSCRIPTCTXINT pThis, PVDSCRIPTASTIF pAstNodeIf)
+{
+ int rc = VINF_SUCCESS;
+
+ if (vdScriptTokenizerSkipIfIsPunctuatorEqual(pThis->pTokenizer, '('))
+ {
+ PVDSCRIPTASTEXPR pCondExpr = NULL;
+ rc = vdScriptParseExpression(pThis, &pCondExpr);
+ if (RT_SUCCESS(rc))
+ {
+ if (vdScriptTokenizerSkipIfIsPunctuatorEqual(pThis->pTokenizer, ')'))
+ {
+ PVDSCRIPTASTSTMT pStmt = NULL;
+ PVDSCRIPTASTSTMT pElseStmt = NULL;
+ rc = vdScriptParseStatement(pThis, &pStmt);
+ if ( RT_SUCCESS(rc)
+ && vdScriptTokenizerSkipIfIsKeywordEqual(pThis->pTokenizer, VDSCRIPTTOKENKEYWORD_ELSE))
+ rc = vdScriptParseStatement(pThis, &pElseStmt);
+
+ if (RT_SUCCESS(rc))
+ {
+ pAstNodeIf->pCond = pCondExpr;
+ pAstNodeIf->pTrueStmt = pStmt;
+ pAstNodeIf->pElseStmt = pElseStmt;
+ }
+ else if (pStmt)
+ vdScriptAstNodeFree(&pStmt->Core);
+ }
+ else
+ rc = vdScriptParserError(pThis, VERR_INVALID_PARAMETER, RT_SRC_POS, "Parser: Expected \")\", got ...\n");
+
+ if (RT_FAILURE(rc))
+ vdScriptAstNodeFree(&pCondExpr->Core);
+ }
+ }
+ else
+ rc = vdScriptParserError(pThis, VERR_INVALID_PARAMETER, RT_SRC_POS, "Parser: Expected \"(\", got ...\n");
+
+ return rc;
+}
+
+/**
+ * Parse a switch statement.
+ *
+ * @returns VBox status code.
+ * @param pThis The script context.
+ * @param pAstNodeSwitch Uninitialized switch AST node.
+ *
+ * @note The caller skipped the "switch" token already.
+ */
+static int vdScriptParseSwitch(PVDSCRIPTCTXINT pThis, PVDSCRIPTASTSWITCH pAstNodeSwitch)
+{
+ int rc = VINF_SUCCESS;
+
+ if (vdScriptTokenizerSkipIfIsPunctuatorEqual(pThis->pTokenizer, '('))
+ {
+ PVDSCRIPTASTEXPR pExpr = NULL;
+
+ rc = vdScriptParseExpression(pThis, &pExpr);
+ if ( RT_SUCCESS(rc)
+ && vdScriptTokenizerSkipIfIsPunctuatorEqual(pThis->pTokenizer, ')'))
+ {
+ PVDSCRIPTASTSTMT pStmt = NULL;
+ rc = vdScriptParseStatement(pThis, &pStmt);
+ if (RT_SUCCESS(rc))
+ {
+ pAstNodeSwitch->pCond = pExpr;
+ pAstNodeSwitch->pStmt = pStmt;
+ }
+ else
+ vdScriptAstNodeFree(&pExpr->Core);
+ }
+ else if (RT_SUCCESS(rc))
+ {
+ rc = vdScriptParserError(pThis, VERR_INVALID_PARAMETER, RT_SRC_POS, "Parser: Expected \")\", got ...\n");
+ vdScriptAstNodeFree(&pExpr->Core);
+ }
+ }
+ else
+ rc = vdScriptParserError(pThis, VERR_INVALID_PARAMETER, RT_SRC_POS, "Parser: Expected \"(\", got ...\n");
+
+ return rc;
+}
+
+/**
+ * Parse a while or do ... while statement.
+ *
+ * @returns VBox status code.
+ * @param pThis The script context.
+ * @param pAstNodeWhile Uninitialized while AST node.
+ *
+ * @note The caller skipped the "while" or "do" token already.
+ */
+static int vdScriptParseWhile(PVDSCRIPTCTXINT pThis, PVDSCRIPTASTWHILE pAstNodeWhile, bool fDoWhile)
+{
+ int rc = VINF_SUCCESS;
+
+ pAstNodeWhile->fDoWhile = fDoWhile;
+
+ if (fDoWhile)
+ {
+ PVDSCRIPTASTSTMT pStmt = NULL;
+ rc = vdScriptParseStatement(pThis, &pStmt);
+ if ( RT_SUCCESS(rc)
+ && vdScriptTokenizerSkipIfIsKeywordEqual(pThis->pTokenizer, VDSCRIPTTOKENKEYWORD_WHILE))
+ {
+ if (vdScriptTokenizerSkipIfIsPunctuatorEqual(pThis->pTokenizer, '('))
+ {
+ PVDSCRIPTASTEXPR pExpr = NULL;
+
+ rc = vdScriptParseExpression(pThis, &pExpr);
+ if ( RT_SUCCESS(rc)
+ && vdScriptTokenizerSkipIfIsPunctuatorEqual(pThis->pTokenizer, ')'))
+ {
+ if (vdScriptTokenizerSkipIfIsPunctuatorEqual(pThis->pTokenizer, ';'))
+ {
+ pAstNodeWhile->pCond = pExpr;
+ pAstNodeWhile->pStmt = pStmt;
+ }
+ else
+ rc = vdScriptParserError(pThis, VERR_INVALID_PARAMETER, RT_SRC_POS, "Parser: Expected \";\", got ...\n");
+ }
+ else if (RT_SUCCESS(rc))
+ {
+ rc = vdScriptParserError(pThis, VERR_INVALID_PARAMETER, RT_SRC_POS, "Parser: Expected \")\", got ...\n");
+ vdScriptAstNodeFree(&pExpr->Core);
+ }
+ }
+ else
+ rc = vdScriptParserError(pThis, VERR_INVALID_PARAMETER, RT_SRC_POS, "Parser: Expected \"(\", got ...\n");
+ }
+ else if (RT_SUCCESS(rc))
+ rc = vdScriptParserError(pThis, VERR_INVALID_PARAMETER, RT_SRC_POS, "Parser: Expected \"while\", got ...\n");
+
+ if ( RT_FAILURE(rc)
+ && pStmt)
+ vdScriptAstNodeFree(&pStmt->Core);
+ }
+ else
+ {
+ if (vdScriptTokenizerSkipIfIsPunctuatorEqual(pThis->pTokenizer, '('))
+ {
+ PVDSCRIPTASTEXPR pExpr = NULL;
+
+ rc = vdScriptParseExpression(pThis, &pExpr);
+ if ( RT_SUCCESS(rc)
+ && vdScriptTokenizerSkipIfIsPunctuatorEqual(pThis->pTokenizer, ')'))
+ {
+ PVDSCRIPTASTSTMT pStmt = NULL;
+ rc = vdScriptParseStatement(pThis, &pStmt);
+ if (RT_SUCCESS(rc))
+ {
+ pAstNodeWhile->pCond = pExpr;
+ pAstNodeWhile->pStmt = pStmt;
+ }
+ else
+ vdScriptAstNodeFree(&pExpr->Core);
+ }
+ else if (RT_SUCCESS(rc))
+ {
+ rc = vdScriptParserError(pThis, VERR_INVALID_PARAMETER, RT_SRC_POS, "Parser: Expected \")\", got ...\n");
+ vdScriptAstNodeFree(&pExpr->Core);
+ }
+ }
+ else
+ rc = vdScriptParserError(pThis, VERR_INVALID_PARAMETER, RT_SRC_POS, "Parser: Expected \"(\", got ...\n");
+ }
+
+ return rc;
+}
+
+/**
+ * Parse a for statement.
+ *
+ * @returns VBox status code.
+ * @param pThis The script context.
+ * @param pAstNodeFor Uninitialized for AST node.
+ *
+ * @note The caller skipped the "for" token already.
+ */
+static int vdScriptParseFor(PVDSCRIPTCTXINT pThis, PVDSCRIPTASTFOR pAstNodeFor)
+{
+ int rc = VINF_SUCCESS;
+
+ if (vdScriptTokenizerSkipIfIsPunctuatorEqual(pThis->pTokenizer, '('))
+ {
+ PVDSCRIPTASTEXPR pExprStart = NULL;
+ PVDSCRIPTASTEXPR pExprCond = NULL;
+ PVDSCRIPTASTEXPR pExpr3 = NULL;
+
+ rc = vdScriptParseExpression(pThis, &pExprStart);
+ if ( RT_SUCCESS(rc)
+ && vdScriptTokenizerSkipIfIsPunctuatorEqual(pThis->pTokenizer, ';'))
+ {
+ rc = vdScriptParseExpression(pThis, &pExprCond);
+ if ( RT_SUCCESS(rc)
+ && vdScriptTokenizerSkipIfIsPunctuatorEqual(pThis->pTokenizer, ';'))
+ {
+ rc = vdScriptParseExpression(pThis, &pExpr3);
+ if ( RT_SUCCESS(rc)
+ && vdScriptTokenizerSkipIfIsPunctuatorEqual(pThis->pTokenizer, ')'))
+ {
+ PVDSCRIPTASTSTMT pStmt = NULL;
+ rc = vdScriptParseStatement(pThis, &pStmt);
+ if (RT_SUCCESS(rc))
+ {
+ pAstNodeFor->pExprStart = pExprStart;
+ pAstNodeFor->pExprCond = pExprCond;
+ pAstNodeFor->pExpr3 = pExpr3;
+ pAstNodeFor->pStmt = pStmt;
+ }
+ }
+ }
+ else if (RT_SUCCESS(rc))
+ rc = vdScriptParserError(pThis, VERR_INVALID_PARAMETER, RT_SRC_POS, "Parser: Expected \";\", got ...\n");
+ }
+ else if (RT_SUCCESS(rc))
+ rc = vdScriptParserError(pThis, VERR_INVALID_PARAMETER, RT_SRC_POS, "Parser: Expected \";\", got ...\n");
+
+ if (RT_FAILURE(rc))
+ {
+ if (pExprStart)
+ vdScriptAstNodeFree(&pExprStart->Core);
+ if (pExprCond)
+ vdScriptAstNodeFree(&pExprCond->Core);
+ if (pExpr3)
+ vdScriptAstNodeFree(&pExpr3->Core);
+ }
+ }
+ else
+ rc = vdScriptParserError(pThis, VERR_INVALID_PARAMETER, RT_SRC_POS, "Parser: Expected \"(\", got ...\n");
+
+ return rc;
+}
+
+/**
+ * Parse a declaration.
+ *
+ * @returns VBox status code.
+ * @param pThis The script context.
+ * @param ppAstNodeDecl Where to store the declaration AST node on success.
+ */
+static int vdScriptParseDeclaration(PVDSCRIPTCTXINT pThis, PVDSCRIPTASTDECL *ppAstNodeDecl)
+{
+ int rc = VERR_NOT_IMPLEMENTED;
+ return rc;
+}
+
+/**
+ * Parse a statement.
+ *
+ * @returns VBox status code.
+ * @param pThis The script context.
+ * @param ppAstNodeStmt Where to store the statement AST node on success.
+ */
+static int vdScriptParseStatement(PVDSCRIPTCTXINT pThis, PVDSCRIPTASTSTMT *ppAstNodeStmt)
+{
+ int rc = VINF_SUCCESS;
+
+ /* Shortcut for a new compound statement. */
+ if (vdScriptTokenizerIsPunctuatorEqual(pThis->pTokenizer, '{'))
+ rc = vdScriptParseCompoundStatement(pThis, ppAstNodeStmt);
+ else
+ {
+ PVDSCRIPTASTSTMT pAstNodeStmt = (PVDSCRIPTASTSTMT)vdScriptAstNodeAlloc(VDSCRIPTASTCLASS_STATEMENT);
+
+ if (pAstNodeStmt)
+ {
+
+ if (vdScriptTokenizerSkipIfIsKeywordEqual(pThis->pTokenizer, VDSCRIPTTOKENKEYWORD_DEFAULT))
+ {
+ if (vdScriptTokenizerSkipIfIsPunctuatorEqual(pThis->pTokenizer, ':'))
+ {
+ PVDSCRIPTASTSTMT pAstNodeStmtDef = NULL;
+ pAstNodeStmt->enmStmtType = VDSCRIPTSTMTTYPE_DEFAULT;
+ rc = vdScriptParseStatement(pThis, &pAstNodeStmtDef);
+ if (RT_SUCCESS(rc))
+ pAstNodeStmt->pStmt = pAstNodeStmtDef;
+ }
+ else
+ rc = vdScriptParserError(pThis, VERR_INVALID_PARAMETER, RT_SRC_POS, "Parser: Expected \":\", got ...\n");
+ }
+ else if (vdScriptTokenizerSkipIfIsKeywordEqual(pThis->pTokenizer, VDSCRIPTTOKENKEYWORD_CASE))
+ {
+ PVDSCRIPTASTEXPR pAstNodeExpr = NULL;
+ rc = vdScriptParseCondExpression(pThis, &pAstNodeExpr);
+ if (RT_SUCCESS(rc))
+ {
+ if (vdScriptTokenizerSkipIfIsPunctuatorEqual(pThis->pTokenizer, ':'))
+ {
+ PVDSCRIPTASTSTMT pAstNodeCaseStmt = NULL;
+ rc = vdScriptParseStatement(pThis, &pAstNodeCaseStmt);
+ if (RT_SUCCESS(rc))
+ {
+ pAstNodeStmt->enmStmtType = VDSCRIPTSTMTTYPE_CASE;
+ pAstNodeStmt->Case.pExpr = pAstNodeExpr;
+ pAstNodeStmt->Case.pStmt = pAstNodeCaseStmt;
+ }
+ }
+ else
+ rc = vdScriptParserError(pThis, VERR_INVALID_PARAMETER, RT_SRC_POS, "Parser: Expected \":\", got ...\n");
+
+ if (RT_FAILURE(rc))
+ vdScriptAstNodeFree(&pAstNodeExpr->Core);
+ }
+ }
+ else if (vdScriptTokenizerSkipIfIsKeywordEqual(pThis->pTokenizer, VDSCRIPTTOKENKEYWORD_IF))
+ {
+ pAstNodeStmt->enmStmtType = VDSCRIPTSTMTTYPE_IF;
+ rc = vdScriptParseIf(pThis, &pAstNodeStmt->If);
+ }
+ else if (vdScriptTokenizerSkipIfIsKeywordEqual(pThis->pTokenizer, VDSCRIPTTOKENKEYWORD_SWITCH))
+ {
+ pAstNodeStmt->enmStmtType = VDSCRIPTSTMTTYPE_SWITCH;
+ rc = vdScriptParseSwitch(pThis, &pAstNodeStmt->Switch);
+ }
+ else if (vdScriptTokenizerSkipIfIsKeywordEqual(pThis->pTokenizer, VDSCRIPTTOKENKEYWORD_WHILE))
+ {
+ pAstNodeStmt->enmStmtType = VDSCRIPTSTMTTYPE_WHILE;
+ rc = vdScriptParseWhile(pThis, &pAstNodeStmt->While, false /* fDoWhile */);
+ }
+ else if (vdScriptTokenizerSkipIfIsKeywordEqual(pThis->pTokenizer, VDSCRIPTTOKENKEYWORD_DO))
+ {
+ pAstNodeStmt->enmStmtType = VDSCRIPTSTMTTYPE_WHILE;
+ rc = vdScriptParseWhile(pThis, &pAstNodeStmt->While, true /* fDoWhile */);
+ }
+ else if (vdScriptTokenizerSkipIfIsKeywordEqual(pThis->pTokenizer, VDSCRIPTTOKENKEYWORD_FOR))
+ {
+ pAstNodeStmt->enmStmtType = VDSCRIPTSTMTTYPE_FOR;
+ rc = vdScriptParseFor(pThis, &pAstNodeStmt->For);
+ }
+ else if (vdScriptTokenizerSkipIfIsKeywordEqual(pThis->pTokenizer, VDSCRIPTTOKENKEYWORD_CONTINUE))
+ {
+ pAstNodeStmt->enmStmtType = VDSCRIPTSTMTTYPE_CONTINUE;
+ if (!vdScriptTokenizerSkipIfIsPunctuatorEqual(pThis->pTokenizer, ';'))
+ rc = vdScriptParserError(pThis, VERR_INVALID_PARAMETER, RT_SRC_POS, "Parser: Expected \";\", got ...\n");
+ }
+ else if (vdScriptTokenizerSkipIfIsKeywordEqual(pThis->pTokenizer, VDSCRIPTTOKENKEYWORD_BREAK))
+ {
+ pAstNodeStmt->enmStmtType = VDSCRIPTSTMTTYPE_BREAK;
+ if (!vdScriptTokenizerSkipIfIsPunctuatorEqual(pThis->pTokenizer, ';'))
+ rc = vdScriptParserError(pThis, VERR_INVALID_PARAMETER, RT_SRC_POS, "Parser: Expected \";\", got ...\n");
+ }
+ else if (vdScriptTokenizerSkipIfIsKeywordEqual(pThis->pTokenizer, VDSCRIPTTOKENKEYWORD_RETURN))
+ {
+ pAstNodeStmt->enmStmtType = VDSCRIPTSTMTTYPE_RETURN;
+ if (!vdScriptTokenizerSkipIfIsPunctuatorEqual(pThis->pTokenizer, ';'))
+ {
+ rc = vdScriptParseExpression(pThis, &pAstNodeStmt->pExpr);
+ if ( RT_SUCCESS(rc)
+ && !vdScriptTokenizerSkipIfIsPunctuatorEqual(pThis->pTokenizer, ';'))
+ rc = vdScriptParserError(pThis, VERR_INVALID_PARAMETER, RT_SRC_POS, "Parser: Expected \";\", got ...\n");
+ }
+ else
+ pAstNodeStmt->pExpr = NULL; /* No expression for return. */
+ }
+ else
+ {
+ /* Must be an expression. */
+ pAstNodeStmt->enmStmtType = VDSCRIPTSTMTTYPE_EXPRESSION;
+ rc = vdScriptParseExpression(pThis, &pAstNodeStmt->pExpr);
+ if ( RT_SUCCESS(rc)
+ && !vdScriptTokenizerSkipIfIsPunctuatorEqual(pThis->pTokenizer, ';'))
+ rc = vdScriptParserError(pThis, VERR_INVALID_PARAMETER, RT_SRC_POS, "Parser: Expected \";\", got ...\n");
+ }
+
+ if (RT_SUCCESS(rc))
+ *ppAstNodeStmt = pAstNodeStmt;
+ else
+ vdScriptAstNodeFree(&pAstNodeStmt->Core);
+ }
+ else
+ rc = vdScriptParserError(pThis, VERR_NO_MEMORY, RT_SRC_POS, "Parser: Out of memory creating statement node\n");
+ }
+
+ return rc;
+}
+
+/**
+ * Parses a compound statement.
+ *
+ * @returns VBox status code.
+ * @param pThis The script context.
+ * @param ppAstNodeCompound Where to store the compound AST node on success.
+ */
+static int vdScriptParseCompoundStatement(PVDSCRIPTCTXINT pThis, PVDSCRIPTASTSTMT *ppAstNodeCompound)
+{
+ int rc = VINF_SUCCESS;
+
+ if (vdScriptTokenizerSkipIfIsPunctuatorEqual(pThis->pTokenizer, '{'))
+ {
+ PVDSCRIPTASTSTMT pAstNodeCompound = (PVDSCRIPTASTSTMT)vdScriptAstNodeAlloc(VDSCRIPTASTCLASS_STATEMENT);
+ if (pAstNodeCompound)
+ {
+ pAstNodeCompound->enmStmtType = VDSCRIPTSTMTTYPE_COMPOUND;
+ RTListInit(&pAstNodeCompound->Compound.ListDecls);
+ RTListInit(&pAstNodeCompound->Compound.ListStmts);
+ while (!vdScriptTokenizerSkipIfIsPunctuatorEqual(pThis->pTokenizer, '}'))
+ {
+ /*
+ * Check whether we have a declaration or a statement.
+ * For now we assume that 2 identifier tokens specify a declaration
+ * (type + variable name). Having two consecutive identifers is not possible
+ * for a statement.
+ */
+ if ( vdScriptTokenizerGetTokenClass(pThis->pTokenizer) == VDTOKENCLASS_IDENTIFIER
+ && vdScriptTokenizerPeekNextClass(pThis->pTokenizer) == VDTOKENCLASS_IDENTIFIER)
+ {
+ PVDSCRIPTASTDECL pAstNodeDecl = NULL;
+ rc = vdScriptParseDeclaration(pThis, &pAstNodeDecl);
+ if (RT_SUCCESS(rc))
+ RTListAppend(&pAstNodeCompound->Compound.ListDecls, &pAstNodeDecl->Core.ListNode);
+ }
+ else
+ {
+ PVDSCRIPTASTSTMT pAstNodeStmt = NULL;
+ rc = vdScriptParseStatement(pThis, &pAstNodeStmt);
+ if (RT_SUCCESS(rc))
+ RTListAppend(&pAstNodeCompound->Compound.ListStmts, &pAstNodeStmt->Core.ListNode);
+ }
+
+ if (RT_FAILURE(rc))
+ break;
+ }
+
+ if (RT_SUCCESS(rc))
+ *ppAstNodeCompound = pAstNodeCompound;
+ else
+ vdScriptAstNodeFree(&pAstNodeCompound->Core);
+ }
+ else
+ rc = vdScriptParserError(pThis, VERR_NO_MEMORY, RT_SRC_POS, "Parser: Out of memory creating compound statement node\n");
+ }
+ else
+ rc = vdScriptParserError(pThis, VERR_INVALID_PARAMETER, RT_SRC_POS, "Parser: Expected \"{\" got...\n");
+
+
+ return rc;
+}
+
+/**
+ * Parses a function definition from the given tokenizer.
+ *
+ * @returns VBox status code.
+ * @param pThis The script context.
+ */
+static int vdScriptParseAddFnDef(PVDSCRIPTCTXINT pThis)
+{
+ int rc = VINF_SUCCESS;
+ PVDSCRIPTASTIDE pRetType = NULL;
+ PVDSCRIPTASTIDE pFnIde = NULL;
+
+ LogFlowFunc(("pThis=%p\n", pThis));
+
+ /* Put return type on the stack. */
+ rc = vdScriptParseIde(pThis, &pRetType);
+ if (RT_SUCCESS(rc))
+ {
+ /* Function name */
+ rc = vdScriptParseIde(pThis, &pFnIde);
+ if (RT_SUCCESS(rc))
+ {
+ if (vdScriptTokenizerSkipIfIsPunctuatorEqual(pThis->pTokenizer, '('))
+ {
+ PVDSCRIPTASTFN pAstNodeFn = (PVDSCRIPTASTFN)vdScriptAstNodeAlloc(VDSCRIPTASTCLASS_FUNCTION);
+
+ if (pAstNodeFn)
+ {
+ pAstNodeFn->pFnIde = pFnIde;
+ pAstNodeFn->pRetType = pRetType;
+ RTListInit(&pAstNodeFn->ListArgs);
+
+ pFnIde = NULL;
+ pRetType = NULL;
+
+ /* Parse parameter list, create empty parameter list AST node and put it on the stack. */
+ while (!vdScriptTokenizerSkipIfIsPunctuatorEqual(pThis->pTokenizer, ')'))
+ {
+ PVDSCRIPTASTIDE pArgType = NULL;
+ PVDSCRIPTASTIDE pArgIde = NULL;
+ /* Parse two identifiers, first one is the type, second the name. */
+ rc = vdScriptParseIde(pThis, &pArgType);
+ if (RT_SUCCESS(rc))
+ rc = vdScriptParseIde(pThis, &pArgIde);
+
+ if (RT_SUCCESS(rc))
+ {
+ PVDSCRIPTASTFNARG pAstNodeFnArg = (PVDSCRIPTASTFNARG)vdScriptAstNodeAlloc(VDSCRIPTASTCLASS_FUNCTIONARG);
+ if (pAstNodeFnArg)
+ {
+ pAstNodeFnArg->pArgIde = pArgIde;
+ pAstNodeFnArg->pType = pArgType;
+ RTListAppend(&pAstNodeFn->ListArgs, &pAstNodeFnArg->Core.ListNode);
+ pAstNodeFn->cArgs++;
+ }
+ else
+ rc = vdScriptParserError(pThis, VERR_NO_MEMORY, RT_SRC_POS, "Parser: Out of memory allocating function argument AST node\n");
+ }
+
+ if (RT_FAILURE(rc))
+ {
+ if (pArgType)
+ vdScriptAstNodeFree(&pArgType->Core);
+ if (pArgIde)
+ vdScriptAstNodeFree(&pArgIde->Core);
+ }
+
+ if ( !vdScriptTokenizerSkipIfIsPunctuatorEqual(pThis->pTokenizer, ',')
+ && !vdScriptTokenizerIsPunctuatorEqual(pThis->pTokenizer, ')'))
+ {
+ rc = vdScriptParserError(pThis, VERR_INVALID_PARAMETER, RT_SRC_POS, "Parser: Expected \",\" or \")\" got...\n");
+ break;
+ }
+ }
+
+ /* Parse the compound or statement block now. */
+ if (RT_SUCCESS(rc))
+ {
+ PVDSCRIPTASTSTMT pAstCompound = NULL;
+
+ rc = vdScriptParseCompoundStatement(pThis, &pAstCompound);
+ if (RT_SUCCESS(rc))
+ {
+ /*
+ * Link compound statement block to function AST node and add it to the
+ * list of functions.
+ */
+ pAstNodeFn->pCompoundStmts = pAstCompound;
+ RTListAppend(&pThis->ListAst, &pAstNodeFn->Core.ListNode);
+
+ PVDSCRIPTFN pFn = (PVDSCRIPTFN)RTMemAllocZ(sizeof(VDSCRIPTFN));
+ if (pFn)
+ {
+ pFn->Core.pszString = pAstNodeFn->pFnIde->aszIde;
+ pFn->Core.cchString = strlen(pFn->Core.pszString);
+ pFn->fExternal = false;
+ pFn->Type.Internal.pAstFn = pAstNodeFn;
+ /** @todo: Parameters. */
+ RTStrSpaceInsert(&pThis->hStrSpaceFn, &pFn->Core);
+ }
+ else
+ rc = vdScriptParserError(pThis, VERR_NO_MEMORY, RT_SRC_POS, "Out of memory allocating memory for function\n");
+ }
+ }
+
+ if (RT_FAILURE(rc))
+ vdScriptAstNodeFree(&pAstNodeFn->Core);
+ }
+ else
+ rc = vdScriptParserError(pThis, VERR_NO_MEMORY, RT_SRC_POS, "Parser: Out of memory allocating function AST node\n");
+ }
+ else
+ rc = vdScriptParserError(pThis, VERR_INVALID_PARAMETER, RT_SRC_POS, "Parser: Expected \"(\" got...\n");
+ }
+ }
+
+ if (RT_FAILURE(rc))
+ {
+ if (pRetType)
+ vdScriptAstNodeFree(&pRetType->Core);
+ if (pFnIde)
+ vdScriptAstNodeFree(&pFnIde->Core);
+ }
+
+ LogFlowFunc(("returns rc=%Rrc\n", rc));
+ return rc;
+}
+
+/**
+ * Parses the script from the given tokenizer.
+ *
+ * @returns VBox status code.
+ * @param pThis The script context.
+ */
+static int vdScriptParseFromTokenizer(PVDSCRIPTCTXINT pThis)
+{
+ int rc = VINF_SUCCESS;
+
+ LogFlowFunc(("pThis=%p\n", pThis));
+
+ /* This is a very very simple LL(1) parser, don't expect much from it for now :). */
+ while ( RT_SUCCESS(rc)
+ && !vdScriptTokenizerIsEos(pThis->pTokenizer))
+ rc = vdScriptParseAddFnDef(pThis);
+
+ LogFlowFunc(("returns rc=%Rrc\n", rc));
+ return rc;
+}
+
+DECLHIDDEN(int) VDScriptCtxCreate(PVDSCRIPTCTX phScriptCtx)
+{
+ int rc = VINF_SUCCESS;
+
+ LogFlowFunc(("phScriptCtx=%p\n", phScriptCtx));
+
+ AssertPtrReturn(phScriptCtx, VERR_INVALID_POINTER);
+
+ PVDSCRIPTCTXINT pThis = (PVDSCRIPTCTXINT)RTMemAllocZ(sizeof(VDSCRIPTCTXINT));
+ if (pThis)
+ {
+ pThis->hStrSpaceFn = NULL;
+ RTListInit(&pThis->ListAst);
+ *phScriptCtx = pThis;
+ }
+ else
+ rc = VINF_SUCCESS;
+
+ LogFlowFunc(("returns rc=%Rrc\n", rc));
+ return rc;
+}
+
+static int vdScriptCtxDestroyFnSpace(PRTSTRSPACECORE pStr, void *pvUser)
+{
+ NOREF(pvUser);
+
+ /*
+ * Just free the whole structure, the AST for internal functions will be
+ * destroyed later.
+ */
+ RTMemFree(pStr);
+ return VINF_SUCCESS;
+}
+
+DECLHIDDEN(void) VDScriptCtxDestroy(VDSCRIPTCTX hScriptCtx)
+{
+ PVDSCRIPTCTXINT pThis = hScriptCtx;
+
+ AssertPtrReturnVoid(pThis);
+
+ LogFlowFunc(("hScriptCtx=%p\n", pThis));
+
+ RTStrSpaceDestroy(&pThis->hStrSpaceFn, vdScriptCtxDestroyFnSpace, NULL);
+
+ /** @todo: Go through the list and destroy all ASTs. */
+ RTMemFree(pThis);
+}
+
+DECLHIDDEN(int) VDScriptCtxCallbacksRegister(VDSCRIPTCTX hScriptCtx, PCVDSCRIPTCALLBACK paCallbacks,
+ unsigned cCallbacks, void *pvUser)
+{
+ int rc = VINF_SUCCESS;
+ PVDSCRIPTCTXINT pThis = hScriptCtx;
+
+ LogFlowFunc(("hScriptCtx=%p paCallbacks=%p cCallbacks=%u pvUser=%p\n",
+ pThis, paCallbacks, cCallbacks, pvUser));
+
+ AssertPtrReturn(pThis, VERR_INVALID_POINTER);
+ AssertPtrReturn(paCallbacks, VERR_INVALID_POINTER);
+ AssertReturn(cCallbacks > 0, VERR_INVALID_PARAMETER);
+
+ /** @todo: Unregister already registered callbacks in case of an error. */
+ do
+ {
+ PVDSCRIPTFN pFn = NULL;
+
+ if (RTStrSpaceGet(&pThis->hStrSpaceFn, paCallbacks->pszFnName))
+ {
+ rc = VERR_DUPLICATE;
+ break;
+ }
+
+ pFn = (PVDSCRIPTFN)RTMemAllocZ(RT_OFFSETOF(VDSCRIPTFN, aenmArgTypes[paCallbacks->cArgs]));
+ if (!pFn)
+ {
+ rc = VERR_NO_MEMORY;
+ break;
+ }
+
+ /** @todo: Validate argument and returns types. */
+ pFn->Core.pszString = paCallbacks->pszFnName;
+ pFn->Core.cchString = strlen(pFn->Core.pszString);
+ pFn->fExternal = true;
+ pFn->Type.External.pfnCallback = paCallbacks->pfnCallback;
+ pFn->Type.External.pvUser = pvUser;
+ pFn->enmTypeRetn = paCallbacks->enmTypeReturn;
+ pFn->cArgs = paCallbacks->cArgs;
+
+ for (unsigned i = 0; i < paCallbacks->cArgs; i++)
+ pFn->aenmArgTypes[i] = paCallbacks->paArgs[i];
+
+ RTStrSpaceInsert(&pThis->hStrSpaceFn, &pFn->Core);
+ cCallbacks--;
+ paCallbacks++;
+ }
+ while (cCallbacks);
+
+ LogFlowFunc(("returns rc=%Rrc\n", rc));
+ return rc;
+}
+
+DECLHIDDEN(int) VDScriptCtxLoadScript(VDSCRIPTCTX hScriptCtx, const char *pszScript)
+{
+ int rc = VINF_SUCCESS;
+ PVDSCRIPTCTXINT pThis = hScriptCtx;
+
+ LogFlowFunc(("hScriptCtx=%p pszScript=%p\n", pThis, pszScript));
+
+ AssertPtrReturn(pThis, VERR_INVALID_POINTER);
+ AssertPtrReturn(pszScript, VERR_INVALID_POINTER);
+
+ PVDTOKENIZER pTokenizer = vdScriptTokenizerCreate(pszScript);
+ if (pTokenizer)
+ {
+ pThis->pTokenizer = pTokenizer;
+ rc = vdScriptParseFromTokenizer(pThis);
+ pThis->pTokenizer = NULL;
+ RTMemFree(pTokenizer);
+ }
+
+ LogFlowFunc(("returns rc=%Rrc\n", rc));
+ return rc;
+}
+
+DECLHIDDEN(int) VDScriptCtxCallFn(VDSCRIPTCTX hScriptCtx, const char *pszFnCall,
+ PVDSCRIPTARG paArgs, unsigned cArgs)
+{
+ PVDSCRIPTCTXINT pThis = hScriptCtx;
+ VDSCRIPTARG Ret;
+ return vdScriptCtxInterprete(pThis, pszFnCall, paArgs, cArgs, &Ret);
+}
diff --git a/src/VBox/Storage/testcase/VDScript.h b/src/VBox/Storage/testcase/VDScript.h
new file mode 100644
index 00000000..f1731dfd
--- /dev/null
+++ b/src/VBox/Storage/testcase/VDScript.h
@@ -0,0 +1,153 @@
+/** @file
+ *
+ * VBox HDD container test utility - scripting engine.
+ */
+
+/*
+ * Copyright (C) 2013 Oracle Corporation
+ *
+ * This file is part of VirtualBox Open Source Edition (OSE), as
+ * available from http://www.virtualbox.org. This file is free software;
+ * you can redistribute it and/or modify it under the terms of the GNU
+ * General Public License (GPL) as published by the Free Software
+ * Foundation, in version 2 as it comes in the "COPYING" file of the
+ * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
+ * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
+ */
+#ifndef _VDScript_h__
+#define _VDScript_h__
+
+/** Handle to the scripting context. */
+typedef struct VDSCRIPTCTXINT *VDSCRIPTCTX;
+/** Pointer to a scripting context handle. */
+typedef VDSCRIPTCTX *PVDSCRIPTCTX;
+
+/**
+ * Supprted primitive types in the scripting engine.
+ */
+typedef enum VDSCRIPTTYPE
+{
+ /** Invalid type, do not use. */
+ VDSCRIPTTYPE_INVALID = 0,
+ /** void type, used for no return value of methods. */
+ VDSCRIPTTYPE_VOID,
+ /** unsigned 8bit integer. */
+ VDSCRIPTTYPE_UINT8,
+ VDSCRIPTTYPE_INT8,
+ VDSCRIPTTYPE_UINT16,
+ VDSCRIPTTYPE_INT16,
+ VDSCRIPTTYPE_UINT32,
+ VDSCRIPTTYPE_INT32,
+ VDSCRIPTTYPE_UINT64,
+ VDSCRIPTTYPE_INT64,
+ VDSCRIPTTYPE_STRING,
+ VDSCRIPTTYPE_BOOL,
+ /** As usual, the 32bit blowup hack. */
+ VDSCRIPTTYPE_32BIT_HACK = 0x7fffffff
+} VDSCRIPTTYPE;
+/** Pointer to a type. */
+typedef VDSCRIPTTYPE *PVDSCRIPTTYPE;
+/** Pointer to a const type. */
+typedef const VDSCRIPTTYPE *PCVDSCRIPTTYPE;
+
+/**
+ * Script argument.
+ */
+typedef struct VDSCRIPTARG
+{
+ /** Type of the argument. */
+ VDSCRIPTTYPE enmType;
+ /** Value */
+ union
+ {
+ uint8_t u8;
+ int8_t i8;
+ uint16_t u16;
+ int16_t i16;
+ uint32_t u32;
+ int32_t i32;
+ uint64_t u64;
+ int64_t i64;
+ const char *psz;
+ bool f;
+ };
+} VDSCRIPTARG;
+/** Pointer to an argument. */
+typedef VDSCRIPTARG *PVDSCRIPTARG;
+
+/** Script callback. */
+typedef DECLCALLBACK(int) FNVDSCRIPTCALLBACK(PVDSCRIPTARG paScriptArgs, void *pvUser);
+/** Pointer to a script callback. */
+typedef FNVDSCRIPTCALLBACK *PFNVDSCRIPTCALLBACK;
+
+/**
+ * Callback registration structure.
+ */
+typedef struct VDSCRIPTCALLBACK
+{
+ /** The function name. */
+ const char *pszFnName;
+ /** The return type of the function. */
+ VDSCRIPTTYPE enmTypeReturn;
+ /** Pointer to the array of argument types. */
+ PCVDSCRIPTTYPE paArgs;
+ /** Number of arguments this method takes. */
+ unsigned cArgs;
+ /** The callback handler. */
+ PFNVDSCRIPTCALLBACK pfnCallback;
+} VDSCRIPTCALLBACK;
+/** Pointer to a callback register entry. */
+typedef VDSCRIPTCALLBACK *PVDSCRIPTCALLBACK;
+/** Pointer to a const callback register entry. */
+typedef const VDSCRIPTCALLBACK *PCVDSCRIPTCALLBACK;
+
+/**
+ * Create a new scripting context.
+ *
+ * @returns VBox status code.
+ * @param phScriptCtx Where to store the scripting context on success.
+ */
+DECLHIDDEN(int) VDScriptCtxCreate(PVDSCRIPTCTX phScriptCtx);
+
+/**
+ * Destroys the given scripting context.
+ *
+ * @returns nothing.
+ * @param hScriptCtx The script context to destroy.
+ */
+DECLHIDDEN(void) VDScriptCtxDestroy(VDSCRIPTCTX hScriptCtx);
+
+/**
+ * Register callbacks for the scripting context.
+ *
+ * @returns VBox status code.
+ * @param hScriptCtx The script context handle.
+ * @param paCallbacks Pointer to the callbacks to register.
+ * @param cCallbacks Number of callbacks in the array.
+ * @param pvUser Opaque user data to pass on the callback invocation.
+ */
+DECLHIDDEN(int) VDScriptCtxCallbacksRegister(VDSCRIPTCTX hScriptCtx, PCVDSCRIPTCALLBACK paCallbacks,
+ unsigned cCallbacks, void *pvUser);
+
+/**
+ * Load a given script into the context.
+ *
+ * @returns VBox status code.
+ * @param hScriptCtx The script context handle.
+ * @param pszScript Pointer to the char buffer containing the script.
+ */
+DECLHIDDEN(int) VDScriptCtxLoadScript(VDSCRIPTCTX hScriptCtx, const char *pszScript);
+
+/**
+ * Execute a given method in the script context.
+ *
+ * @returns VBox status code.
+ * @param hScriptCtx The script context handle.
+ * @param pszFnCall The method to call.
+ * @param paArgs Pointer to arguments to pass.
+ * @param cArgs Number of arguments.
+ */
+DECLHIDDEN(int) VDScriptCtxCallFn(VDSCRIPTCTX hScriptCtx, const char *pszFnCall,
+ PVDSCRIPTARG paArgs, unsigned cArgs);
+
+#endif /* _VDScript_h__ */
diff --git a/src/VBox/Storage/testcase/VDScriptAst.cpp b/src/VBox/Storage/testcase/VDScriptAst.cpp
new file mode 100644
index 00000000..8b3d0011
--- /dev/null
+++ b/src/VBox/Storage/testcase/VDScriptAst.cpp
@@ -0,0 +1,343 @@
+/** $Id: VDScriptAst.cpp $ */
+/** @file
+ *
+ * VBox HDD container test utility - scripting engine AST node related functions.
+ */
+
+/*
+ * Copyright (C) 2013 Oracle Corporation
+ *
+ * This file is part of VirtualBox Open Source Edition (OSE), as
+ * available from http://www.virtualbox.org. This file is free software;
+ * you can redistribute it and/or modify it under the terms of the GNU
+ * General Public License (GPL) as published by the Free Software
+ * Foundation, in version 2 as it comes in the "COPYING" file of the
+ * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
+ * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
+ */
+#define LOGGROUP LOGGROUP_DEFAULT
+#include <iprt/list.h>
+#include <iprt/mem.h>
+#include <iprt/assert.h>
+#include <iprt/string.h>
+
+#include <VBox/log.h>
+
+#include "VDScriptAst.h"
+
+/**
+ * Put all child nodes of the given expression AST node onto the given to free list.
+ *
+ * @returns nothing.
+ * @param pList The free list to append everything to.
+ * @param pAstNode The expression node to free.
+ */
+static void vdScriptAstNodeExpressionPutOnFreeList(PRTLISTANCHOR pList, PVDSCRIPTASTCORE pAstNode)
+{
+ AssertMsgReturnVoid(pAstNode->enmClass == VDSCRIPTASTCLASS_EXPRESSION,
+ ("Given AST node is not a statement\n"));
+
+ PVDSCRIPTASTEXPR pExpr = (PVDSCRIPTASTEXPR)pAstNode;
+ switch (pExpr->enmType)
+ {
+ case VDSCRIPTEXPRTYPE_PRIMARY_NUMCONST:
+ case VDSCRIPTEXPRTYPE_PRIMARY_BOOLEAN:
+ break;
+ case VDSCRIPTEXPRTYPE_PRIMARY_STRINGCONST:
+ RTStrFree((char *)pExpr->pszStr);
+ break;
+ case VDSCRIPTEXPRTYPE_PRIMARY_IDENTIFIER:
+ {
+ RTListAppend(pList, &pExpr->pIde->Core.ListNode);
+ break;
+ }
+ case VDSCRIPTEXPRTYPE_ASSIGNMENT_LIST:
+ {
+ while (!RTListIsEmpty(&pExpr->ListExpr))
+ {
+ PVDSCRIPTASTCORE pNode = RTListGetFirst(&pExpr->ListExpr, VDSCRIPTASTCORE, ListNode);
+ RTListNodeRemove(&pNode->ListNode);
+ RTListAppend(pList, &pNode->ListNode);
+ }
+ break;
+ }
+ case VDSCRIPTEXPRTYPE_POSTFIX_FNCALL:
+ {
+ RTListAppend(pList, &pExpr->FnCall.pFnIde->Core.ListNode);
+ while (!RTListIsEmpty(&pExpr->FnCall.ListArgs))
+ {
+ PVDSCRIPTASTCORE pNode = RTListGetFirst(&pExpr->FnCall.ListArgs, VDSCRIPTASTCORE, ListNode);
+ RTListNodeRemove(&pNode->ListNode);
+ RTListAppend(pList, &pNode->ListNode);
+ }
+ break;
+ }
+ case VDSCRIPTEXPRTYPE_POSTFIX_INCREMENT:
+ case VDSCRIPTEXPRTYPE_POSTFIX_DECREMENT:
+ case VDSCRIPTEXPRTYPE_UNARY_INCREMENT:
+ case VDSCRIPTEXPRTYPE_UNARY_DECREMENT:
+ case VDSCRIPTEXPRTYPE_UNARY_POSSIGN:
+ case VDSCRIPTEXPRTYPE_UNARY_NEGSIGN:
+ case VDSCRIPTEXPRTYPE_UNARY_INVERT:
+ case VDSCRIPTEXPRTYPE_UNARY_NEGATE:
+ {
+ RTListAppend(pList, &pExpr->pExpr->Core.ListNode);
+ break;
+ }
+ case VDSCRIPTEXPRTYPE_MULTIPLICATION:
+ case VDSCRIPTEXPRTYPE_DIVISION:
+ case VDSCRIPTEXPRTYPE_MODULUS:
+ case VDSCRIPTEXPRTYPE_ADDITION:
+ case VDSCRIPTEXPRTYPE_SUBTRACTION:
+ case VDSCRIPTEXPRTYPE_LSR:
+ case VDSCRIPTEXPRTYPE_LSL:
+ case VDSCRIPTEXPRTYPE_LOWER:
+ case VDSCRIPTEXPRTYPE_HIGHER:
+ case VDSCRIPTEXPRTYPE_LOWEREQUAL:
+ case VDSCRIPTEXPRTYPE_HIGHEREQUAL:
+ case VDSCRIPTEXPRTYPE_EQUAL:
+ case VDSCRIPTEXPRTYPE_NOTEQUAL:
+ case VDSCRIPTEXPRTYPE_BITWISE_AND:
+ case VDSCRIPTEXPRTYPE_BITWISE_XOR:
+ case VDSCRIPTEXPRTYPE_BITWISE_OR:
+ case VDSCRIPTEXPRTYPE_LOGICAL_AND:
+ case VDSCRIPTEXPRTYPE_LOGICAL_OR:
+ case VDSCRIPTEXPRTYPE_ASSIGN:
+ case VDSCRIPTEXPRTYPE_ASSIGN_MULT:
+ case VDSCRIPTEXPRTYPE_ASSIGN_DIV:
+ case VDSCRIPTEXPRTYPE_ASSIGN_MOD:
+ case VDSCRIPTEXPRTYPE_ASSIGN_ADD:
+ case VDSCRIPTEXPRTYPE_ASSIGN_SUB:
+ case VDSCRIPTEXPRTYPE_ASSIGN_LSL:
+ case VDSCRIPTEXPRTYPE_ASSIGN_LSR:
+ case VDSCRIPTEXPRTYPE_ASSIGN_AND:
+ case VDSCRIPTEXPRTYPE_ASSIGN_XOR:
+ case VDSCRIPTEXPRTYPE_ASSIGN_OR:
+ {
+ RTListAppend(pList, &pExpr->BinaryOp.pLeftExpr->Core.ListNode);
+ RTListAppend(pList, &pExpr->BinaryOp.pRightExpr->Core.ListNode);
+ break;
+ }
+ case VDSCRIPTEXPRTYPE_INVALID:
+ default:
+ AssertMsgFailedReturnVoid(("Invalid AST node expression type %d\n",
+ pExpr->enmType));
+ }
+}
+
+/**
+ * Free a given statement AST node and put everything on the given to free list.
+ *
+ * @returns nothing.
+ * @param pList The free list to append everything to.
+ * @param pAstNode The statement node to free.
+ */
+static void vdScriptAstNodeStatmentPutOnFreeList(PRTLISTANCHOR pList, PVDSCRIPTASTCORE pAstNode)
+{
+ AssertMsgReturnVoid(pAstNode->enmClass == VDSCRIPTASTCLASS_STATEMENT,
+ ("Given AST node is not a statement\n"));
+
+ PVDSCRIPTASTSTMT pStmt = (PVDSCRIPTASTSTMT)pAstNode;
+ switch (pStmt->enmStmtType)
+ {
+ case VDSCRIPTSTMTTYPE_COMPOUND:
+ {
+ /* Put all declarations on the to free list. */
+ while (!RTListIsEmpty(&pStmt->Compound.ListDecls))
+ {
+ PVDSCRIPTASTCORE pNode = RTListGetFirst(&pStmt->Compound.ListDecls, VDSCRIPTASTCORE, ListNode);
+ RTListNodeRemove(&pNode->ListNode);
+ RTListAppend(pList, &pNode->ListNode);
+ }
+
+ /* Put all statements on the to free list. */
+ while (!RTListIsEmpty(&pStmt->Compound.ListStmts))
+ {
+ PVDSCRIPTASTCORE pNode = RTListGetFirst(&pStmt->Compound.ListDecls, VDSCRIPTASTCORE, ListNode);
+ RTListNodeRemove(&pNode->ListNode);
+ RTListAppend(pList, &pNode->ListNode);
+ }
+ break;
+ }
+ case VDSCRIPTSTMTTYPE_EXPRESSION:
+ {
+ if (pStmt->pExpr)
+ RTListAppend(pList, &pStmt->pExpr->Core.ListNode);
+ break;
+ }
+ case VDSCRIPTSTMTTYPE_IF:
+ {
+ RTListAppend(pList, &pStmt->If.pCond->Core.ListNode);
+ RTListAppend(pList, &pStmt->If.pTrueStmt->Core.ListNode);
+ if (pStmt->If.pElseStmt)
+ RTListAppend(pList, &pStmt->If.pElseStmt->Core.ListNode);
+ break;
+ }
+ case VDSCRIPTSTMTTYPE_SWITCH:
+ {
+ RTListAppend(pList, &pStmt->Switch.pCond->Core.ListNode);
+ RTListAppend(pList, &pStmt->Switch.pStmt->Core.ListNode);
+ break;
+ }
+ case VDSCRIPTSTMTTYPE_WHILE:
+ {
+ RTListAppend(pList, &pStmt->While.pCond->Core.ListNode);
+ RTListAppend(pList, &pStmt->While.pStmt->Core.ListNode);
+ break;
+ }
+ case VDSCRIPTSTMTTYPE_FOR:
+ {
+ RTListAppend(pList, &pStmt->For.pExprStart->Core.ListNode);
+ RTListAppend(pList, &pStmt->For.pExprCond->Core.ListNode);
+ RTListAppend(pList, &pStmt->For.pExpr3->Core.ListNode);
+ RTListAppend(pList, &pStmt->For.pStmt->Core.ListNode);
+ break;
+ }
+ case VDSCRIPTSTMTTYPE_RETURN:
+ {
+ if (pStmt->pExpr)
+ RTListAppend(pList, &pStmt->pExpr->Core.ListNode);
+ break;
+ }
+ case VDSCRIPTSTMTTYPE_CASE:
+ {
+ RTListAppend(pList, &pStmt->Case.pExpr->Core.ListNode);
+ RTListAppend(pList, &pStmt->Case.pStmt->Core.ListNode);
+ break;
+ }
+ case VDSCRIPTSTMTTYPE_DEFAULT:
+ {
+ RTListAppend(pList, &pStmt->Case.pStmt->Core.ListNode);
+ break;
+ }
+ case VDSCRIPTSTMTTYPE_CONTINUE:
+ case VDSCRIPTSTMTTYPE_BREAK:
+ break;
+ case VDSCRIPTSTMTTYPE_INVALID:
+ default:
+ AssertMsgFailedReturnVoid(("Invalid AST node statement type %d\n",
+ pStmt->enmStmtType));
+ }
+}
+
+DECLHIDDEN(void) vdScriptAstNodeFree(PVDSCRIPTASTCORE pAstNode)
+{
+ RTLISTANCHOR ListFree;
+
+ /*
+ * The node is not allowed to be part of a list because we need it
+ * for the nodes to free list.
+ */
+ Assert(RTListIsEmpty(&pAstNode->ListNode));
+ RTListInit(&ListFree);
+ RTListAppend(&ListFree, &pAstNode->ListNode);
+
+ do
+ {
+ pAstNode = RTListGetFirst(&ListFree, VDSCRIPTASTCORE, ListNode);
+ RTListNodeRemove(&pAstNode->ListNode);
+
+ switch (pAstNode->enmClass)
+ {
+ case VDSCRIPTASTCLASS_FUNCTION:
+ {
+ PVDSCRIPTASTFN pFn = (PVDSCRIPTASTFN)pAstNode;
+
+ if (pFn->pRetType)
+ RTListAppend(&ListFree, &pFn->pRetType->Core.ListNode);
+ if (pFn->pFnIde)
+ RTListAppend(&ListFree, &pFn->pFnIde->Core.ListNode);
+
+ /* Put argument list on the to free list. */
+ while (!RTListIsEmpty(&pFn->ListArgs))
+ {
+ PVDSCRIPTASTCORE pArg = RTListGetFirst(&pFn->ListArgs, VDSCRIPTASTCORE, ListNode);
+ RTListNodeRemove(&pArg->ListNode);
+ RTListAppend(&ListFree, &pArg->ListNode);
+ }
+
+ /* Put compound statement onto the list. */
+ RTListAppend(&ListFree, &pFn->pCompoundStmts->Core.ListNode);
+ break;
+ }
+ case VDSCRIPTASTCLASS_FUNCTIONARG:
+ {
+ PVDSCRIPTASTFNARG pAstNodeArg = (PVDSCRIPTASTFNARG)pAstNode;
+ if (pAstNodeArg->pType)
+ RTListAppend(&ListFree, &pAstNodeArg->pType->Core.ListNode);
+ if (pAstNodeArg->pArgIde)
+ RTListAppend(&ListFree, &pAstNodeArg->pArgIde->Core.ListNode);
+ break;
+ }
+ case VDSCRIPTASTCLASS_IDENTIFIER:
+ break;
+ case VDSCRIPTASTCLASS_DECLARATION:
+ break;
+ case VDSCRIPTASTCLASS_STATEMENT:
+ {
+ vdScriptAstNodeStatmentPutOnFreeList(&ListFree, pAstNode);
+ break;
+ }
+ case VDSCRIPTASTCLASS_EXPRESSION:
+ {
+ vdScriptAstNodeExpressionPutOnFreeList(&ListFree, pAstNode);
+ break;
+ }
+ case VDSCRIPTASTCLASS_INVALID:
+ default:
+ AssertMsgFailedReturnVoid(("Invalid AST node class given %d\n", pAstNode->enmClass));
+ }
+
+ RTMemFree(pAstNode);
+ } while (!RTListIsEmpty(&ListFree));
+
+}
+
+DECLHIDDEN(PVDSCRIPTASTCORE) vdScriptAstNodeAlloc(VDSCRIPTASTCLASS enmClass)
+{
+ size_t cbAlloc = 0;
+
+ switch (enmClass)
+ {
+ case VDSCRIPTASTCLASS_FUNCTION:
+ cbAlloc = sizeof(VDSCRIPTASTFN);
+ break;
+ case VDSCRIPTASTCLASS_FUNCTIONARG:
+ cbAlloc = sizeof(VDSCRIPTASTFNARG);
+ break;
+ case VDSCRIPTASTCLASS_DECLARATION:
+ cbAlloc = sizeof(VDSCRIPTASTDECL);
+ break;
+ case VDSCRIPTASTCLASS_STATEMENT:
+ cbAlloc = sizeof(VDSCRIPTASTSTMT);
+ break;
+ case VDSCRIPTASTCLASS_EXPRESSION:
+ cbAlloc = sizeof(VDSCRIPTASTEXPR);
+ break;
+ case VDSCRIPTASTCLASS_IDENTIFIER:
+ case VDSCRIPTASTCLASS_INVALID:
+ default:
+ AssertMsgFailedReturn(("Invalid AST node class given %d\n", enmClass), NULL);
+ }
+
+ PVDSCRIPTASTCORE pAstNode = (PVDSCRIPTASTCORE)RTMemAllocZ(cbAlloc);
+ if (pAstNode)
+ {
+ pAstNode->enmClass = enmClass;
+ RTListInit(&pAstNode->ListNode);
+ }
+
+ return pAstNode;
+}
+
+DECLHIDDEN(PVDSCRIPTASTIDE) vdScriptAstNodeIdeAlloc(unsigned cchIde)
+{
+ PVDSCRIPTASTIDE pAstNode = (PVDSCRIPTASTIDE)RTMemAllocZ(RT_OFFSETOF(VDSCRIPTASTIDE, aszIde[cchIde + 1]));
+ if (pAstNode)
+ {
+ pAstNode->Core.enmClass = VDSCRIPTASTCLASS_IDENTIFIER;
+ RTListInit(&pAstNode->Core.ListNode);
+ }
+
+ return pAstNode;
+}
diff --git a/src/VBox/Storage/testcase/VDScriptAst.h b/src/VBox/Storage/testcase/VDScriptAst.h
new file mode 100644
index 00000000..3f324094
--- /dev/null
+++ b/src/VBox/Storage/testcase/VDScriptAst.h
@@ -0,0 +1,452 @@
+/** @file
+ *
+ * VBox HDD container test utility - scripting engine, AST related structures.
+ */
+
+/*
+ * Copyright (C) 2013 Oracle Corporation
+ *
+ * This file is part of VirtualBox Open Source Edition (OSE), as
+ * available from http://www.virtualbox.org. This file is free software;
+ * you can redistribute it and/or modify it under the terms of the GNU
+ * General Public License (GPL) as published by the Free Software
+ * Foundation, in version 2 as it comes in the "COPYING" file of the
+ * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
+ * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
+ */
+#ifndef _VDScriptAst_h__
+#define _VDScriptAst_h__
+
+#include <iprt/list.h>
+
+/**
+ * Position information.
+ */
+typedef struct VDSRCPOS
+{
+ /** Line in the source. */
+ unsigned iLine;
+ /** Current start character .*/
+ unsigned iChStart;
+ /** Current end character. */
+ unsigned iChEnd;
+} VDSRCPOS;
+/** Pointer to a source position. */
+typedef struct VDSRCPOS *PVDSRCPOS;
+
+/**
+ * AST node classes.
+ */
+typedef enum VDSCRIPTASTCLASS
+{
+ /** Invalid. */
+ VDSCRIPTASTCLASS_INVALID = 0,
+ /** Function node. */
+ VDSCRIPTASTCLASS_FUNCTION,
+ /** Function argument. */
+ VDSCRIPTASTCLASS_FUNCTIONARG,
+ /** Identifier node. */
+ VDSCRIPTASTCLASS_IDENTIFIER,
+ /** Declaration node. */
+ VDSCRIPTASTCLASS_DECLARATION,
+ /** Statement node. */
+ VDSCRIPTASTCLASS_STATEMENT,
+ /** Expression node. */
+ VDSCRIPTASTCLASS_EXPRESSION,
+ /** 32bit blowup. */
+ VDSCRIPTASTCLASS_32BIT_HACK = 0x7fffffff
+} VDSCRIPTASTCLASS;
+/** Pointer to an AST node class. */
+typedef VDSCRIPTASTCLASS *PVDSCRIPTASTCLASS;
+
+/**
+ * Core AST structure.
+ */
+typedef struct VDSCRIPTASTCORE
+{
+ /** The node class, used for verification. */
+ VDSCRIPTASTCLASS enmClass;
+ /** List which might be used. */
+ RTLISTNODE ListNode;
+ /** Position in the source file of this node. */
+ VDSRCPOS Pos;
+} VDSCRIPTASTCORE;
+/** Pointer to an AST core structure. */
+typedef VDSCRIPTASTCORE *PVDSCRIPTASTCORE;
+
+/** Pointer to an statement node - forward declaration. */
+typedef struct VDSCRIPTASTSTMT *PVDSCRIPTASTSTMT;
+/** Pointer to an expression node - forward declaration. */
+typedef struct VDSCRIPTASTEXPR *PVDSCRIPTASTEXPR;
+
+/**
+ * AST identifier node.
+ */
+typedef struct VDSCRIPTASTIDE
+{
+ /** Core structure. */
+ VDSCRIPTASTCORE Core;
+ /** Number of characters in the identifier, excluding the zero terminator. */
+ unsigned cchIde;
+ /** Identifier, variable size. */
+ char aszIde[1];
+} VDSCRIPTASTIDE;
+/** Pointer to an identifer node. */
+typedef VDSCRIPTASTIDE *PVDSCRIPTASTIDE;
+
+/**
+ * AST declaration node.
+ */
+typedef struct VDSCRIPTASTDECL
+{
+ /** Core structure. */
+ VDSCRIPTASTCORE Core;
+ /** @todo */
+} VDSCRIPTASTDECL;
+/** Pointer to an declaration node. */
+typedef VDSCRIPTASTDECL *PVDSCRIPTASTDECL;
+
+/**
+ * Expression types.
+ */
+typedef enum VDSCRIPTEXPRTYPE
+{
+ /** Invalid. */
+ VDSCRIPTEXPRTYPE_INVALID = 0,
+ /** Numerical constant. */
+ VDSCRIPTEXPRTYPE_PRIMARY_NUMCONST,
+ /** String constant. */
+ VDSCRIPTEXPRTYPE_PRIMARY_STRINGCONST,
+ /** Boolean constant. */
+ VDSCRIPTEXPRTYPE_PRIMARY_BOOLEAN,
+ /** Identifier. */
+ VDSCRIPTEXPRTYPE_PRIMARY_IDENTIFIER,
+ /** List of assignment expressions as in a = b = c = ... . */
+ VDSCRIPTEXPRTYPE_ASSIGNMENT_LIST,
+ /** Postfix increment expression. */
+ VDSCRIPTEXPRTYPE_POSTFIX_INCREMENT,
+ /** Postfix decrement expression. */
+ VDSCRIPTEXPRTYPE_POSTFIX_DECREMENT,
+ /** Postfix function call expression. */
+ VDSCRIPTEXPRTYPE_POSTFIX_FNCALL,
+ /** Unary increment expression. */
+ VDSCRIPTEXPRTYPE_UNARY_INCREMENT,
+ /** Unary decrement expression. */
+ VDSCRIPTEXPRTYPE_UNARY_DECREMENT,
+ /** Unary positive sign expression. */
+ VDSCRIPTEXPRTYPE_UNARY_POSSIGN,
+ /** Unary negtive sign expression. */
+ VDSCRIPTEXPRTYPE_UNARY_NEGSIGN,
+ /** Unary invert expression. */
+ VDSCRIPTEXPRTYPE_UNARY_INVERT,
+ /** Unary negate expression. */
+ VDSCRIPTEXPRTYPE_UNARY_NEGATE,
+ /** Multiplicative expression. */
+ VDSCRIPTEXPRTYPE_MULTIPLICATION,
+ /** Division expression. */
+ VDSCRIPTEXPRTYPE_DIVISION,
+ /** Modulus expression. */
+ VDSCRIPTEXPRTYPE_MODULUS,
+ /** Addition expression. */
+ VDSCRIPTEXPRTYPE_ADDITION,
+ /** Subtraction expression. */
+ VDSCRIPTEXPRTYPE_SUBTRACTION,
+ /** Logical shift right. */
+ VDSCRIPTEXPRTYPE_LSR,
+ /** Logical shift left. */
+ VDSCRIPTEXPRTYPE_LSL,
+ /** Lower than expression */
+ VDSCRIPTEXPRTYPE_LOWER,
+ /** Higher than expression */
+ VDSCRIPTEXPRTYPE_HIGHER,
+ /** Lower or equal than expression */
+ VDSCRIPTEXPRTYPE_LOWEREQUAL,
+ /** Higher or equal than expression */
+ VDSCRIPTEXPRTYPE_HIGHEREQUAL,
+ /** Equals expression */
+ VDSCRIPTEXPRTYPE_EQUAL,
+ /** Not equal expression */
+ VDSCRIPTEXPRTYPE_NOTEQUAL,
+ /** Bitwise and expression */
+ VDSCRIPTEXPRTYPE_BITWISE_AND,
+ /** Bitwise xor expression */
+ VDSCRIPTEXPRTYPE_BITWISE_XOR,
+ /** Bitwise or expression */
+ VDSCRIPTEXPRTYPE_BITWISE_OR,
+ /** Logical and expression */
+ VDSCRIPTEXPRTYPE_LOGICAL_AND,
+ /** Logical or expression */
+ VDSCRIPTEXPRTYPE_LOGICAL_OR,
+ /** Assign expression */
+ VDSCRIPTEXPRTYPE_ASSIGN,
+ /** Multiplicative assign expression */
+ VDSCRIPTEXPRTYPE_ASSIGN_MULT,
+ /** Division assign expression */
+ VDSCRIPTEXPRTYPE_ASSIGN_DIV,
+ /** Modulus assign expression */
+ VDSCRIPTEXPRTYPE_ASSIGN_MOD,
+ /** Additive assign expression */
+ VDSCRIPTEXPRTYPE_ASSIGN_ADD,
+ /** Subtractive assign expression */
+ VDSCRIPTEXPRTYPE_ASSIGN_SUB,
+ /** Bitwise left shift assign expression */
+ VDSCRIPTEXPRTYPE_ASSIGN_LSL,
+ /** Bitwise right shift assign expression */
+ VDSCRIPTEXPRTYPE_ASSIGN_LSR,
+ /** Bitwise and assign expression */
+ VDSCRIPTEXPRTYPE_ASSIGN_AND,
+ /** Bitwise xor assign expression */
+ VDSCRIPTEXPRTYPE_ASSIGN_XOR,
+ /** Bitwise or assign expression */
+ VDSCRIPTEXPRTYPE_ASSIGN_OR,
+ /** 32bit hack. */
+ VDSCRIPTEXPRTYPE_32BIT_HACK = 0x7fffffff
+} VDSCRIPTEXPRTYPE;
+/** Pointer to an expression type. */
+typedef VDSCRIPTEXPRTYPE *PVDSCRIPTEXPRTYPE;
+
+/**
+ * AST expression node.
+ */
+typedef struct VDSCRIPTASTEXPR
+{
+ /** Core structure. */
+ VDSCRIPTASTCORE Core;
+ /** Expression type. */
+ VDSCRIPTEXPRTYPE enmType;
+ /** Expression type dependent data. */
+ union
+ {
+ /** Numerical constant. */
+ uint64_t u64;
+ /** Primary identifier. */
+ PVDSCRIPTASTIDE pIde;
+ /** String literal */
+ const char *pszStr;
+ /** Boolean constant. */
+ bool f;
+ /** List of expressions - VDSCRIPTASTEXPR. */
+ RTLISTANCHOR ListExpr;
+ /** Pointer to another expression. */
+ PVDSCRIPTASTEXPR pExpr;
+ /** Function call expression. */
+ struct
+ {
+ /** Other postfix expression used as the identifier for the function. */
+ PVDSCRIPTASTEXPR pFnIde;
+ /** Argument list if existing. */
+ RTLISTANCHOR ListArgs;
+ } FnCall;
+ /** Binary operation. */
+ struct
+ {
+ /** Left operator. */
+ PVDSCRIPTASTEXPR pLeftExpr;
+ /** Right operator. */
+ PVDSCRIPTASTEXPR pRightExpr;
+ } BinaryOp;
+ };
+} VDSCRIPTASTEXPR;
+
+/**
+ * AST if node.
+ */
+typedef struct VDSCRIPTASTIF
+{
+ /** Conditional expression. */
+ PVDSCRIPTASTEXPR pCond;
+ /** The true branch */
+ PVDSCRIPTASTSTMT pTrueStmt;
+ /** The else branch, can be NULL if no else branch. */
+ PVDSCRIPTASTSTMT pElseStmt;
+} VDSCRIPTASTIF;
+/** Pointer to an expression node. */
+typedef VDSCRIPTASTIF *PVDSCRIPTASTIF;
+
+/**
+ * AST switch node.
+ */
+typedef struct VDSCRIPTASTSWITCH
+{
+ /** Conditional expression. */
+ PVDSCRIPTASTEXPR pCond;
+ /** The statement to follow. */
+ PVDSCRIPTASTSTMT pStmt;
+} VDSCRIPTASTSWITCH;
+/** Pointer to an expression node. */
+typedef VDSCRIPTASTSWITCH *PVDSCRIPTASTSWITCH;
+
+/**
+ * AST while or do ... while node.
+ */
+typedef struct VDSCRIPTASTWHILE
+{
+ /** Flag whether this is a do while loop. */
+ bool fDoWhile;
+ /** Conditional expression. */
+ PVDSCRIPTASTEXPR pCond;
+ /** The statement to follow. */
+ PVDSCRIPTASTSTMT pStmt;
+} VDSCRIPTASTWHILE;
+/** Pointer to an expression node. */
+typedef VDSCRIPTASTWHILE *PVDSCRIPTASTWHILE;
+
+/**
+ * AST for node.
+ */
+typedef struct VDSCRIPTASTFOR
+{
+ /** Initializer expression. */
+ PVDSCRIPTASTEXPR pExprStart;
+ /** The exit condition. */
+ PVDSCRIPTASTEXPR pExprCond;
+ /** The third expression (normally used to increase/decrease loop variable). */
+ PVDSCRIPTASTEXPR pExpr3;
+ /** The for loop body. */
+ PVDSCRIPTASTSTMT pStmt;
+} VDSCRIPTASTFOR;
+/** Pointer to an expression node. */
+typedef VDSCRIPTASTFOR *PVDSCRIPTASTFOR;
+
+/**
+ * Statement types.
+ */
+typedef enum VDSCRIPTSTMTTYPE
+{
+ /** Invalid. */
+ VDSCRIPTSTMTTYPE_INVALID = 0,
+ /** Compound statement. */
+ VDSCRIPTSTMTTYPE_COMPOUND,
+ /** Expression statement. */
+ VDSCRIPTSTMTTYPE_EXPRESSION,
+ /** if statement. */
+ VDSCRIPTSTMTTYPE_IF,
+ /** switch statement. */
+ VDSCRIPTSTMTTYPE_SWITCH,
+ /** while statement. */
+ VDSCRIPTSTMTTYPE_WHILE,
+ /** for statement. */
+ VDSCRIPTSTMTTYPE_FOR,
+ /** continue statement. */
+ VDSCRIPTSTMTTYPE_CONTINUE,
+ /** break statement. */
+ VDSCRIPTSTMTTYPE_BREAK,
+ /** return statement. */
+ VDSCRIPTSTMTTYPE_RETURN,
+ /** case statement. */
+ VDSCRIPTSTMTTYPE_CASE,
+ /** default statement. */
+ VDSCRIPTSTMTTYPE_DEFAULT,
+ /** 32bit hack. */
+ VDSCRIPTSTMTTYPE_32BIT_HACK = 0x7fffffff
+} VDSCRIPTSTMTTYPE;
+/** Pointer to a statement type. */
+typedef VDSCRIPTSTMTTYPE *PVDSCRIPTSTMTTYPE;
+
+/**
+ * AST statement node.
+ */
+typedef struct VDSCRIPTASTSTMT
+{
+ /** Core structure. */
+ VDSCRIPTASTCORE Core;
+ /** Statement type */
+ VDSCRIPTSTMTTYPE enmStmtType;
+ /** Statement type dependent data. */
+ union
+ {
+ /** Compound statement. */
+ struct
+ {
+ /** List of declarations - VDSCRIPTASTDECL. */
+ RTLISTANCHOR ListDecls;
+ /** List of statements - VDSCRIPTASTSTMT. */
+ RTLISTANCHOR ListStmts;
+ } Compound;
+ /** case, default statement. */
+ struct
+ {
+ /** Pointer to the expression. */
+ PVDSCRIPTASTEXPR pExpr;
+ /** Pointer to the statement. */
+ PVDSCRIPTASTSTMT pStmt;
+ } Case;
+ /** "if" statement. */
+ VDSCRIPTASTIF If;
+ /** "switch" statement. */
+ VDSCRIPTASTSWITCH Switch;
+ /** "while" or "do ... while" loop. */
+ VDSCRIPTASTWHILE While;
+ /** "for" loop. */
+ VDSCRIPTASTFOR For;
+ /** Pointer to another statement. */
+ PVDSCRIPTASTSTMT pStmt;
+ /** Expression statement. */
+ PVDSCRIPTASTEXPR pExpr;
+ };
+} VDSCRIPTASTSTMT;
+
+/**
+ * AST node for one function argument.
+ */
+typedef struct VDSCRIPTASTFNARG
+{
+ /** Core structure. */
+ VDSCRIPTASTCORE Core;
+ /** Identifier describing the type of the argument. */
+ PVDSCRIPTASTIDE pType;
+ /** The name of the argument. */
+ PVDSCRIPTASTIDE pArgIde;
+} VDSCRIPTASTFNARG;
+/** Pointer to a AST function argument node. */
+typedef VDSCRIPTASTFNARG *PVDSCRIPTASTFNARG;
+
+/**
+ * AST node describing a function.
+ */
+typedef struct VDSCRIPTASTFN
+{
+ /** Core structure. */
+ VDSCRIPTASTCORE Core;
+ /** Identifier describing the return type. */
+ PVDSCRIPTASTIDE pRetType;
+ /** Name of the function. */
+ PVDSCRIPTASTIDE pFnIde;
+ /** Number of arguments in the list. */
+ unsigned cArgs;
+ /** Argument list - VDSCRIPTASTFNARG. */
+ RTLISTANCHOR ListArgs;
+ /** Compound statement node. */
+ PVDSCRIPTASTSTMT pCompoundStmts;
+} VDSCRIPTASTFN;
+/** Pointer to a function AST node. */
+typedef VDSCRIPTASTFN *PVDSCRIPTASTFN;
+
+/**
+ * Free the given AST node and all subsequent nodes pointed to
+ * by the given node.
+ *
+ * @returns nothing.
+ * @param pAstNode The AST node to free.
+ */
+DECLHIDDEN(void) vdScriptAstNodeFree(PVDSCRIPTASTCORE pAstNode);
+
+/**
+ * Allocate a non variable in size AST node of the given class.
+ *
+ * @returns Pointer to the allocated node.
+ * NULL if out of memory.
+ * @param enmClass The class of the AST node.
+ */
+DECLHIDDEN(PVDSCRIPTASTCORE) vdScriptAstNodeAlloc(VDSCRIPTASTCLASS enmClass);
+
+/**
+ * Allocate a IDE node which can hold the given number of characters.
+ *
+ * @returns Pointer to the allocated node.
+ * NULL if out of memory.
+ * @param cchIde Number of characters which can be stored in the node.
+ */
+DECLHIDDEN(PVDSCRIPTASTIDE) vdScriptAstNodeIdeAlloc(unsigned cchIde);
+
+#endif /* _VDScriptAst_h__ */
diff --git a/src/VBox/Storage/testcase/VDScriptChecker.cpp b/src/VBox/Storage/testcase/VDScriptChecker.cpp
new file mode 100644
index 00000000..42dfd51a
--- /dev/null
+++ b/src/VBox/Storage/testcase/VDScriptChecker.cpp
@@ -0,0 +1,31 @@
+/** $Id: VDScriptChecker.cpp $ */
+/** @file
+ *
+ * VBox HDD container test utility - scripting engine, type and context checker.
+ */
+
+/*
+ * Copyright (C) 2013 Oracle Corporation
+ *
+ * This file is part of VirtualBox Open Source Edition (OSE), as
+ * available from http://www.virtualbox.org. This file is free software;
+ * you can redistribute it and/or modify it under the terms of the GNU
+ * General Public License (GPL) as published by the Free Software
+ * Foundation, in version 2 as it comes in the "COPYING" file of the
+ * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
+ * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
+ */
+#define LOGGROUP LOGGROUP_DEFAULT
+#include <iprt/list.h>
+#include <iprt/mem.h>
+#include <iprt/assert.h>
+
+#include <VBox/log.h>
+
+#include "VDScriptAst.h"
+#include "VDScriptInternal.h"
+
+DECLHIDDEN(int) vdScriptCtxCheck(PVDSCRIPTCTXINT pThis)
+{
+ return VERR_NOT_IMPLEMENTED;
+}
diff --git a/src/VBox/Storage/testcase/VDScriptInternal.h b/src/VBox/Storage/testcase/VDScriptInternal.h
new file mode 100644
index 00000000..9e1b78a2
--- /dev/null
+++ b/src/VBox/Storage/testcase/VDScriptInternal.h
@@ -0,0 +1,107 @@
+/** @file
+ *
+ * VBox HDD container test utility - scripting engine, internal script structures.
+ */
+
+/*
+ * Copyright (C) 2013 Oracle Corporation
+ *
+ * This file is part of VirtualBox Open Source Edition (OSE), as
+ * available from http://www.virtualbox.org. This file is free software;
+ * you can redistribute it and/or modify it under the terms of the GNU
+ * General Public License (GPL) as published by the Free Software
+ * Foundation, in version 2 as it comes in the "COPYING" file of the
+ * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
+ * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
+ */
+#ifndef _VDScriptInternal_h__
+#define _VDScriptInternal_h__
+
+#include <iprt/list.h>
+#include <iprt/string.h>
+
+#include "VDScript.h"
+
+/**
+ * Script function which can be called.
+ */
+typedef struct VDSCRIPTFN
+{
+ /** String space core. */
+ RTSTRSPACECORE Core;
+ /** Flag whether function is defined in the source or was
+ * registered from the outside. */
+ bool fExternal;
+ /** Flag dependent data. */
+ union
+ {
+ /** Data for functions defined in the source. */
+ struct
+ {
+ /** Pointer to the AST defining the function. */
+ PVDSCRIPTASTFN pAstFn;
+ } Internal;
+ /** Data for external defined functions. */
+ struct
+ {
+ /** Callback function. */
+ PFNVDSCRIPTCALLBACK pfnCallback;
+ /** Opaque user data. */
+ void *pvUser;
+ } External;
+ } Type;
+ /** Return type of the function. */
+ VDSCRIPTTYPE enmTypeRetn;
+ /** Number of arguments the function takes. */
+ unsigned cArgs;
+ /** Variable sized array of argument types. */
+ VDSCRIPTTYPE aenmArgTypes[1];
+} VDSCRIPTFN;
+/** Pointer to a script function registration structure. */
+typedef VDSCRIPTFN *PVDSCRIPTFN;
+
+/** Pointer to a tokenize state. */
+typedef struct VDTOKENIZER *PVDTOKENIZER;
+
+/**
+ * Script context.
+ */
+typedef struct VDSCRIPTCTXINT
+{
+ /** String space of external registered and source defined functions. */
+ RTSTRSPACE hStrSpaceFn;
+ /** List of ASTs for functions - VDSCRIPTASTFN. */
+ RTLISTANCHOR ListAst;
+ /** Pointer to the current tokenizer state. */
+ PVDTOKENIZER pTokenizer;
+} VDSCRIPTCTXINT;
+/** Pointer to a script context. */
+typedef VDSCRIPTCTXINT *PVDSCRIPTCTXINT;
+
+/**
+ * Check the context for type correctness.
+ *
+ * @returns VBox status code.
+ * @param pThis The script context.
+ */
+DECLHIDDEN(int) vdScriptCtxCheck(PVDSCRIPTCTXINT pThis);
+
+/**
+ * Interprete a given function AST. The executed functions
+ * must be type correct, otherwise the behavior is undefined
+ * (Will assert in debug builds).
+ *
+ * @returns VBox status code.
+ * @param pThis The script context.
+ * @param pszFn The function name to interprete.
+ * @param paArgs Arguments to pass to the function.
+ * @param cArgs Number of arguments.
+ * @param pRet Where to store the return value on success.
+ *
+ * @note: The AST is not modified in any way during the interpretation process.
+ */
+DECLHIDDEN(int) vdScriptCtxInterprete(PVDSCRIPTCTXINT pThis, const char *pszFn,
+ PVDSCRIPTARG paArgs, unsigned cArgs,
+ PVDSCRIPTARG pRet);
+
+#endif /* _VDScriptInternal_h__ */
diff --git a/src/VBox/Storage/testcase/VDScriptInterp.cpp b/src/VBox/Storage/testcase/VDScriptInterp.cpp
new file mode 100644
index 00000000..27f2bd12
--- /dev/null
+++ b/src/VBox/Storage/testcase/VDScriptInterp.cpp
@@ -0,0 +1,1054 @@
+/** $Id: VDScriptInterp.cpp $ */
+/** @file
+ *
+ * VBox HDD container test utility - scripting engine, interpreter.
+ */
+
+/*
+ * Copyright (C) 2013 Oracle Corporation
+ *
+ * This file is part of VirtualBox Open Source Edition (OSE), as
+ * available from http://www.virtualbox.org. This file is free software;
+ * you can redistribute it and/or modify it under the terms of the GNU
+ * General Public License (GPL) as published by the Free Software
+ * Foundation, in version 2 as it comes in the "COPYING" file of the
+ * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
+ * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
+ */
+#define LOGGROUP LOGGROUP_DEFAULT
+#include <iprt/list.h>
+#include <iprt/mem.h>
+#include <iprt/assert.h>
+#include <iprt/string.h>
+#include <iprt/stream.h>
+
+#include <VBox/log.h>
+
+#include "VDScriptAst.h"
+#include "VDScriptStack.h"
+#include "VDScriptInternal.h"
+
+/**
+ * Interpreter variable.
+ */
+typedef struct VDSCRIPTINTERPVAR
+{
+ /** String space core. */
+ RTSTRSPACECORE Core;
+ /** Value. */
+ VDSCRIPTARG Value;
+} VDSCRIPTINTERPVAR;
+/** Pointer to an interpreter variable. */
+typedef VDSCRIPTINTERPVAR *PVDSCRIPTINTERPVAR;
+
+/**
+ * Block scope.
+ */
+typedef struct VDSCRIPTINTERPSCOPE
+{
+ /** Pointer to the enclosing scope if available. */
+ struct VDSCRIPTINTERPSCOPE *pParent;
+ /** String space of declared variables in this scope. */
+ RTSTRSPACE hStrSpaceVar;
+} VDSCRIPTINTERPSCOPE;
+/** Pointer to a scope block. */
+typedef VDSCRIPTINTERPSCOPE *PVDSCRIPTINTERPSCOPE;
+
+/**
+ * Function call.
+ */
+typedef struct VDSCRIPTINTERPFNCALL
+{
+ /** Pointer to the caller of this function. */
+ struct VDSCRIPTINTERPFNCALL *pCaller;
+ /** Root scope of this function. */
+ VDSCRIPTINTERPSCOPE ScopeRoot;
+ /** Current scope in this function. */
+ PVDSCRIPTINTERPSCOPE pScopeCurr;
+} VDSCRIPTINTERPFNCALL;
+/** Pointer to a function call. */
+typedef VDSCRIPTINTERPFNCALL *PVDSCRIPTINTERPFNCALL;
+
+/**
+ * Interpreter context.
+ */
+typedef struct VDSCRIPTINTERPCTX
+{
+ /** Pointer to the script context. */
+ PVDSCRIPTCTXINT pScriptCtx;
+ /** Current function call entry. */
+ PVDSCRIPTINTERPFNCALL pFnCallCurr;
+ /** Stack of calculated values. */
+ VDSCRIPTSTACK StackValues;
+ /** Evaluation control stack. */
+ VDSCRIPTSTACK StackCtrl;
+} VDSCRIPTINTERPCTX;
+/** Pointer to an interpreter context. */
+typedef VDSCRIPTINTERPCTX *PVDSCRIPTINTERPCTX;
+
+/**
+ * Interpreter control type.
+ */
+typedef enum VDSCRIPTINTERPCTRLTYPE
+{
+ VDSCRIPTINTERPCTRLTYPE_INVALID = 0,
+ /** Function call to evaluate now, all values are computed
+ * and are stored on the value stack.
+ */
+ VDSCRIPTINTERPCTRLTYPE_FN_CALL,
+ /** Cleanup the function call, deleting the scope and restoring the previous one. */
+ VDSCRIPTINTERPCTRLTYPE_FN_CALL_CLEANUP,
+ /** If statement to evaluate now, the guard is on the stack. */
+ VDSCRIPTINTERPCTRLTYPE_IF,
+ /** While or for statement. */
+ VDSCRIPTINTERPCTRLTYPE_LOOP,
+ /** switch statement. */
+ VDSCRIPTINTERPCTRLTYPE_SWITCH,
+ /** Compound statement. */
+ VDSCRIPTINTERPCTRLTYPE_COMPOUND,
+ /** 32bit blowup. */
+ VDSCRIPTINTERPCTRLTYPE_32BIT_HACK = 0x7fffffff
+} VDSCRIPTINTERPCTRLTYPE;
+/** Pointer to a control type. */
+typedef VDSCRIPTINTERPCTRLTYPE *PVDSCRIPTINTERPCTRLTYPE;
+
+/**
+ * Interpreter stack control entry.
+ */
+typedef struct VDSCRIPTINTERPCTRL
+{
+ /** Flag whether this entry points to an AST node to evaluate. */
+ bool fEvalAst;
+ /** Flag dependent data. */
+ union
+ {
+ /** Pointer to the AST node to interprete. */
+ PVDSCRIPTASTCORE pAstNode;
+ /** Interpreter control structure. */
+ struct
+ {
+ /** Type of control. */
+ VDSCRIPTINTERPCTRLTYPE enmCtrlType;
+ /** Function call data. */
+ struct
+ {
+ /** Function to call. */
+ PVDSCRIPTFN pFn;
+ } FnCall;
+ /** Compound statement. */
+ struct
+ {
+ /** The compound statement node. */
+ PVDSCRIPTASTSTMT pStmtCompound;
+ /** Current statement evaluated. */
+ PVDSCRIPTASTSTMT pStmtCurr;
+ } Compound;
+ /** Pointer to an AST node. */
+ PVDSCRIPTASTCORE pAstNode;
+ } Ctrl;
+ };
+} VDSCRIPTINTERPCTRL;
+/** Pointer to an exec stack control entry. */
+typedef VDSCRIPTINTERPCTRL *PVDSCRIPTINTERPCTRL;
+
+/**
+ * Record an error while interpreting.
+ *
+ * @returns VBox status code passed.
+ * @param pThis The interpreter context.
+ * @param rc The status code to record.
+ * @param RT_SRC_POS Position in the source code.
+ * @param pszFmt Format string.
+ */
+static int vdScriptInterpreterError(PVDSCRIPTINTERPCTX pThis, int rc, RT_SRC_POS_DECL, const char *pszFmt, ...)
+{
+ RTPrintf(pszFmt);
+ return rc;
+}
+
+/**
+ * Pops the topmost value from the value stack.
+ *
+ * @returns nothing.
+ * @param pThis The interpreter context.
+ * @param pVal Where to store the value.
+ */
+DECLINLINE(void) vdScriptInterpreterPopValue(PVDSCRIPTINTERPCTX pThis, PVDSCRIPTARG pVal)
+{
+ PVDSCRIPTARG pValStack = (PVDSCRIPTARG)vdScriptStackGetUsed(&pThis->StackValues);
+ if (!pValStack)
+ {
+ RT_ZERO(*pVal);
+ AssertPtrReturnVoid(pValStack);
+ }
+
+ *pVal = *pValStack;
+ vdScriptStackPop(&pThis->StackValues);
+}
+
+/**
+ * Pushes a given value onto the value stack.
+ */
+DECLINLINE(int) vdScriptInterpreterPushValue(PVDSCRIPTINTERPCTX pThis, PVDSCRIPTARG pVal)
+{
+ PVDSCRIPTARG pValStack = (PVDSCRIPTARG)vdScriptStackGetUnused(&pThis->StackValues);
+ if (!pValStack)
+ return vdScriptInterpreterError(pThis, VERR_NO_MEMORY, RT_SRC_POS, "Out of memory pushing a value on the value stack");
+
+ *pValStack = *pVal;
+ vdScriptStackPush(&pThis->StackValues);
+ return VINF_SUCCESS;
+}
+
+/**
+ * Pushes an AST node onto the control stack.
+ *
+ * @returns VBox status code.
+ * @param pThis The interpreter context.
+ * @param enmCtrlType The control entry type.
+ */
+DECLINLINE(int) vdScriptInterpreterPushAstEntry(PVDSCRIPTINTERPCTX pThis,
+ PVDSCRIPTASTCORE pAstNode)
+{
+ PVDSCRIPTINTERPCTRL pCtrl = NULL;
+
+ pCtrl = (PVDSCRIPTINTERPCTRL)vdScriptStackGetUnused(&pThis->StackCtrl);
+
+ if (pCtrl)
+ {
+ pCtrl->fEvalAst = true;
+ pCtrl->pAstNode = pAstNode;
+ vdScriptStackPush(&pThis->StackCtrl);
+ return VINF_SUCCESS;
+ }
+
+ return vdScriptInterpreterError(pThis, VERR_NO_MEMORY, RT_SRC_POS, "Out of memory adding an entry on the control stack");
+}
+
+/**
+ * Pushes a control entry without additional data onto the stack.
+ *
+ * @returns VBox status code.
+ * @param pThis The interpreter context.
+ * @param enmCtrlType The control entry type.
+ */
+DECLINLINE(int) vdScriptInterpreterPushNonDataCtrlEntry(PVDSCRIPTINTERPCTX pThis,
+ VDSCRIPTINTERPCTRLTYPE enmCtrlType)
+{
+ PVDSCRIPTINTERPCTRL pCtrl = NULL;
+
+ pCtrl = (PVDSCRIPTINTERPCTRL)vdScriptStackGetUnused(&pThis->StackCtrl);
+ if (pCtrl)
+ {
+ pCtrl->fEvalAst = false;
+ pCtrl->Ctrl.enmCtrlType = enmCtrlType;
+ vdScriptStackPush(&pThis->StackCtrl);
+ return VINF_SUCCESS;
+ }
+
+ return vdScriptInterpreterError(pThis, VERR_NO_MEMORY, RT_SRC_POS, "Out of memory adding an entry on the control stack");
+}
+
+/**
+ * Pushes a compound statement control entry onto the stack.
+ *
+ * @returns VBox status code.
+ * @param pThis The interpreter context.
+ * @param pStmtFirst The first statement of the compound statement
+ */
+DECLINLINE(int) vdScriptInterpreterPushCompoundCtrlEntry(PVDSCRIPTINTERPCTX pThis, PVDSCRIPTASTSTMT pStmt)
+{
+ PVDSCRIPTINTERPCTRL pCtrl = NULL;
+
+ pCtrl = (PVDSCRIPTINTERPCTRL)vdScriptStackGetUnused(&pThis->StackCtrl);
+ if (pCtrl)
+ {
+ pCtrl->fEvalAst = false;
+ pCtrl->Ctrl.enmCtrlType = VDSCRIPTINTERPCTRLTYPE_COMPOUND;
+ pCtrl->Ctrl.Compound.pStmtCompound = pStmt;
+ pCtrl->Ctrl.Compound.pStmtCurr = RTListGetFirst(&pStmt->Compound.ListStmts, VDSCRIPTASTSTMT, Core.ListNode);
+ vdScriptStackPush(&pThis->StackCtrl);
+ return VINF_SUCCESS;
+ }
+
+ return vdScriptInterpreterError(pThis, VERR_NO_MEMORY, RT_SRC_POS, "Out of memory adding an entry on the control stack");
+}
+
+/**
+ * Pushes a while statement control entry onto the stack.
+ *
+ * @returns VBox status code.
+ * @param pThis The interpreter context.
+ * @param pStmt The while statement.
+ */
+DECLINLINE(int) vdScriptInterpreterPushWhileCtrlEntry(PVDSCRIPTINTERPCTX pThis, PVDSCRIPTASTSTMT pStmt)
+{
+ int rc = VINF_SUCCESS;
+ PVDSCRIPTINTERPCTRL pCtrl = NULL;
+
+ pCtrl = (PVDSCRIPTINTERPCTRL)vdScriptStackGetUnused(&pThis->StackCtrl);
+ if (pCtrl)
+ {
+ pCtrl->fEvalAst = false;
+ pCtrl->Ctrl.enmCtrlType = VDSCRIPTINTERPCTRLTYPE_LOOP;
+ pCtrl->Ctrl.pAstNode = &pStmt->Core;
+ vdScriptStackPush(&pThis->StackCtrl);
+
+ rc = vdScriptInterpreterPushAstEntry(pThis, &pStmt->While.pCond->Core);
+ if ( RT_SUCCESS(rc)
+ && pStmt->While.fDoWhile)
+ {
+ /* Push the statement to execute for do ... while loops because they run at least once. */
+ rc = vdScriptInterpreterPushAstEntry(pThis, &pStmt->While.pStmt->Core);
+ if (RT_FAILURE(rc))
+ {
+ /* Cleanup while control statement and AST node. */
+ vdScriptStackPop(&pThis->StackCtrl);
+ vdScriptStackPop(&pThis->StackCtrl);
+ }
+ }
+ else if (RT_FAILURE(rc))
+ vdScriptStackPop(&pThis->StackCtrl); /* Cleanup the while control statement. */
+ }
+ else
+ rc = vdScriptInterpreterError(pThis, VERR_NO_MEMORY, RT_SRC_POS, "Out of memory adding an entry on the control stack");
+
+ return rc;
+}
+
+/**
+ * Pushes an if statement control entry onto the stack.
+ *
+ * @returns VBox status code.
+ * @param pThis The interpreter context.
+ * @param pStmt The if statement.
+ */
+static int vdScriptInterpreterPushIfCtrlEntry(PVDSCRIPTINTERPCTX pThis, PVDSCRIPTASTSTMT pStmt)
+{
+ int rc = VINF_SUCCESS;
+ PVDSCRIPTINTERPCTRL pCtrl = NULL;
+
+ pCtrl = (PVDSCRIPTINTERPCTRL)vdScriptStackGetUnused(&pThis->StackCtrl);
+ if (pCtrl)
+ {
+ pCtrl->fEvalAst = false;
+ pCtrl->Ctrl.enmCtrlType = VDSCRIPTINTERPCTRLTYPE_IF;
+ pCtrl->Ctrl.pAstNode = &pStmt->Core;
+ vdScriptStackPush(&pThis->StackCtrl);
+
+ rc = vdScriptInterpreterPushAstEntry(pThis, &pStmt->If.pCond->Core);
+ }
+ else
+ rc = vdScriptInterpreterError(pThis, VERR_NO_MEMORY, RT_SRC_POS, "Out of memory adding an entry on the control stack");
+
+ return rc;
+}
+
+/**
+ * Pushes a for statement control entry onto the stack.
+ *
+ * @returns VBox status code.
+ * @param pThis The interpreter context.
+ * @param pStmt The while statement.
+ */
+DECLINLINE(int) vdScriptInterpreterPushForCtrlEntry(PVDSCRIPTINTERPCTX pThis, PVDSCRIPTASTSTMT pStmt)
+{
+ int rc = VINF_SUCCESS;
+ PVDSCRIPTINTERPCTRL pCtrl = NULL;
+
+ pCtrl = (PVDSCRIPTINTERPCTRL)vdScriptStackGetUnused(&pThis->StackCtrl);
+ if (pCtrl)
+ {
+ pCtrl->fEvalAst = false;
+ pCtrl->Ctrl.enmCtrlType = VDSCRIPTINTERPCTRLTYPE_LOOP;
+ pCtrl->Ctrl.pAstNode = &pStmt->Core;
+ vdScriptStackPush(&pThis->StackCtrl);
+
+ /* Push the conditional first and the the initializer .*/
+ rc = vdScriptInterpreterPushAstEntry(pThis, &pStmt->For.pExprCond->Core);
+ if (RT_SUCCESS(rc))
+ {
+ rc = vdScriptInterpreterPushAstEntry(pThis, &pStmt->For.pExprStart->Core);
+ if (RT_FAILURE(rc))
+ vdScriptStackPop(&pThis->StackCtrl);
+ }
+ }
+ else
+ rc = vdScriptInterpreterError(pThis, VERR_NO_MEMORY, RT_SRC_POS, "Out of memory adding an entry on the control stack");
+
+ return rc;
+}
+
+/**
+ * Destroy variable string space callback.
+ */
+static DECLCALLBACK(int) vdScriptInterpreterVarSpaceDestroy(PRTSTRSPACECORE pStr, void *pvUser)
+{
+ RTMemFree(pStr);
+ return VINF_SUCCESS;
+}
+
+/**
+ * Setsup a new scope in the current function call.
+ *
+ * @returns VBox status code.
+ * @param pThis The interpreter context.
+ */
+static int vdScriptInterpreterScopeCreate(PVDSCRIPTINTERPCTX pThis)
+{
+ int rc = VINF_SUCCESS;
+ PVDSCRIPTINTERPSCOPE pScope = (PVDSCRIPTINTERPSCOPE)RTMemAllocZ(sizeof(VDSCRIPTINTERPSCOPE));
+ if (pScope)
+ {
+ pScope->pParent = pThis->pFnCallCurr->pScopeCurr;
+ pScope->hStrSpaceVar = NULL;
+ pThis->pFnCallCurr->pScopeCurr = pScope;
+ }
+ else
+ rc = vdScriptInterpreterError(pThis, VERR_NO_MEMORY, RT_SRC_POS, "Out of memory allocating new scope");
+
+ return rc;
+}
+
+/**
+ * Destroys the current scope.
+ *
+ * @returns nothing.
+ * @param pThis The interpreter context.
+ */
+static void vdScriptInterpreterScopeDestroyCurr(PVDSCRIPTINTERPCTX pThis)
+{
+ AssertMsgReturnVoid(pThis->pFnCallCurr->pScopeCurr != &pThis->pFnCallCurr->ScopeRoot,
+ ("Current scope is root scope of function call\n"));
+
+ PVDSCRIPTINTERPSCOPE pScope = pThis->pFnCallCurr->pScopeCurr;
+ pThis->pFnCallCurr->pScopeCurr = pScope->pParent;
+ RTStrSpaceDestroy(&pScope->hStrSpaceVar, vdScriptInterpreterVarSpaceDestroy, NULL);
+ RTMemFree(pScope);
+}
+
+/**
+ * Get the content of the given variable identifier from the current or parent scope.
+ */
+static PVDSCRIPTINTERPVAR vdScriptInterpreterGetVar(PVDSCRIPTINTERPCTX pThis, const char *pszVar)
+{
+ PVDSCRIPTINTERPSCOPE pScopeCurr = pThis->pFnCallCurr->pScopeCurr;
+ PVDSCRIPTINTERPVAR pVar = NULL;
+
+ while ( !pVar
+ && pScopeCurr)
+ {
+ pVar = (PVDSCRIPTINTERPVAR)RTStrSpaceGet(&pScopeCurr->hStrSpaceVar, pszVar);
+ if (pVar)
+ break;
+ pScopeCurr = pScopeCurr->pParent;
+ }
+
+
+ return pVar;
+}
+
+/**
+ * Evaluate an expression.
+ *
+ * @returns VBox status code.
+ * @param pThis The interpreter context.
+ * @param pExpr The expression to evaluate.
+ */
+static int vdScriptInterpreterEvaluateExpression(PVDSCRIPTINTERPCTX pThis, PVDSCRIPTASTEXPR pExpr)
+{
+ int rc = VINF_SUCCESS;
+
+ switch (pExpr->enmType)
+ {
+ case VDSCRIPTEXPRTYPE_PRIMARY_NUMCONST:
+ {
+ /* Push the numerical constant on the value stack. */
+ VDSCRIPTARG NumConst;
+ NumConst.enmType = VDSCRIPTTYPE_UINT64;
+ NumConst.u64 = pExpr->u64;
+ rc = vdScriptInterpreterPushValue(pThis, &NumConst);
+ break;
+ }
+ case VDSCRIPTEXPRTYPE_PRIMARY_STRINGCONST:
+ {
+ /* Push the string literal on the value stack. */
+ VDSCRIPTARG StringConst;
+ StringConst.enmType = VDSCRIPTTYPE_STRING;
+ StringConst.psz = pExpr->pszStr;
+ rc = vdScriptInterpreterPushValue(pThis, &StringConst);
+ break;
+ }
+ case VDSCRIPTEXPRTYPE_PRIMARY_BOOLEAN:
+ {
+ VDSCRIPTARG BoolConst;
+ BoolConst.enmType = VDSCRIPTTYPE_BOOL;
+ BoolConst.f = pExpr->f;
+ rc = vdScriptInterpreterPushValue(pThis, &BoolConst);
+ break;
+ }
+ case VDSCRIPTEXPRTYPE_PRIMARY_IDENTIFIER:
+ {
+ /* Look it up and push the value onto the value stack. */
+ PVDSCRIPTINTERPVAR pVar = vdScriptInterpreterGetVar(pThis, pExpr->pIde->aszIde);
+
+ AssertPtrReturn(pVar, VERR_IPE_UNINITIALIZED_STATUS);
+ rc = vdScriptInterpreterPushValue(pThis, &pVar->Value);
+ break;
+ }
+ case VDSCRIPTEXPRTYPE_POSTFIX_INCREMENT:
+ case VDSCRIPTEXPRTYPE_POSTFIX_DECREMENT:
+ AssertMsgFailed(("TODO\n"));
+ case VDSCRIPTEXPRTYPE_POSTFIX_FNCALL:
+ {
+ PVDSCRIPTFN pFn = (PVDSCRIPTFN)RTStrSpaceGet(&pThis->pScriptCtx->hStrSpaceFn, pExpr->FnCall.pFnIde->pIde->aszIde);
+ if (pFn)
+ {
+ /* Push a function call control entry on the stack. */
+ PVDSCRIPTINTERPCTRL pCtrlFn = (PVDSCRIPTINTERPCTRL)vdScriptStackGetUnused(&pThis->StackCtrl);
+ if (pCtrlFn)
+ {
+ pCtrlFn->fEvalAst = false;
+ pCtrlFn->Ctrl.enmCtrlType = VDSCRIPTINTERPCTRLTYPE_FN_CALL;
+ pCtrlFn->Ctrl.FnCall.pFn = pFn;
+ vdScriptStackPush(&pThis->StackCtrl);
+
+ /* Push parameter expressions on the stack. */
+ PVDSCRIPTASTEXPR pArg = RTListGetFirst(&pExpr->FnCall.ListArgs, VDSCRIPTASTEXPR, Core.ListNode);
+ while (pArg)
+ {
+ rc = vdScriptInterpreterPushAstEntry(pThis, &pArg->Core);
+ if (RT_FAILURE(rc))
+ break;
+ pArg = RTListGetNext(&pExpr->FnCall.ListArgs, pArg, VDSCRIPTASTEXPR, Core.ListNode);
+ }
+ }
+ }
+ else
+ AssertMsgFailed(("Invalid program given, unknown function: %s\n", pExpr->FnCall.pFnIde->pIde->aszIde));
+ break;
+ }
+ case VDSCRIPTEXPRTYPE_UNARY_INCREMENT:
+ case VDSCRIPTEXPRTYPE_UNARY_DECREMENT:
+ case VDSCRIPTEXPRTYPE_UNARY_POSSIGN:
+ case VDSCRIPTEXPRTYPE_UNARY_NEGSIGN:
+ case VDSCRIPTEXPRTYPE_UNARY_INVERT:
+ case VDSCRIPTEXPRTYPE_UNARY_NEGATE:
+ case VDSCRIPTEXPRTYPE_MULTIPLICATION:
+ case VDSCRIPTEXPRTYPE_DIVISION:
+ case VDSCRIPTEXPRTYPE_MODULUS:
+ case VDSCRIPTEXPRTYPE_ADDITION:
+ case VDSCRIPTEXPRTYPE_SUBTRACTION:
+ case VDSCRIPTEXPRTYPE_LSR:
+ case VDSCRIPTEXPRTYPE_LSL:
+ case VDSCRIPTEXPRTYPE_LOWER:
+ case VDSCRIPTEXPRTYPE_HIGHER:
+ case VDSCRIPTEXPRTYPE_LOWEREQUAL:
+ case VDSCRIPTEXPRTYPE_HIGHEREQUAL:
+ case VDSCRIPTEXPRTYPE_EQUAL:
+ case VDSCRIPTEXPRTYPE_NOTEQUAL:
+ case VDSCRIPTEXPRTYPE_BITWISE_AND:
+ case VDSCRIPTEXPRTYPE_BITWISE_XOR:
+ case VDSCRIPTEXPRTYPE_BITWISE_OR:
+ case VDSCRIPTEXPRTYPE_LOGICAL_AND:
+ case VDSCRIPTEXPRTYPE_LOGICAL_OR:
+ case VDSCRIPTEXPRTYPE_ASSIGN:
+ case VDSCRIPTEXPRTYPE_ASSIGN_MULT:
+ case VDSCRIPTEXPRTYPE_ASSIGN_DIV:
+ case VDSCRIPTEXPRTYPE_ASSIGN_MOD:
+ case VDSCRIPTEXPRTYPE_ASSIGN_ADD:
+ case VDSCRIPTEXPRTYPE_ASSIGN_SUB:
+ case VDSCRIPTEXPRTYPE_ASSIGN_LSL:
+ case VDSCRIPTEXPRTYPE_ASSIGN_LSR:
+ case VDSCRIPTEXPRTYPE_ASSIGN_AND:
+ case VDSCRIPTEXPRTYPE_ASSIGN_XOR:
+ case VDSCRIPTEXPRTYPE_ASSIGN_OR:
+ case VDSCRIPTEXPRTYPE_ASSIGNMENT_LIST:
+ AssertMsgFailed(("TODO\n"));
+ default:
+ AssertMsgFailed(("Invalid expression type: %d\n", pExpr->enmType));
+ }
+ return rc;
+}
+
+/**
+ * Evaluate a statement.
+ *
+ * @returns VBox status code.
+ * @param pThis The interpreter context.
+ * @param pStmt The statement to evaluate.
+ */
+static int vdScriptInterpreterEvaluateStatement(PVDSCRIPTINTERPCTX pThis, PVDSCRIPTASTSTMT pStmt)
+{
+ int rc = VINF_SUCCESS;
+
+ switch (pStmt->enmStmtType)
+ {
+ case VDSCRIPTSTMTTYPE_COMPOUND:
+ {
+ /* Setup new scope. */
+ rc = vdScriptInterpreterScopeCreate(pThis);
+ if (RT_SUCCESS(rc))
+ {
+ /** @todo: Declarations */
+ rc = vdScriptInterpreterPushCompoundCtrlEntry(pThis, pStmt);
+ }
+ break;
+ }
+ case VDSCRIPTSTMTTYPE_EXPRESSION:
+ {
+ rc = vdScriptInterpreterPushAstEntry(pThis, &pStmt->pExpr->Core);
+ break;
+ }
+ case VDSCRIPTSTMTTYPE_IF:
+ {
+ rc = vdScriptInterpreterPushIfCtrlEntry(pThis, pStmt);
+ break;
+ }
+ case VDSCRIPTSTMTTYPE_SWITCH:
+ AssertMsgFailed(("TODO\n"));
+ break;
+ case VDSCRIPTSTMTTYPE_WHILE:
+ {
+ rc = vdScriptInterpreterPushWhileCtrlEntry(pThis, pStmt);
+ break;
+ }
+ case VDSCRIPTSTMTTYPE_RETURN:
+ {
+ /* Walk up the control stack until we reach a function cleanup entry. */
+ PVDSCRIPTINTERPCTRL pCtrl = (PVDSCRIPTINTERPCTRL)vdScriptStackGetUsed(&pThis->StackCtrl);
+ while ( pCtrl
+ && ( pCtrl->fEvalAst
+ || pCtrl->Ctrl.enmCtrlType != VDSCRIPTINTERPCTRLTYPE_FN_CALL_CLEANUP))
+ {
+ /* Cleanup up any compound statement scope. */
+ if ( !pCtrl->fEvalAst
+ && pCtrl->Ctrl.enmCtrlType == VDSCRIPTINTERPCTRLTYPE_COMPOUND)
+ vdScriptInterpreterScopeDestroyCurr(pThis);
+
+ vdScriptStackPop(&pThis->StackCtrl);
+ pCtrl = (PVDSCRIPTINTERPCTRL)vdScriptStackGetUsed(&pThis->StackCtrl);
+ }
+ AssertMsg(VALID_PTR(pCtrl), ("Incorrect program, return outside of function\n"));
+ break;
+ }
+ case VDSCRIPTSTMTTYPE_FOR:
+ {
+ rc = vdScriptInterpreterPushForCtrlEntry(pThis, pStmt);
+ break;
+ }
+ case VDSCRIPTSTMTTYPE_CONTINUE:
+ {
+ /* Remove everything up to a loop control entry. */
+ PVDSCRIPTINTERPCTRL pCtrl = (PVDSCRIPTINTERPCTRL)vdScriptStackGetUsed(&pThis->StackCtrl);
+ while ( pCtrl
+ && ( pCtrl->fEvalAst
+ || pCtrl->Ctrl.enmCtrlType != VDSCRIPTINTERPCTRLTYPE_LOOP))
+ {
+ /* Cleanup up any compound statement scope. */
+ if ( !pCtrl->fEvalAst
+ && pCtrl->Ctrl.enmCtrlType == VDSCRIPTINTERPCTRLTYPE_COMPOUND)
+ vdScriptInterpreterScopeDestroyCurr(pThis);
+
+ vdScriptStackPop(&pThis->StackCtrl);
+ pCtrl = (PVDSCRIPTINTERPCTRL)vdScriptStackGetUsed(&pThis->StackCtrl);
+ }
+ AssertMsg(VALID_PTR(pCtrl), ("Incorrect program, continue outside of loop\n"));
+
+ /* Put the conditionals for while and for loops onto the control stack again. */
+ PVDSCRIPTASTSTMT pLoopStmt = (PVDSCRIPTASTSTMT)pCtrl->Ctrl.pAstNode;
+
+ AssertMsg( pLoopStmt->enmStmtType == VDSCRIPTSTMTTYPE_WHILE
+ || pLoopStmt->enmStmtType == VDSCRIPTSTMTTYPE_FOR,
+ ("Invalid statement type, must be for or while loop\n"));
+
+ if (pLoopStmt->enmStmtType == VDSCRIPTSTMTTYPE_FOR)
+ rc = vdScriptInterpreterPushAstEntry(pThis, &pLoopStmt->For.pExprCond->Core);
+ else if (!pLoopStmt->While.fDoWhile)
+ rc = vdScriptInterpreterPushAstEntry(pThis, &pLoopStmt->While.pCond->Core);
+ break;
+ }
+ case VDSCRIPTSTMTTYPE_BREAK:
+ {
+ /* Remove everything including the loop control statement. */
+ PVDSCRIPTINTERPCTRL pCtrl = (PVDSCRIPTINTERPCTRL)vdScriptStackGetUsed(&pThis->StackCtrl);
+ while ( pCtrl
+ && ( pCtrl->fEvalAst
+ || pCtrl->Ctrl.enmCtrlType != VDSCRIPTINTERPCTRLTYPE_LOOP))
+ {
+ /* Cleanup up any compound statement scope. */
+ if ( !pCtrl->fEvalAst
+ && pCtrl->Ctrl.enmCtrlType == VDSCRIPTINTERPCTRLTYPE_COMPOUND)
+ vdScriptInterpreterScopeDestroyCurr(pThis);
+
+ vdScriptStackPop(&pThis->StackCtrl);
+ pCtrl = (PVDSCRIPTINTERPCTRL)vdScriptStackGetUsed(&pThis->StackCtrl);
+ }
+ AssertMsg(VALID_PTR(pCtrl), ("Incorrect program, break outside of loop\n"));
+ vdScriptStackPop(&pThis->StackCtrl); /* Remove loop control statement. */
+ break;
+ }
+ case VDSCRIPTSTMTTYPE_CASE:
+ case VDSCRIPTSTMTTYPE_DEFAULT:
+ AssertMsgFailed(("TODO\n"));
+ break;
+ default:
+ AssertMsgFailed(("Invalid statement type: %d\n", pStmt->enmStmtType));
+ }
+
+ return rc;
+}
+
+/**
+ * Evaluates the given AST node.
+ *
+ * @returns VBox statuse code.
+ * @param pThis The interpreter context.
+ * @param pAstNode The AST node to interpret.
+ */
+static int vdScriptInterpreterEvaluateAst(PVDSCRIPTINTERPCTX pThis, PVDSCRIPTASTCORE pAstNode)
+{
+ int rc = VERR_NOT_IMPLEMENTED;
+
+ switch (pAstNode->enmClass)
+ {
+ case VDSCRIPTASTCLASS_DECLARATION:
+ {
+ AssertMsgFailed(("TODO\n"));
+ break;
+ }
+ case VDSCRIPTASTCLASS_STATEMENT:
+ {
+ rc = vdScriptInterpreterEvaluateStatement(pThis, (PVDSCRIPTASTSTMT)pAstNode);
+ break;
+ }
+ case VDSCRIPTASTCLASS_EXPRESSION:
+ {
+ rc = vdScriptInterpreterEvaluateExpression(pThis, (PVDSCRIPTASTEXPR)pAstNode);
+ break;
+ }
+ /* These should never ever appear here. */
+ case VDSCRIPTASTCLASS_IDENTIFIER:
+ case VDSCRIPTASTCLASS_FUNCTION:
+ case VDSCRIPTASTCLASS_FUNCTIONARG:
+ case VDSCRIPTASTCLASS_INVALID:
+ default:
+ AssertMsgFailed(("Invalid AST node class: %d\n", pAstNode->enmClass));
+ }
+
+ return rc;
+}
+
+/**
+ * Evaluate a function call.
+ *
+ * @returns VBox status code.
+ * @param pThis The interpreter context.
+ * @param pFn The function execute.
+ */
+static int vdScriptInterpreterFnCall(PVDSCRIPTINTERPCTX pThis, PVDSCRIPTFN pFn)
+{
+ int rc = VINF_SUCCESS;
+
+ if (!pFn->fExternal)
+ {
+ PVDSCRIPTASTFN pAstFn = pFn->Type.Internal.pAstFn;
+
+ /* Add function call cleanup marker on the stack first. */
+ rc = vdScriptInterpreterPushNonDataCtrlEntry(pThis, VDSCRIPTINTERPCTRLTYPE_FN_CALL_CLEANUP);
+ if (RT_SUCCESS(rc))
+ {
+ /* Create function call frame and set it up. */
+ PVDSCRIPTINTERPFNCALL pFnCall = (PVDSCRIPTINTERPFNCALL)RTMemAllocZ(sizeof(VDSCRIPTINTERPFNCALL));
+ if (pFnCall)
+ {
+ pFnCall->pCaller = pThis->pFnCallCurr;
+ pFnCall->ScopeRoot.pParent = NULL;
+ pFnCall->ScopeRoot.hStrSpaceVar = NULL;
+ pFnCall->pScopeCurr = &pFnCall->ScopeRoot;
+
+ /* Add the variables, remember order. The first variable in the argument has the value at the top of the value stack. */
+ PVDSCRIPTASTFNARG pArg = RTListGetFirst(&pAstFn->ListArgs, VDSCRIPTASTFNARG, Core.ListNode);
+ for (unsigned i = 0; i < pAstFn->cArgs; i++)
+ {
+ PVDSCRIPTINTERPVAR pVar = (PVDSCRIPTINTERPVAR)RTMemAllocZ(sizeof(VDSCRIPTINTERPVAR));
+ if (pVar)
+ {
+ pVar->Core.pszString = pArg->pArgIde->aszIde;
+ pVar->Core.cchString = pArg->pArgIde->cchIde;
+ vdScriptInterpreterPopValue(pThis, &pVar->Value);
+ bool fInserted = RTStrSpaceInsert(&pFnCall->ScopeRoot.hStrSpaceVar, &pVar->Core);
+ Assert(fInserted);
+ }
+ else
+ {
+ rc = vdScriptInterpreterError(pThis, VERR_NO_MEMORY, RT_SRC_POS, "Out of memory creating a variable");
+ break;
+ }
+ pArg = RTListGetNext(&pAstFn->ListArgs, pArg, VDSCRIPTASTFNARG, Core.ListNode);
+ }
+
+ if (RT_SUCCESS(rc))
+ {
+ /*
+ * Push compount statement on the control stack and make the newly created
+ * call frame the current one.
+ */
+ rc = vdScriptInterpreterPushAstEntry(pThis, &pAstFn->pCompoundStmts->Core);
+ if (RT_SUCCESS(rc))
+ pThis->pFnCallCurr = pFnCall;
+ }
+
+ if (RT_FAILURE(rc))
+ {
+ RTStrSpaceDestroy(&pFnCall->ScopeRoot.hStrSpaceVar, vdScriptInterpreterVarSpaceDestroy, NULL);
+ RTMemFree(pFnCall);
+ }
+ }
+ else
+ rc = vdScriptInterpreterError(pThis, VERR_NO_MEMORY, RT_SRC_POS, "Out of memory creating a call frame");
+ }
+ }
+ else
+ {
+ /* External function call, build the argument list. */
+ if (pFn->cArgs)
+ {
+ PVDSCRIPTARG paArgs = (PVDSCRIPTARG)RTMemAllocZ(pFn->cArgs * sizeof(VDSCRIPTARG));
+ if (paArgs)
+ {
+ for (unsigned i = 0; i < pFn->cArgs; i++)
+ vdScriptInterpreterPopValue(pThis, &paArgs[i]);
+
+ rc = pFn->Type.External.pfnCallback(paArgs, pFn->Type.External.pvUser);
+ RTMemFree(paArgs);
+ }
+ else
+ rc = vdScriptInterpreterError(pThis, VERR_NO_MEMORY, RT_SRC_POS,
+ "Out of memory creating argument array for external function call");
+ }
+ else
+ rc = pFn->Type.External.pfnCallback(NULL, pFn->Type.External.pvUser);
+ }
+
+ return rc;
+}
+
+/**
+ * Evaluate interpreter control statement.
+ *
+ * @returns VBox status code.
+ * @param pThis The interpreter context.
+ * @param pCtrl The control entry to evaluate.
+ */
+static int vdScriptInterpreterEvaluateCtrlEntry(PVDSCRIPTINTERPCTX pThis, PVDSCRIPTINTERPCTRL pCtrl)
+{
+ int rc = VINF_SUCCESS;
+
+ Assert(!pCtrl->fEvalAst);
+ switch (pCtrl->Ctrl.enmCtrlType)
+ {
+ case VDSCRIPTINTERPCTRLTYPE_FN_CALL:
+ {
+ PVDSCRIPTFN pFn = pCtrl->Ctrl.FnCall.pFn;
+
+ vdScriptStackPop(&pThis->StackCtrl);
+ rc = vdScriptInterpreterFnCall(pThis, pFn);
+ break;
+ }
+ case VDSCRIPTINTERPCTRLTYPE_FN_CALL_CLEANUP:
+ {
+ vdScriptStackPop(&pThis->StackCtrl);
+
+ /* Delete function call entry. */
+ AssertPtr(pThis->pFnCallCurr);
+ PVDSCRIPTINTERPFNCALL pFnCallFree = pThis->pFnCallCurr;
+
+ pThis->pFnCallCurr = pFnCallFree->pCaller;
+ Assert(pFnCallFree->pScopeCurr == &pFnCallFree->ScopeRoot);
+ RTStrSpaceDestroy(&pFnCallFree->ScopeRoot.hStrSpaceVar, vdScriptInterpreterVarSpaceDestroy, NULL);
+ RTMemFree(pFnCallFree);
+ break;
+ }
+ case VDSCRIPTINTERPCTRLTYPE_COMPOUND:
+ {
+ if (!pCtrl->Ctrl.Compound.pStmtCurr)
+ {
+ /* Evaluated last statement, cleanup and remove the control statement from the stack. */
+ vdScriptInterpreterScopeDestroyCurr(pThis);
+ vdScriptStackPop(&pThis->StackCtrl);
+ }
+ else
+ {
+ /* Push the current statement onto the control stack and move on. */
+ rc = vdScriptInterpreterPushAstEntry(pThis, &pCtrl->Ctrl.Compound.pStmtCurr->Core);
+ if (RT_SUCCESS(rc))
+ {
+ pCtrl->Ctrl.Compound.pStmtCurr = RTListGetNext(&pCtrl->Ctrl.Compound.pStmtCompound->Compound.ListStmts,
+ pCtrl->Ctrl.Compound.pStmtCurr, VDSCRIPTASTSTMT, Core.ListNode);
+ }
+ }
+ break;
+ }
+ case VDSCRIPTINTERPCTRLTYPE_LOOP:
+ {
+ PVDSCRIPTASTSTMT pLoopStmt = (PVDSCRIPTASTSTMT)pCtrl->Ctrl.pAstNode;
+
+ /* Check whether the condition passed. */
+ VDSCRIPTARG Cond;
+ vdScriptInterpreterPopValue(pThis, &Cond);
+ AssertMsg(Cond.enmType == VDSCRIPTTYPE_BOOL,
+ ("Value on stack is not of boolean type\n"));
+
+ if (Cond.f)
+ {
+ /* Execute the loop another round. */
+ if (pLoopStmt->enmStmtType == VDSCRIPTSTMTTYPE_WHILE)
+ {
+ rc = vdScriptInterpreterPushAstEntry(pThis, &pLoopStmt->While.pCond->Core);
+ if (RT_SUCCESS(rc))
+ {
+ rc = vdScriptInterpreterPushAstEntry(pThis, &pLoopStmt->While.pStmt->Core);
+ if (RT_FAILURE(rc))
+ vdScriptStackPop(&pThis->StackCtrl);
+ }
+ }
+ else
+ {
+ AssertMsg(pLoopStmt->enmStmtType == VDSCRIPTSTMTTYPE_FOR,
+ ("Not a for statement\n"));
+
+ rc = vdScriptInterpreterPushAstEntry(pThis, &pLoopStmt->For.pExprCond->Core);
+ if (RT_SUCCESS(rc))
+ {
+ rc = vdScriptInterpreterPushAstEntry(pThis, &pLoopStmt->For.pExpr3->Core);
+ if (RT_SUCCESS(rc))
+ {
+ rc = vdScriptInterpreterPushAstEntry(pThis, &pLoopStmt->For.pStmt->Core);
+ if (RT_FAILURE(rc))
+ vdScriptStackPop(&pThis->StackCtrl);
+ }
+
+ if (RT_FAILURE(rc))
+ vdScriptStackPop(&pThis->StackCtrl);
+ }
+ }
+ }
+ else
+ vdScriptStackPop(&pThis->StackCtrl); /* Remove loop control statement. */
+ break;
+ }
+ case VDSCRIPTINTERPCTRLTYPE_IF:
+ {
+ PVDSCRIPTASTSTMT pIfStmt = (PVDSCRIPTASTSTMT)pCtrl->Ctrl.pAstNode;
+
+ vdScriptStackPop(&pThis->StackCtrl); /* Remove if control statement. */
+
+ /* Check whether the condition passed. */
+ VDSCRIPTARG Cond;
+ vdScriptInterpreterPopValue(pThis, &Cond);
+ AssertMsg(Cond.enmType == VDSCRIPTTYPE_BOOL,
+ ("Value on stack is not of boolean type\n"));
+
+ if (Cond.f)
+ {
+ /* Execute the true branch. */
+ rc = vdScriptInterpreterPushAstEntry(pThis, &pIfStmt->If.pTrueStmt->Core);
+ }
+ else if (pIfStmt->If.pElseStmt)
+ rc = vdScriptInterpreterPushAstEntry(pThis, &pIfStmt->If.pElseStmt->Core);
+
+ break;
+ }
+ default:
+ AssertMsgFailed(("Invalid evaluation control type on the stack: %d\n",
+ pCtrl->Ctrl.enmCtrlType));
+ }
+
+ return rc;
+}
+
+/**
+ * The interpreter evaluation core loop.
+ *
+ * @returns VBox status code.
+ * @param pThis The interpreter context.
+ */
+static int vdScriptInterpreterEvaluate(PVDSCRIPTINTERPCTX pThis)
+{
+ int rc = VINF_SUCCESS;
+ PVDSCRIPTINTERPCTRL pCtrl = NULL;
+
+ pCtrl = (PVDSCRIPTINTERPCTRL)vdScriptStackGetUsed(&pThis->StackCtrl);
+ while (pCtrl)
+ {
+ if (pCtrl->fEvalAst)
+ {
+ PVDSCRIPTASTCORE pAstNode = pCtrl->pAstNode;
+ vdScriptStackPop(&pThis->StackCtrl);
+
+ rc = vdScriptInterpreterEvaluateAst(pThis, pAstNode);
+ }
+ else
+ rc = vdScriptInterpreterEvaluateCtrlEntry(pThis, pCtrl);
+
+ pCtrl = (PVDSCRIPTINTERPCTRL)vdScriptStackGetUsed(&pThis->StackCtrl);
+ }
+
+ return rc;
+}
+
+DECLHIDDEN(int) vdScriptCtxInterprete(PVDSCRIPTCTXINT pThis, const char *pszFn,
+ PVDSCRIPTARG paArgs, unsigned cArgs,
+ PVDSCRIPTARG pRet)
+{
+ int rc = VINF_SUCCESS;
+ VDSCRIPTINTERPCTX InterpCtx;
+ PVDSCRIPTFN pFn = NULL;
+
+ AssertPtrReturn(pThis, VERR_INVALID_POINTER);
+ AssertPtrReturn(pszFn, VERR_INVALID_POINTER);
+ AssertReturn( (!cArgs && !paArgs)
+ || (cArgs && paArgs), VERR_INVALID_PARAMETER);
+
+ InterpCtx.pScriptCtx = pThis;
+ InterpCtx.pFnCallCurr = NULL;
+ vdScriptStackInit(&InterpCtx.StackValues, sizeof(VDSCRIPTARG));
+ vdScriptStackInit(&InterpCtx.StackCtrl, sizeof(VDSCRIPTINTERPCTRL));
+
+ pFn = (PVDSCRIPTFN)RTStrSpaceGet(&pThis->hStrSpaceFn, pszFn);
+ if (pFn)
+ {
+ if (cArgs == pFn->cArgs)
+ {
+ /* Push the arguments onto the stack. */
+ /** @todo: Check expected and given argument types. */
+ for (unsigned i = 0; i < cArgs; i++)
+ {
+ PVDSCRIPTARG pArg = (PVDSCRIPTARG)vdScriptStackGetUnused(&InterpCtx.StackValues);
+ *pArg = paArgs[i];
+ vdScriptStackPush(&InterpCtx.StackValues);
+ }
+
+ if (RT_SUCCESS(rc))
+ {
+ /* Setup function call frame and parameters. */
+ rc = vdScriptInterpreterFnCall(&InterpCtx, pFn);
+ if (RT_SUCCESS(rc))
+ {
+ /* Run the interpreter. */
+ rc = vdScriptInterpreterEvaluate(&InterpCtx);
+ vdScriptStackDestroy(&InterpCtx.StackValues);
+ vdScriptStackDestroy(&InterpCtx.StackCtrl);
+ }
+ }
+ }
+ else
+ rc = vdScriptInterpreterError(&InterpCtx, VERR_INVALID_PARAMETER, RT_SRC_POS, "Invalid number of parameters, expected %d got %d", pFn->cArgs, cArgs);
+ }
+ else
+ rc = vdScriptInterpreterError(&InterpCtx, VERR_NOT_FOUND, RT_SRC_POS, "Function with identifier \"%s\" not found", pszFn);
+
+
+ return rc;
+}
diff --git a/src/VBox/Storage/testcase/VDScriptStack.h b/src/VBox/Storage/testcase/VDScriptStack.h
new file mode 100644
index 00000000..93eb1c36
--- /dev/null
+++ b/src/VBox/Storage/testcase/VDScriptStack.h
@@ -0,0 +1,142 @@
+/** @file
+ *
+ * VBox HDD container test utility - scripting engine, internal stack implementation.
+ */
+
+/*
+ * Copyright (C) 2013 Oracle Corporation
+ *
+ * This file is part of VirtualBox Open Source Edition (OSE), as
+ * available from http://www.virtualbox.org. This file is free software;
+ * you can redistribute it and/or modify it under the terms of the GNU
+ * General Public License (GPL) as published by the Free Software
+ * Foundation, in version 2 as it comes in the "COPYING" file of the
+ * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
+ * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
+ */
+#ifndef _VDScriptStack_h__
+#define _VDScriptStack_h__
+
+#include <iprt/list.h>
+#include <iprt/string.h>
+
+#include "VDScript.h"
+
+/**
+ * Stack structure.
+ */
+typedef struct VDSCRIPTSTACK
+{
+ /** Size of one stack element. */
+ size_t cbStackEntry;
+ /** Stack memory. */
+ void *pvStack;
+ /** Number of elements on the stack. */
+ unsigned cOnStack;
+ /** Maximum number of elements the stack can hold. */
+ unsigned cOnStackMax;
+} VDSCRIPTSTACK;
+/** Pointer to a stack. */
+typedef VDSCRIPTSTACK *PVDSCRIPTSTACK;
+
+/**
+ * Init the stack structure.
+ *
+ * @returns nothing.
+ * @param pStack The stack to initialize.
+ * @param cbStackEntry The size of one stack entry.
+ */
+DECLINLINE(void) vdScriptStackInit(PVDSCRIPTSTACK pStack, size_t cbStackEntry)
+{
+ pStack->cbStackEntry = cbStackEntry;
+ pStack->pvStack = NULL;
+ pStack->cOnStack = 0;
+ pStack->cOnStackMax = 0;
+}
+
+/**
+ * Destroys the given stack freeing all memory.
+ *
+ * @returns nothing.
+ * @param pStack The stack to destroy.
+ */
+DECLINLINE(void) vdScriptStackDestroy(PVDSCRIPTSTACK pStack)
+{
+ if (pStack->pvStack)
+ RTMemFree(pStack->pvStack);
+ pStack->cbStackEntry = 0;
+ pStack->pvStack = NULL;
+ pStack->cOnStack = 0;
+ pStack->cOnStackMax = 0;
+}
+
+/**
+ * Gets the topmost unused stack entry.
+ *
+ * @returns Pointer to the first unused entry.
+ * NULL if there is no room left and increasing the stack failed.
+ * @param pStack The stack.
+ */
+DECLINLINE(void *)vdScriptStackGetUnused(PVDSCRIPTSTACK pStack)
+{
+ void *pvElem = NULL;
+
+ if (pStack->cOnStack >= pStack->cOnStackMax)
+ {
+ unsigned cOnStackMaxNew = pStack->cOnStackMax + 10;
+ void *pvStackNew = NULL;
+
+ /* Try to increase stack space. */
+ pvStackNew = RTMemRealloc(pStack->pvStack, cOnStackMaxNew * pStack->cbStackEntry);
+ if (pvStackNew)
+ {
+ pStack->pvStack = pvStackNew;
+ pStack->cOnStackMax = cOnStackMaxNew;
+ }
+
+ }
+
+ if (pStack->cOnStack < pStack->cOnStackMax)
+ pvElem = (char *)pStack->pvStack + pStack->cOnStack * pStack->cbStackEntry;
+
+ return pvElem;
+}
+
+/**
+ * Gets the topmost used entry on the stack.
+ *
+ * @returns Pointer to the first used entry
+ * or NULL if the stack is empty.
+ * @param pStack The stack.
+ */
+DECLINLINE(void *)vdScriptStackGetUsed(PVDSCRIPTSTACK pStack)
+{
+ if (!pStack->cOnStack)
+ return NULL;
+ else
+ return (char *)pStack->pvStack + (pStack->cOnStack - 1) * pStack->cbStackEntry;
+}
+
+/**
+ * Increases the used element count for the given stack.
+ *
+ * @returns nothing.
+ * @param pStack The stack.
+ */
+DECLINLINE(void) vdScriptStackPush(PVDSCRIPTSTACK pStack)
+{
+ pStack->cOnStack++;
+}
+
+/**
+ * Decreases the used element count for the given stack.
+ *
+ * @returns nothing.
+ * @param pStack The stack.
+ */
+DECLINLINE(void) vdScriptStackPop(PVDSCRIPTSTACK pStack)
+{
+ pStack->cOnStack--;
+}
+
+#endif /* _VDScriptStack_h__ */
diff --git a/src/VBox/Storage/testcase/tstVD-2.cpp b/src/VBox/Storage/testcase/tstVD-2.cpp
index 13821789..7e81ffc9 100644
--- a/src/VBox/Storage/testcase/tstVD-2.cpp
+++ b/src/VBox/Storage/testcase/tstVD-2.cpp
@@ -4,7 +4,7 @@
*/
/*
- * Copyright (C) 2006-2007 Oracle Corporation
+ * Copyright (C) 2006-2011 Oracle Corporation
*
* This file is part of VirtualBox Open Source Edition (OSE), as
* available from http://www.virtualbox.org. This file is free software;
diff --git a/src/VBox/Storage/testcase/tstVD.cpp b/src/VBox/Storage/testcase/tstVD.cpp
index c858e96c..953b5c34 100644
--- a/src/VBox/Storage/testcase/tstVD.cpp
+++ b/src/VBox/Storage/testcase/tstVD.cpp
@@ -4,7 +4,7 @@
*/
/*
- * Copyright (C) 2006-2010 Oracle Corporation
+ * Copyright (C) 2006-2011 Oracle Corporation
*
* This file is part of VirtualBox Open Source Edition (OSE), as
* available from http://www.virtualbox.org. This file is free software;
diff --git a/src/VBox/Storage/testcase/tstVDCompact.vd b/src/VBox/Storage/testcase/tstVDCompact.vd
index 9d511baa..95532d23 100644
--- a/src/VBox/Storage/testcase/tstVDCompact.vd
+++ b/src/VBox/Storage/testcase/tstVDCompact.vd
@@ -1,77 +1,62 @@
-# $Id: tstVDCompact.vd $
-#
-# Storage: Testcase for compacting disks.
-#
+/* $Id: tstVDCompact.vd $ */
+/**
+ * Storage: Testcase for compacting disks.
+ */
-#
-# Copyright (C) 2011 Oracle Corporation
-#
-# This file is part of VirtualBox Open Source Edition (OSE), as
-# available from http://www.virtualbox.org. This file is free software;
-# you can redistribute it and/or modify it under the terms of the GNU
-# General Public License (GPL) as published by the Free Software
-# Foundation, in version 2 as it comes in the "COPYING" file of the
-# VirtualBox OSE distribution. VirtualBox OSE is distributed in the
-# hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
-#
+/*
+ * Copyright (C) 2011 Oracle Corporation
+ *
+ * This file is part of VirtualBox Open Source Edition (OSE), as
+ * available from http://www.virtualbox.org. This file is free software;
+ * you can redistribute it and/or modify it under the terms of the GNU
+ * General Public License (GPL) as published by the Free Software
+ * Foundation, in version 2 as it comes in the "COPYING" file of the
+ * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
+ * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
+ */
-# Init I/O RNG for generating random data for writes
-iorngcreate size=10M mode=manual seed=1234567890
+void tstCompact(string strMsg, string strBackend)
+{
+ print(strMsg);
-# Create zero pattern
-iopatterncreatefromnumber name=zero size=1M pattern=0
+ /* Create disk containers, read verification is on. */
+ createdisk("disk", true);
+ create("disk", "base", "tstCompact.disk", "dynamic", strBackend, 200M, false);
-print msg=Testing_VDI
-# Create disk containers, read verification is on.
-createdisk name=disk verify=yes
-# Create the disk.
-create disk=disk mode=base name=tstCompact.vdi type=dynamic backend=VDI size=200M
-# Fill the disk with random data
-io disk=disk async=no mode=seq blocksize=64k off=0-200M size=200M writes=100
-# Read the data to verify it once.
-io disk=disk async=no mode=seq blocksize=64k off=0-200M size=200M writes=0
-# Fill a part with 0's
-io disk=disk async=no mode=seq blocksize=64k off=100M-150M size=50M writes=100 pattern=zero
-# Now compact
-compact disk=disk image=0
-# Read again to verify that the content hasn't changed
-io disk=disk async=no mode=seq blocksize=64k off=0-200M size=200M writes=0
-# Fill everything with 0
-io disk=disk async=no mode=seq blocksize=64k off=0M-200M size=200M writes=100 pattern=zero
-# Now compact
-compact disk=disk image=0
-# Read again to verify that the content hasn't changed
-io disk=disk async=no mode=seq blocksize=64k off=0-200M size=200M writes=0
-# Cleanup
-close disk=disk mode=single delete=yes
-destroydisk name=disk
+ /* Fill the disk with random data. */
+ io("disk", false, 1, "seq", 64K, 0, 200M, 200M, 100, "none");
+ /* Read the data to verify it once. */
+ io("disk", false, 1, "seq", 64K, 0, 200M, 200M, 0, "none");
+ /* Fill a part with 0's. */
+ io("disk", false, 1, "seq", 64K, 100M, 150M, 50M, 100, "zero");
-print msg=Testing_VHD
-# Create disk containers, read verification is on.
-createdisk name=disk verify=yes
-# Create the disk.
-create disk=disk mode=base name=tstCompact.vhd type=dynamic backend=VHD size=200M
-# Fill the disk with random data
-io disk=disk async=no mode=seq blocksize=64k off=0-200M size=200M writes=100
-# Read the data to verify it once.
-io disk=disk async=no mode=seq blocksize=64k off=0-200M size=200M writes=0
-# Fill a part with 0's
-io disk=disk async=no mode=seq blocksize=64k off=100M-150M size=50M writes=100 pattern=zero
-# Now compact
-compact disk=disk image=0
-# Read again to verify that the content hasn't changed
-io disk=disk async=no mode=seq blocksize=64k off=0-200M size=200M writes=0
-# Fill everything with 0
-io disk=disk async=no mode=seq blocksize=64k off=0M-200M size=200M writes=100 pattern=zero
-# Now compact
-compact disk=disk image=0
-# Read again to verify that the content hasn't changed
-io disk=disk async=no mode=seq blocksize=64k off=0-200M size=200M writes=0
-# Cleanup
-close disk=disk mode=single delete=yes
-destroydisk name=disk
+ /* Now compact. */
+ compact("disk", 0);
+ /* Read again to verify that the content hasn't changed. */
+ io("disk", false, 1, "seq", 64K, 0, 200M, 200M, 0, "none");
+ /* Fill everything with 0. */
+ io("disk", false, 1, "seq", 64K, 0, 200M, 200M, 100, "zero");
+ /* Now compact again. */
+ compact("disk", 0);
+ /* Read again to verify that the content hasn't changed. */
+ io("disk", false, 1, "seq", 64K, 0, 200M, 200M, 0, "none");
-# Destroy RNG and pattern
-iopatterndestroy name=zero
-iorngdestroy
+ close("disk", "single", true);
+ destroydisk("disk");
+}
+void main()
+{
+ /* Init I/O RNG for generating random data for writes. */
+ iorngcreate(10M, "manual", 1234567890);
+
+ /* Create zero pattern */
+ iopatterncreatefromnumber("zero", 1M, 0);
+
+ tstCompact("Testing VDI", "VDI");
+ tstCompact("Testing VHD", "VHD");
+
+ /* Destroy RNG and pattern */
+ iopatterndestroy("zero");
+ iorngdestroy();
+}
diff --git a/src/VBox/Storage/testcase/tstVDCopy.cpp b/src/VBox/Storage/testcase/tstVDCopy.cpp
index ef8a37e2..6d73560b 100644
--- a/src/VBox/Storage/testcase/tstVDCopy.cpp
+++ b/src/VBox/Storage/testcase/tstVDCopy.cpp
@@ -5,7 +5,7 @@
*/
/*
- * Copyright (C) 2006-2010 Oracle Corporation
+ * Copyright (C) 2006-2011 Oracle Corporation
*
* This file is part of VirtualBox Open Source Edition (OSE), as
* available from http://www.virtualbox.org. This file is free software;
diff --git a/src/VBox/Storage/testcase/tstVDCopy.vd b/src/VBox/Storage/testcase/tstVDCopy.vd
index cd4c97f3..9cd4e034 100644
--- a/src/VBox/Storage/testcase/tstVDCopy.vd
+++ b/src/VBox/Storage/testcase/tstVDCopy.vd
@@ -1,89 +1,86 @@
-# $Id: tstVDCopy.vd $
-#
-# Storage: Testcase for VDCopy with snapshots and optimizations.
-#
-
-#
-# Copyright (C) 2011 Oracle Corporation
-#
-# This file is part of VirtualBox Open Source Edition (OSE), as
-# available from http://www.virtualbox.org. This file is free software;
-# you can redistribute it and/or modify it under the terms of the GNU
-# General Public License (GPL) as published by the Free Software
-# Foundation, in version 2 as it comes in the "COPYING" file of the
-# VirtualBox OSE distribution. VirtualBox OSE is distributed in the
-# hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
-#
-
-# Init I/O RNG for generating random data for writes
-iorngcreate size=10M mode=manual seed=1234567890
-
-# Create source disk and fill data
-print msg=Creating_Source_Disk
- createdisk name=source verify=no
- create disk=source mode=base name=source_base.vdi type=dynamic backend=VDI size=1G
- io disk=source async=no mode=rnd blocksize=64k off=0-512M size=256M writes=100
-
-# Create 1st snapshot
-print msg=Creating_First_Diff
- create disk=source mode=diff name=source_diff1.vdi type=dynamic backend=VDI size=1G
- io disk=source async=no mode=rnd blocksize=64k off=512M-1G size=256M writes=50
-
-# Create 2nd snapshot
-print msg=Creating_Second_Diff
- create disk=source mode=diff name=source_diff2.vdi type=dynamic backend=VDI size=1G
- io disk=source async=no mode=rnd blocksize=1M off=0M-1G size=45M writes=100
-
-print msg=Creating_Third_Diff
- create disk=source mode=diff name=source_diff3.vdi type=dynamic backend=VDI size=1G
- io disk=source async=no mode=rnd blocksize=1M off=0M-1G size=45M writes=100
-
-print msg=Creating_Fourth_Diff
- create disk=source mode=diff name=source_diff4.vdi type=dynamic backend=VDI size=1G
- io disk=source async=no mode=rnd blocksize=1M off=0M-1G size=45M writes=100
-
-# Create destination disk
-print msg=Creating_Destination_Disk
- createdisk name=dest verify=no
-
-# Copy base image
-print msg=Copying_Base_Image
- copy diskfrom=source diskto=dest imagefrom=0 backend=VDI filename=dest_base.vdi
-
-print msg=Copying_First_Diff_optimized
- copy diskfrom=source diskto=dest imagefrom=1 backend=VDI filename=dest_diff1.vdi fromsame=0 tosame=0
-
-print msg=Copying_Second_Diff_optimized
- copy diskfrom=source diskto=dest imagefrom=2 backend=VDI filename=dest_diff2.vdi fromsame=1 tosame=1
- copy diskfrom=source diskto=dest imagefrom=3 backend=VDI filename=dest_diff3.vdi fromsame=2 tosame=2
- copy diskfrom=source diskto=dest imagefrom=4 backend=VDI filename=dest_diff4.vdi fromsame=3 tosame=3
-
-print msg=Comparing_Disks
- comparedisks disk1=source disk2=dest
-
-printfilesize disk=source image=0
-printfilesize disk=source image=1
-printfilesize disk=source image=2
-printfilesize disk=source image=3
-printfilesize disk=source image=4
-
-printfilesize disk=dest image=0
-printfilesize disk=dest image=1
-printfilesize disk=dest image=2
-printfilesize disk=dest image=3
-printfilesize disk=dest image=4
-
-# Cleanup
-print msg=Cleaning_up
- close disk=dest mode=single delete=yes
- close disk=dest mode=single delete=yes
- close disk=dest mode=single delete=yes
-
- close disk=source mode=single delete=yes
- close disk=source mode=single delete=yes
- close disk=source mode=single delete=yes
- destroydisk name=source
- destroydisk name=dest
-
-iorngdestroy
-
+/* $Id: tstVDCopy.vd $ */
+/**
+ * Storage: Testcase for VDCopy with snapshots and optimizations.
+ */
+
+/*
+ * Copyright (C) 2011 Oracle Corporation
+ *
+ * This file is part of VirtualBox Open Source Edition (OSE), as
+ * available from http://www.virtualbox.org. This file is free software;
+ * you can redistribute it and/or modify it under the terms of the GNU
+ * General Public License (GPL) as published by the Free Software
+ * Foundation, in version 2 as it comes in the "COPYING" file of the
+ * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
+ * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
+ */
+
+void main()
+{
+ /* Init I/O RNG for generating random data for writes. */
+ iorngcreate(10M, "manual", 1234567890);
+
+ /* Create source disk and fill data. */
+ print("Creating Source Disk");
+ createdisk("source", false);
+ create("source", "base", "source_base.vdi", "dynamic", "VDI", 1G, false);
+ io("source", false, 1, "rnd", 64K, 0, 512M, 256M, 100, "none");
+
+ print("Creating first diff");
+ create("source", "diff", "source_diff1.vdi", "dynamic", "VDI", 1G, false);
+ io("source", false, 1, "rnd", 64K, 512M, 1G, 256M, 50, "none");
+
+ print("Creating second diff");
+ create("source", "diff", "source_diff2.vdi", "dynamic", "VDI", 1G, false);
+ io("source", false, 1, "rnd", 1M, 0, 1G, 45M, 100, "none");
+
+ print("Creating third diff");
+ create("source", "diff", "source_diff3.vdi", "dynamic", "VDI", 1G, false);
+ io("source", false, 1, "rnd", 1M, 0, 1G, 45M, 100, "none");
+
+ print("Creating fourth diff");
+ create("source", "diff", "source_diff4.vdi", "dynamic", "VDI", 1G, false);
+ io("source", false, 1, "rnd", 1M, 0, 1G, 45M, 100, "none");
+
+ print("Creating destination disk");
+ createdisk("dest", false);
+
+ print("Copying base image");
+ copy("source", "dest", 0, "VDI", "dest_base.vdi", false, 0, 0xffffffff, 0xffffffff); /* Image content unknown */
+
+ print("Copying first diff optimized");
+ copy("source", "dest", 1, "VDI", "dest_diff1.vdi", false, 0, 0, 0);
+
+ print("Copying other diffs optimized");
+ copy("source", "dest", 2, "VDI", "dest_diff1.vdi", false, 0, 1, 1);
+ copy("source", "dest", 3, "VDI", "dest_diff1.vdi", false, 0, 2, 2);
+ copy("source", "dest", 4, "VDI", "dest_diff1.vdi", false, 0, 3, 3);
+
+ print("Comparing_Disks");
+ comparedisks("source", "dest");
+
+ printfilesize("source", 0);
+ printfilesize("source", 1);
+ printfilesize("source", 2);
+ printfilesize("source", 3);
+ printfilesize("source", 4);
+
+ printfilesize("dest", 0);
+ printfilesize("dest", 1);
+ printfilesize("dest", 2);
+ printfilesize("dest", 3);
+ printfilesize("dest", 4);
+
+ print("Cleaning up");
+ close("dest", "single", true);
+ close("dest", "single", true);
+ close("dest", "single", true);
+
+ close("source", "single", true);
+ close("source", "single", true);
+ close("source", "single", true);
+ destroydisk("source");
+ destroydisk("dest");
+
+ iorngdestroy();
+}
diff --git a/src/VBox/Storage/testcase/tstVDDiscard.vd b/src/VBox/Storage/testcase/tstVDDiscard.vd
index 7ac1a0ad..56fadf58 100644
--- a/src/VBox/Storage/testcase/tstVDDiscard.vd
+++ b/src/VBox/Storage/testcase/tstVDDiscard.vd
@@ -1,55 +1,59 @@
-# $Id: tstVDDiscard.vd $
-#
-# Storage: Testcase for discarding data in a disk.
-#
-
-#
-# Copyright (C) 2011 Oracle Corporation
-#
-# This file is part of VirtualBox Open Source Edition (OSE), as
-# available from http://www.virtualbox.org. This file is free software;
-# you can redistribute it and/or modify it under the terms of the GNU
-# General Public License (GPL) as published by the Free Software
-# Foundation, in version 2 as it comes in the "COPYING" file of the
-# VirtualBox OSE distribution. VirtualBox OSE is distributed in the
-# hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
-#
-
-# Init I/O RNG for generating random data for writes
-iorngcreate size=10M mode=manual seed=1234567890
-
-print msg=Testing_VDI
-# Create disk containers, read verification is on.
-createdisk name=disk verify=yes
-# Create the disk.
-create disk=disk mode=base name=tstCompact.vdi type=dynamic backend=VDI size=2G
-# Fill the disk with random data
-io disk=disk async=no mode=seq blocksize=64k off=0-2G size=2G writes=100
-# Read the data to verify it once.
-io disk=disk async=no mode=seq blocksize=64k off=0-2G size=2G writes=0
-close disk=disk mode=single delete=no
-
-open disk=disk name=tstCompact.vdi backend=VDI async=yes discard=yes
-printfilesize disk=disk image=0
-discard async=yes disk=disk ranges=6,0M,512k,1M,512k,2M,512k,3M,512k,4M,512k,5M,512k
-discard async=yes disk=disk ranges=6,6M,512k,7M,512k,8M,512k,9M,512k,10M,512k,11M,512k
-discard async=yes disk=disk ranges=1,512k,512k
-printfilesize disk=disk image=0
-
-print msg=Discard_whole_block
-discard async=yes disk=disk ranges=1,20M,1M
-printfilesize disk=disk image=0
-
-print msg=Split_Discard
-discard async=yes disk=disk ranges=1,21M,512k
-printfilesize disk=disk image=0
-discard async=yes disk=disk ranges=1,22016k,512k
-printfilesize disk=disk image=0
-
-# Cleanup
-close disk=disk mode=single delete=yes
-destroydisk name=disk
-
-# Destroy RNG and pattern
-iorngdestroy
-
+/* $Id: tstVDDiscard.vd $ */
+/**
+ * Storage: Testcase for discarding data in a disk.
+ */
+
+/*
+ * Copyright (C) 2011 Oracle Corporation
+ *
+ * This file is part of VirtualBox Open Source Edition (OSE), as
+ * available from http://www.virtualbox.org. This file is free software;
+ * you can redistribute it and/or modify it under the terms of the GNU
+ * General Public License (GPL) as published by the Free Software
+ * Foundation, in version 2 as it comes in the "COPYING" file of the
+ * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
+ * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
+ */
+
+void main()
+{
+ /* Init I/O RNG for generating random data for writes. */
+ iorngcreate(10M, "manual", 1234567890);
+
+ print("Testing VDI");
+
+ /* Create disk containers, read verification is on. */
+ createdisk("disk", true /* fVerify */);
+ /* Create the disk. */
+ create("disk", "base", "tstCompact.vdi", "dynamic", "VDI", 2G, false /* fIgnoreFlush */);
+ /* Fill the disk with random data */
+ io("disk", false, 1, "seq", 64K, 0, 2G, 2G, 100, "none");
+ /* Read the data to verify it once. */
+ io("disk", false, 1, "seq", 64K, 0, 2G, 2G, 0, "none");
+ close("disk", "single", false);
+
+ open("disk", "tstCompact.vdi", "VDI", true, false, false, true, false);
+ printfilesize("disk", 0);
+ discard("disk", true, "6,0M,512K,1M,512K,2M,512K,3M,512K,4M,512K,5M,512K");
+ discard("disk", true, "6,6M,512K,7M,512K,8M,512K,9M,512K,10M,512K,11M,512K");
+ discard("disk", true, "1,512K,512K");
+ discard("disk", false, "1,1024K,64K");
+ printfilesize("disk", 0);
+
+ print("Discard whole block");
+ discard("disk", true, "1,20M,1M");
+ printfilesize("disk", 0);
+
+ print("Split Discard");
+ discard("disk", true, "1,21M,512K");
+ printfilesize("disk", 0);
+ discard("disk", true, "1,22016K,512K");
+ printfilesize("disk", 0);
+
+ /* Cleanup */
+ close("disk", "single", true);
+ destroydisk("disk");
+
+ /* Destroy RNG and pattern */
+ iorngdestroy();
+}
diff --git a/src/VBox/Storage/testcase/tstVDIo.cpp b/src/VBox/Storage/testcase/tstVDIo.cpp
index d492c50a..9422f477 100644
--- a/src/VBox/Storage/testcase/tstVDIo.cpp
+++ b/src/VBox/Storage/testcase/tstVDIo.cpp
@@ -5,7 +5,7 @@
*/
/*
- * Copyright (C) 2011 Oracle Corporation
+ * Copyright (C) 2011-2013 Oracle Corporation
*
* This file is part of VirtualBox Open Source Edition (OSE), as
* available from http://www.virtualbox.org. This file is free software;
@@ -34,9 +34,11 @@
#include <iprt/critsect.h>
#include "VDMemDisk.h"
-#include "VDIoBackendMem.h"
+#include "VDIoBackend.h"
#include "VDIoRnd.h"
+#include "VDScript.h"
+
/**
* A virtual file backed by memory.
*/
@@ -46,8 +48,8 @@ typedef struct VDFILE
RTLISTNODE Node;
/** Name of the file. */
char *pszName;
- /** Memory file baking the file. */
- PVDMEMDISK pMemDisk;
+ /** Storage backing the file. */
+ PVDIOSTORAGE pIoStorage;
/** Flag whether the file is read locked. */
bool fReadLock;
/** Flag whether the file is write locked. */
@@ -124,8 +126,8 @@ typedef struct VDTESTGLOB
RTLISTNODE ListFiles;
/** Head of the pattern list. */
RTLISTNODE ListPatterns;
- /** Memory I/O backend. */
- PVDIOBACKENDMEM pIoBackend;
+ /** I/O backend, common data. */
+ PVDIOBACKEND pIoBackend;
/** Error interface. */
VDINTERFACEERROR VDIfError;
/** Pointer to the per disk interface list. */
@@ -136,6 +138,8 @@ typedef struct VDTESTGLOB
PVDINTERFACE pInterfacesImages;
/** I/O RNG handle. */
PVDIORND pIoRnd;
+ /** Current storage backend to use. */
+ char *pszIoBackend;
} VDTESTGLOB, *PVDTESTGLOB;
/**
@@ -213,369 +217,279 @@ typedef struct VDIOTEST
} u;
} VDIOTEST, *PVDIOTEST;
-/**
- * Argument types.
- */
-typedef enum VDSCRIPTARGTYPE
-{
- /** Argument is a string. */
- VDSCRIPTARGTYPE_STRING = 0,
- /** Argument is a 64bit unsigned number. */
- VDSCRIPTARGTYPE_UNSIGNED_NUMBER,
- /** Argument is a 64bit signed number. */
- VDSCRIPTARGTYPE_SIGNED_NUMBER,
- /** Arugment is a unsigned 64bit range */
- VDSCRIPTARGTYPE_UNSIGNED_RANGE,
- /** Arugment is a boolean. */
- VDSCRIPTARGTYPE_BOOL
-} VDSCRIPTARGTYPE;
-
-/**
- * Script argument.
- */
-typedef struct VDSCRIPTARG
-{
- /** Argument identifier. */
- char chId;
- /** Type of the argument. */
- VDSCRIPTARGTYPE enmType;
- /** Type depndent data. */
- union
- {
- /** String. */
- const char *pcszString;
- /** Bool. */
- bool fFlag;
- /** unsigned number. */
- uint64_t u64;
- /** Signed number. */
- int64_t i64;
- /** Unsigned range. */
- struct
- {
- uint64_t Start;
- uint64_t End;
- } Range;
- } u;
-} VDSCRIPTARG, *PVDSCRIPTARG;
-
-/** Script action handler. */
-typedef DECLCALLBACK(int) FNVDSCRIPTACTION(PVDTESTGLOB pGlob, PVDSCRIPTARG paScriptArgs, unsigned cScriptArgs);
-/** Pointer to a script action handler. */
-typedef FNVDSCRIPTACTION *PFNVDSCRIPTACTION;
-
-/**
- * Script argument descriptor.
- */
-typedef struct VDSCRIPTARGDESC
-{
- /** Name of the arugment. */
- const char *pcszName;
- /** Identifier for the argument. */
- char chId;
- /** Type of the argument. */
- VDSCRIPTARGTYPE enmType;
- /** Flags */
- uint32_t fFlags;
-} VDSCRIPTARGDESC, *PVDSCRIPTARGDESC;
-/** Pointer to a const script argument descriptor. */
-typedef const VDSCRIPTARGDESC *PCVDSCRIPTARGDESC;
-
-/** Flag whether the argument is mandatory. */
-#define VDSCRIPTARGDESC_FLAG_MANDATORY RT_BIT(0)
-/** Flag whether the number can have a size suffix (K|M|G) */
-#define VDSCRIPTARGDESC_FLAG_SIZE_SUFFIX RT_BIT(1)
-
-/**
- * Script action.
- */
-typedef struct VDSCRIPTACTION
-{
- /** Action name. */
- const char *pcszAction;
- /** Pointer to the arguments. */
- const PCVDSCRIPTARGDESC paArgDesc;
- /** Number of arugments in the array. */
- unsigned cArgDescs;
- /** Pointer to the action handler. */
- PFNVDSCRIPTACTION pfnHandler;
-} VDSCRIPTACTION, *PVDSCRIPTACTION;
-
-typedef const VDSCRIPTACTION *PCVDSCRIPTACTION;
-
-static DECLCALLBACK(int) vdScriptHandlerCreate(PVDTESTGLOB pGlob, PVDSCRIPTARG paScriptArgs, unsigned cScriptArgs);
-static DECLCALLBACK(int) vdScriptHandlerOpen(PVDTESTGLOB pGlob, PVDSCRIPTARG paScriptArgs, unsigned cScriptArgs);
-static DECLCALLBACK(int) vdScriptHandlerIo(PVDTESTGLOB pGlob, PVDSCRIPTARG paScriptArgs, unsigned cScriptArgs);
-static DECLCALLBACK(int) vdScriptHandlerFlush(PVDTESTGLOB pGlob, PVDSCRIPTARG paScriptArgs, unsigned cScriptArgs);
-static DECLCALLBACK(int) vdScriptHandlerMerge(PVDTESTGLOB pGlob, PVDSCRIPTARG paScriptArgs, unsigned cScriptArgs);
-static DECLCALLBACK(int) vdScriptHandlerCompact(PVDTESTGLOB pGlob, PVDSCRIPTARG paScriptArgs, unsigned cScriptArgs);
-static DECLCALLBACK(int) vdScriptHandlerDiscard(PVDTESTGLOB pGlob, PVDSCRIPTARG paScriptArgs, unsigned cScriptArgs);
-static DECLCALLBACK(int) vdScriptHandlerCopy(PVDTESTGLOB pGlob, PVDSCRIPTARG paScriptArgs, unsigned cScriptArgs);
-static DECLCALLBACK(int) vdScriptHandlerClose(PVDTESTGLOB pGlob, PVDSCRIPTARG paScriptArgs, unsigned cScriptArgs);
-static DECLCALLBACK(int) vdScriptHandlerPrintFileSize(PVDTESTGLOB pGlob, PVDSCRIPTARG paScriptArgs, unsigned cScriptArgs);
-static DECLCALLBACK(int) vdScriptHandlerIoLogReplay(PVDTESTGLOB pGlob, PVDSCRIPTARG paScriptArgs, unsigned cScriptArgs);
-static DECLCALLBACK(int) vdScriptHandlerIoRngCreate(PVDTESTGLOB pGlob, PVDSCRIPTARG paScriptArgs, unsigned cScriptArgs);
-static DECLCALLBACK(int) vdScriptHandlerIoRngDestroy(PVDTESTGLOB pGlob, PVDSCRIPTARG paScriptArgs, unsigned cScriptArgs);
-static DECLCALLBACK(int) vdScriptHandlerIoPatternCreateFromNumber(PVDTESTGLOB pGlob, PVDSCRIPTARG paScriptArgs, unsigned cScriptArgs);
-static DECLCALLBACK(int) vdScriptHandlerIoPatternCreateFromFile(PVDTESTGLOB pGlob, PVDSCRIPTARG paScriptArgs, unsigned cScriptArgs);
-static DECLCALLBACK(int) vdScriptHandlerIoPatternDestroy(PVDTESTGLOB pGlob, PVDSCRIPTARG paScriptArgs, unsigned cScriptArgs);
-static DECLCALLBACK(int) vdScriptHandlerSleep(PVDTESTGLOB pGlob, PVDSCRIPTARG paScriptArgs, unsigned cScriptArgs);
-static DECLCALLBACK(int) vdScriptHandlerDumpFile(PVDTESTGLOB pGlob, PVDSCRIPTARG paScriptArgs, unsigned cScriptArgs);
-static DECLCALLBACK(int) vdScriptHandlerCreateDisk(PVDTESTGLOB pGlob, PVDSCRIPTARG paScriptArgs, unsigned cScriptArgs);
-static DECLCALLBACK(int) vdScriptHandlerDestroyDisk(PVDTESTGLOB pGlob, PVDSCRIPTARG paScriptArgs, unsigned cScriptArgs);
-static DECLCALLBACK(int) vdScriptHandlerCompareDisks(PVDTESTGLOB pGlob, PVDSCRIPTARG paScriptArgs, unsigned cScriptArgs);
-static DECLCALLBACK(int) vdScriptHandlerDumpDiskInfo(PVDTESTGLOB pGlob, PVDSCRIPTARG paScriptArgs, unsigned cScriptArgs);
-static DECLCALLBACK(int) vdScriptHandlerPrintMsg(PVDTESTGLOB pGlob, PVDSCRIPTARG paScriptArgs, unsigned cScriptArgs);
-static DECLCALLBACK(int) vdScriptHandlerShowStatistics(PVDTESTGLOB pGlob, PVDSCRIPTARG paScriptArgs, unsigned cScriptArgs);
-static DECLCALLBACK(int) vdScriptHandlerResetStatistics(PVDTESTGLOB pGlob, PVDSCRIPTARG paScriptArgs, unsigned cScriptArgs);
+static DECLCALLBACK(int) vdScriptHandlerCreate(PVDSCRIPTARG paScriptArgs, void *pvUser);
+static DECLCALLBACK(int) vdScriptHandlerOpen(PVDSCRIPTARG paScriptArgs, void *pvUser);
+static DECLCALLBACK(int) vdScriptHandlerIo(PVDSCRIPTARG paScriptArgs, void *pvUser);
+static DECLCALLBACK(int) vdScriptHandlerFlush(PVDSCRIPTARG paScriptArgs, void *pvUser);
+static DECLCALLBACK(int) vdScriptHandlerMerge(PVDSCRIPTARG paScriptArgs, void *pvUser);
+static DECLCALLBACK(int) vdScriptHandlerCompact(PVDSCRIPTARG paScriptArgs, void *pvUser);
+static DECLCALLBACK(int) vdScriptHandlerDiscard(PVDSCRIPTARG paScriptArgs, void *pvUser);
+static DECLCALLBACK(int) vdScriptHandlerCopy(PVDSCRIPTARG paScriptArgs, void *pvUser);
+static DECLCALLBACK(int) vdScriptHandlerClose(PVDSCRIPTARG paScriptArgs, void *pvUser);
+static DECLCALLBACK(int) vdScriptHandlerPrintFileSize(PVDSCRIPTARG paScriptArgs, void *pvUser);
+static DECLCALLBACK(int) vdScriptHandlerIoLogReplay(PVDSCRIPTARG paScriptArgs, void *pvUser);
+static DECLCALLBACK(int) vdScriptHandlerIoRngCreate(PVDSCRIPTARG paScriptArgs, void *pvUser);
+static DECLCALLBACK(int) vdScriptHandlerIoRngDestroy(PVDSCRIPTARG paScriptArgs, void *pvUser);
+static DECLCALLBACK(int) vdScriptHandlerIoPatternCreateFromNumber(PVDSCRIPTARG paScriptArgs, void *pvUser);
+static DECLCALLBACK(int) vdScriptHandlerIoPatternCreateFromFile(PVDSCRIPTARG paScriptArgs, void *pvUser);
+static DECLCALLBACK(int) vdScriptHandlerIoPatternDestroy(PVDSCRIPTARG paScriptArgs, void *pvUser);
+static DECLCALLBACK(int) vdScriptHandlerSleep(PVDSCRIPTARG paScriptArgs, void *pvUser);
+static DECLCALLBACK(int) vdScriptHandlerDumpFile(PVDSCRIPTARG paScriptArgs, void *pvUser);
+static DECLCALLBACK(int) vdScriptHandlerCreateDisk(PVDSCRIPTARG paScriptArgs, void *pvUser);
+static DECLCALLBACK(int) vdScriptHandlerDestroyDisk(PVDSCRIPTARG paScriptArgs, void *pvUser);
+static DECLCALLBACK(int) vdScriptHandlerCompareDisks(PVDSCRIPTARG paScriptArgs, void *pvUser);
+static DECLCALLBACK(int) vdScriptHandlerDumpDiskInfo(PVDSCRIPTARG paScriptArgs, void *pvUser);
+static DECLCALLBACK(int) vdScriptHandlerPrintMsg(PVDSCRIPTARG paScriptArgs, void *pvUser);
+static DECLCALLBACK(int) vdScriptHandlerShowStatistics(PVDSCRIPTARG paScriptArgs, void *pvUser);
+static DECLCALLBACK(int) vdScriptHandlerResetStatistics(PVDSCRIPTARG paScriptArgs, void *pvUser);
+static DECLCALLBACK(int) vdScriptHandlerResize(PVDSCRIPTARG paScriptArgs, void *pvUser);
+static DECLCALLBACK(int) vdScriptHandlerSetFileBackend(PVDSCRIPTARG paScriptArgs, void *pvUser);
/* create action */
-const VDSCRIPTARGDESC g_aArgCreate[] =
-{
- /* pcszName chId enmType fFlags */
- {"disk", 'd', VDSCRIPTARGTYPE_STRING, VDSCRIPTARGDESC_FLAG_MANDATORY},
- {"mode", 'm', VDSCRIPTARGTYPE_STRING, VDSCRIPTARGDESC_FLAG_MANDATORY},
- {"name", 'n', VDSCRIPTARGTYPE_STRING, VDSCRIPTARGDESC_FLAG_MANDATORY},
- {"type", 't', VDSCRIPTARGTYPE_STRING, 0},
- {"backend", 'b', VDSCRIPTARGTYPE_STRING, VDSCRIPTARGDESC_FLAG_MANDATORY},
- {"size", 's', VDSCRIPTARGTYPE_UNSIGNED_NUMBER, VDSCRIPTARGDESC_FLAG_MANDATORY | VDSCRIPTARGDESC_FLAG_SIZE_SUFFIX},
- {"ignoreflush", 'f', VDSCRIPTARGTYPE_BOOL, 0}
+const VDSCRIPTTYPE g_aArgCreate[] =
+{
+ VDSCRIPTTYPE_STRING,
+ VDSCRIPTTYPE_STRING,
+ VDSCRIPTTYPE_STRING,
+ VDSCRIPTTYPE_STRING,
+ VDSCRIPTTYPE_STRING,
+ VDSCRIPTTYPE_UINT64,
+ VDSCRIPTTYPE_BOOL
};
/* open action */
-const VDSCRIPTARGDESC g_aArgOpen[] =
-{
- /* pcszName chId enmType fFlags */
- {"disk", 'd', VDSCRIPTARGTYPE_STRING, VDSCRIPTARGDESC_FLAG_MANDATORY},
- {"name", 'n', VDSCRIPTARGTYPE_STRING, VDSCRIPTARGDESC_FLAG_MANDATORY},
- {"backend", 'b', VDSCRIPTARGTYPE_STRING, VDSCRIPTARGDESC_FLAG_MANDATORY},
- {"async", 'a', VDSCRIPTARGTYPE_BOOL, 0},
- {"shareable", 's', VDSCRIPTARGTYPE_BOOL, 0},
- {"readonly", 'r', VDSCRIPTARGTYPE_BOOL, 0},
- {"discard", 'i', VDSCRIPTARGTYPE_BOOL, 0},
- {"ignoreflush", 'f', VDSCRIPTARGTYPE_BOOL, 0},
+const VDSCRIPTTYPE g_aArgOpen[] =
+{
+ VDSCRIPTTYPE_STRING, /* disk */
+ VDSCRIPTTYPE_STRING, /* name */
+ VDSCRIPTTYPE_STRING, /* backend */
+ VDSCRIPTTYPE_BOOL, /* async */
+ VDSCRIPTTYPE_BOOL, /* shareable */
+ VDSCRIPTTYPE_BOOL, /* readonly */
+ VDSCRIPTTYPE_BOOL, /* discard */
+ VDSCRIPTTYPE_BOOL /* ignoreflush */
};
/* I/O action */
-const VDSCRIPTARGDESC g_aArgIo[] =
-{
- /* pcszName chId enmType fFlags */
- {"disk", 'd', VDSCRIPTARGTYPE_STRING, VDSCRIPTARGDESC_FLAG_MANDATORY},
- {"async", 'a', VDSCRIPTARGTYPE_BOOL, 0},
- {"max-reqs", 'l', VDSCRIPTARGTYPE_UNSIGNED_NUMBER, 0},
- {"mode", 'm', VDSCRIPTARGTYPE_STRING, VDSCRIPTARGDESC_FLAG_MANDATORY},
- {"size", 's', VDSCRIPTARGTYPE_UNSIGNED_NUMBER, VDSCRIPTARGDESC_FLAG_SIZE_SUFFIX},
- {"blocksize", 'b', VDSCRIPTARGTYPE_UNSIGNED_NUMBER, VDSCRIPTARGDESC_FLAG_MANDATORY | VDSCRIPTARGDESC_FLAG_SIZE_SUFFIX},
- {"off", 'o', VDSCRIPTARGTYPE_UNSIGNED_RANGE, VDSCRIPTARGDESC_FLAG_SIZE_SUFFIX},
- {"writes", 'w', VDSCRIPTARGTYPE_UNSIGNED_NUMBER, VDSCRIPTARGDESC_FLAG_MANDATORY},
- {"pattern", 'p', VDSCRIPTARGTYPE_STRING, 0},
+const VDSCRIPTTYPE g_aArgIo[] =
+{
+ VDSCRIPTTYPE_STRING, /* disk */
+ VDSCRIPTTYPE_BOOL, /* async */
+ VDSCRIPTTYPE_UINT32, /* max-reqs */
+ VDSCRIPTTYPE_STRING, /* mode */
+ VDSCRIPTTYPE_UINT64, /* size */
+ VDSCRIPTTYPE_UINT64, /* blocksize */
+ VDSCRIPTTYPE_UINT64, /* offStart */
+ VDSCRIPTTYPE_UINT64, /* offEnd */
+ VDSCRIPTTYPE_UINT32, /* writes */
+ VDSCRIPTTYPE_STRING /* pattern */
};
/* flush action */
-const VDSCRIPTARGDESC g_aArgFlush[] =
+const VDSCRIPTTYPE g_aArgFlush[] =
{
- /* pcszName chId enmType fFlags */
- {"disk", 'd', VDSCRIPTARGTYPE_STRING, VDSCRIPTARGDESC_FLAG_MANDATORY},
- {"async", 'a', VDSCRIPTARGTYPE_BOOL, 0}
+ VDSCRIPTTYPE_STRING, /* disk */
+ VDSCRIPTTYPE_BOOL /* async */
};
/* merge action */
-const VDSCRIPTARGDESC g_aArgMerge[] =
+const VDSCRIPTTYPE g_aArgMerge[] =
{
- /* pcszName chId enmType fFlags */
- {"disk", 'd', VDSCRIPTARGTYPE_STRING, VDSCRIPTARGDESC_FLAG_MANDATORY},
- {"from", 'f', VDSCRIPTARGTYPE_UNSIGNED_NUMBER, VDSCRIPTARGDESC_FLAG_MANDATORY},
- {"to", 't', VDSCRIPTARGTYPE_UNSIGNED_NUMBER, VDSCRIPTARGDESC_FLAG_MANDATORY},
+ VDSCRIPTTYPE_STRING, /* disk */
+ VDSCRIPTTYPE_UINT32, /* from */
+ VDSCRIPTTYPE_UINT32 /* to */
};
/* Compact a disk */
-const VDSCRIPTARGDESC g_aArgCompact[] =
+const VDSCRIPTTYPE g_aArgCompact[] =
{
- /* pcszName chId enmType fFlags */
- {"disk", 'd', VDSCRIPTARGTYPE_STRING, VDSCRIPTARGDESC_FLAG_MANDATORY},
- {"image", 'i', VDSCRIPTARGTYPE_UNSIGNED_NUMBER, VDSCRIPTARGDESC_FLAG_MANDATORY},
+ VDSCRIPTTYPE_STRING, /* disk */
+ VDSCRIPTTYPE_UINT32 /* image */
};
/* Discard a part of a disk */
-const VDSCRIPTARGDESC g_aArgDiscard[] =
+const VDSCRIPTTYPE g_aArgDiscard[] =
{
- /* pcszName chId enmType fFlags */
- {"disk", 'd', VDSCRIPTARGTYPE_STRING, VDSCRIPTARGDESC_FLAG_MANDATORY},
- {"async", 'a', VDSCRIPTARGTYPE_BOOL, 0},
- {"ranges", 'r', VDSCRIPTARGTYPE_STRING, VDSCRIPTARGDESC_FLAG_MANDATORY}
+ VDSCRIPTTYPE_STRING, /* disk */
+ VDSCRIPTTYPE_BOOL, /* async */
+ VDSCRIPTTYPE_STRING /* ranges */
};
/* Compact a disk */
-const VDSCRIPTARGDESC g_aArgCopy[] =
-{
- /* pcszName chId enmType fFlags */
- {"diskfrom", 's', VDSCRIPTARGTYPE_STRING, VDSCRIPTARGDESC_FLAG_MANDATORY},
- {"diskto", 'd', VDSCRIPTARGTYPE_STRING, VDSCRIPTARGDESC_FLAG_MANDATORY},
- {"imagefrom", 'i', VDSCRIPTARGTYPE_UNSIGNED_NUMBER, VDSCRIPTARGDESC_FLAG_MANDATORY},
- {"backend", 'b', VDSCRIPTARGTYPE_STRING, 0},
- {"filename", 'f', VDSCRIPTARGTYPE_STRING, 0},
- {"movebyrename", 'm', VDSCRIPTARGTYPE_BOOL, 0},
- {"size", 'z', VDSCRIPTARGTYPE_UNSIGNED_NUMBER, 0},
- {"fromsame", 'o', VDSCRIPTARGTYPE_UNSIGNED_NUMBER, 0},
- {"tosame", 't', VDSCRIPTARGTYPE_UNSIGNED_NUMBER, 0}
+const VDSCRIPTTYPE g_aArgCopy[] =
+{
+ VDSCRIPTTYPE_STRING, /* diskfrom */
+ VDSCRIPTTYPE_STRING, /* diskto */
+ VDSCRIPTTYPE_UINT32, /* imagefrom */
+ VDSCRIPTTYPE_STRING, /* backend */
+ VDSCRIPTTYPE_STRING, /* filename */
+ VDSCRIPTTYPE_BOOL, /* movebyrename */
+ VDSCRIPTTYPE_UINT64, /* size */
+ VDSCRIPTTYPE_UINT32, /* fromsame */
+ VDSCRIPTTYPE_UINT32 /* tosame */
};
/* close action */
-const VDSCRIPTARGDESC g_aArgClose[] =
+const VDSCRIPTTYPE g_aArgClose[] =
{
- /* pcszName chId enmType fFlags */
- {"disk", 'd', VDSCRIPTARGTYPE_STRING, VDSCRIPTARGDESC_FLAG_MANDATORY},
- {"mode", 'm', VDSCRIPTARGTYPE_STRING, VDSCRIPTARGDESC_FLAG_MANDATORY},
- {"delete", 'r', VDSCRIPTARGTYPE_BOOL, VDSCRIPTARGDESC_FLAG_MANDATORY}
+ VDSCRIPTTYPE_STRING, /* disk */
+ VDSCRIPTTYPE_STRING, /* mode */
+ VDSCRIPTTYPE_BOOL /* delete */
};
/* print file size action */
-const VDSCRIPTARGDESC g_aArgPrintFileSize[] =
+const VDSCRIPTTYPE g_aArgPrintFileSize[] =
{
- /* pcszName chId enmType fFlags */
- {"disk", 'd', VDSCRIPTARGTYPE_STRING, VDSCRIPTARGDESC_FLAG_MANDATORY},
- {"image", 'i', VDSCRIPTARGTYPE_UNSIGNED_NUMBER, VDSCRIPTARGDESC_FLAG_MANDATORY}
+ VDSCRIPTTYPE_STRING, /* disk */
+ VDSCRIPTTYPE_UINT32 /* image */
};
/* print file size action */
-const VDSCRIPTARGDESC g_aArgIoLogReplay[] =
+const VDSCRIPTTYPE g_aArgIoLogReplay[] =
{
- /* pcszName chId enmType fFlags */
- {"disk", 'd', VDSCRIPTARGTYPE_STRING, VDSCRIPTARGDESC_FLAG_MANDATORY},
- {"iolog", 'i', VDSCRIPTARGTYPE_STRING, VDSCRIPTARGDESC_FLAG_MANDATORY}
+ VDSCRIPTTYPE_STRING, /* disk */
+ VDSCRIPTTYPE_STRING /* iolog */
};
/* I/O RNG create action */
-const VDSCRIPTARGDESC g_aArgIoRngCreate[] =
+const VDSCRIPTTYPE g_aArgIoRngCreate[] =
{
- /* pcszName chId enmType fFlags */
- {"size", 'd', VDSCRIPTARGTYPE_UNSIGNED_NUMBER, VDSCRIPTARGDESC_FLAG_MANDATORY | VDSCRIPTARGDESC_FLAG_SIZE_SUFFIX},
- {"mode", 'm', VDSCRIPTARGTYPE_STRING, VDSCRIPTARGDESC_FLAG_MANDATORY},
- {"seed", 's', VDSCRIPTARGTYPE_UNSIGNED_NUMBER, 0}
+ VDSCRIPTTYPE_UINT32, /* size */
+ VDSCRIPTTYPE_STRING, /* mode */
+ VDSCRIPTTYPE_UINT32, /* seed */
};
/* I/O pattern create action */
-const VDSCRIPTARGDESC g_aArgIoPatternCreateFromNumber[] =
+const VDSCRIPTTYPE g_aArgIoPatternCreateFromNumber[] =
{
- /* pcszName chId enmType fFlags */
- {"name", 'n', VDSCRIPTARGTYPE_STRING, VDSCRIPTARGDESC_FLAG_MANDATORY},
- {"size", 's', VDSCRIPTARGTYPE_UNSIGNED_NUMBER, VDSCRIPTARGDESC_FLAG_MANDATORY | VDSCRIPTARGDESC_FLAG_SIZE_SUFFIX},
- {"pattern", 'p', VDSCRIPTARGTYPE_UNSIGNED_NUMBER, VDSCRIPTARGDESC_FLAG_MANDATORY},
+ VDSCRIPTTYPE_STRING, /* name */
+ VDSCRIPTTYPE_UINT32, /* size */
+ VDSCRIPTTYPE_UINT32 /* pattern */
};
/* I/O pattern create action */
-const VDSCRIPTARGDESC g_aArgIoPatternCreateFromFile[] =
+const VDSCRIPTTYPE g_aArgIoPatternCreateFromFile[] =
{
- /* pcszName chId enmType fFlags */
- {"name", 'n', VDSCRIPTARGTYPE_STRING, VDSCRIPTARGDESC_FLAG_MANDATORY},
- {"file", 'f', VDSCRIPTARGTYPE_STRING, VDSCRIPTARGDESC_FLAG_MANDATORY},
+ VDSCRIPTTYPE_STRING, /* name */
+ VDSCRIPTTYPE_STRING /* file */
};
/* I/O pattern destroy action */
-const VDSCRIPTARGDESC g_aArgIoPatternDestroy[] =
+const VDSCRIPTTYPE g_aArgIoPatternDestroy[] =
{
- /* pcszName chId enmType fFlags */
- {"name", 'n', VDSCRIPTARGTYPE_STRING, VDSCRIPTARGDESC_FLAG_MANDATORY}
+ VDSCRIPTTYPE_STRING /* name */
};
/* Sleep */
-const VDSCRIPTARGDESC g_aArgSleep[] =
+const VDSCRIPTTYPE g_aArgSleep[] =
{
- /* pcszName chId enmType fFlags */
- {"time", 't', VDSCRIPTARGTYPE_UNSIGNED_NUMBER, VDSCRIPTARGDESC_FLAG_MANDATORY}
+ VDSCRIPTTYPE_UINT32 /* time */
};
/* Dump memory file */
-const VDSCRIPTARGDESC g_aArgDumpFile[] =
+const VDSCRIPTTYPE g_aArgDumpFile[] =
{
- /* pcszName chId enmType fFlags */
- {"file", 'f', VDSCRIPTARGTYPE_STRING, VDSCRIPTARGDESC_FLAG_MANDATORY},
- {"path", 'p', VDSCRIPTARGTYPE_STRING, VDSCRIPTARGDESC_FLAG_MANDATORY}
+ VDSCRIPTTYPE_STRING, /* file */
+ VDSCRIPTTYPE_STRING /* path */
};
/* Create virtual disk handle */
-const VDSCRIPTARGDESC g_aArgCreateDisk[] =
+const VDSCRIPTTYPE g_aArgCreateDisk[] =
{
- /* pcszName chId enmType fFlags */
- {"name", 'n', VDSCRIPTARGTYPE_STRING, VDSCRIPTARGDESC_FLAG_MANDATORY},
- {"verify", 'v', VDSCRIPTARGTYPE_BOOL, 0}
+ VDSCRIPTTYPE_STRING, /* name */
+ VDSCRIPTTYPE_BOOL /* verify */
};
/* Create virtual disk handle */
-const VDSCRIPTARGDESC g_aArgDestroyDisk[] =
+const VDSCRIPTTYPE g_aArgDestroyDisk[] =
{
- /* pcszName chId enmType fFlags */
- {"name", 'n', VDSCRIPTARGTYPE_STRING, VDSCRIPTARGDESC_FLAG_MANDATORY}
+ VDSCRIPTTYPE_STRING /* name */
};
/* Compare virtual disks */
-const VDSCRIPTARGDESC g_aArgCompareDisks[] =
+const VDSCRIPTTYPE g_aArgCompareDisks[] =
{
- /* pcszName chId enmType fFlags */
- {"disk1", '1', VDSCRIPTARGTYPE_STRING, VDSCRIPTARGDESC_FLAG_MANDATORY},
- {"disk2", '2', VDSCRIPTARGTYPE_STRING, VDSCRIPTARGDESC_FLAG_MANDATORY}
+ VDSCRIPTTYPE_STRING, /* disk1 */
+ VDSCRIPTTYPE_STRING /* disk2 */
};
/* Dump disk info */
-const VDSCRIPTARGDESC g_aArgDumpDiskInfo[] =
+const VDSCRIPTTYPE g_aArgDumpDiskInfo[] =
{
- /* pcszName chId enmType fFlags */
- {"disk", 'd', VDSCRIPTARGTYPE_STRING, VDSCRIPTARGDESC_FLAG_MANDATORY},
+ VDSCRIPTTYPE_STRING /* disk */
};
/* Print message */
-const VDSCRIPTARGDESC g_aArgPrintMsg[] =
+const VDSCRIPTTYPE g_aArgPrintMsg[] =
{
- /* pcszName chId enmType fFlags */
- {"msg", 'm', VDSCRIPTARGTYPE_STRING, VDSCRIPTARGDESC_FLAG_MANDATORY},
+ VDSCRIPTTYPE_STRING /* msg */
};
/* Show statistics */
-const VDSCRIPTARGDESC g_aArgShowStatistics[] =
+const VDSCRIPTTYPE g_aArgShowStatistics[] =
{
- /* pcszName chId enmType fFlags */
- {"file", 'f', VDSCRIPTARGTYPE_STRING, VDSCRIPTARGDESC_FLAG_MANDATORY},
+ VDSCRIPTTYPE_STRING /* file */
};
/* Reset statistics */
-const VDSCRIPTARGDESC g_aArgResetStatistics[] =
+const VDSCRIPTTYPE g_aArgResetStatistics[] =
{
- /* pcszName chId enmType fFlags */
- {"file", 'f', VDSCRIPTARGTYPE_STRING, VDSCRIPTARGDESC_FLAG_MANDATORY},
+ VDSCRIPTTYPE_STRING /* file */
};
-const VDSCRIPTACTION g_aScriptActions[] =
-{
- /* pcszAction paArgDesc cArgDescs pfnHandler */
- {"create", g_aArgCreate, RT_ELEMENTS(g_aArgCreate), vdScriptHandlerCreate},
- {"open", g_aArgOpen, RT_ELEMENTS(g_aArgOpen), vdScriptHandlerOpen},
- {"io", g_aArgIo, RT_ELEMENTS(g_aArgIo), vdScriptHandlerIo},
- {"flush", g_aArgFlush, RT_ELEMENTS(g_aArgFlush), vdScriptHandlerFlush},
- {"close", g_aArgClose, RT_ELEMENTS(g_aArgClose), vdScriptHandlerClose},
- {"printfilesize", g_aArgPrintFileSize, RT_ELEMENTS(g_aArgPrintFileSize), vdScriptHandlerPrintFileSize},
- {"ioreplay", g_aArgIoLogReplay, RT_ELEMENTS(g_aArgIoLogReplay), vdScriptHandlerIoLogReplay},
- {"merge", g_aArgMerge, RT_ELEMENTS(g_aArgMerge), vdScriptHandlerMerge},
- {"compact", g_aArgCompact, RT_ELEMENTS(g_aArgCompact), vdScriptHandlerCompact},
- {"discard", g_aArgDiscard, RT_ELEMENTS(g_aArgDiscard), vdScriptHandlerDiscard},
- {"copy", g_aArgCopy, RT_ELEMENTS(g_aArgCopy), vdScriptHandlerCopy},
- {"iorngcreate", g_aArgIoRngCreate, RT_ELEMENTS(g_aArgIoRngCreate), vdScriptHandlerIoRngCreate},
- {"iorngdestroy", NULL, 0, vdScriptHandlerIoRngDestroy},
- {"iopatterncreatefromnumber", g_aArgIoPatternCreateFromNumber, RT_ELEMENTS(g_aArgIoPatternCreateFromNumber), vdScriptHandlerIoPatternCreateFromNumber},
- {"iopatterncreatefromfile", g_aArgIoPatternCreateFromFile, RT_ELEMENTS(g_aArgIoPatternCreateFromFile), vdScriptHandlerIoPatternCreateFromFile},
- {"iopatterndestroy", g_aArgIoPatternDestroy, RT_ELEMENTS(g_aArgIoPatternDestroy), vdScriptHandlerIoPatternDestroy},
- {"sleep", g_aArgSleep, RT_ELEMENTS(g_aArgSleep), vdScriptHandlerSleep},
- {"dumpfile", g_aArgDumpFile, RT_ELEMENTS(g_aArgDumpFile), vdScriptHandlerDumpFile},
- {"createdisk", g_aArgCreateDisk, RT_ELEMENTS(g_aArgCreateDisk), vdScriptHandlerCreateDisk},
- {"destroydisk", g_aArgDestroyDisk, RT_ELEMENTS(g_aArgDestroyDisk), vdScriptHandlerDestroyDisk},
- {"comparedisks", g_aArgCompareDisks, RT_ELEMENTS(g_aArgCompareDisks), vdScriptHandlerCompareDisks},
- {"dumpdiskinfo", g_aArgDumpDiskInfo, RT_ELEMENTS(g_aArgDumpDiskInfo), vdScriptHandlerDumpDiskInfo},
- {"print", g_aArgPrintMsg, RT_ELEMENTS(g_aArgPrintMsg), vdScriptHandlerPrintMsg},
- {"showstatistics", g_aArgShowStatistics, RT_ELEMENTS(g_aArgShowStatistics), vdScriptHandlerShowStatistics},
- {"resetstatistics", g_aArgResetStatistics, RT_ELEMENTS(g_aArgResetStatistics), vdScriptHandlerResetStatistics}
+/* Resize disk. */
+const VDSCRIPTTYPE g_aArgResize[] =
+{
+ VDSCRIPTTYPE_STRING, /* disk */
+ VDSCRIPTTYPE_UINT64 /* size */
+};
+
+/* Set file backend. */
+const VDSCRIPTTYPE g_aArgSetFileBackend[] =
+{
+ VDSCRIPTTYPE_STRING /* new file backend */
+};
+
+const VDSCRIPTCALLBACK g_aScriptActions[] =
+{
+ /* pcszFnName enmTypeReturn paArgDesc cArgDescs pfnHandler */
+ {"create", VDSCRIPTTYPE_VOID, g_aArgCreate, RT_ELEMENTS(g_aArgCreate), vdScriptHandlerCreate},
+ {"open", VDSCRIPTTYPE_VOID, g_aArgOpen, RT_ELEMENTS(g_aArgOpen), vdScriptHandlerOpen},
+ {"io", VDSCRIPTTYPE_VOID, g_aArgIo, RT_ELEMENTS(g_aArgIo), vdScriptHandlerIo},
+ {"flush", VDSCRIPTTYPE_VOID, g_aArgFlush, RT_ELEMENTS(g_aArgFlush), vdScriptHandlerFlush},
+ {"close", VDSCRIPTTYPE_VOID, g_aArgClose, RT_ELEMENTS(g_aArgClose), vdScriptHandlerClose},
+ {"printfilesize", VDSCRIPTTYPE_VOID, g_aArgPrintFileSize, RT_ELEMENTS(g_aArgPrintFileSize), vdScriptHandlerPrintFileSize},
+ {"ioreplay", VDSCRIPTTYPE_VOID, g_aArgIoLogReplay, RT_ELEMENTS(g_aArgIoLogReplay), vdScriptHandlerIoLogReplay},
+ {"merge", VDSCRIPTTYPE_VOID, g_aArgMerge, RT_ELEMENTS(g_aArgMerge), vdScriptHandlerMerge},
+ {"compact", VDSCRIPTTYPE_VOID, g_aArgCompact, RT_ELEMENTS(g_aArgCompact), vdScriptHandlerCompact},
+ {"discard", VDSCRIPTTYPE_VOID, g_aArgDiscard, RT_ELEMENTS(g_aArgDiscard), vdScriptHandlerDiscard},
+ {"copy", VDSCRIPTTYPE_VOID, g_aArgCopy, RT_ELEMENTS(g_aArgCopy), vdScriptHandlerCopy},
+ {"iorngcreate", VDSCRIPTTYPE_VOID, g_aArgIoRngCreate, RT_ELEMENTS(g_aArgIoRngCreate), vdScriptHandlerIoRngCreate},
+ {"iorngdestroy", VDSCRIPTTYPE_VOID, NULL, 0, vdScriptHandlerIoRngDestroy},
+ {"iopatterncreatefromnumber", VDSCRIPTTYPE_VOID, g_aArgIoPatternCreateFromNumber, RT_ELEMENTS(g_aArgIoPatternCreateFromNumber), vdScriptHandlerIoPatternCreateFromNumber},
+ {"iopatterncreatefromfile", VDSCRIPTTYPE_VOID, g_aArgIoPatternCreateFromFile, RT_ELEMENTS(g_aArgIoPatternCreateFromFile), vdScriptHandlerIoPatternCreateFromFile},
+ {"iopatterndestroy", VDSCRIPTTYPE_VOID, g_aArgIoPatternDestroy, RT_ELEMENTS(g_aArgIoPatternDestroy), vdScriptHandlerIoPatternDestroy},
+ {"sleep", VDSCRIPTTYPE_VOID, g_aArgSleep, RT_ELEMENTS(g_aArgSleep), vdScriptHandlerSleep},
+ {"dumpfile", VDSCRIPTTYPE_VOID, g_aArgDumpFile, RT_ELEMENTS(g_aArgDumpFile), vdScriptHandlerDumpFile},
+ {"createdisk", VDSCRIPTTYPE_VOID, g_aArgCreateDisk, RT_ELEMENTS(g_aArgCreateDisk), vdScriptHandlerCreateDisk},
+ {"destroydisk", VDSCRIPTTYPE_VOID, g_aArgDestroyDisk, RT_ELEMENTS(g_aArgDestroyDisk), vdScriptHandlerDestroyDisk},
+ {"comparedisks", VDSCRIPTTYPE_VOID, g_aArgCompareDisks, RT_ELEMENTS(g_aArgCompareDisks), vdScriptHandlerCompareDisks},
+ {"dumpdiskinfo", VDSCRIPTTYPE_VOID, g_aArgDumpDiskInfo, RT_ELEMENTS(g_aArgDumpDiskInfo), vdScriptHandlerDumpDiskInfo},
+ {"print", VDSCRIPTTYPE_VOID, g_aArgPrintMsg, RT_ELEMENTS(g_aArgPrintMsg), vdScriptHandlerPrintMsg},
+ {"showstatistics", VDSCRIPTTYPE_VOID, g_aArgShowStatistics, RT_ELEMENTS(g_aArgShowStatistics), vdScriptHandlerShowStatistics},
+ {"resetstatistics", VDSCRIPTTYPE_VOID, g_aArgResetStatistics, RT_ELEMENTS(g_aArgResetStatistics), vdScriptHandlerResetStatistics},
+ {"resize", VDSCRIPTTYPE_VOID, g_aArgResize, RT_ELEMENTS(g_aArgResize), vdScriptHandlerResize},
+ {"setfilebackend", VDSCRIPTTYPE_VOID, g_aArgSetFileBackend, RT_ELEMENTS(g_aArgSetFileBackend), vdScriptHandlerSetFileBackend},
};
const unsigned g_cScriptActions = RT_ELEMENTS(g_aScriptActions);
+static DECLCALLBACK(int) vdScriptCallbackPrint(PVDSCRIPTARG paScriptArgs, void *pvUser)
+{
+ RTPrintf(paScriptArgs[0].psz);
+ return VINF_SUCCESS;
+}
+
static void tstVDError(void *pvUser, int rc, RT_SRC_POS_DECL,
const char *pszFormat, va_list va)
{
@@ -605,9 +519,10 @@ static PVDPATTERN tstVDIoGetPatternByName(PVDTESTGLOB pGlob, const char *pcszNam
static PVDPATTERN tstVDIoPatternCreate(const char *pcszName, size_t cbPattern);
static int tstVDIoPatternGetBuffer(PVDPATTERN pPattern, void **ppv, size_t cb);
-static DECLCALLBACK(int) vdScriptHandlerCreate(PVDTESTGLOB pGlob, PVDSCRIPTARG paScriptArgs, unsigned cScriptArgs)
+static DECLCALLBACK(int) vdScriptHandlerCreate(PVDSCRIPTARG paScriptArgs, void *pvUser)
{
int rc = VINF_SUCCESS;
+ PVDTESTGLOB pGlob = (PVDTESTGLOB)pvUser;
uint64_t cbSize = 0;
const char *pcszBackend = NULL;
const char *pcszImage = NULL;
@@ -616,69 +531,31 @@ static DECLCALLBACK(int) vdScriptHandlerCreate(PVDTESTGLOB pGlob, PVDSCRIPTARG p
bool fBase = false;
bool fDynamic = true;
bool fIgnoreFlush = false;
+ PVDIOBACKEND pIoBackend = NULL;
- for (unsigned i = 0; i < cScriptArgs; i++)
+ pcszDisk = paScriptArgs[0].psz;
+ if (!RTStrICmp(paScriptArgs[1].psz, "base"))
+ fBase = true;
+ else if (!RTStrICmp(paScriptArgs[1].psz, "diff"))
+ fBase = false;
+ else
{
- switch (paScriptArgs[i].chId)
- {
- case 'd':
- {
- pcszDisk = paScriptArgs[i].u.pcszString;
- break;
- }
- case 'm':
- {
- if (!RTStrICmp(paScriptArgs[i].u.pcszString, "base"))
- fBase = true;
- else if (!RTStrICmp(paScriptArgs[i].u.pcszString, "diff"))
- fBase = false;
- else
- {
- RTPrintf("Invalid image mode '%s' given\n", paScriptArgs[i].u.pcszString);
- rc = VERR_INVALID_PARAMETER;
- }
- break;
- }
- case 'n':
- {
- pcszImage = paScriptArgs[i].u.pcszString;
- break;
- }
- case 'b':
- {
- pcszBackend = paScriptArgs[i].u.pcszString;
- break;
- }
- case 's':
- {
- cbSize = paScriptArgs[i].u.u64;
- break;
- }
- case 't':
- {
- if (!RTStrICmp(paScriptArgs[i].u.pcszString, "fixed"))
- fDynamic = false;
- else if (!RTStrICmp(paScriptArgs[i].u.pcszString, "dynamic"))
- fDynamic = true;
- else
- {
- RTPrintf("Invalid image type '%s' given\n", paScriptArgs[i].u.pcszString);
- rc = VERR_INVALID_PARAMETER;
- }
- break;
- }
- case 'f':
- {
- fIgnoreFlush = paScriptArgs[i].u.fFlag;
- break;
- }
- default:
- AssertMsgFailed(("Invalid argument given!\n"));
- }
-
- if (RT_FAILURE(rc))
- break;
+ RTPrintf("Invalid image mode '%s' given\n", paScriptArgs[1].psz);
+ rc = VERR_INVALID_PARAMETER;
}
+ pcszImage = paScriptArgs[2].psz;
+ if (!RTStrICmp(paScriptArgs[3].psz, "fixed"))
+ fDynamic = false;
+ else if (!RTStrICmp(paScriptArgs[3].psz, "dynamic"))
+ fDynamic = true;
+ else
+ {
+ RTPrintf("Invalid image type '%s' given\n", paScriptArgs[3].psz);
+ rc = VERR_INVALID_PARAMETER;
+ }
+ pcszBackend = paScriptArgs[4].psz;
+ cbSize = paScriptArgs[5].u64;
+ fIgnoreFlush = paScriptArgs[6].f;
if (RT_SUCCESS(rc))
{
@@ -709,9 +586,10 @@ static DECLCALLBACK(int) vdScriptHandlerCreate(PVDTESTGLOB pGlob, PVDSCRIPTARG p
return rc;
}
-static DECLCALLBACK(int) vdScriptHandlerOpen(PVDTESTGLOB pGlob, PVDSCRIPTARG paScriptArgs, unsigned cScriptArgs)
+static DECLCALLBACK(int) vdScriptHandlerOpen(PVDSCRIPTARG paScriptArgs, void *pvUser)
{
int rc = VINF_SUCCESS;
+ PVDTESTGLOB pGlob = (PVDTESTGLOB)pvUser;
const char *pcszBackend = NULL;
const char *pcszImage = NULL;
const char *pcszDisk = NULL;
@@ -722,52 +600,13 @@ static DECLCALLBACK(int) vdScriptHandlerOpen(PVDTESTGLOB pGlob, PVDSCRIPTARG paS
bool fDiscard = false;
bool fIgnoreFlush = false;
- for (unsigned i = 0; i < cScriptArgs; i++)
- {
- switch (paScriptArgs[i].chId)
- {
- case 'd':
- {
- pcszDisk = paScriptArgs[i].u.pcszString;
- break;
- }
- case 'n':
- {
- pcszImage = paScriptArgs[i].u.pcszString;
- break;
- }
- case 'b':
- {
- pcszBackend = paScriptArgs[i].u.pcszString;
- break;
- }
- case 's':
- {
- fShareable = paScriptArgs[i].u.fFlag;
- break;
- }
- case 'r':
- {
- fReadonly = paScriptArgs[i].u.fFlag;
- break;
- }
- case 'a':
- {
- fAsyncIo = paScriptArgs[i].u.fFlag;
- break;
- }
- case 'i':
- {
- fDiscard = paScriptArgs[i].u.fFlag;
- break;
- }
- default:
- AssertMsgFailed(("Invalid argument given!\n"));
- }
-
- if (RT_FAILURE(rc))
- break;
- }
+ pcszDisk = paScriptArgs[0].psz;
+ pcszImage = paScriptArgs[1].psz;
+ pcszBackend = paScriptArgs[2].psz;
+ fShareable = paScriptArgs[3].f;
+ fReadonly = paScriptArgs[4].f;
+ fAsyncIo = paScriptArgs[5].f;
+ fDiscard = paScriptArgs[6].f;
if (RT_SUCCESS(rc))
{
@@ -796,9 +635,10 @@ static DECLCALLBACK(int) vdScriptHandlerOpen(PVDTESTGLOB pGlob, PVDSCRIPTARG paS
return rc;
}
-static DECLCALLBACK(int) vdScriptHandlerIo(PVDTESTGLOB pGlob, PVDSCRIPTARG paScriptArgs, unsigned cScriptArgs)
+static DECLCALLBACK(int) vdScriptHandlerIo(PVDSCRIPTARG paScriptArgs, void *pvUser)
{
int rc = VINF_SUCCESS;
+ PVDTESTGLOB pGlob = (PVDTESTGLOB)pvUser;
bool fAsync = false;
bool fRandomAcc = false;
uint64_t cbIo = 0;
@@ -814,71 +654,24 @@ static DECLCALLBACK(int) vdScriptHandlerIo(PVDTESTGLOB pGlob, PVDSCRIPTARG paScr
PVDDISK pDisk = NULL;
PVDPATTERN pPattern = NULL;
- for (unsigned i = 0; i < cScriptArgs; i++)
+ pcszDisk = paScriptArgs[0].psz;
+ fAsync = paScriptArgs[1].f;
+ cMaxReqs = paScriptArgs[2].u64;
+ if (!RTStrICmp(paScriptArgs[3].psz, "seq"))
+ fRandomAcc = false;
+ else if (!RTStrICmp(paScriptArgs[3].psz, "rnd"))
+ fRandomAcc = true;
+ else
{
- switch (paScriptArgs[i].chId)
- {
- case 'd':
- {
- pcszDisk = paScriptArgs[i].u.pcszString;
- break;
- }
- case 'a':
- {
- fAsync = paScriptArgs[i].u.fFlag;
- break;
- }
- case 'l':
- {
- cMaxReqs = paScriptArgs[i].u.u64;
- break;
- }
- case 'm':
- {
- if (!RTStrICmp(paScriptArgs[i].u.pcszString, "seq"))
- fRandomAcc = false;
- else if (!RTStrICmp(paScriptArgs[i].u.pcszString, "rnd"))
- fRandomAcc = true;
- else
- {
- RTPrintf("Invalid access mode '%s'\n", paScriptArgs[i].u.pcszString);
- rc = VERR_INVALID_PARAMETER;
- }
- break;
- }
- case 's':
- {
- cbIo = paScriptArgs[i].u.u64;
- break;
- }
- case 'b':
- {
- cbBlkSize = paScriptArgs[i].u.u64;
- break;
- }
- case 'o':
- {
- offStart = paScriptArgs[i].u.Range.Start;
- offEnd = paScriptArgs[i].u.Range.End;
- break;
- }
- case 'w':
- {
- uWriteChance = (uint8_t)paScriptArgs[i].u.u64;
- break;
- }
- case 'p':
- {
- pcszPattern = paScriptArgs[i].u.pcszString;
- break;
- }
- default:
- AssertMsgFailed(("Invalid argument given!\n"));
- }
-
- if (RT_FAILURE(rc))
- break;
+ RTPrintf("Invalid access mode '%s'\n", paScriptArgs[3].psz);
+ rc = VERR_INVALID_PARAMETER;
}
+ cbBlkSize = paScriptArgs[4].u64;
+ offStart = paScriptArgs[5].u64;
+ offEnd = paScriptArgs[6].u64;
+ cbIo = paScriptArgs[7].u64;
+ uWriteChance = (uint8_t)paScriptArgs[8].u64;
+ pcszPattern = paScriptArgs[9].psz;
if ( RT_SUCCESS(rc)
&& fAsync
@@ -907,7 +700,7 @@ static DECLCALLBACK(int) vdScriptHandlerIo(PVDTESTGLOB pGlob, PVDSCRIPTARG paScr
}
if ( RT_SUCCESS(rc)
- && pcszPattern)
+ && RTStrCmp(pcszPattern, "none"))
{
pPattern = tstVDIoGetPatternByName(pGlob, pcszPattern);
if (!pPattern)
@@ -1144,35 +937,16 @@ static DECLCALLBACK(int) vdScriptHandlerIo(PVDTESTGLOB pGlob, PVDSCRIPTARG paScr
return rc;
}
-static DECLCALLBACK(int) vdScriptHandlerFlush(PVDTESTGLOB pGlob, PVDSCRIPTARG paScriptArgs, unsigned cScriptArgs)
+static DECLCALLBACK(int) vdScriptHandlerFlush(PVDSCRIPTARG paScriptArgs, void *pvUser)
{
int rc = VINF_SUCCESS;
+ PVDTESTGLOB pGlob = (PVDTESTGLOB)pvUser;
bool fAsync = false;
const char *pcszDisk = NULL;
PVDDISK pDisk = NULL;
- for (unsigned i = 0; i < cScriptArgs; i++)
- {
- switch (paScriptArgs[i].chId)
- {
- case 'd':
- {
- pcszDisk = paScriptArgs[i].u.pcszString;
- break;
- }
- case 'a':
- {
- fAsync = paScriptArgs[i].u.fFlag;
- break;
- }
-
- default:
- AssertMsgFailed(("Invalid argument given!\n"));
- }
-
- if (RT_FAILURE(rc))
- break;
- }
+ pcszDisk = paScriptArgs[0].psz;
+ fAsync = paScriptArgs[1].f;
if (RT_SUCCESS(rc))
{
@@ -1210,137 +984,70 @@ static DECLCALLBACK(int) vdScriptHandlerFlush(PVDTESTGLOB pGlob, PVDSCRIPTARG pa
return rc;
}
-static DECLCALLBACK(int) vdScriptHandlerMerge(PVDTESTGLOB pGlob, PVDSCRIPTARG paScriptArgs, unsigned cScriptArgs)
+static DECLCALLBACK(int) vdScriptHandlerMerge(PVDSCRIPTARG paScriptArgs, void *pvUser)
{
int rc = VINF_SUCCESS;
+ PVDTESTGLOB pGlob = (PVDTESTGLOB)pvUser;
const char *pcszDisk = NULL;
PVDDISK pDisk = NULL;
unsigned nImageFrom = 0;
unsigned nImageTo = 0;
- for (unsigned i = 0; i < cScriptArgs; i++)
- {
- switch (paScriptArgs[i].chId)
- {
- case 'd':
- {
- pcszDisk = paScriptArgs[i].u.pcszString;
- break;
- }
- case 'f':
- {
- nImageFrom = (unsigned)paScriptArgs[i].u.u64;
- break;
- }
- case 't':
- {
- nImageTo = (unsigned)paScriptArgs[i].u.u64;
- break;
- }
-
- default:
- AssertMsgFailed(("Invalid argument given!\n"));
- }
+ pcszDisk = paScriptArgs[0].psz;
+ nImageFrom = paScriptArgs[1].u32;
+ nImageTo = paScriptArgs[2].u32;
- if (RT_FAILURE(rc))
- break;
- }
-
- if (RT_SUCCESS(rc))
+ pDisk = tstVDIoGetDiskByName(pGlob, pcszDisk);
+ if (!pDisk)
+ rc = VERR_NOT_FOUND;
+ else
{
- pDisk = tstVDIoGetDiskByName(pGlob, pcszDisk);
- if (!pDisk)
- rc = VERR_NOT_FOUND;
- else
- {
- /** @todo: Provide progress interface to test that cancelation
- * doesn't corrupt the data.
- */
- rc = VDMerge(pDisk->pVD, nImageFrom, nImageTo, NULL);
- }
+ /** @todo: Provide progress interface to test that cancelation
+ * doesn't corrupt the data.
+ */
+ rc = VDMerge(pDisk->pVD, nImageFrom, nImageTo, NULL);
}
return rc;
}
-static DECLCALLBACK(int) vdScriptHandlerCompact(PVDTESTGLOB pGlob, PVDSCRIPTARG paScriptArgs, unsigned cScriptArgs)
+static DECLCALLBACK(int) vdScriptHandlerCompact(PVDSCRIPTARG paScriptArgs, void *pvUser)
{
int rc = VINF_SUCCESS;
+ PVDTESTGLOB pGlob = (PVDTESTGLOB)pvUser;
const char *pcszDisk = NULL;
PVDDISK pDisk = NULL;
unsigned nImage = 0;
- for (unsigned i = 0; i < cScriptArgs; i++)
- {
- switch (paScriptArgs[i].chId)
- {
- case 'd':
- {
- pcszDisk = paScriptArgs[i].u.pcszString;
- break;
- }
- case 'i':
- {
- nImage = (unsigned)paScriptArgs[i].u.u64;
- break;
- }
-
- default:
- AssertMsgFailed(("Invalid argument given!\n"));
- }
-
- if (RT_FAILURE(rc))
- break;
- }
+ pcszDisk = paScriptArgs[0].psz;
+ nImage = paScriptArgs[1].u32;
- if (RT_SUCCESS(rc))
+ pDisk = tstVDIoGetDiskByName(pGlob, pcszDisk);
+ if (!pDisk)
+ rc = VERR_NOT_FOUND;
+ else
{
- pDisk = tstVDIoGetDiskByName(pGlob, pcszDisk);
- if (!pDisk)
- rc = VERR_NOT_FOUND;
- else
- {
- /** @todo: Provide progress interface to test that cancelation
- * doesn't corrupt the data.
- */
- rc = VDCompact(pDisk->pVD, nImage, NULL);
- }
+ /** @todo: Provide progress interface to test that cancelation
+ * doesn't corrupt the data.
+ */
+ rc = VDCompact(pDisk->pVD, nImage, NULL);
}
return rc;
}
-static DECLCALLBACK(int) vdScriptHandlerDiscard(PVDTESTGLOB pGlob, PVDSCRIPTARG paScriptArgs, unsigned cScriptArgs)
+static DECLCALLBACK(int) vdScriptHandlerDiscard(PVDSCRIPTARG paScriptArgs, void *pvUser)
{
int rc = VINF_SUCCESS;
+ PVDTESTGLOB pGlob = (PVDTESTGLOB)pvUser;
const char *pcszDisk = NULL;
PVDDISK pDisk = NULL;
bool fAsync = false;
const char *pcszRanges = NULL;
- for (unsigned i = 0; i < cScriptArgs; i++)
- {
- switch (paScriptArgs[i].chId)
- {
- case 'd':
- {
- pcszDisk = paScriptArgs[i].u.pcszString;
- break;
- }
- case 'a':
- {
- fAsync = paScriptArgs[i].u.fFlag;
- break;
- }
- case 'r':
- {
- pcszRanges = paScriptArgs[i].u.pcszString;
- break;
- }
- default:
- AssertMsgFailed(("Invalid argument given!\n"));
- }
- }
+ pcszDisk = paScriptArgs[0].psz;
+ fAsync = paScriptArgs[1].f;
+ pcszRanges = paScriptArgs[2].psz;
pDisk = tstVDIoGetDiskByName(pGlob, pcszDisk);
if (!pDisk)
@@ -1546,9 +1253,10 @@ static DECLCALLBACK(int) vdScriptHandlerDiscard(PVDTESTGLOB pGlob, PVDSCRIPTARG
return rc;
}
-static DECLCALLBACK(int) vdScriptHandlerCopy(PVDTESTGLOB pGlob, PVDSCRIPTARG paScriptArgs, unsigned cScriptArgs)
+static DECLCALLBACK(int) vdScriptHandlerCopy(PVDSCRIPTARG paScriptArgs, void *pvUser)
{
int rc = VINF_SUCCESS;
+ PVDTESTGLOB pGlob = (PVDTESTGLOB)pvUser;
const char *pcszDiskFrom = NULL;
const char *pcszDiskTo = NULL;
PVDDISK pDiskFrom = NULL;
@@ -1561,130 +1269,56 @@ static DECLCALLBACK(int) vdScriptHandlerCopy(PVDTESTGLOB pGlob, PVDSCRIPTARG paS
unsigned nImageFromSame = VD_IMAGE_CONTENT_UNKNOWN;
unsigned nImageToSame = VD_IMAGE_CONTENT_UNKNOWN;
- for (unsigned i = 0; i < cScriptArgs; i++)
- {
- switch (paScriptArgs[i].chId)
- {
- case 's':
- {
- pcszDiskFrom = paScriptArgs[i].u.pcszString;
- break;
- }
- case 'd':
- {
- pcszDiskTo = paScriptArgs[i].u.pcszString;
- break;
- }
- case 'i':
- {
- nImageFrom = (unsigned)paScriptArgs[i].u.u64;
- break;
- }
- case 'b':
- {
- pcszBackend = paScriptArgs[i].u.pcszString;
- break;
- }
- case 'f':
- {
- pcszFilename = paScriptArgs[i].u.pcszString;
- break;
- }
- case 'm':
- {
- fMoveByRename = paScriptArgs[i].u.fFlag;
- break;
- }
- case 'z':
- {
- cbSize = paScriptArgs[i].u.u64;
- break;
- }
- case 'o':
- {
- nImageFromSame = (unsigned)paScriptArgs[i].u.u64;
- break;
- }
- case 't':
- {
- nImageToSame = (unsigned)paScriptArgs[i].u.u64;
- break;
- }
-
- default:
- AssertMsgFailed(("Invalid argument given!\n"));
- }
-
- if (RT_FAILURE(rc))
- break;
- }
-
- if (RT_SUCCESS(rc))
+ pcszDiskFrom = paScriptArgs[0].psz;
+ pcszDiskTo = paScriptArgs[1].psz;
+ nImageFrom = paScriptArgs[2].u32;
+ pcszBackend = paScriptArgs[3].psz;
+ pcszFilename = paScriptArgs[4].psz;
+ fMoveByRename = paScriptArgs[5].f;
+ cbSize = paScriptArgs[6].u64;
+ nImageFromSame = paScriptArgs[7].u32;
+ nImageToSame = paScriptArgs[8].u32;
+
+ pDiskFrom = tstVDIoGetDiskByName(pGlob, pcszDiskFrom);
+ pDiskTo = tstVDIoGetDiskByName(pGlob, pcszDiskTo);
+ if (!pDiskFrom || !pDiskTo)
+ rc = VERR_NOT_FOUND;
+ else
{
- pDiskFrom = tstVDIoGetDiskByName(pGlob, pcszDiskFrom);
- pDiskTo = tstVDIoGetDiskByName(pGlob, pcszDiskTo);
- if (!pDiskFrom || !pDiskTo)
- rc = VERR_NOT_FOUND;
- else
- {
- /** @todo: Provide progress interface to test that cancelation
- * works as intended.
- */
- rc = VDCopyEx(pDiskFrom->pVD, nImageFrom, pDiskTo->pVD, pcszBackend, pcszFilename,
- fMoveByRename, cbSize, nImageFromSame, nImageToSame,
- VD_IMAGE_FLAGS_NONE, NULL, VD_OPEN_FLAGS_ASYNC_IO,
- NULL, pGlob->pInterfacesImages, NULL);
- }
+ /** @todo: Provide progress interface to test that cancelation
+ * works as intended.
+ */
+ rc = VDCopyEx(pDiskFrom->pVD, nImageFrom, pDiskTo->pVD, pcszBackend, pcszFilename,
+ fMoveByRename, cbSize, nImageFromSame, nImageToSame,
+ VD_IMAGE_FLAGS_NONE, NULL, VD_OPEN_FLAGS_ASYNC_IO,
+ NULL, pGlob->pInterfacesImages, NULL);
}
return rc;
}
-static DECLCALLBACK(int) vdScriptHandlerClose(PVDTESTGLOB pGlob, PVDSCRIPTARG paScriptArgs, unsigned cScriptArgs)
+static DECLCALLBACK(int) vdScriptHandlerClose(PVDSCRIPTARG paScriptArgs, void *pvUser)
{
int rc = VINF_SUCCESS;
+ PVDTESTGLOB pGlob = (PVDTESTGLOB)pvUser;
bool fAll = false;
bool fDelete = false;
const char *pcszDisk = NULL;
PVDDISK pDisk = NULL;
- for (unsigned i = 0; i < cScriptArgs; i++)
+ pcszDisk = paScriptArgs[0].psz;
+ if (!RTStrICmp(paScriptArgs[1].psz, "all"))
+ fAll = true;
+ else if (!RTStrICmp(paScriptArgs[1].psz, "single"))
+ fAll = false;
+ else
{
- switch (paScriptArgs[i].chId)
- {
- case 'd':
- {
- pcszDisk = paScriptArgs[i].u.pcszString;
- break;
- }
- case 'm':
- {
- if (!RTStrICmp(paScriptArgs[i].u.pcszString, "all"))
- fAll = true;
- else if (!RTStrICmp(paScriptArgs[i].u.pcszString, "single"))
- fAll = false;
- else
- {
- RTPrintf("Invalid mode '%s' given\n", paScriptArgs[i].u.pcszString);
- rc = VERR_INVALID_PARAMETER;
- }
- break;
- }
- case 'r':
- {
- fDelete = paScriptArgs[i].u.fFlag;
- break;
- }
- default:
- AssertMsgFailed(("Invalid argument given!\n"));
- }
-
- if (RT_FAILURE(rc))
- break;
+ RTPrintf("Invalid mode '%s' given\n", paScriptArgs[1].psz);
+ rc = VERR_INVALID_PARAMETER;
}
+ fDelete = paScriptArgs[2].f;
- if ( RT_SUCCESS(rc)
- && fAll
+ if ( fAll
&& fDelete)
{
RTPrintf("mode=all doesn't work with delete=yes\n");
@@ -1708,31 +1342,16 @@ static DECLCALLBACK(int) vdScriptHandlerClose(PVDTESTGLOB pGlob, PVDSCRIPTARG pa
}
-static DECLCALLBACK(int) vdScriptHandlerPrintFileSize(PVDTESTGLOB pGlob, PVDSCRIPTARG paScriptArgs, unsigned cScriptArgs)
+static DECLCALLBACK(int) vdScriptHandlerPrintFileSize(PVDSCRIPTARG paScriptArgs, void *pvUser)
{
int rc = VINF_SUCCESS;
+ PVDTESTGLOB pGlob = (PVDTESTGLOB)pvUser;
const char *pcszDisk = NULL;
PVDDISK pDisk = NULL;
unsigned nImage = 0;
- for (unsigned i = 0; i < cScriptArgs; i++)
- {
- switch (paScriptArgs[i].chId)
- {
- case 'd':
- {
- pcszDisk = paScriptArgs[i].u.pcszString;
- break;
- }
- case 'i':
- {
- nImage = (unsigned)paScriptArgs[i].u.u64;
- break;
- }
- default:
- AssertMsgFailed(("Invalid argument given!\n"));
- }
- }
+ pcszDisk = paScriptArgs[0].psz;
+ nImage = paScriptArgs[1].u32;
pDisk = tstVDIoGetDiskByName(pGlob, pcszDisk);
if (pDisk)
@@ -1744,31 +1363,16 @@ static DECLCALLBACK(int) vdScriptHandlerPrintFileSize(PVDTESTGLOB pGlob, PVDSCRI
}
-static DECLCALLBACK(int) vdScriptHandlerIoLogReplay(PVDTESTGLOB pGlob, PVDSCRIPTARG paScriptArgs, unsigned cScriptArgs)
+static DECLCALLBACK(int) vdScriptHandlerIoLogReplay(PVDSCRIPTARG paScriptArgs, void *pvUser)
{
int rc = VINF_SUCCESS;
+ PVDTESTGLOB pGlob = (PVDTESTGLOB)pvUser;
const char *pcszDisk = NULL;
PVDDISK pDisk = NULL;
const char *pcszIoLog = NULL;
- for (unsigned i = 0; i < cScriptArgs; i++)
- {
- switch (paScriptArgs[i].chId)
- {
- case 'd':
- {
- pcszDisk = paScriptArgs[i].u.pcszString;
- break;
- }
- case 'i':
- {
- pcszIoLog = paScriptArgs[i].u.pcszString;
- break;
- }
- default:
- AssertMsgFailed(("Invalid argument given!\n"));
- }
- }
+ pcszDisk = paScriptArgs[0].psz;
+ pcszIoLog = paScriptArgs[1].psz;
pDisk = tstVDIoGetDiskByName(pGlob, pcszDisk);
if (pDisk)
@@ -1911,36 +1515,17 @@ static DECLCALLBACK(int) vdScriptHandlerIoLogReplay(PVDTESTGLOB pGlob, PVDSCRIPT
}
-static DECLCALLBACK(int) vdScriptHandlerIoRngCreate(PVDTESTGLOB pGlob, PVDSCRIPTARG paScriptArgs, unsigned cScriptArgs)
+static DECLCALLBACK(int) vdScriptHandlerIoRngCreate(PVDSCRIPTARG paScriptArgs, void *pvUser)
{
int rc = VINF_SUCCESS;
+ PVDTESTGLOB pGlob = (PVDTESTGLOB)pvUser;
size_t cbPattern = 0;
uint64_t uSeed = 0;
const char *pcszSeeder = NULL;
- for (unsigned i = 0; i < cScriptArgs; i++)
- {
- switch (paScriptArgs[i].chId)
- {
- case 'd':
- {
- cbPattern = paScriptArgs[i].u.u64;
- break;
- }
- case 's':
- {
- uSeed = paScriptArgs[i].u.u64;
- break;
- }
- case 'm':
- {
- pcszSeeder = paScriptArgs[i].u.pcszString;
- break;
- }
- default:
- AssertMsgFailed(("Invalid argument given!\n"));
- }
- }
+ cbPattern = paScriptArgs[0].u64;
+ pcszSeeder = paScriptArgs[1].psz;
+ uSeed = paScriptArgs[2].u64;
if (pGlob->pIoRnd)
{
@@ -1973,8 +1558,10 @@ static DECLCALLBACK(int) vdScriptHandlerIoRngCreate(PVDTESTGLOB pGlob, PVDSCRIPT
return rc;
}
-static DECLCALLBACK(int) vdScriptHandlerIoRngDestroy(PVDTESTGLOB pGlob, PVDSCRIPTARG paScriptArgs, unsigned cScriptArgs)
+static DECLCALLBACK(int) vdScriptHandlerIoRngDestroy(PVDSCRIPTARG paScriptArgs, void *pvUser)
{
+ PVDTESTGLOB pGlob = (PVDTESTGLOB)pvUser;
+
if (pGlob->pIoRnd)
{
VDIoRndDestroy(pGlob->pIoRnd);
@@ -1986,36 +1573,17 @@ static DECLCALLBACK(int) vdScriptHandlerIoRngDestroy(PVDTESTGLOB pGlob, PVDSCRIP
return VINF_SUCCESS;
}
-static DECLCALLBACK(int) vdScriptHandlerIoPatternCreateFromNumber(PVDTESTGLOB pGlob, PVDSCRIPTARG paScriptArgs, unsigned cScriptArgs)
+static DECLCALLBACK(int) vdScriptHandlerIoPatternCreateFromNumber(PVDSCRIPTARG paScriptArgs, void *pvUser)
{
int rc = VINF_SUCCESS;
+ PVDTESTGLOB pGlob = (PVDTESTGLOB)pvUser;
size_t cbPattern = 0;
const char *pcszName = NULL;
uint64_t u64Pattern = 0;
- for (unsigned i = 0; i < cScriptArgs; i++)
- {
- switch (paScriptArgs[i].chId)
- {
- case 'n':
- {
- pcszName = paScriptArgs[i].u.pcszString;
- break;
- }
- case 's':
- {
- cbPattern = paScriptArgs[i].u.u64;
- break;
- }
- case 'p':
- {
- u64Pattern = paScriptArgs[i].u.u64;
- break;
- }
- default:
- AssertMsgFailed(("Invalid argument given!\n"));
- }
- }
+ pcszName = paScriptArgs[0].psz;
+ cbPattern = paScriptArgs[1].u64;
+ u64Pattern = paScriptArgs[2].u64;
PVDPATTERN pPattern = tstVDIoGetPatternByName(pGlob, pcszName);
if (!pPattern)
@@ -2045,30 +1613,15 @@ static DECLCALLBACK(int) vdScriptHandlerIoPatternCreateFromNumber(PVDTESTGLOB pG
return rc;
}
-static DECLCALLBACK(int) vdScriptHandlerIoPatternCreateFromFile(PVDTESTGLOB pGlob, PVDSCRIPTARG paScriptArgs, unsigned cScriptArgs)
+static DECLCALLBACK(int) vdScriptHandlerIoPatternCreateFromFile(PVDSCRIPTARG paScriptArgs, void *pvUser)
{
int rc = VINF_SUCCESS;
+ PVDTESTGLOB pGlob = (PVDTESTGLOB)pvUser;
const char *pcszName = NULL;
const char *pcszFile = NULL;
- for (unsigned i = 0; i < cScriptArgs; i++)
- {
- switch (paScriptArgs[i].chId)
- {
- case 'n':
- {
- pcszName = paScriptArgs[i].u.pcszString;
- break;
- }
- case 'f':
- {
- pcszFile = paScriptArgs[i].u.pcszString;
- break;
- }
- default:
- AssertMsgFailed(("Invalid argument given!\n"));
- }
- }
+ pcszName = paScriptArgs[0].psz;
+ pcszFile = paScriptArgs[1].psz;
PVDPATTERN pPattern = tstVDIoGetPatternByName(pGlob, pcszName);
if (!pPattern)
@@ -2107,24 +1660,13 @@ static DECLCALLBACK(int) vdScriptHandlerIoPatternCreateFromFile(PVDTESTGLOB pGlo
return rc;
}
-static DECLCALLBACK(int) vdScriptHandlerIoPatternDestroy(PVDTESTGLOB pGlob, PVDSCRIPTARG paScriptArgs, unsigned cScriptArgs)
+static DECLCALLBACK(int) vdScriptHandlerIoPatternDestroy(PVDSCRIPTARG paScriptArgs, void *pvUser)
{
int rc = VINF_SUCCESS;
+ PVDTESTGLOB pGlob = (PVDTESTGLOB)pvUser;
const char *pcszName = NULL;
- for (unsigned i = 0; i < cScriptArgs; i++)
- {
- switch (paScriptArgs[i].chId)
- {
- case 'n':
- {
- pcszName = paScriptArgs[i].u.pcszString;
- break;
- }
- default:
- AssertMsgFailed(("Invalid argument given!\n"));
- }
- }
+ pcszName = paScriptArgs[0].psz;
PVDPATTERN pPattern = tstVDIoGetPatternByName(pGlob, pcszName);
if (pPattern)
@@ -2140,53 +1682,24 @@ static DECLCALLBACK(int) vdScriptHandlerIoPatternDestroy(PVDTESTGLOB pGlob, PVDS
return rc;
}
-static DECLCALLBACK(int) vdScriptHandlerSleep(PVDTESTGLOB pGlob, PVDSCRIPTARG paScriptArgs, unsigned cScriptArgs)
+static DECLCALLBACK(int) vdScriptHandlerSleep(PVDSCRIPTARG paScriptArgs, void *pvUser)
{
int rc = VINF_SUCCESS;
- uint64_t cMillies = 0;
-
- for (unsigned i = 0; i < cScriptArgs; i++)
- {
- switch (paScriptArgs[i].chId)
- {
- case 't':
- {
- cMillies = paScriptArgs[i].u.u64;
- break;
- }
- default:
- AssertMsgFailed(("Invalid argument given!\n"));
- }
- }
+ uint64_t cMillies = paScriptArgs[0].u64;
rc = RTThreadSleep(cMillies);
return rc;
}
-static DECLCALLBACK(int) vdScriptHandlerDumpFile(PVDTESTGLOB pGlob, PVDSCRIPTARG paScriptArgs, unsigned cScriptArgs)
+static DECLCALLBACK(int) vdScriptHandlerDumpFile(PVDSCRIPTARG paScriptArgs, void *pvUser)
{
int rc = VINF_SUCCESS;
+ PVDTESTGLOB pGlob = (PVDTESTGLOB)pvUser;
const char *pcszFile = NULL;
const char *pcszPathToDump = NULL;
- for (unsigned i = 0; i < cScriptArgs; i++)
- {
- switch (paScriptArgs[i].chId)
- {
- case 'f':
- {
- pcszFile = paScriptArgs[i].u.pcszString;
- break;
- }
- case 'p':
- {
- pcszPathToDump = paScriptArgs[i].u.pcszString;
- break;
- }
- default:
- AssertMsgFailed(("Invalid argument given!\n"));
- }
- }
+ pcszFile = paScriptArgs[0].psz;
+ pcszPathToDump = paScriptArgs[1].psz;
/* Check for the file. */
PVDFILE pIt = NULL;
@@ -2203,7 +1716,8 @@ static DECLCALLBACK(int) vdScriptHandlerDumpFile(PVDTESTGLOB pGlob, PVDSCRIPTARG
if (fFound)
{
RTPrintf("Dumping memory file %s to %s, this might take some time\n", pcszFile, pcszPathToDump);
- rc = VDMemDiskWriteToFile(pIt->pMemDisk, pcszPathToDump);
+ //rc = VDMemDiskWriteToFile(pIt->pIo, pcszPathToDump);
+ rc = VERR_NOT_IMPLEMENTED;
}
else
rc = VERR_FILE_NOT_FOUND;
@@ -2211,31 +1725,16 @@ static DECLCALLBACK(int) vdScriptHandlerDumpFile(PVDTESTGLOB pGlob, PVDSCRIPTARG
return rc;
}
-static DECLCALLBACK(int) vdScriptHandlerCreateDisk(PVDTESTGLOB pGlob, PVDSCRIPTARG paScriptArgs, unsigned cScriptArgs)
+static DECLCALLBACK(int) vdScriptHandlerCreateDisk(PVDSCRIPTARG paScriptArgs, void *pvUser)
{
int rc = VINF_SUCCESS;
+ PVDTESTGLOB pGlob = (PVDTESTGLOB)pvUser;
const char *pcszDisk = NULL;
PVDDISK pDisk = NULL;
bool fVerify = false;
- for (unsigned i = 0; i < cScriptArgs; i++)
- {
- switch (paScriptArgs[i].chId)
- {
- case 'n':
- {
- pcszDisk = paScriptArgs[i].u.pcszString;
- break;
- }
- case 'v':
- {
- fVerify = paScriptArgs[i].u.fFlag;
- break;
- }
- default:
- AssertMsgFailed(("Invalid argument given!\n"));
- }
- }
+ pcszDisk = paScriptArgs[0].psz;
+ fVerify = paScriptArgs[1].f;
pDisk = tstVDIoGetDiskByName(pGlob, pcszDisk);
if (pDisk)
@@ -2290,25 +1789,14 @@ static DECLCALLBACK(int) vdScriptHandlerCreateDisk(PVDTESTGLOB pGlob, PVDSCRIPTA
return rc;
}
-static DECLCALLBACK(int) vdScriptHandlerDestroyDisk(PVDTESTGLOB pGlob, PVDSCRIPTARG paScriptArgs, unsigned cScriptArgs)
+static DECLCALLBACK(int) vdScriptHandlerDestroyDisk(PVDSCRIPTARG paScriptArgs, void *pvUser)
{
int rc = VINF_SUCCESS;
+ PVDTESTGLOB pGlob = (PVDTESTGLOB)pvUser;
const char *pcszDisk = NULL;
PVDDISK pDisk = NULL;
- for (unsigned i = 0; i < cScriptArgs; i++)
- {
- switch (paScriptArgs[i].chId)
- {
- case 'n':
- {
- pcszDisk = paScriptArgs[i].u.pcszString;
- break;
- }
- default:
- AssertMsgFailed(("Invalid argument given!\n"));
- }
- }
+ pcszDisk = paScriptArgs[0].psz;
pDisk = tstVDIoGetDiskByName(pGlob, pcszDisk);
if (pDisk)
@@ -2329,32 +1817,17 @@ static DECLCALLBACK(int) vdScriptHandlerDestroyDisk(PVDTESTGLOB pGlob, PVDSCRIPT
return rc;
}
-static DECLCALLBACK(int) vdScriptHandlerCompareDisks(PVDTESTGLOB pGlob, PVDSCRIPTARG paScriptArgs, unsigned cScriptArgs)
+static DECLCALLBACK(int) vdScriptHandlerCompareDisks(PVDSCRIPTARG paScriptArgs, void *pvUser)
{
int rc = VINF_SUCCESS;
+ PVDTESTGLOB pGlob = (PVDTESTGLOB)pvUser;
const char *pcszDisk1 = NULL;
PVDDISK pDisk1 = NULL;
const char *pcszDisk2 = NULL;
PVDDISK pDisk2 = NULL;
- for (unsigned i = 0; i < cScriptArgs; i++)
- {
- switch (paScriptArgs[i].chId)
- {
- case '1':
- {
- pcszDisk1 = paScriptArgs[i].u.pcszString;
- break;
- }
- case '2':
- {
- pcszDisk2 = paScriptArgs[i].u.pcszString;
- break;
- }
- default:
- AssertMsgFailed(("Invalid argument given!\n"));
- }
- }
+ pcszDisk1 = paScriptArgs[0].psz;
+ pcszDisk2 = paScriptArgs[1].psz;
pDisk1 = tstVDIoGetDiskByName(pGlob, pcszDisk1);
pDisk2 = tstVDIoGetDiskByName(pGlob, pcszDisk2);
@@ -2420,25 +1893,14 @@ static DECLCALLBACK(int) vdScriptHandlerCompareDisks(PVDTESTGLOB pGlob, PVDSCRIP
return rc;
}
-static DECLCALLBACK(int) vdScriptHandlerDumpDiskInfo(PVDTESTGLOB pGlob, PVDSCRIPTARG paScriptArgs, unsigned cScriptArgs)
+static DECLCALLBACK(int) vdScriptHandlerDumpDiskInfo(PVDSCRIPTARG paScriptArgs, void *pvUser)
{
int rc = VINF_SUCCESS;
+ PVDTESTGLOB pGlob = (PVDTESTGLOB)pvUser;
const char *pcszDisk = NULL;
PVDDISK pDisk = NULL;
- for (unsigned i = 0; i < cScriptArgs; i++)
- {
- switch (paScriptArgs[i].chId)
- {
- case 'd':
- {
- pcszDisk = paScriptArgs[i].u.pcszString;
- break;
- }
- default:
- AssertMsgFailed(("Invalid argument given!\n"));
- }
- }
+ pcszDisk = paScriptArgs[0].psz;
pDisk = tstVDIoGetDiskByName(pGlob, pcszDisk);
@@ -2450,30 +1912,17 @@ static DECLCALLBACK(int) vdScriptHandlerDumpDiskInfo(PVDTESTGLOB pGlob, PVDSCRIP
return rc;
}
-static DECLCALLBACK(int) vdScriptHandlerPrintMsg(PVDTESTGLOB pGlob, PVDSCRIPTARG paScriptArgs, unsigned cScriptArgs)
+static DECLCALLBACK(int) vdScriptHandlerPrintMsg(PVDSCRIPTARG paScriptArgs, void *pvUser)
{
- RTPrintf("%s\n", paScriptArgs[0].u.pcszString);
+ RTPrintf("%s\n", paScriptArgs[0].psz);
return VINF_SUCCESS;
}
-static DECLCALLBACK(int) vdScriptHandlerShowStatistics(PVDTESTGLOB pGlob, PVDSCRIPTARG paScriptArgs, unsigned cScriptArgs)
+static DECLCALLBACK(int) vdScriptHandlerShowStatistics(PVDSCRIPTARG paScriptArgs, void *pvUser)
{
int rc = VINF_SUCCESS;
- const char *pcszFile = NULL;
-
- for (unsigned i = 0; i < cScriptArgs; i++)
- {
- switch (paScriptArgs[i].chId)
- {
- case 'f':
- {
- pcszFile = paScriptArgs[i].u.pcszString;
- break;
- }
- default:
- AssertMsgFailed(("Invalid argument given!\n"));
- }
- }
+ PVDTESTGLOB pGlob = (PVDTESTGLOB)pvUser;
+ const char *pcszFile = paScriptArgs[0].psz;
/* Check for the file. */
PVDFILE pIt = NULL;
@@ -2502,24 +1951,11 @@ static DECLCALLBACK(int) vdScriptHandlerShowStatistics(PVDTESTGLOB pGlob, PVDSCR
return rc;
}
-static DECLCALLBACK(int) vdScriptHandlerResetStatistics(PVDTESTGLOB pGlob, PVDSCRIPTARG paScriptArgs, unsigned cScriptArgs)
+static DECLCALLBACK(int) vdScriptHandlerResetStatistics(PVDSCRIPTARG paScriptArgs, void *pvUser)
{
int rc = VINF_SUCCESS;
- const char *pcszFile = NULL;
-
- for (unsigned i = 0; i < cScriptArgs; i++)
- {
- switch (paScriptArgs[i].chId)
- {
- case 'f':
- {
- pcszFile = paScriptArgs[i].u.pcszString;
- break;
- }
- default:
- AssertMsgFailed(("Invalid argument given!\n"));
- }
- }
+ PVDTESTGLOB pGlob = (PVDTESTGLOB)pvUser;
+ const char *pcszFile = paScriptArgs[0].psz;
/* Check for the file. */
PVDFILE pIt = NULL;
@@ -2549,6 +1985,39 @@ static DECLCALLBACK(int) vdScriptHandlerResetStatistics(PVDTESTGLOB pGlob, PVDSC
return rc;
}
+static DECLCALLBACK(int) vdScriptHandlerResize(PVDSCRIPTARG paScriptArgs, void *pvUser)
+{
+ int rc = VINF_SUCCESS;
+ PVDTESTGLOB pGlob = (PVDTESTGLOB)pvUser;
+ const char *pcszDisk = paScriptArgs[0].psz;
+ uint64_t cbDiskNew = 0;
+ PVDDISK pDisk = NULL;
+
+ pDisk = tstVDIoGetDiskByName(pGlob, pcszDisk);
+ if (pDisk)
+ {
+ rc = VDResize(pDisk->pVD, cbDiskNew, &pDisk->PhysGeom, &pDisk->LogicalGeom, NULL);
+ }
+ else
+ rc = VERR_NOT_FOUND;
+
+ return rc;
+}
+
+static DECLCALLBACK(int) vdScriptHandlerSetFileBackend(PVDSCRIPTARG paScriptArgs, void *pvUser)
+{
+ int rc = VINF_SUCCESS;
+ PVDTESTGLOB pGlob = (PVDTESTGLOB)pvUser;
+ const char *pcszBackend = paScriptArgs[0].psz;
+
+ RTStrFree(pGlob->pszIoBackend);
+ pGlob->pszIoBackend = RTStrDup(pcszBackend);
+ if (!pGlob->pszIoBackend)
+ rc = VERR_NO_MEMORY;
+
+ return rc;
+}
+
static DECLCALLBACK(int) tstVDIoFileOpen(void *pvUser, const char *pszLocation,
uint32_t fOpen,
PFNVDCOMPLETED pfnCompleted,
@@ -2583,7 +2052,7 @@ static DECLCALLBACK(int) tstVDIoFileOpen(void *pvUser, const char *pszLocation,
{
/* If the file exists delete the memory disk. */
if (fFound)
- rc = VDMemDiskSetSize(pIt->pMemDisk, 0);
+ rc = VDIoBackendStorageSetSize(pIt->pIoStorage, 0);
else
{
/* Create completey new. */
@@ -2594,7 +2063,8 @@ static DECLCALLBACK(int) tstVDIoFileOpen(void *pvUser, const char *pszLocation,
if (pIt->pszName)
{
- rc = VDMemDiskCreate(&pIt->pMemDisk, 0);
+ rc = VDIoBackendStorageCreate(pGlob->pIoBackend, pGlob->pszIoBackend,
+ pszLocation, pfnCompleted, &pIt->pIoStorage);
}
else
rc = VERR_NO_MEMORY;
@@ -2605,11 +2075,11 @@ static DECLCALLBACK(int) tstVDIoFileOpen(void *pvUser, const char *pszLocation,
RTStrFree(pIt->pszName);
RTMemFree(pIt);
}
+ else
+ RTListAppend(&pGlob->ListFiles, &pIt->Node);
}
else
rc = VERR_NO_MEMORY;
-
- RTListAppend(&pGlob->ListFiles, &pIt->Node);
}
}
else if ((fOpen & RTFILE_O_ACTION_MASK) == RTFILE_O_OPEN)
@@ -2673,7 +2143,7 @@ static DECLCALLBACK(int) tstVDIoFileDelete(void *pvUser, const char *pcszFilenam
if (fFound)
{
RTListNodeRemove(&pIt->Node);
- VDMemDiskDestroy(pIt->pMemDisk);
+ VDIoBackendStorageDestroy(pIt->pIoStorage);
RTStrFree(pIt->pszName);
RTMemFree(pIt);
}
@@ -2737,14 +2207,14 @@ static DECLCALLBACK(int) tstVDIoFileGetSize(void *pvUser, void *pStorage, uint64
{
PVDSTORAGE pIoStorage = (PVDSTORAGE)pStorage;
- return VDMemDiskGetSize(pIoStorage->pFile->pMemDisk, pcbSize);
+ return VDIoBackendStorageGetSize(pIoStorage->pFile->pIoStorage, pcbSize);
}
static DECLCALLBACK(int) tstVDIoFileSetSize(void *pvUser, void *pStorage, uint64_t cbSize)
{
PVDSTORAGE pIoStorage = (PVDSTORAGE)pStorage;
- return VDMemDiskSetSize(pIoStorage->pFile->pMemDisk, cbSize);
+ return VDIoBackendStorageSetSize(pIoStorage->pFile->pIoStorage, cbSize);
}
static DECLCALLBACK(int) tstVDIoFileWriteSync(void *pvUser, void *pStorage, uint64_t uOffset,
@@ -2759,7 +2229,8 @@ static DECLCALLBACK(int) tstVDIoFileWriteSync(void *pvUser, void *pStorage, uint
Seg.pvSeg = (void *)pvBuffer;
Seg.cbSeg = cbBuffer;
RTSgBufInit(&SgBuf, &Seg, 1);
- rc = VDMemDiskWrite(pIoStorage->pFile->pMemDisk, uOffset, cbBuffer, &SgBuf);
+ rc = VDIoBackendTransfer(pIoStorage->pFile->pIoStorage, VDIOTXDIR_WRITE, uOffset,
+ cbBuffer, &SgBuf, NULL, true /* fSync */);
if (RT_SUCCESS(rc))
{
pIoStorage->pFile->cWrites++;
@@ -2782,7 +2253,8 @@ static DECLCALLBACK(int) tstVDIoFileReadSync(void *pvUser, void *pStorage, uint6
Seg.pvSeg = pvBuffer;
Seg.cbSeg = cbBuffer;
RTSgBufInit(&SgBuf, &Seg, 1);
- rc = VDMemDiskRead(pIoStorage->pFile->pMemDisk, uOffset, cbBuffer, &SgBuf);
+ rc = VDIoBackendTransfer(pIoStorage->pFile->pIoStorage, VDIOTXDIR_READ, uOffset,
+ cbBuffer, &SgBuf, NULL, true /* fSync */);
if (RT_SUCCESS(rc))
{
pIoStorage->pFile->cReads++;
@@ -2796,8 +2268,10 @@ static DECLCALLBACK(int) tstVDIoFileReadSync(void *pvUser, void *pStorage, uint6
static DECLCALLBACK(int) tstVDIoFileFlushSync(void *pvUser, void *pStorage)
{
PVDSTORAGE pIoStorage = (PVDSTORAGE)pStorage;
+ int rc = VDIoBackendTransfer(pIoStorage->pFile->pIoStorage, VDIOTXDIR_FLUSH, 0,
+ 0, NULL, NULL, true /* fSync */);
pIoStorage->pFile->cFlushes++;
- return VINF_SUCCESS;
+ return rc;
}
static DECLCALLBACK(int) tstVDIoFileReadAsync(void *pvUser, void *pStorage, uint64_t uOffset,
@@ -2808,9 +2282,11 @@ static DECLCALLBACK(int) tstVDIoFileReadAsync(void *pvUser, void *pStorage, uint
int rc = VINF_SUCCESS;
PVDTESTGLOB pGlob = (PVDTESTGLOB)pvUser;
PVDSTORAGE pIoStorage = (PVDSTORAGE)pStorage;
+ RTSGBUF SgBuf;
- rc = VDIoBackendMemTransfer(pGlob->pIoBackend, pIoStorage->pFile->pMemDisk, VDIOTXDIR_READ, uOffset,
- cbRead, paSegments, cSegments, pIoStorage->pfnComplete, pvCompletion);
+ RTSgBufInit(&SgBuf, paSegments, cSegments);
+ rc = VDIoBackendTransfer(pIoStorage->pFile->pIoStorage, VDIOTXDIR_READ, uOffset,
+ cbRead, &SgBuf, pvCompletion, false /* fSync */);
if (RT_SUCCESS(rc))
{
pIoStorage->pFile->cAsyncReads++;
@@ -2828,9 +2304,11 @@ static DECLCALLBACK(int) tstVDIoFileWriteAsync(void *pvUser, void *pStorage, uin
int rc = VINF_SUCCESS;
PVDTESTGLOB pGlob = (PVDTESTGLOB)pvUser;
PVDSTORAGE pIoStorage = (PVDSTORAGE)pStorage;
+ RTSGBUF SgBuf;
- rc = VDIoBackendMemTransfer(pGlob->pIoBackend, pIoStorage->pFile->pMemDisk, VDIOTXDIR_WRITE, uOffset,
- cbWrite, paSegments, cSegments, pIoStorage->pfnComplete, pvCompletion);
+ RTSgBufInit(&SgBuf, paSegments, cSegments);
+ rc = VDIoBackendTransfer(pIoStorage->pFile->pIoStorage, VDIOTXDIR_WRITE, uOffset,
+ cbWrite, &SgBuf, pvCompletion, false /* fSync */);
if (RT_SUCCESS(rc))
{
pIoStorage->pFile->cAsyncWrites++;
@@ -2847,8 +2325,8 @@ static DECLCALLBACK(int) tstVDIoFileFlushAsync(void *pvUser, void *pStorage, voi
PVDTESTGLOB pGlob = (PVDTESTGLOB)pvUser;
PVDSTORAGE pIoStorage = (PVDSTORAGE)pStorage;
- rc = VDIoBackendMemTransfer(pGlob->pIoBackend, pIoStorage->pFile->pMemDisk, VDIOTXDIR_FLUSH, 0,
- 0, NULL, 0, pIoStorage->pfnComplete, pvCompletion);
+ rc = VDIoBackendTransfer(pIoStorage->pFile->pIoStorage, VDIOTXDIR_FLUSH, 0,
+ 0, NULL, pvCompletion, false /* fSync */);
if (RT_SUCCESS(rc))
{
pIoStorage->pFile->cAsyncFlushes++;
@@ -3169,454 +2647,7 @@ static int tstVDIoPatternGetBuffer(PVDPATTERN pPattern, void **ppv, size_t cb)
}
/**
- * Skips the characters until the given character is reached.
- *
- * @returns Start of the string with the given character
- * or NULL if the string ended before.
- *
- * @param psz The string to skip.
- * @param ch The character.
- */
-static char *tstVDIoScriptSkipUntil(char *psz, char ch)
-{
- while ( *psz != '\0'
- && *psz != ch)
- psz++;
-
- return psz;
-}
-
-/**
- * Skips the spaces of the current string.
- *
- * @returns Start of the string with a non space character
- * or NULL if the string ended before.
- *
- * @param psz The string to skip.
- */
-static char *tstVDIoScriptSkipSpace(char *psz)
-{
- while ( *psz != '\0'
- && RT_C_IS_SPACE(*psz))
- psz++;
-
- return psz;
-}
-
-/**
- * Skips all characters until a space is reached of the current
- * string.
- *
- * @returns Start of the string with a space character
- * or NULL if the string ended before.
- *
- * @param psz The string to skip.
- */
-static char *tstVDIoScriptSkipNonSpace(char *psz)
-{
- while ( *psz != '\0'
- && !RT_C_IS_SPACE(*psz))
- psz++;
-
- return psz;
-}
-
-/**
- * Returns true if the first character of the given string
- * contains a character marking a line end (comment or \0
- * terminator).
- *
- * @returns true if the line contains no more characters of
- * interest and false otherwise.
- *
- * @param psz The string to check for.
- */
-static bool tstVDIoIsLineEnd(const char *psz)
-{
- return *psz == '\0' || *psz == '#';
-}
-
-/**
- * Parses one argument name, value pair.
- *
- * @returns IPRT status code.
- *
- * @param pVDScriptAction Script action.
- * @param pcszName Argument name.
- * @param pcszValue Argument value.
- * @param pScriptArg Where to fill in the parsed
- * argument.
- * @param pfMandatory Where to store whether the argument
- * is mandatory.
- */
-static int tstVDIoScriptArgumentParse(PCVDSCRIPTACTION pVDScriptAction, const char *pcszName,
- const char *pcszValue, PVDSCRIPTARG pScriptArg, bool *pfMandatory)
-{
- int rc = VERR_NOT_FOUND;
-
- for (unsigned i = 0; i < pVDScriptAction->cArgDescs; i++)
- {
- if (!RTStrCmp(pVDScriptAction->paArgDesc[i].pcszName, pcszName))
- {
- rc = VINF_SUCCESS;
-
- switch (pVDScriptAction->paArgDesc[i].enmType)
- {
- case VDSCRIPTARGTYPE_BOOL:
- {
- pScriptArg->enmType = VDSCRIPTARGTYPE_BOOL;
- if (!RTStrICmp(pcszValue, "yes") || !RTStrICmp(pcszValue, "on"))
- pScriptArg->u.fFlag = true;
- else if (!RTStrICmp(pcszValue, "no") || !RTStrICmp(pcszValue, "off"))
- pScriptArg->u.fFlag = false;
- else
- {
- RTPrintf("Boolean argument malformed '%s'\n", pcszValue);
- rc = VERR_INVALID_PARAMETER;
- }
- break;
- }
- case VDSCRIPTARGTYPE_SIGNED_NUMBER:
- {
- pScriptArg->enmType = VDSCRIPTARGTYPE_SIGNED_NUMBER;
- AssertMsgFailed(("todo\n"));
- break;
- }
- case VDSCRIPTARGTYPE_STRING:
- {
- pScriptArg->enmType = VDSCRIPTARGTYPE_STRING;
- pScriptArg->u.pcszString = pcszValue;
- break;
- }
- case VDSCRIPTARGTYPE_UNSIGNED_NUMBER:
- {
- char *pszSuffix = NULL;
-
- pScriptArg->enmType = VDSCRIPTARGTYPE_UNSIGNED_NUMBER;
- rc = RTStrToUInt64Ex(pcszValue, &pszSuffix, 10, &pScriptArg->u.u64);
- if (rc == VWRN_TRAILING_CHARS)
- {
- switch (*pszSuffix)
- {
- case 'k':
- case 'K':
- {
- pScriptArg->u.u64 *= _1K;
- break;
- }
- case 'm':
- case 'M':
- {
- pScriptArg->u.u64 *= _1M;
- break;
- }
- case 'g':
- case 'G':
- {
- pScriptArg->u.u64 *= _1G;
- break;
- }
- default:
- {
- RTPrintf("Invalid size suffix '%s'\n", pszSuffix);
- rc = VERR_INVALID_PARAMETER;
- }
- }
- if (rc != VERR_INVALID_PARAMETER)
- rc = VINF_SUCCESS;
- }
-
- break;
- }
- case VDSCRIPTARGTYPE_UNSIGNED_RANGE:
- {
- char *pszSuffix = NULL;
-
- pScriptArg->enmType = VDSCRIPTARGTYPE_UNSIGNED_RANGE;
- rc = RTStrToUInt64Ex(pcszValue, &pszSuffix, 10, &pScriptArg->u.Range.Start);
- if (rc == VWRN_TRAILING_CHARS)
- {
- if (*pszSuffix != '-')
- {
- switch (*pszSuffix)
- {
- case 'k':
- case 'K':
- {
- pScriptArg->u.u64 *= _1K;
- break;
- }
- case 'm':
- case 'M':
- {
- pScriptArg->u.u64 *= _1M;
- break;
- }
- case 'g':
- case 'G':
- {
- pScriptArg->u.u64 *= _1G;
- break;
- }
- default:
- {
- RTPrintf("Invalid size suffix '%s'\n", pszSuffix);
- rc = VERR_INVALID_PARAMETER;
- }
- }
- if (RT_SUCCESS(rc))
- pszSuffix++;
- }
-
- if (*pszSuffix == '-')
- {
- pszSuffix++;
- rc = RTStrToUInt64Ex(pszSuffix, &pszSuffix, 10, &pScriptArg->u.Range.End);
- if (rc == VWRN_TRAILING_CHARS)
- {
- switch (*pszSuffix)
- {
- case 'k':
- case 'K':
- {
- pScriptArg->u.Range.End *= _1K;
- break;
- }
- case 'm':
- case 'M':
- {
- pScriptArg->u.Range.End *= _1M;
- break;
- }
- case 'g':
- case 'G':
- {
- pScriptArg->u.Range.End *= _1G;
- break;
- }
- default:
- {
- RTPrintf("Invalid size suffix '%s'\n", pszSuffix);
- rc = VERR_INVALID_PARAMETER;
- }
- }
- }
- }
- else
- rc = VERR_INVALID_PARAMETER;
- }
- else
- rc = VERR_INVALID_PARAMETER;
-
- if (rc == VERR_INVALID_PARAMETER)
- RTPrintf("Invalid range format\n");
- break;
- }
- default:
- AssertMsgFailed(("Invalid script argument type\n"));
- }
-
- if (RT_SUCCESS(rc))
- {
- pScriptArg->chId = pVDScriptAction->paArgDesc[i].chId;
- *pfMandatory = !!(pVDScriptAction->paArgDesc[i].fFlags & VDSCRIPTARGDESC_FLAG_MANDATORY);
- }
- break;
- }
- }
-
- if (rc == VERR_NOT_FOUND)
- RTPrintf("Argument '%s' not found\n", pcszName);
-
- return rc;
-}
-
-/**
- * Parses the arguments of a action in the script.
- *
- * @returns IPRT status code.
- *
- * @param psz Argument string.
- * @param pVDScriptAction The script action to parses
- * arguments for.
- * @param paScriptArgs Where to store the arguments.
- * @param pcScriptArgs Where to store the actual number of
- * arguments parsed.
- */
-static int tstVDIoScriptArgumentListParse(char *psz, PCVDSCRIPTACTION pVDScriptAction, PVDSCRIPTARG paScriptArgs, unsigned *pcScriptArgs)
-{
- int rc = VINF_SUCCESS;
- unsigned cMandatoryArgsReq = 0;
- unsigned cScriptArgs = 0;
-
- /* Count the number of mandatory arguments first. */
- for (unsigned i = 0; i < pVDScriptAction->cArgDescs; i++)
- if (pVDScriptAction->paArgDesc[i].fFlags & VDSCRIPTARGDESC_FLAG_MANDATORY)
- cMandatoryArgsReq++;
-
- /* One argument is given in the form name=value. */
- *pcScriptArgs = 0;
-
- while ( psz
- && !tstVDIoIsLineEnd(psz))
- {
- const char *pcszName = psz;
-
- psz = tstVDIoScriptSkipUntil(psz, '=');
- if (!tstVDIoIsLineEnd(psz))
- {
- *psz = '\0'; /* Overwrite */
- psz++;
- const char *pcszValue = psz;
-
- psz = tstVDIoScriptSkipNonSpace(psz);
- if (!tstVDIoIsLineEnd(psz))
- {
- *psz = '\0'; /* Overwrite */
- psz++;
- psz = tstVDIoScriptSkipSpace(psz);
- }
-
- pcszValue = tstVDIoScriptSkipSpace((char *)pcszValue);
- if (*pcszValue == '\0')
- {
- RTPrintf("Value missing for argument '%s'\n", pcszName);
- rc = VERR_INVALID_STATE;
- break;
- }
-
- /* We have the name and value pair now. */
- bool fMandatory = false; /* Shut up gcc */
- rc = tstVDIoScriptArgumentParse(pVDScriptAction, pcszName, pcszValue, &paScriptArgs[cScriptArgs], &fMandatory);
- if (RT_SUCCESS(rc))
- {
- if (fMandatory)
- cMandatoryArgsReq--;
- cScriptArgs++;
- }
- }
- else
- {
- RTPrintf("Argument in invalid form\n");
- rc = VERR_INVALID_STATE;
- break;
- }
- }
-
- if ( RT_SUCCESS(rc)
- && cMandatoryArgsReq)
- {
- /* No arguments anymore but there are still mandatory arguments left. */
- RTPrintf("There are %u arguments missing for script action '%s\n", pVDScriptAction->pcszAction);
- rc = VERR_INVALID_STATE;
- }
-
- if (RT_SUCCESS(rc))
- *pcScriptArgs = cScriptArgs;
-
- return rc;
-}
-
-/**
- * Executes the script pointed to by the given stream.
- *
- * @returns IPRT status code.
- *
- * @param pStrm The stream handle of the script.
- * @param pGlob Global test data.
- */
-static int tstVDIoScriptExecute(PRTSTREAM pStrm, PVDTESTGLOB pGlob)
-{
- int rc = VINF_SUCCESS;
- char abBuffer[0x1000]; /* Current assumption that a line is never longer than 4096 bytes. */
- PVDSCRIPTARG paScriptArgs = NULL;
- unsigned cScriptArgsMax = 0;
-
- do
- {
- memset(abBuffer, 0, sizeof(abBuffer));
- rc = RTStrmGetLine(pStrm, abBuffer, sizeof(abBuffer));
- if (RT_SUCCESS(rc))
- {
- const char *pcszAction = NULL;
- char *psz = abBuffer;
-
- /* Skip space */
- psz = tstVDIoScriptSkipSpace(psz);
- if (!tstVDIoIsLineEnd(psz))
- {
- PCVDSCRIPTACTION pVDScriptAction = NULL;
-
- /* Get the action name. */
- pcszAction = psz;
-
- psz = tstVDIoScriptSkipNonSpace(psz);
- if (!tstVDIoIsLineEnd(psz))
- {
- Assert(RT_C_IS_SPACE(*psz));
- *psz++ = '\0';
- }
-
- /* Find the action. */
- for (unsigned i = 0; i < g_cScriptActions; i++)
- {
- if (!RTStrCmp(pcszAction, g_aScriptActions[i].pcszAction))
- {
- pVDScriptAction = &g_aScriptActions[i];
- break;
- }
- }
-
- if (pVDScriptAction)
- {
- /* Parse arguments. */
- if (cScriptArgsMax < pVDScriptAction->cArgDescs)
- {
- /* Increase arguments array. */
- if (paScriptArgs)
- RTMemFree(paScriptArgs);
-
- cScriptArgsMax = pVDScriptAction->cArgDescs;
- paScriptArgs = (PVDSCRIPTARG)RTMemAllocZ(cScriptArgsMax * sizeof(VDSCRIPTARG));
- }
-
- if (paScriptArgs)
- {
- unsigned cScriptArgs;
-
- rc = tstVDIoScriptArgumentListParse(psz, pVDScriptAction, paScriptArgs, &cScriptArgs);
- if (RT_SUCCESS(rc))
- {
- /* Execute the handler. */
- rc = pVDScriptAction->pfnHandler(pGlob, paScriptArgs, cScriptArgs);
- }
- }
- else
- {
- RTPrintf("Out of memory while allocating argument array for script action %s\n", pcszAction);
- rc = VERR_NO_MEMORY;
- }
- }
- else
- {
- RTPrintf("Script action %s is not known\n", pcszAction);
- rc = VERR_NOT_FOUND;
- }
- }
- /* else empty line, just continue */
- }
- } while(RT_SUCCESS(rc));
-
- if (rc == VERR_EOF)
- {
- RTPrintf("Successfully executed I/O script\n");
- rc = VINF_SUCCESS;
- }
- return rc;
-}
-
-/**
- * Executes the given I/O script.
+ * Executes the given I/O script using the new scripting engine.
*
* @returns nothing.
*
@@ -3625,17 +2656,28 @@ static int tstVDIoScriptExecute(PRTSTREAM pStrm, PVDTESTGLOB pGlob)
static void tstVDIoScriptRun(const char *pcszFilename)
{
int rc = VINF_SUCCESS;
- PRTSTREAM pScriptStrm; /**< Stream of the script file. */
VDTESTGLOB GlobTest; /**< Global test data. */
+ void *pvFile = NULL;
+ size_t cbFile = 0;
memset(&GlobTest, 0, sizeof(VDTESTGLOB));
RTListInit(&GlobTest.ListFiles);
RTListInit(&GlobTest.ListDisks);
RTListInit(&GlobTest.ListPatterns);
+ GlobTest.pszIoBackend = RTStrDup("memory");
+ if (!GlobTest.pszIoBackend)
+ {
+ RTPrintf("Out of memory allocating I/O backend string\n");
+ return;
+ }
- rc = RTStrmOpen(pcszFilename, "r", &pScriptStrm);
+ rc = RTFileReadAll(pcszFilename, &pvFile, &cbFile);
if (RT_SUCCESS(rc))
{
+ char *pszScript = RTStrDupN((char *)pvFile, cbFile);
+ RTFileReadAllFree(pvFile, cbFile);
+
+ AssertPtr(pszScript);
/* Init global test data. */
GlobTest.VDIfError.pfnError = tstVDError;
GlobTest.VDIfError.pfnMessage = tstVDMessage;
@@ -3664,24 +2706,34 @@ static void tstVDIoScriptRun(const char *pcszFilename)
AssertRC(rc);
/* Init I/O backend. */
- rc = VDIoBackendMemCreate(&GlobTest.pIoBackend);
+ rc = VDIoBackendCreate(&GlobTest.pIoBackend);
if (RT_SUCCESS(rc))
{
- /* Execute the script. */
- rc = tstVDIoScriptExecute(pScriptStrm, &GlobTest);
- if (RT_FAILURE(rc))
+ VDSCRIPTCTX hScriptCtx = NULL;
+ rc = VDScriptCtxCreate(&hScriptCtx);
+ if (RT_SUCCESS(rc))
{
- RTPrintf("Executing the script stream failed rc=%Rrc\n", rc);
+ rc = VDScriptCtxCallbacksRegister(hScriptCtx, g_aScriptActions, g_cScriptActions, &GlobTest);
+ AssertRC(rc);
+
+ rc = VDScriptCtxLoadScript(hScriptCtx, pszScript);
+ if (RT_FAILURE(rc))
+ {
+ RTPrintf("Loading the script failed rc=%Rrc\n", rc);
+ }
+ else
+ rc = VDScriptCtxCallFn(hScriptCtx, "main", NULL, 0);
+ VDScriptCtxDestroy(hScriptCtx);
}
- VDIoBackendMemDestroy(GlobTest.pIoBackend);
+ VDIoBackendDestroy(GlobTest.pIoBackend);
}
else
RTPrintf("Creating the I/O backend failed rc=%Rrc\n");
-
- RTStrmClose(pScriptStrm);
}
else
RTPrintf("Opening script failed rc=%Rrc\n", rc);
+
+ RTStrFree(GlobTest.pszIoBackend);
}
/**
diff --git a/src/VBox/Storage/testcase/tstVDIo.vd b/src/VBox/Storage/testcase/tstVDIo.vd
index b808ffa3..0074be68 100644
--- a/src/VBox/Storage/testcase/tstVDIo.vd
+++ b/src/VBox/Storage/testcase/tstVDIo.vd
@@ -1,108 +1,53 @@
-# $Id: tstVDIo.vd $
-#
-# Storage: Simple I/O testing for most backends.
-#
-
-#
-# Copyright (C) 2011 Oracle Corporation
-#
-# This file is part of VirtualBox Open Source Edition (OSE), as
-# available from http://www.virtualbox.org. This file is free software;
-# you can redistribute it and/or modify it under the terms of the GNU
-# General Public License (GPL) as published by the Free Software
-# Foundation, in version 2 as it comes in the "COPYING" file of the
-# VirtualBox OSE distribution. VirtualBox OSE is distributed in the
-# hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
-#
-
-# Init I/O RNG for generating random data for writes
-iorngcreate size=10M mode=manual seed=1234567890
-
-# VMDK disk
-print msg=Testing_VMDK
-createdisk name=test verify=yes
-create disk=test mode=base name=tstShared.vmdk type=dynamic backend=VMDK size=200M
-io disk=test async=yes max-reqs=32 mode=seq blocksize=64k off=0-200M size=200M writes=100
-io disk=test async=yes max-reqs=32 mode=seq blocksize=64k off=0-200M size=200M writes=0
-create disk=test mode=diff name=tstShared2.vmdk type=dynamic backend=VMDK size=200M
-io disk=test async=yes max-reqs=32 mode=rnd blocksize=64k off=0-200M size=200M writes=50
-create disk=test mode=diff name=tstShared3.vmdk type=dynamic backend=VMDK size=200M
-io disk=test async=yes max-reqs=32 mode=rnd blocksize=64k off=0-200M size=200M writes=50
-close disk=test mode=single delete=yes
-close disk=test mode=single delete=yes
-close disk=test mode=single delete=yes
-destroydisk name=test
-
-# VDI disk
-print msg=Testing_VDI
-createdisk name=test verify=yes
-create disk=test mode=base name=tstShared.vdi type=dynamic backend=VDI size=200M
-io disk=test async=yes max-reqs=32 mode=seq blocksize=64k off=0-200M size=200M writes=100
-io disk=test async=yes max-reqs=32 mode=seq blocksize=64k off=0-200M size=200M writes=0
-create disk=test mode=diff name=tstShared2.vdi type=dynamic backend=VDI size=200M
-io disk=test async=yes max-reqs=32 mode=rnd blocksize=64k off=0-200M size=200M writes=50
-create disk=test mode=diff name=tstShared3.vdi type=dynamic backend=VDI size=200M
-io disk=test async=yes max-reqs=32 mode=rnd blocksize=64k off=0-200M size=200M writes=50
-close disk=test mode=single delete=yes
-close disk=test mode=single delete=yes
-close disk=test mode=single delete=yes
-destroydisk name=test
-
-# VHD disk
-print msg=Testing_VHD
-createdisk name=test verify=yes
-create disk=test mode=base name=tstShared.vhd type=dynamic backend=VHD size=200M
-io disk=test async=yes max-reqs=32 mode=seq blocksize=64k off=0-200M size=200M writes=100
-io disk=test async=yes max-reqs=32 mode=seq blocksize=64k off=0-200M size=200M writes=0
-create disk=test mode=diff name=tstShared2.vhd type=dynamic backend=VHD size=200M
-io disk=test async=yes max-reqs=32 mode=rnd blocksize=64k off=0-200M size=200M writes=50
-create disk=test mode=diff name=tstShared3.vhd type=dynamic backend=VHD size=200M
-io disk=test async=yes max-reqs=32 mode=rnd blocksize=64k off=0-200M size=200M writes=50
-close disk=test mode=single delete=yes
-close disk=test mode=single delete=yes
-close disk=test mode=single delete=yes
-destroydisk name=test
-
-# Parallels disk
-print msg=Testing_Parallels
-createdisk name=test verify=yes
-create disk=test mode=base name=tstShared.hdd type=dynamic backend=Parallels size=200M
-io disk=test async=yes max-reqs=32 mode=seq blocksize=64k off=0-200M size=200M writes=100
-io disk=test async=yes max-reqs=32 mode=seq blocksize=64k off=0-200M size=200M writes=0
-create disk=test mode=diff name=tstShared2.hdd type=dynamic backend=Parallels size=200M
-io disk=test async=yes max-reqs=32 mode=rnd blocksize=64k off=0-200M size=200M writes=50
-create disk=test mode=diff name=tstShared3.hdd type=dynamic backend=Parallels size=200M
-io disk=test async=yes max-reqs=32 mode=rnd blocksize=64k off=0-200M size=200M writes=50
-close disk=test mode=single delete=yes
-close disk=test mode=single delete=yes
-close disk=test mode=single delete=yes
-destroydisk name=test
-
-# QED disk
-print msg=Testing_QED
-createdisk name=test verify=yes
-create disk=test mode=base name=tstShared.qed type=dynamic backend=QED size=200M
-io disk=test async=yes max-reqs=32 mode=seq blocksize=64k off=0-200M size=200M writes=100
-io disk=test async=yes max-reqs=32 mode=seq blocksize=64k off=0-200M size=200M writes=0
-io disk=test async=yes max-reqs=32 mode=rnd blocksize=64k off=0-200M size=200M writes=50
-io disk=test async=yes max-reqs=32 mode=rnd blocksize=64k off=0-200M size=200M writes=0
-close disk=test mode=single delete=no
-open disk=test name=tstShared.qed backend=QED async=yes
-io disk=test async=yes max-reqs=32 mode=rnd blocksize=64k off=0-200M size=200M writes=0
-destroydisk name=test
-
-# QCOW disk
-print msg=Testing_QCOW
-createdisk name=test verify=yes
-create disk=test mode=base name=tstShared.qed type=dynamic backend=QCOW size=200M
-io disk=test async=yes max-reqs=32 mode=seq blocksize=64k off=0-200M size=200M writes=100
-io disk=test async=yes max-reqs=32 mode=seq blocksize=64k off=0-200M size=200M writes=0
-io disk=test async=yes max-reqs=32 mode=rnd blocksize=64k off=0-200M size=200M writes=50
-io disk=test async=yes max-reqs=32 mode=rnd blocksize=64k off=0-200M size=200M writes=0
-close disk=test mode=single delete=no
-open disk=test name=tstShared.qed backend=QCOW async=yes
-io disk=test async=yes max-reqs=32 mode=rnd blocksize=64k off=0-200M size=200M writes=0
-destroydisk name=test
-
-iorngdestroy
+/* $Id: tstVDIo.vd $ */
+/**
+ * Storage: Simple I/O testing for most backends.
+ */
+
+/*
+ * Copyright (C) 2011-2013 Oracle Corporation
+ *
+ * This file is part of VirtualBox Open Source Edition (OSE), as
+ * available from http://www.virtualbox.org. This file is free software;
+ * you can redistribute it and/or modify it under the terms of the GNU
+ * General Public License (GPL) as published by the Free Software
+ * Foundation, in version 2 as it comes in the "COPYING" file of the
+ * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
+ * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
+ */
+
+void tstIo(string strMessage, string strBackend)
+{
+ print(strMessage);
+ createdisk("test", true /* fVerify */);
+ create("test", "base", "tst.disk", "dynamic", strBackend, 200M, false /* fIgnoreFlush */);
+ io("test", true, 32, "seq", 64K, 0, 200M, 200M, 100, "none");
+ io("test", false, 1, "seq", 64K, 0, 200M, 200M, 100, "none");
+ io("test", true, 32, "seq", 64K, 0, 200M, 200M, 0, "none");
+ io("test", false, 1, "seq", 64K, 0, 200M, 200M, 0, "none");
+ create("test", "diff", "tst2.disk", "dynamic", strBackend, 200M, false /* fIgnoreFlush */);
+ io("test", true, 32, "rnd", 64K, 0, 200M, 200M, 50, "none");
+ io("test", false, 1, "rnd", 64K, 0, 200M, 200M, 50, "none");
+ create("test", "diff", "tst3.disk", "dynamic", strBackend, 200M, false /* fIgnoreFlush */);
+ io("test", true, 32, "rnd", 64K, 0, 200M, 200M, 50, "none");
+ io("test", false, 1, "rnd", 64K, 0, 200M, 200M, 50, "none");
+ close("test", "single", true /* fDelete */);
+ close("test", "single", true /* fDelete */);
+ close("test", "single", true /* fDelete */);
+ destroydisk("test");
+}
+
+void main()
+{
+ /* Init I/O RNG for generating random data for writes */
+ iorngcreate(10M, "manual", 1234567890);
+
+ tstIo("Testing VDI", "VDI");
+ tstIo("Testing VMDK", "VMDK");
+ tstIo("Testing VHD", "VHD");
+ tstIo("Testing Parallels", "Parallels");
+ tstIo("Testing QED", "QED");
+ tstIo("Testing QCOW", "QCOW");
+
+ iorngdestroy();
+}
diff --git a/src/VBox/Storage/testcase/tstVDResize.vd b/src/VBox/Storage/testcase/tstVDResize.vd
new file mode 100644
index 00000000..0a3dcf40
--- /dev/null
+++ b/src/VBox/Storage/testcase/tstVDResize.vd
@@ -0,0 +1,32 @@
+/* $Id: tstVDResize.vd $ */
+/**
+ * Storage: Resize testing for VDI.
+ */
+
+/*
+ * Copyright (C) 2013 Oracle Corporation
+ *
+ * This file is part of VirtualBox Open Source Edition (OSE), as
+ * available from http://www.virtualbox.org. This file is free software;
+ * you can redistribute it and/or modify it under the terms of the GNU
+ * General Public License (GPL) as published by the Free Software
+ * Foundation, in version 2 as it comes in the "COPYING" file of the
+ * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
+ * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
+ */
+
+void main()
+{
+ /* Init I/O RNG for generating random data for writes. */
+ iorngcreate(10M, "manual", 1234567890);
+
+ print("Testing VDI");
+ createdisk("test", true);
+ create("test", "base", "tst.vdi", "dynamic", "VDI", 1T, false);
+ io("test", false, 1, "seq", 64K, 255G, 257G, 2G, 100, "none");
+ resize("test", 1331200M);
+ io("test", false, 1, "seq", 64K, 255G, 257G, 2G, 0, "none");
+ destroydisk("test");
+
+ iorngdestroy();
+}
diff --git a/src/VBox/Storage/testcase/tstVDShareable.cpp b/src/VBox/Storage/testcase/tstVDShareable.cpp
index 98957fb7..834f6a96 100644
--- a/src/VBox/Storage/testcase/tstVDShareable.cpp
+++ b/src/VBox/Storage/testcase/tstVDShareable.cpp
@@ -4,7 +4,7 @@
*/
/*
- * Copyright (C) 2010 Oracle Corporation
+ * Copyright (C) 2010-2011 Oracle Corporation
*
* This file is part of VirtualBox Open Source Edition (OSE), as
* available from http://www.virtualbox.org. This file is free software;
diff --git a/src/VBox/Storage/testcase/tstVDShareable.vd b/src/VBox/Storage/testcase/tstVDShareable.vd
index b2f5cd0e..7cbe4e2b 100644
--- a/src/VBox/Storage/testcase/tstVDShareable.vd
+++ b/src/VBox/Storage/testcase/tstVDShareable.vd
@@ -1,53 +1,55 @@
-# $Id: tstVDShareable.vd $
-#
-# Storage: Testcase for shareable disks.
-#
-
-#
-# Copyright (C) 2011 Oracle Corporation
-#
-# This file is part of VirtualBox Open Source Edition (OSE), as
-# available from http://www.virtualbox.org. This file is free software;
-# you can redistribute it and/or modify it under the terms of the GNU
-# General Public License (GPL) as published by the Free Software
-# Foundation, in version 2 as it comes in the "COPYING" file of the
-# VirtualBox OSE distribution. VirtualBox OSE is distributed in the
-# hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
-#
-
-# Init I/O RNG for generating random data for writes
-iorngcreate size=10M mode=manual seed=1234567890
-
-# Create disk containers.
-createdisk name=shared1
-createdisk name=shared2
-
-# Create the disk and close it.
-create disk=shared1 mode=base name=tstShared.vdi type=fixed backend=VDI size=20M
-close disk=shared1 mode=all delete=no
-
-# Open the disk with sharing enabled.
-open disk=shared1 name=tstShared.vdi backend=VDI shareable=yes
-open disk=shared2 name=tstShared.vdi backend=VDI shareable=yes
-
-# Write to one disk and verify that the other disk can see the content.
-io disk=shared1 async=yes max-reqs=32 mode=seq blocksize=64k off=0-20M size=20M writes=100
-comparedisks disk1=shared1 disk2=shared2
-
-# Write to the second disk and verify that the first can see the content.
-io disk=shared2 async=yes max-reqs=64 mode=seq blocksize=8k off=0-20M size=20M writes=50
-comparedisks disk1=shared1 disk2=shared2
-
-# Close but don't delete yet.
-close disk=shared1 mode=all delete=no
-close disk=shared2 mode=all delete=no
-
-# Open and delete
-open disk=shared1 name=tstShared.vdi backend=VDI shareable=no
-close disk=shared1 mode=single delete=yes
-
-# Cleanup
-destroydisk name=shared1
-destroydisk name=shared2
-iorngdestroy
-
+/* $Id: tstVDShareable.vd $ */
+/**
+ * Storage: Testcase for shareable disks.
+ */
+
+/*
+ * Copyright (C) 2011 Oracle Corporation
+ *
+ * This file is part of VirtualBox Open Source Edition (OSE), as
+ * available from http://www.virtualbox.org. This file is free software;
+ * you can redistribute it and/or modify it under the terms of the GNU
+ * General Public License (GPL) as published by the Free Software
+ * Foundation, in version 2 as it comes in the "COPYING" file of the
+ * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
+ * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
+ */
+
+void main()
+{
+ /* Init I/O RNG for generating random data for writes. */
+ iorngcreate(10M, "manual", 1234567890);
+
+ /* Create disk containers. */
+ createdisk("shared1", false);
+ createdisk("shared2", false);
+
+ /* Create the disk and close it. */
+ create("shared1", "base", "tstShared.vdi", "fixed", "VDI", 20M, false);
+ close("shared1", "all", false);
+
+ /* Open the disk with sharing enabled. */
+ open("shared1", "tstShared.vdi", "VDI", true /* fAsync */, true /* fShareable */, false, false, false);
+ open("shared2", "tstShared.vdi", "VDI", true /* fAsync */, true /* fShareable */, false, false, false);
+
+ /* Write to one disk and verify that the other disk can see the content. */
+ io("shared1", true, 32, "seq", 64K, 0, 20M, 20M, 100, "none");
+ comparedisks("shared1", "shared2");
+
+ /* Write to the second disk and verify that the first can see the content. */
+ io("shared2", true, 64, "seq", 8K, 0, 20M, 20M, 50, "none");
+ comparedisks("shared1", "shared2");
+
+ /* Close but don't delete yet. */
+ close("shared1", "all", false);
+ close("shared2", "all", false);
+
+ /* Open and delete. */
+ open("shared1", "tstShared.vdi", "VDI", false /* fAsync */, false /* fShareable */, false, false, false);
+ close("shared1", "single", true);
+
+ /* Cleanup */
+ destroydisk("shared1");
+ destroydisk("shared2");
+ iorngdestroy();
+}
diff --git a/src/VBox/Storage/testcase/tstVDSnap.cpp b/src/VBox/Storage/testcase/tstVDSnap.cpp
index 83313e1f..d32d3b55 100644
--- a/src/VBox/Storage/testcase/tstVDSnap.cpp
+++ b/src/VBox/Storage/testcase/tstVDSnap.cpp
@@ -4,7 +4,7 @@
*/
/*
- * Copyright (C) 2010 Oracle Corporation
+ * Copyright (C) 2010-2011 Oracle Corporation
*
* This file is part of VirtualBox Open Source Edition (OSE), as
* available from http://www.virtualbox.org. This file is free software;
@@ -229,6 +229,21 @@ static int tstVDOpenCreateWriteMerge(PVDSNAPTEST pTest)
unsigned cDiffs = 0;
unsigned idDiff = 0; /* Diff ID counter for the filename */
+ /* Delete all images from a previous run. */
+ RTFileDelete(pTest->pcszBaseImage);
+ for (unsigned i = 0; i < pTest->cIterations; i++)
+ {
+ char *pszDiffFilename = NULL;
+
+ rc = RTStrAPrintf(&pszDiffFilename, "tstVDSnapDiff%u.%s", i, pTest->pcszDiffSuff);
+ if (RT_SUCCESS(rc))
+ {
+ if (RTFileExists(pszDiffFilename))
+ RTFileDelete(pszDiffFilename);
+ RTStrFree(pszDiffFilename);
+ }
+ }
+
/* Create the virtual disk test data */
pbTestPattern = (uint8_t *)RTMemAlloc(pTest->cbTestPattern);
@@ -260,10 +275,22 @@ static int tstVDOpenCreateWriteMerge(PVDSNAPTEST pTest)
if (pbTestPattern) \
RTMemFree(pbTestPattern); \
VDDestroy(pVD); \
+ g_cErrors++; \
return rc; \
} \
} while (0)
+#define CHECK_BREAK(str) \
+ do \
+ { \
+ RTPrintf("%s rc=%Rrc\n", str, rc); \
+ if (RT_FAILURE(rc)) \
+ { \
+ g_cErrors++; \
+ break; \
+ } \
+ } while (0)
+
/* Create error interface. */
/* Create error interface. */
VDIfError.pfnError = tstVDError;
@@ -292,7 +319,7 @@ static int tstVDOpenCreateWriteMerge(PVDSNAPTEST pTest)
{
/* Write */
rc = tstVDSnapWrite(pVD, paDiskSeg, cDiskSegments, cbDisk, fInit);
- CHECK("tstVDSnapWrite()");
+ CHECK_BREAK("tstVDSnapWrite()");
fInit = false;
@@ -313,7 +340,7 @@ static int tstVDOpenCreateWriteMerge(PVDSNAPTEST pTest)
rc = VDCreateDiff(pVD, pTest->pcszBackend, pszDiffFilename,
VD_IMAGE_FLAGS_NONE, "Test diff image", NULL, NULL,
VD_OPEN_FLAGS_NORMAL, NULL, NULL);
- CHECK("VDCreateDiff()");
+ CHECK_BREAK("VDCreateDiff()");
RTStrFree(pszDiffFilename);
VDDumpImages(pVD);
@@ -333,7 +360,7 @@ static int tstVDOpenCreateWriteMerge(PVDSNAPTEST pTest)
rc = VDMerge(pVD, uStartMerge, uEndMerge, NULL);
else
rc = VDMerge(pVD, uEndMerge, uStartMerge, NULL);
- CHECK("VDMerge()");
+ CHECK_BREAK("VDMerge()");
cDiffs -= uEndMerge - uStartMerge;
@@ -351,7 +378,7 @@ static int tstVDOpenCreateWriteMerge(PVDSNAPTEST pTest)
/* Now compare the result with our test pattern */
rc = tstVDSnapReadVerify(pVD, paDiskSeg, cDiskSegments, cbDisk);
- CHECK("tstVDSnapReadVerify()");
+ CHECK_BREAK("tstVDSnapReadVerify()");
}
cIteration++;
}
@@ -372,7 +399,7 @@ static int tstVDOpenCreateWriteMerge(PVDSNAPTEST pTest)
RTStrFree(pszDiffFilename);
}
#undef CHECK
- return 0;
+ return rc;
}
int main(int argc, char *argv[])
diff --git a/src/VBox/Storage/testcase/vbox-img.cpp b/src/VBox/Storage/testcase/vbox-img.cpp
index 8fb9e0bf..7d3f9a48 100644
--- a/src/VBox/Storage/testcase/vbox-img.cpp
+++ b/src/VBox/Storage/testcase/vbox-img.cpp
@@ -4,7 +4,7 @@
*/
/*
- * Copyright (C) 2010-2011 Oracle Corporation
+ * Copyright (C) 2010-2012 Oracle Corporation
*
* This file is part of VirtualBox Open Source Edition (OSE), as
* available from http://www.virtualbox.org. This file is free software;
@@ -64,10 +64,13 @@ static void printUsage(PRTSTREAM pStrm)
" --size <size in bytes>\n"
" [--format VDI|VMDK|VHD] (default: VDI)\n"
" [--variant Standard,Fixed,Split2G,Stream,ESX]\n"
+ " [--dataalignment <alignment in bytes>]\n"
"\n"
" repair --filename <filename>\n"
" [--dry-run]\n"
- " [--format VDI|VMDK|VHD] (default: autodetect)\n",
+ " [--format VDI|VMDK|VHD] (default: autodetect)\n"
+ "\n"
+ " clearcomment --filename <filename>\n",
g_pszProgName);
}
@@ -260,8 +263,8 @@ int handleSetUUID(HandlerArg *a)
if (RT_FAILURE(rc))
return errorRuntime("Cannot create the virtual disk container: %Rrc\n", rc);
-
- rc = VDOpen(pVD, pszFormat, pszFilename, VD_OPEN_FLAGS_NORMAL, NULL);
+ /* Open in info mode to be able to open diff images without their parent. */
+ rc = VDOpen(pVD, pszFormat, pszFilename, VD_OPEN_FLAGS_INFO, NULL);
if (RT_FAILURE(rc))
return errorRuntime("Cannot open the virtual disk image \"%s\": %Rrc\n",
pszFilename, rc);
@@ -430,21 +433,22 @@ static int convInRead(void *pvUser, void *pStorage, uint64_t uOffset,
if (pFS->offBuffer == UINT64_MAX)
{
/* Repeat reading until buffer is full or EOF. */
- size_t cbSumRead = 0, cbRead;
- uint8_t *pTmp = (uint8_t *)&pFS->abBuffer[0];
+ size_t cbRead;
+ size_t cbSumRead = 0;
+ uint8_t *pbTmp = (uint8_t *)&pFS->abBuffer[0];
size_t cbTmp = sizeof(pFS->abBuffer);
do
{
- rc = RTFileRead(pFS->file, pTmp, cbTmp, &cbRead);
+ rc = RTFileRead(pFS->file, pbTmp, cbTmp, &cbRead);
if (RT_FAILURE(rc))
return rc;
- pTmp += cbRead;
+ pbTmp += cbRead;
cbTmp -= cbRead;
cbSumRead += cbRead;
} while (cbTmp && cbRead);
pFS->offBuffer = 0;
- pFS->cbBuffer = cbSumRead;
+ pFS->cbBuffer = (uint32_t)cbSumRead;
if (!cbSumRead && !pcbRead) /* Caller can't handle partial reads. */
return VERR_EOF;
}
@@ -464,25 +468,26 @@ static int convInRead(void *pvUser, void *pStorage, uint64_t uOffset,
}
/* Repeat reading until buffer is full or EOF. */
- size_t cbSumRead = 0, cbRead;
- uint8_t *pTmp = (uint8_t *)&pFS->abBuffer[0];
+ size_t cbRead;
+ size_t cbSumRead = 0;
+ uint8_t *pbTmp = (uint8_t *)&pFS->abBuffer[0];
size_t cbTmp = sizeof(pFS->abBuffer);
do
{
- rc = RTFileRead(pFS->file, pTmp, cbTmp, &cbRead);
+ rc = RTFileRead(pFS->file, pbTmp, cbTmp, &cbRead);
if (RT_FAILURE(rc))
return rc;
- pTmp += cbRead;
+ pbTmp += cbRead;
cbTmp -= cbRead;
cbSumRead += cbRead;
} while (cbTmp && cbRead);
pFS->offBuffer += pFS->cbBuffer;
- pFS->cbBuffer = cbSumRead;
+ pFS->cbBuffer = (uint32_t)cbSumRead;
}
- uint32_t cbThisRead = RT_MIN(cbBuffer,
- pFS->cbBuffer - uOffset % sizeof(pFS->abBuffer));
+ uint32_t cbThisRead = (uint32_t)RT_MIN(cbBuffer,
+ pFS->cbBuffer - uOffset % sizeof(pFS->abBuffer));
memcpy(pvBuffer, &pFS->abBuffer[uOffset % sizeof(pFS->abBuffer)],
cbThisRead);
uOffset += cbThisRead;
@@ -654,8 +659,8 @@ static int convOutWrite(void *pvUser, void *pStorage, uint64_t uOffset,
pFS->cbBuffer = 0;
}
- uint32_t cbThisWrite = RT_MIN(cbBuffer,
- sizeof(pFS->abBuffer) - uOffset % sizeof(pFS->abBuffer));
+ uint32_t cbThisWrite = (uint32_t)RT_MIN(cbBuffer,
+ sizeof(pFS->abBuffer) - uOffset % sizeof(pFS->abBuffer));
memcpy(&pFS->abBuffer[uOffset % sizeof(pFS->abBuffer)], pvBuffer,
cbThisWrite);
uOffset += cbThisWrite;
@@ -954,7 +959,7 @@ int handleInfo(HandlerArg *a)
return errorRuntime("Error while creating the virtual disk container: %Rrc\n", rc);
/* Open the image */
- rc = VDOpen(pDisk, pszFormat, pszFilename, VD_OPEN_FLAGS_INFO, NULL);
+ rc = VDOpen(pDisk, pszFormat, pszFilename, VD_OPEN_FLAGS_INFO | VD_OPEN_FLAGS_READONLY, NULL);
if (RT_FAILURE(rc))
return errorRuntime("Error while opening the image: %Rrc\n", rc);
@@ -1296,6 +1301,42 @@ int handleCreateCache(HandlerArg *a)
return rc;
}
+static DECLCALLBACK(bool) vdIfCfgCreateBaseAreKeysValid(void *pvUser, const char *pszzValid)
+{
+ return VINF_SUCCESS; /** @todo: Implement. */
+}
+
+static DECLCALLBACK(int) vdIfCfgCreateBaseQuerySize(void *pvUser, const char *pszName, size_t *pcbValue)
+{
+ AssertReturn(VALID_PTR(pcbValue), VERR_INVALID_POINTER);
+
+ AssertPtrReturn(pvUser, VERR_GENERAL_FAILURE);
+
+ if (RTStrCmp(pszName, "DataAlignment"))
+ return VERR_CFGM_VALUE_NOT_FOUND;
+
+ *pcbValue = strlen((const char *)pvUser) + 1 /* include terminator */;
+
+ return VINF_SUCCESS;
+}
+
+static DECLCALLBACK(int) vdIfCfgCreateBaseQuery(void *pvUser, const char *pszName, char *pszValue, size_t cchValue)
+{
+ AssertReturn(VALID_PTR(pszValue), VERR_INVALID_POINTER);
+
+ AssertPtrReturn(pvUser, VERR_GENERAL_FAILURE);
+
+ if (RTStrCmp(pszName, "DataAlignment"))
+ return VERR_CFGM_VALUE_NOT_FOUND;
+
+ if (strlen((const char *)pvUser) >= cchValue)
+ return VERR_CFGM_NOT_ENOUGH_SPACE;
+
+ memcpy(pszValue, pvUser, strlen((const char *)pvUser) + 1);
+
+ return VINF_SUCCESS;
+
+}
int handleCreateBase(HandlerArg *a)
{
@@ -1306,7 +1347,10 @@ int handleCreateBase(HandlerArg *a)
const char *pszVariant = NULL;
unsigned uImageFlags = VD_IMAGE_FLAGS_NONE;
uint64_t cbSize = 0;
+ const char *pszDataAlignment = NULL;
VDGEOMETRY LCHSGeometry, PCHSGeometry;
+ PVDINTERFACE pVDIfsOperation = NULL;
+ VDINTERFACECONFIG vdIfCfg;
memset(&LCHSGeometry, 0, sizeof(VDGEOMETRY));
memset(&PCHSGeometry, 0, sizeof(VDGEOMETRY));
@@ -1314,10 +1358,11 @@ int handleCreateBase(HandlerArg *a)
/* Parse the command line. */
static const RTGETOPTDEF s_aOptions[] =
{
- { "--filename", 'f', RTGETOPT_REQ_STRING },
- { "--size", 's', RTGETOPT_REQ_UINT64 },
- { "--format", 'b', RTGETOPT_REQ_STRING },
- { "--variant", 'v', RTGETOPT_REQ_STRING }
+ { "--filename", 'f', RTGETOPT_REQ_STRING },
+ { "--size", 's', RTGETOPT_REQ_UINT64 },
+ { "--format", 'b', RTGETOPT_REQ_STRING },
+ { "--variant", 'v', RTGETOPT_REQ_STRING },
+ { "--dataalignment", 'a', RTGETOPT_REQ_STRING }
};
int ch;
RTGETOPTUNION ValueUnion;
@@ -1343,6 +1388,10 @@ int handleCreateBase(HandlerArg *a)
pszVariant = ValueUnion.psz;
break;
+ case 'a': // --dataalignment
+ pszDataAlignment = ValueUnion.psz;
+ break;
+
default:
ch = RTGetOptPrintError(ch, &ValueUnion);
printUsage(g_pStdErr);
@@ -1364,6 +1413,16 @@ int handleCreateBase(HandlerArg *a)
return errorSyntax("Invalid variant %s given\n", pszVariant);
}
+ /* Setup the config interface if required. */
+ if (pszDataAlignment)
+ {
+ vdIfCfg.pfnAreKeysValid = vdIfCfgCreateBaseAreKeysValid;
+ vdIfCfg.pfnQuerySize = vdIfCfgCreateBaseQuerySize;
+ vdIfCfg.pfnQuery = vdIfCfgCreateBaseQuery;
+ VDInterfaceAdd(&vdIfCfg.Core, "Config", VDINTERFACETYPE_CONFIG, (void *)pszDataAlignment,
+ sizeof(vdIfCfg), &pVDIfsOperation);
+ }
+
/* just try it */
rc = VDCreate(pVDIfs, VDTYPE_HDD, &pDisk);
if (RT_FAILURE(rc))
@@ -1371,7 +1430,7 @@ int handleCreateBase(HandlerArg *a)
rc = VDCreateBase(pDisk, pszBackend, pszFilename, cbSize, uImageFlags,
NULL, &PCHSGeometry, &LCHSGeometry, NULL, VD_OPEN_FLAGS_NORMAL,
- NULL, NULL);
+ NULL, pVDIfsOperation);
if (RT_FAILURE(rc))
return errorRuntime("Error while creating the virtual disk: %Rrc\n", rc);
@@ -1448,6 +1507,64 @@ int handleRepair(HandlerArg *a)
}
+int handleClearComment(HandlerArg *a)
+{
+ int rc = VINF_SUCCESS;
+ PVBOXHDD pDisk = NULL;
+ const char *pszFilename = NULL;
+ bool fDryRun = false;
+
+ /* Parse the command line. */
+ static const RTGETOPTDEF s_aOptions[] =
+ {
+ { "--filename", 'f', RTGETOPT_REQ_STRING }
+ };
+ int ch;
+ RTGETOPTUNION ValueUnion;
+ RTGETOPTSTATE GetState;
+ RTGetOptInit(&GetState, a->argc, a->argv, s_aOptions, RT_ELEMENTS(s_aOptions), 0, 0 /* fFlags */);
+ while ((ch = RTGetOpt(&GetState, &ValueUnion)))
+ {
+ switch (ch)
+ {
+ case 'f': // --filename
+ pszFilename = ValueUnion.psz;
+ break;
+
+ default:
+ ch = RTGetOptPrintError(ch, &ValueUnion);
+ printUsage(g_pStdErr);
+ return ch;
+ }
+ }
+
+ /* Check for mandatory parameters. */
+ if (!pszFilename)
+ return errorSyntax("Mandatory --filename option missing\n");
+
+ /* just try it */
+ char *pszFormat = NULL;
+ VDTYPE enmType = VDTYPE_INVALID;
+ rc = VDGetFormat(NULL, NULL, pszFilename, &pszFormat, &enmType);
+ if (RT_FAILURE(rc))
+ return errorSyntax("Format autodetect failed: %Rrc\n", rc);
+
+ rc = VDCreate(pVDIfs, enmType, &pDisk);
+ if (RT_FAILURE(rc))
+ return errorRuntime("Error while creating the virtual disk container: %Rrc\n", rc);
+
+ /* Open the image */
+ rc = VDOpen(pDisk, pszFormat, pszFilename, VD_OPEN_FLAGS_INFO, NULL);
+ if (RT_FAILURE(rc))
+ return errorRuntime("Error while opening the image: %Rrc\n", rc);
+
+ VDSetComment(pDisk, 0, NULL);
+
+ VDDestroy(pDisk);
+ return rc;
+}
+
+
int main(int argc, char *argv[])
{
int exitcode = 0;
@@ -1530,13 +1647,14 @@ int main(int argc, char *argv[])
int (*handler)(HandlerArg *a);
} s_commandHandlers[] =
{
- { "setuuid", handleSetUUID },
- { "convert", handleConvert },
- { "info", handleInfo },
- { "compact", handleCompact },
- { "createcache", handleCreateCache },
- { "createbase", handleCreateBase },
- { "repair", handleRepair },
+ { "setuuid", handleSetUUID },
+ { "convert", handleConvert },
+ { "info", handleInfo },
+ { "compact", handleCompact },
+ { "createcache", handleCreateCache },
+ { "createbase", handleCreateBase },
+ { "repair", handleRepair },
+ { "clearcomment", handleClearComment },
{ NULL, NULL }
};