2277 lines
64 KiB
C++
2277 lines
64 KiB
C++
|
#include <string.h>
|
||
|
#include <ctype.h>
|
||
|
#include <new>
|
||
|
#include <mk4.h>
|
||
|
|
||
|
#include "jim.h"
|
||
|
#include "jimautoconf.h"
|
||
|
#include "jim-subcmd.h"
|
||
|
|
||
|
extern "C" { /* The whole file is essentially C */
|
||
|
|
||
|
#define MK_PROPERTY_BINARY 'B'
|
||
|
#define MK_PROPERTY_INT 'I'
|
||
|
#define MK_PROPERTY_LONG 'L'
|
||
|
#define MK_PROPERTY_FLOAT 'F'
|
||
|
#define MK_PROPERTY_DOUBLE 'D'
|
||
|
#define MK_PROPERTY_STRING 'S'
|
||
|
#define MK_PROPERTY_VIEW 'V'
|
||
|
|
||
|
#define MK_MODE_ORIGINAL -1
|
||
|
#define MK_MODE_READONLY 0
|
||
|
#define MK_MODE_READWRITE 1
|
||
|
#define MK_MODE_EXTEND 2
|
||
|
|
||
|
#define MK_CMD_LEN 32
|
||
|
#define JIM_CURSOR_SPACE (35+JIM_REFERENCE_TAGLEN + 1 + 20)
|
||
|
#define JIM_POSITION_SPACE 32
|
||
|
#define MK_VERSION_SPACE 16
|
||
|
#define JIM_MK_DESCR_LEN 64 /* Default, will be reallocated if needed */
|
||
|
|
||
|
#define isnamech(c) ( (c) && !strchr(":,[^]!", (c)) )
|
||
|
|
||
|
#ifndef max
|
||
|
#define max(x, y) ((x) >= (y) ? (x) : (y))
|
||
|
#endif
|
||
|
|
||
|
/* utilities */
|
||
|
static int JimCheckMkName(Jim_Interp *interp, Jim_Obj *name, const char *type);
|
||
|
static const char *JimMkTypeName(char type);
|
||
|
static Jim_Obj *JimFromMkDescription(Jim_Interp *interp, const char *descr, const char **endPtr);
|
||
|
static int JimToMkDescription(Jim_Interp *interp, Jim_Obj *obj, char **descrPtr);
|
||
|
static Jim_Obj *JimGetMkValue(Jim_Interp *interp, c4_Cursor cur, const c4_Property &prop);
|
||
|
static int JimSetMkValue(Jim_Interp *interp, c4_Cursor cur, const c4_Property &prop, Jim_Obj *obj);
|
||
|
|
||
|
static int JimPipelineBoundary(int argc, Jim_Obj *const *argv);
|
||
|
|
||
|
/* property object */
|
||
|
static Jim_Obj *JimNewPropertyObj (Jim_Interp *interp, c4_Property prop);
|
||
|
static int JimGetProperty (Jim_Interp *interp, Jim_Obj *obj,
|
||
|
c4_View view, const char *what, const c4_Property **propPtr);
|
||
|
static int JimGetPropertyTyped (Jim_Interp *interp, Jim_Obj *obj,
|
||
|
char type, const c4_Property **propPtr);
|
||
|
static int JimGetNewProperty (Jim_Interp *interp, Jim_Obj *obj,
|
||
|
c4_View view, char type, const c4_Property **propPtr);
|
||
|
static int JimGetProperties (Jim_Interp *interp, int objc, Jim_Obj *const *objv,
|
||
|
c4_View view, c4_View *propsPtr);
|
||
|
static Jim_Obj *JimViewPropertiesList (Jim_Interp *interp, c4_View view);
|
||
|
|
||
|
/* cursor object */
|
||
|
static int JimGetPosition (Jim_Interp *interp, Jim_Obj *obj, c4_View view, int *indexPtr);
|
||
|
static int JimGetCursor (Jim_Interp *interp, Jim_Obj *obj, c4_Cursor *curPtr);
|
||
|
static int JimGetCursorView (Jim_Interp *interp, Jim_Obj *obj,
|
||
|
Jim_Obj **viewObjPtr);
|
||
|
static int JimCursorPos (Jim_Interp *interp, Jim_Obj *obj, Jim_Obj **posObjPtr);
|
||
|
static int JimIncrCursor (Jim_Interp *interp, Jim_Obj *obj, int offset);
|
||
|
static int JimSeekCursor (Jim_Interp *interp, Jim_Obj *obj, Jim_Obj *posObj);
|
||
|
|
||
|
/* Also accepts JIM_ERRMSG */
|
||
|
#define JIM_CURSOR_GET (1 << JIM_PRIV_FLAG_SHIFT)
|
||
|
#define JIM_CURSOR_SET (2 << JIM_PRIV_FLAG_SHIFT)
|
||
|
#define JIM_CURSOR_INSERT (4 << JIM_PRIV_FLAG_SHIFT)
|
||
|
|
||
|
static int JimCheckCursor (Jim_Interp *interp, Jim_Obj *curObj, int flags);
|
||
|
|
||
|
/* view handle */
|
||
|
static Jim_Obj *JimNewViewObj (Jim_Interp *interp, c4_View view);
|
||
|
static int JimGetView (Jim_Interp *interp, Jim_Obj *obj, c4_View *viewPtr);
|
||
|
static void JimPinView (Jim_Interp *interp, Jim_Obj *obj);
|
||
|
|
||
|
/* -------------------------------------------------------------------------
|
||
|
* Utilities
|
||
|
* ------------------------------------------------------------------------- */
|
||
|
|
||
|
static int JimCheckMkName(Jim_Interp *interp, Jim_Obj *name, const char *type)
|
||
|
{
|
||
|
const char *s;
|
||
|
int i, len;
|
||
|
|
||
|
s = Jim_GetString(name, &len);
|
||
|
|
||
|
if (len > 0 && s[0] == '-')
|
||
|
goto err;
|
||
|
for (i = 0; i < len; i++) {
|
||
|
if (!isnamech(s[i]))
|
||
|
goto err;
|
||
|
}
|
||
|
|
||
|
return JIM_OK;
|
||
|
|
||
|
err:
|
||
|
Jim_SetResultFormatted(interp, "expected %s name but got \"%#s\"", type ? type : "property", name);
|
||
|
return JIM_ERR;
|
||
|
}
|
||
|
|
||
|
static const char *const jim_mktype_options[] = {
|
||
|
"-integer",
|
||
|
"-long",
|
||
|
"-float",
|
||
|
"-double",
|
||
|
"-string",
|
||
|
"-subview",
|
||
|
/* FIXME "-binary", */
|
||
|
0
|
||
|
};
|
||
|
|
||
|
static const char *const jim_mktype_names[] = {
|
||
|
"integer",
|
||
|
"long",
|
||
|
"float",
|
||
|
"double",
|
||
|
"string",
|
||
|
"subview",
|
||
|
/* FIXME "binary", */
|
||
|
0
|
||
|
};
|
||
|
|
||
|
static const char jim_mktype_types[] = {
|
||
|
MK_PROPERTY_INT,
|
||
|
MK_PROPERTY_LONG,
|
||
|
MK_PROPERTY_FLOAT,
|
||
|
MK_PROPERTY_DOUBLE,
|
||
|
MK_PROPERTY_STRING,
|
||
|
MK_PROPERTY_VIEW,
|
||
|
/* MK_PROPERTY_BINARY, */
|
||
|
};
|
||
|
|
||
|
#define JIM_MKTYPES ((int)(sizeof(jim_mktype_types) / sizeof(jim_mktype_types[0])))
|
||
|
|
||
|
static const char *JimMkTypeName(char type)
|
||
|
{
|
||
|
int i;
|
||
|
|
||
|
for (i = 0; i < JIM_MKTYPES; i++) {
|
||
|
if (type == jim_mktype_types[i])
|
||
|
return jim_mktype_names[i];
|
||
|
}
|
||
|
return "(unknown type)";
|
||
|
}
|
||
|
|
||
|
static Jim_Obj *JimFromMkDescription(Jim_Interp *interp, const char *descr, const char **endPtr)
|
||
|
{
|
||
|
Jim_Obj *result;
|
||
|
const char *delim;
|
||
|
|
||
|
result = Jim_NewListObj(interp, NULL, 0);
|
||
|
for (;;) {
|
||
|
if (*descr == ']') {
|
||
|
descr++;
|
||
|
break;
|
||
|
}
|
||
|
else if (*descr == '\0')
|
||
|
break;
|
||
|
else if (*descr == ',')
|
||
|
descr++;
|
||
|
|
||
|
delim = strpbrk(descr, ",:[]");
|
||
|
/* JimPanic((!delim, "Invalid Metakit description string")); */
|
||
|
|
||
|
Jim_ListAppendElement(interp, result,
|
||
|
Jim_NewStringObj(interp, descr, delim - descr));
|
||
|
|
||
|
if (delim[0] == '[') {
|
||
|
Jim_ListAppendElement(interp, result,
|
||
|
JimFromMkDescription(interp, delim + 1, &descr));
|
||
|
}
|
||
|
else if (delim[0] == ':') {
|
||
|
Jim_ListAppendElement(interp, result,
|
||
|
Jim_NewStringObj(interp, JimMkTypeName(delim[1]), -1));
|
||
|
descr = delim + 2;
|
||
|
}
|
||
|
else {
|
||
|
/* Seems that Metakit never generates descriptions without type
|
||
|
* tags, but let's handle this just to be safe
|
||
|
*/
|
||
|
|
||
|
Jim_ListAppendElement(interp, result,
|
||
|
Jim_NewStringObj(interp, JimMkTypeName(MK_PROPERTY_STRING), -1));
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if (endPtr)
|
||
|
*endPtr = descr;
|
||
|
return result;
|
||
|
}
|
||
|
|
||
|
/* This allocates the buffer once per user call and stores it in a static
|
||
|
* variable. Recursive calls are distinguished by descrPtr == NULL.
|
||
|
*/
|
||
|
static int JimToMkDescription(Jim_Interp *interp, Jim_Obj *descrObj, char **descrPtr)
|
||
|
{
|
||
|
static char *descr, *outPtr;
|
||
|
static int bufSize;
|
||
|
|
||
|
#define ENLARGE(size) do { \
|
||
|
if ((descr - outPtr) + (size) > bufSize) { \
|
||
|
bufSize = max(2*bufSize, (descr - outPtr) + (size)); \
|
||
|
descr = (char *)Jim_Realloc(descr, bufSize); \
|
||
|
} \
|
||
|
} while(0)
|
||
|
|
||
|
int i, count;
|
||
|
Jim_Obj *name, *struc;
|
||
|
|
||
|
const char *rep;
|
||
|
int len;
|
||
|
|
||
|
count = Jim_ListLength(interp, descrObj);
|
||
|
if (count % 2) {
|
||
|
Jim_SetResultString(interp,
|
||
|
"view description must have an even number of elements", -1);
|
||
|
return JIM_ERR;
|
||
|
}
|
||
|
|
||
|
if (descrPtr) {
|
||
|
descr = (char *)Jim_Alloc(bufSize = JIM_MK_DESCR_LEN);
|
||
|
outPtr = descr;
|
||
|
}
|
||
|
|
||
|
for (i = 0; i < count; i += 2) {
|
||
|
Jim_ListIndex(interp, descrObj, i, &name, 0);
|
||
|
Jim_ListIndex(interp, descrObj, i + 1, &struc, 0);
|
||
|
|
||
|
if (JimCheckMkName(interp, name, NULL) != JIM_OK)
|
||
|
goto err;
|
||
|
|
||
|
rep = Jim_GetString(name, &len);
|
||
|
ENLARGE(len + 3); /* At least :T, or [], */
|
||
|
memcpy(outPtr, rep, len);
|
||
|
outPtr += len;
|
||
|
|
||
|
if (Jim_ListLength(interp, struc) == 1) {
|
||
|
int idx;
|
||
|
|
||
|
if (Jim_GetEnum(interp, struc, jim_mktype_names, &idx,
|
||
|
"property type", JIM_ERRMSG | JIM_ENUM_ABBREV) != JIM_OK)
|
||
|
goto err;
|
||
|
|
||
|
*outPtr++ = ':';
|
||
|
*outPtr++ = jim_mktype_types[idx];
|
||
|
}
|
||
|
else {
|
||
|
*outPtr++ = '[';
|
||
|
|
||
|
if (JimToMkDescription(interp, struc, NULL) != JIM_OK)
|
||
|
goto err;
|
||
|
|
||
|
ENLARGE(2); /* bracket, comma */
|
||
|
*outPtr++ = ']';
|
||
|
}
|
||
|
|
||
|
*outPtr++ = ',';
|
||
|
}
|
||
|
*(--outPtr) = '\0';
|
||
|
|
||
|
#undef ENLARGE
|
||
|
|
||
|
if (descrPtr) {
|
||
|
*descrPtr = (char *)Jim_Realloc(descr, strlen(descr) + 1);
|
||
|
descr = NULL; /* Safety measure */
|
||
|
}
|
||
|
|
||
|
return JIM_OK;
|
||
|
|
||
|
err:
|
||
|
|
||
|
if (descrPtr)
|
||
|
Jim_Free(descr);
|
||
|
|
||
|
return JIM_ERR;
|
||
|
}
|
||
|
|
||
|
static Jim_Obj *JimGetMkValue(Jim_Interp *interp, c4_Cursor cur, const c4_Property &prop)
|
||
|
{
|
||
|
switch (prop.Type()) {
|
||
|
case MK_PROPERTY_INT:
|
||
|
return Jim_NewIntObj(interp, ((c4_IntProp &)prop).Get(*cur));
|
||
|
case MK_PROPERTY_LONG:
|
||
|
return Jim_NewIntObj(interp, ((c4_LongProp &)prop).Get(*cur));
|
||
|
case MK_PROPERTY_FLOAT:
|
||
|
return Jim_NewDoubleObj(interp, ((c4_FloatProp &)prop).Get(*cur));
|
||
|
case MK_PROPERTY_DOUBLE:
|
||
|
return Jim_NewDoubleObj(interp, ((c4_DoubleProp &)prop).Get(*cur));
|
||
|
case MK_PROPERTY_STRING:
|
||
|
return Jim_NewStringObj(interp, ((c4_StringProp &)prop).Get(*cur), -1);
|
||
|
case MK_PROPERTY_VIEW:
|
||
|
return JimNewViewObj(interp, ((c4_ViewProp &)prop).Get(*cur));
|
||
|
|
||
|
case MK_PROPERTY_BINARY:
|
||
|
/* FIXME */
|
||
|
default:
|
||
|
/* FIXME Something more meaningful here? */
|
||
|
return Jim_NewEmptyStringObj(interp);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
static int JimSetMkValue(Jim_Interp *interp, c4_Cursor cur, const c4_Property &prop, Jim_Obj *obj)
|
||
|
{
|
||
|
switch (prop.Type()) {
|
||
|
case MK_PROPERTY_INT: {
|
||
|
jim_wide value;
|
||
|
|
||
|
if (Jim_GetWide(interp, obj, &value) != JIM_OK)
|
||
|
return JIM_ERR;
|
||
|
|
||
|
((c4_IntProp &)prop).Set(*cur, value);
|
||
|
return JIM_OK;
|
||
|
}
|
||
|
case MK_PROPERTY_LONG: {
|
||
|
jim_wide value;
|
||
|
|
||
|
if (Jim_GetWide(interp, obj, &value) != JIM_OK)
|
||
|
return JIM_ERR;
|
||
|
|
||
|
((c4_LongProp &)prop).Set(*cur, value);
|
||
|
return JIM_OK;
|
||
|
}
|
||
|
case MK_PROPERTY_FLOAT: {
|
||
|
double value;
|
||
|
|
||
|
if (Jim_GetDouble(interp, obj, &value) != JIM_OK)
|
||
|
return JIM_ERR;
|
||
|
|
||
|
((c4_FloatProp &)prop).Set(*cur, value);
|
||
|
return JIM_OK;
|
||
|
}
|
||
|
case MK_PROPERTY_DOUBLE: {
|
||
|
double value;
|
||
|
|
||
|
if (Jim_GetDouble(interp, obj, &value) != JIM_OK)
|
||
|
return JIM_ERR;
|
||
|
|
||
|
((c4_DoubleProp &)prop).Set(*cur, value);
|
||
|
return JIM_OK;
|
||
|
}
|
||
|
case MK_PROPERTY_STRING: {
|
||
|
int len;
|
||
|
const char *rep;
|
||
|
|
||
|
rep = Jim_GetString(obj, &len);
|
||
|
if (len != (int)strlen(rep)) {
|
||
|
Jim_SetResultString(interp, "null characters are not allowed in Metakit strings", -1);
|
||
|
return JIM_ERR;
|
||
|
}
|
||
|
|
||
|
((c4_StringProp &)prop).Set(*cur, rep);
|
||
|
return JIM_OK;
|
||
|
}
|
||
|
case MK_PROPERTY_VIEW: {
|
||
|
c4_View value;
|
||
|
|
||
|
if (JimGetView(interp, obj, &value) != JIM_OK)
|
||
|
return JIM_ERR;
|
||
|
|
||
|
((c4_ViewProp &)prop).Set(*cur, value);
|
||
|
}
|
||
|
case MK_PROPERTY_BINARY:
|
||
|
/* FIXME */
|
||
|
default:
|
||
|
Jim_SetResultString(interp, "unsupported Metakit type", -1);
|
||
|
return JIM_ERR;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
static int JimPipelineBoundary(int argc, Jim_Obj *const *argv) {
|
||
|
const char *rep;
|
||
|
int pipe, len;
|
||
|
|
||
|
for (pipe = 0; pipe < argc; pipe++) {
|
||
|
rep = Jim_GetString(argv[pipe], &len);
|
||
|
if (len == 1 && rep[0] == '|')
|
||
|
break;
|
||
|
}
|
||
|
return pipe;
|
||
|
}
|
||
|
|
||
|
/* -------------------------------------------------------------------------
|
||
|
* Property object
|
||
|
* ------------------------------------------------------------------------- */
|
||
|
|
||
|
#define JimPropertyValue(o) ((c4_Property *)((o)->internalRep.ptr))
|
||
|
|
||
|
static void FreePropertyInternalRep(Jim_Interp *interp, Jim_Obj *obj)
|
||
|
{
|
||
|
delete JimPropertyValue(obj);
|
||
|
}
|
||
|
|
||
|
static void DupPropertyInternalRep(Jim_Interp *interp, Jim_Obj *oldObj, Jim_Obj *newObj)
|
||
|
{
|
||
|
newObj->internalRep.ptr = new c4_Property(*JimPropertyValue(oldObj));
|
||
|
newObj->typePtr = oldObj->typePtr;
|
||
|
}
|
||
|
|
||
|
static void UpdateStringOfProperty(Jim_Obj* obj)
|
||
|
{
|
||
|
const char *name = JimPropertyValue(obj)->Name();
|
||
|
int len = strlen(name);
|
||
|
|
||
|
obj->bytes = (char *) Jim_Alloc(len + 1);
|
||
|
memcpy(obj->bytes, name, len + 1);
|
||
|
obj->length = len;
|
||
|
}
|
||
|
|
||
|
static Jim_ObjType propertyObjType = {
|
||
|
"mk.property",
|
||
|
FreePropertyInternalRep,
|
||
|
DupPropertyInternalRep,
|
||
|
UpdateStringOfProperty,
|
||
|
JIM_TYPE_NONE
|
||
|
};
|
||
|
|
||
|
static int JimGetProperty(Jim_Interp *interp, Jim_Obj *obj, c4_View view, const char *name, const c4_Property **propPtr)
|
||
|
{
|
||
|
int index;
|
||
|
|
||
|
if (obj->typePtr == &propertyObjType) {
|
||
|
index = view.FindProperty(JimPropertyValue(obj)->GetId());
|
||
|
}
|
||
|
else {
|
||
|
if (JimCheckMkName(interp, obj, name) != JIM_OK)
|
||
|
return JIM_ERR;
|
||
|
index = view.FindPropIndexByName(Jim_String(obj));
|
||
|
}
|
||
|
|
||
|
if (index != -1) {
|
||
|
*propPtr = &view.NthProperty(index);
|
||
|
return JIM_OK;
|
||
|
}
|
||
|
else {
|
||
|
Jim_SetResultFormatted(interp, "%s \"%#s\" does not exist",
|
||
|
name ? name : "property", obj);
|
||
|
return JIM_ERR;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
static int JimGetPropertyTyped(Jim_Interp *interp, Jim_Obj *obj, char type, const c4_Property **propPtr)
|
||
|
{
|
||
|
c4_Property *prop;
|
||
|
|
||
|
if (obj->typePtr == &propertyObjType) {
|
||
|
if (JimPropertyValue(obj)->Type() != type) {
|
||
|
/* coerce the property type */
|
||
|
|
||
|
prop = new c4_Property(type, JimPropertyValue(obj)->Name());
|
||
|
delete JimPropertyValue(obj);
|
||
|
obj->internalRep.ptr = prop;
|
||
|
}
|
||
|
}
|
||
|
else {
|
||
|
if (JimCheckMkName(interp, obj, NULL) != JIM_OK)
|
||
|
return JIM_ERR;
|
||
|
|
||
|
prop = new c4_Property(type, Jim_String(obj));
|
||
|
|
||
|
Jim_FreeIntRep(interp, obj);
|
||
|
obj->typePtr = &propertyObjType;
|
||
|
obj->internalRep.ptr = (void *)prop;
|
||
|
}
|
||
|
|
||
|
*propPtr = JimPropertyValue(obj);
|
||
|
return JIM_OK;
|
||
|
}
|
||
|
|
||
|
static int JimGetNewProperty(Jim_Interp *interp, Jim_Obj *obj, c4_View view, char type, const c4_Property **propPtr)
|
||
|
{
|
||
|
const c4_Property *newp, *prop;
|
||
|
|
||
|
if (JimGetPropertyTyped(interp, obj, type, &newp) != JIM_OK)
|
||
|
return JIM_ERR;
|
||
|
|
||
|
prop = &view.NthProperty(view.AddProperty(*newp));
|
||
|
|
||
|
if (prop->Type() != newp->Type()) {
|
||
|
Jim_SetResultFormatted(interp, "property \"%#s\" is %s, not %s",
|
||
|
obj, JimMkTypeName(prop->Type()), JimMkTypeName(newp->Type()));
|
||
|
return JIM_ERR;
|
||
|
}
|
||
|
|
||
|
*propPtr = prop;
|
||
|
return JIM_OK;
|
||
|
}
|
||
|
|
||
|
static int JimGetProperties(Jim_Interp *interp, int objc, Jim_Obj *const *objv, c4_View view, c4_View *propsPtr)
|
||
|
{
|
||
|
int i;
|
||
|
const c4_Property *prop;
|
||
|
c4_View props;
|
||
|
|
||
|
for (i = 0; i < objc; i++) {
|
||
|
if (JimGetProperty(interp, objv[i], view, NULL, &prop) != JIM_OK)
|
||
|
return JIM_ERR;
|
||
|
|
||
|
props.AddProperty(*prop);
|
||
|
}
|
||
|
|
||
|
*propsPtr = props;
|
||
|
return JIM_OK;
|
||
|
}
|
||
|
|
||
|
static Jim_Obj *JimNewPropertyObj(Jim_Interp *interp, c4_Property prop)
|
||
|
{
|
||
|
Jim_Obj *obj;
|
||
|
|
||
|
obj = Jim_NewObj(interp);
|
||
|
obj->typePtr = &propertyObjType;
|
||
|
obj->bytes = NULL;
|
||
|
obj->internalRep.ptr = new c4_Property(prop);
|
||
|
return obj;
|
||
|
}
|
||
|
|
||
|
/* -------------------------------------------------------------------------
|
||
|
* Cursor object
|
||
|
* ------------------------------------------------------------------------- */
|
||
|
|
||
|
/* Position ---------------------------------------------------------------- */
|
||
|
|
||
|
/* A normal position if endFlag == 0; otherwise an offset from end+1 (!) */
|
||
|
typedef struct MkPosition {
|
||
|
int index;
|
||
|
int endFlag;
|
||
|
} MkPosition;
|
||
|
|
||
|
/* This is mostly the same as SetIndexFromAny, but preserves more information
|
||
|
* and allows multiple [+-]integer parts.
|
||
|
*/
|
||
|
static int GetPosition(Jim_Interp *interp, Jim_Obj *obj, MkPosition *posPtr)
|
||
|
{
|
||
|
MkPosition pos;
|
||
|
const char *rep;
|
||
|
char *end;
|
||
|
int sign, offset;
|
||
|
|
||
|
rep = Jim_String(obj);
|
||
|
|
||
|
if (strncmp(rep, "end", 3) == 0) {
|
||
|
pos.endFlag = 1;
|
||
|
pos.index = -1;
|
||
|
|
||
|
rep += 3;
|
||
|
}
|
||
|
else {
|
||
|
pos.endFlag = 0;
|
||
|
pos.index = strtol(rep, &end, 10);
|
||
|
if (end == rep)
|
||
|
goto err;
|
||
|
|
||
|
rep = end;
|
||
|
}
|
||
|
|
||
|
while ((rep[0] == '+') || (rep[0] == '-')) {
|
||
|
sign = (rep[0] == '+' ? 1 : -1);
|
||
|
rep++;
|
||
|
|
||
|
offset = strtol(rep, &end, 10);
|
||
|
if (end == rep)
|
||
|
goto err;
|
||
|
|
||
|
pos.index += sign * offset;
|
||
|
rep = end;
|
||
|
}
|
||
|
|
||
|
while (isspace(UCHAR(*rep)))
|
||
|
rep++;
|
||
|
if (*rep != '\0')
|
||
|
goto err;
|
||
|
|
||
|
*posPtr = pos;
|
||
|
return JIM_OK;
|
||
|
|
||
|
err:
|
||
|
Jim_SetResultFormatted(interp, "expected cursor position but got \"%#s\"", obj);
|
||
|
return JIM_ERR;
|
||
|
}
|
||
|
|
||
|
static int PositionIndex(const MkPosition *posPtr, c4_View view)
|
||
|
{
|
||
|
if (posPtr->endFlag)
|
||
|
return view.GetSize() + posPtr->index;
|
||
|
else
|
||
|
return posPtr->index;
|
||
|
}
|
||
|
|
||
|
static int JimGetPosition(Jim_Interp *interp, Jim_Obj *obj, c4_View view, int *indexPtr)
|
||
|
{
|
||
|
MkPosition pos;
|
||
|
|
||
|
if (GetPosition(interp, obj, &pos) != JIM_OK)
|
||
|
return JIM_ERR;
|
||
|
|
||
|
*indexPtr = PositionIndex(&pos, view);
|
||
|
return JIM_OK;
|
||
|
}
|
||
|
|
||
|
/* Cursor type ------------------------------------------------------------- */
|
||
|
|
||
|
typedef struct MkCursor {
|
||
|
MkPosition pos;
|
||
|
Jim_Obj *viewObj;
|
||
|
} MkCursor;
|
||
|
|
||
|
#define JimCursorValue(obj) ((MkCursor *)(obj->internalRep.ptr))
|
||
|
static void FreeCursorInternalRep(Jim_Interp *interp, Jim_Obj *obj)
|
||
|
{
|
||
|
Jim_DecrRefCount(interp, JimCursorValue(obj)->viewObj);
|
||
|
Jim_Free(obj->internalRep.ptr);
|
||
|
}
|
||
|
|
||
|
static void DupCursorInternalRep(Jim_Interp *interp, Jim_Obj *oldObj, Jim_Obj *newObj)
|
||
|
{
|
||
|
newObj->internalRep.ptr = Jim_Alloc(sizeof(MkCursor));
|
||
|
*JimCursorValue(newObj) = *JimCursorValue(oldObj);
|
||
|
Jim_IncrRefCount(JimCursorValue(oldObj)->viewObj);
|
||
|
|
||
|
newObj->typePtr = oldObj->typePtr;
|
||
|
}
|
||
|
|
||
|
static void UpdateStringOfCursor(Jim_Obj *obj)
|
||
|
{
|
||
|
char buf[JIM_CURSOR_SPACE + 1];
|
||
|
MkCursor *curPtr = JimCursorValue(obj);
|
||
|
int idx, len;
|
||
|
|
||
|
len = snprintf(buf, JIM_CURSOR_SPACE + 1, "%s!", Jim_String(curPtr->viewObj));
|
||
|
|
||
|
if (curPtr->pos.endFlag) {
|
||
|
idx = curPtr->pos.index + 1;
|
||
|
if (idx == 0)
|
||
|
len += snprintf(buf + len, JIM_CURSOR_SPACE + 1 - len, "end");
|
||
|
else
|
||
|
len += snprintf(buf + len, JIM_CURSOR_SPACE + 1 - len, "end%+d", idx);
|
||
|
}
|
||
|
else {
|
||
|
len += snprintf(buf + len, JIM_CURSOR_SPACE + 1 - len, "%d",
|
||
|
curPtr->pos.index);
|
||
|
}
|
||
|
|
||
|
obj->bytes = (char *)Jim_Alloc(len + 1);
|
||
|
memcpy(obj->bytes, buf, len + 1);
|
||
|
obj->length = len;
|
||
|
}
|
||
|
|
||
|
static Jim_ObjType cursorObjType = {
|
||
|
"mk.cursor",
|
||
|
FreeCursorInternalRep,
|
||
|
DupCursorInternalRep,
|
||
|
UpdateStringOfCursor,
|
||
|
JIM_TYPE_REFERENCES
|
||
|
};
|
||
|
|
||
|
static int SetCursorFromAny(Jim_Interp *interp, Jim_Obj *obj)
|
||
|
{
|
||
|
const char *rep, *delim;
|
||
|
int len;
|
||
|
Jim_Obj *posObj;
|
||
|
MkCursor cur;
|
||
|
|
||
|
rep = Jim_GetString(obj, &len);
|
||
|
delim = strrchr(rep, '!');
|
||
|
|
||
|
if (!delim) {
|
||
|
Jim_SetResultFormatted(interp, "expected cursor but got \"%#s\"", obj);
|
||
|
return JIM_ERR;
|
||
|
}
|
||
|
|
||
|
cur.viewObj = Jim_NewStringObj(interp, rep, delim - rep);
|
||
|
posObj = Jim_NewStringObj(interp, delim + 1, len - (delim - rep) - 1);
|
||
|
|
||
|
if (GetPosition(interp, posObj, &cur.pos) != JIM_OK) {
|
||
|
Jim_FreeNewObj(interp, posObj);
|
||
|
Jim_FreeNewObj(interp, cur.viewObj);
|
||
|
return JIM_ERR;
|
||
|
}
|
||
|
|
||
|
Jim_FreeIntRep(interp, obj);
|
||
|
Jim_FreeNewObj(interp, posObj);
|
||
|
Jim_IncrRefCount(cur.viewObj);
|
||
|
|
||
|
obj->typePtr = &cursorObjType;
|
||
|
obj->internalRep.ptr = Jim_Alloc(sizeof(MkCursor));
|
||
|
*JimCursorValue(obj) = cur;
|
||
|
|
||
|
return JIM_OK;
|
||
|
}
|
||
|
|
||
|
/* Functions --------------------------------------------------------------- */
|
||
|
|
||
|
static int JimCursorPos(Jim_Interp *interp, Jim_Obj *obj, Jim_Obj **posObjPtr)
|
||
|
{
|
||
|
if (obj->typePtr != &cursorObjType && SetCursorFromAny(interp, obj) != JIM_OK)
|
||
|
return JIM_ERR;
|
||
|
|
||
|
*posObjPtr = Jim_NewStringObj(interp, strrchr(Jim_String(obj), '!') + 1, -1);
|
||
|
return JIM_OK;
|
||
|
}
|
||
|
|
||
|
static int JimGetCursorView(Jim_Interp *interp, Jim_Obj *obj, Jim_Obj **viewObjPtr)
|
||
|
{
|
||
|
if (obj->typePtr != &cursorObjType && SetCursorFromAny(interp, obj) != JIM_OK)
|
||
|
return JIM_ERR;
|
||
|
|
||
|
*viewObjPtr = JimCursorValue(obj)->viewObj;
|
||
|
return JIM_OK;
|
||
|
}
|
||
|
|
||
|
static int JimGetCursor(Jim_Interp *interp, Jim_Obj *obj, c4_Cursor *curPtr)
|
||
|
{
|
||
|
c4_View view;
|
||
|
|
||
|
if (obj->typePtr != &cursorObjType && SetCursorFromAny(interp, obj) != JIM_OK)
|
||
|
return JIM_ERR;
|
||
|
if (JimGetView(interp, JimCursorValue(obj)->viewObj, &view) != JIM_OK)
|
||
|
return JIM_ERR;
|
||
|
|
||
|
if (curPtr)
|
||
|
*curPtr = &view[PositionIndex(&JimCursorValue(obj)->pos, view)];
|
||
|
return JIM_OK;
|
||
|
}
|
||
|
|
||
|
static int JimIncrCursor(Jim_Interp *interp, Jim_Obj *obj, int offset)
|
||
|
{
|
||
|
/* JimPanic((Jim_IsShared(obj), "JimIncrCursor called with shared object")) */
|
||
|
|
||
|
if (obj->typePtr != &cursorObjType && SetCursorFromAny(interp, obj) != JIM_OK)
|
||
|
return JIM_ERR;
|
||
|
|
||
|
Jim_InvalidateStringRep(obj);
|
||
|
JimCursorValue(obj)->pos.index += offset;
|
||
|
return JIM_OK;
|
||
|
}
|
||
|
|
||
|
static int JimSeekCursor(Jim_Interp *interp, Jim_Obj *obj, Jim_Obj *posObj)
|
||
|
{
|
||
|
/* JimPanic((Jim_IsShared(obj), "JimSeekCursor called with shared object")) */
|
||
|
|
||
|
if (obj->typePtr != &cursorObjType && SetCursorFromAny(interp, obj) != JIM_OK)
|
||
|
return JIM_ERR;
|
||
|
|
||
|
Jim_InvalidateStringRep(obj);
|
||
|
return GetPosition(interp, posObj, &JimCursorValue(obj)->pos);
|
||
|
}
|
||
|
|
||
|
static int JimCheckCursor(Jim_Interp *interp, Jim_Obj *curObj, int flags)
|
||
|
{
|
||
|
static c4_View nullView;
|
||
|
|
||
|
c4_Cursor cur = &nullView[0];
|
||
|
int size;
|
||
|
|
||
|
if (JimGetCursor(interp, curObj, &cur) != JIM_OK)
|
||
|
return JIM_ERR;
|
||
|
size = (*cur).Container().GetSize();
|
||
|
|
||
|
if ((flags & JIM_CURSOR_GET) && (cur._index < 0 || cur._index >= size)) {
|
||
|
if (flags & JIM_ERRMSG) {
|
||
|
Jim_SetResultFormatted(interp,
|
||
|
"cursor \"%#s\" does not point to an existing row", curObj);
|
||
|
}
|
||
|
return JIM_ERR;
|
||
|
}
|
||
|
else if ((flags & JIM_CURSOR_SET) && cur._index < 0) {
|
||
|
if (flags & JIM_ERRMSG) {
|
||
|
Jim_SetResultFormatted(interp,
|
||
|
"cursor \"%#s\" points before start of view", curObj);
|
||
|
}
|
||
|
return JIM_ERR;
|
||
|
}
|
||
|
else if ((flags & JIM_CURSOR_INSERT) && (cur._index < 0 || cur._index > size)) {
|
||
|
if (flags & JIM_ERRMSG) {
|
||
|
Jim_SetResultFormatted(interp,
|
||
|
"cursor \"%#s\" does not point to a valid insert position", curObj);
|
||
|
}
|
||
|
return JIM_ERR;
|
||
|
}
|
||
|
|
||
|
return JIM_OK;
|
||
|
}
|
||
|
|
||
|
/* Records ----------------------------------------------------------------- */
|
||
|
|
||
|
static int cursor_cmd_get(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
|
||
|
{
|
||
|
c4_View view;
|
||
|
c4_Cursor cur = &view[0];
|
||
|
|
||
|
if (JimGetCursor(interp, argv[0], &cur) != JIM_OK)
|
||
|
return JIM_ERR;
|
||
|
if (JimCheckCursor(interp, argv[0], JIM_ERRMSG | JIM_CURSOR_GET) != JIM_OK)
|
||
|
return JIM_ERR;
|
||
|
|
||
|
view = (*cur).Container();
|
||
|
|
||
|
if (argc == 1) { /* Return all properties */
|
||
|
int i, count;
|
||
|
Jim_Obj *result;
|
||
|
|
||
|
result = Jim_NewListObj(interp, NULL, 0);
|
||
|
count = view.NumProperties();
|
||
|
|
||
|
for (i = 0; i < count; i++) {
|
||
|
c4_Property prop = view.NthProperty(i);
|
||
|
|
||
|
Jim_ListAppendElement(interp, result, JimNewPropertyObj(interp, prop));
|
||
|
Jim_ListAppendElement(interp, result, JimGetMkValue(interp, cur, prop));
|
||
|
}
|
||
|
|
||
|
Jim_SetResult(interp, result);
|
||
|
return JIM_OK;
|
||
|
}
|
||
|
else { /* Return a single property */
|
||
|
const c4_Property *propPtr;
|
||
|
int pipe;
|
||
|
|
||
|
pipe = JimPipelineBoundary(argc, argv);
|
||
|
if (pipe == 2) {
|
||
|
/* No type annotation, existing property */
|
||
|
if (JimGetProperty(interp, argv[1], view, NULL, &propPtr) != JIM_OK)
|
||
|
return JIM_ERR;
|
||
|
}
|
||
|
else if (pipe == 3) {
|
||
|
/* Explicit type annotation; the property may be new */
|
||
|
int idx;
|
||
|
|
||
|
if (Jim_GetEnum(interp, argv[1], jim_mktype_options, &idx,
|
||
|
"property type", JIM_ERRMSG | JIM_ENUM_ABBREV) != JIM_OK)
|
||
|
return JIM_ERR;
|
||
|
if (JimGetNewProperty(interp, argv[2], view, jim_mktype_types[idx], &propPtr) != JIM_OK)
|
||
|
return JIM_ERR;
|
||
|
}
|
||
|
else {
|
||
|
Jim_WrongNumArgs(interp, 0, NULL, "cursor get ?-type? ?prop?");
|
||
|
return JIM_ERR;
|
||
|
}
|
||
|
|
||
|
Jim_SetResult(interp, JimGetMkValue(interp, cur, *propPtr));
|
||
|
|
||
|
if (pipe == argc)
|
||
|
return JIM_OK;
|
||
|
else
|
||
|
return Jim_EvalObjPrefix(interp, Jim_GetResult(interp), argc - pipe - 1, argv + pipe + 1);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
static int cursor_cmd_set(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
|
||
|
{
|
||
|
c4_View view;
|
||
|
c4_Cursor cur = &view[0];
|
||
|
const c4_Property *propPtr;
|
||
|
int i, oldSize;
|
||
|
|
||
|
if (JimGetCursor(interp, argv[0], &cur) != JIM_OK)
|
||
|
return JIM_ERR;
|
||
|
if (JimCheckCursor(interp, argv[0], JIM_ERRMSG | JIM_CURSOR_SET) != JIM_OK)
|
||
|
return JIM_ERR;
|
||
|
|
||
|
view = (*cur).Container();
|
||
|
oldSize = view.GetSize();
|
||
|
|
||
|
if (cur._index >= oldSize)
|
||
|
view.SetSize(cur._index + 1);
|
||
|
|
||
|
if (argc == 2) {
|
||
|
/* Update everything except subviews from a dictionary in argv[1].
|
||
|
* No new properties are permitted.
|
||
|
*/
|
||
|
|
||
|
int objc;
|
||
|
Jim_Obj **objv;
|
||
|
|
||
|
if (Jim_DictPairs(interp, argv[1], &objv, &objc) != JIM_OK)
|
||
|
goto err;
|
||
|
|
||
|
for (i = 0; i < objc; i += 2) {
|
||
|
if (JimGetProperty(interp, objv[i], view, NULL, &propPtr) != JIM_OK ||
|
||
|
JimSetMkValue(interp, cur, *propPtr, objv[i+1]) != JIM_OK)
|
||
|
{
|
||
|
Jim_Free(objv);
|
||
|
goto err;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
else {
|
||
|
/* Update everything from argv[1..]. New properties are permitted if
|
||
|
* explicitly typed.
|
||
|
*/
|
||
|
|
||
|
for (i = 1; i < argc; i += 2) {
|
||
|
if (Jim_String(argv[i])[0] == '-') {
|
||
|
int idx;
|
||
|
|
||
|
if (i + 2 >= argc) {
|
||
|
Jim_WrongNumArgs(interp, 2, argv, "?-type? prop value ?...?");
|
||
|
goto err;
|
||
|
}
|
||
|
|
||
|
if (Jim_GetEnum(interp, argv[i], jim_mktype_options, &idx,
|
||
|
"property type", JIM_ERRMSG | JIM_ENUM_ABBREV) != JIM_OK)
|
||
|
goto err;
|
||
|
if (JimGetNewProperty(interp, argv[i+1], view, jim_mktype_types[idx], &propPtr) != JIM_OK)
|
||
|
goto err;
|
||
|
i++;
|
||
|
}
|
||
|
else {
|
||
|
if (i + 1 >= argc) {
|
||
|
Jim_WrongNumArgs(interp, 2, argv, "?-type? prop value ?...?");
|
||
|
goto err;
|
||
|
}
|
||
|
|
||
|
if (JimGetProperty(interp, argv[i], view, NULL, &propPtr) != JIM_OK)
|
||
|
goto err;
|
||
|
}
|
||
|
|
||
|
if (JimSetMkValue(interp, cur, *propPtr, argv[i+1]) != JIM_OK)
|
||
|
goto err;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return JIM_OK;
|
||
|
|
||
|
err:
|
||
|
view.SetSize(oldSize);
|
||
|
return JIM_ERR;
|
||
|
}
|
||
|
|
||
|
static int cursor_cmd_insert(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
|
||
|
{
|
||
|
c4_View view;
|
||
|
c4_Cursor cur = &view[0];
|
||
|
jim_wide count;
|
||
|
|
||
|
if (JimGetCursor(interp, argv[0], &cur) != JIM_OK)
|
||
|
return JIM_ERR;
|
||
|
if (JimCheckCursor(interp, argv[0], JIM_ERRMSG | JIM_CURSOR_INSERT) != JIM_OK)
|
||
|
return JIM_ERR;
|
||
|
|
||
|
view = (*cur).Container();
|
||
|
|
||
|
if (argc == 1)
|
||
|
count = 1;
|
||
|
else {
|
||
|
if (Jim_GetWide(interp, argv[1], &count) != JIM_OK)
|
||
|
return JIM_ERR;
|
||
|
}
|
||
|
|
||
|
if (count > 0) {
|
||
|
c4_Row empty;
|
||
|
view.InsertAt(cur._index, empty, (int)count);
|
||
|
}
|
||
|
|
||
|
Jim_SetEmptyResult(interp);
|
||
|
return JIM_OK;
|
||
|
}
|
||
|
|
||
|
static int cursor_cmd_remove(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
|
||
|
{
|
||
|
c4_View view;
|
||
|
c4_Cursor cur = &view[0];
|
||
|
int pos;
|
||
|
jim_wide count;
|
||
|
|
||
|
if (JimGetCursor(interp, argv[0], &cur) != JIM_OK)
|
||
|
return JIM_ERR;
|
||
|
if (JimCheckCursor(interp, argv[0], JIM_ERRMSG | JIM_CURSOR_SET) != JIM_OK)
|
||
|
return JIM_ERR;
|
||
|
|
||
|
view = (*cur).Container();
|
||
|
pos = cur._index;
|
||
|
|
||
|
if (argc == 1)
|
||
|
count = 1;
|
||
|
else {
|
||
|
if (Jim_GetWide(interp, argv[1], &count) != JIM_OK)
|
||
|
return JIM_ERR;
|
||
|
}
|
||
|
|
||
|
if (pos + count < view.GetSize())
|
||
|
count = view.GetSize() - pos;
|
||
|
|
||
|
if (pos < view.GetSize())
|
||
|
view.RemoveAt(pos, (int)count);
|
||
|
|
||
|
return JIM_OK;
|
||
|
}
|
||
|
|
||
|
/* Attributes -------------------------------------------------------------- */
|
||
|
|
||
|
static int cursor_cmd_view(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
|
||
|
{
|
||
|
Jim_Obj *viewObj;
|
||
|
|
||
|
if (JimGetCursorView(interp, argv[0], &viewObj) != JIM_OK)
|
||
|
return JIM_ERR;
|
||
|
|
||
|
JimPinView(interp, viewObj);
|
||
|
Jim_SetResult(interp, viewObj);
|
||
|
return JIM_OK;
|
||
|
}
|
||
|
|
||
|
/* Positioning ------------------------------------------------------------- */
|
||
|
|
||
|
static int cursor_cmd_tell(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
|
||
|
{
|
||
|
if (argc == 1) {
|
||
|
Jim_Obj *result;
|
||
|
|
||
|
if (JimCursorPos(interp, argv[0], &result) != JIM_OK)
|
||
|
return JIM_ERR;
|
||
|
Jim_SetResult(interp, result);
|
||
|
}
|
||
|
else {
|
||
|
static c4_View nullView;
|
||
|
c4_Cursor cur = &nullView[0];
|
||
|
|
||
|
if (!Jim_CompareStringImmediate(interp, argv[0], "-absolute")) {
|
||
|
Jim_SetResultFormatted(interp,
|
||
|
"bad option \"%#s\": must be -absolute", argv[0]);
|
||
|
return JIM_ERR;
|
||
|
}
|
||
|
|
||
|
if (JimGetCursor(interp, argv[1], &cur) != JIM_OK)
|
||
|
return JIM_ERR;
|
||
|
|
||
|
Jim_SetResultInt(interp, cur._index);
|
||
|
}
|
||
|
|
||
|
return JIM_OK;
|
||
|
}
|
||
|
|
||
|
static int cursor_cmd_validfor(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
|
||
|
{
|
||
|
static const char *options[] = {
|
||
|
"get", "set", "insert", "remove", 0
|
||
|
};
|
||
|
static int optflags[] = {
|
||
|
JIM_CURSOR_GET,
|
||
|
JIM_CURSOR_SET,
|
||
|
JIM_CURSOR_INSERT,
|
||
|
JIM_CURSOR_SET
|
||
|
};
|
||
|
|
||
|
int idx;
|
||
|
|
||
|
if (argc == 1)
|
||
|
idx = 0;
|
||
|
else {
|
||
|
if (Jim_GetEnum(interp, argv[0], options, &idx, NULL,
|
||
|
JIM_ERRMSG | JIM_ENUM_ABBREV) != JIM_OK)
|
||
|
return JIM_ERR;
|
||
|
}
|
||
|
|
||
|
if (JimGetCursor(interp, argv[argc-1], NULL) != JIM_OK)
|
||
|
return JIM_ERR;
|
||
|
|
||
|
Jim_SetResultBool(interp, JimCheckCursor(interp, argv[argc-1], optflags[idx]) == JIM_OK);
|
||
|
return JIM_OK;
|
||
|
}
|
||
|
|
||
|
static int cursor_cmd_seek(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
|
||
|
{
|
||
|
Jim_Obj *curObj;
|
||
|
|
||
|
curObj = Jim_GetVariable(interp, argv[0], JIM_ERRMSG | JIM_UNSHARED);
|
||
|
if (curObj == NULL)
|
||
|
return JIM_ERR;
|
||
|
|
||
|
if (JimSeekCursor(interp, curObj, argv[1]) != JIM_OK)
|
||
|
return JIM_ERR;
|
||
|
|
||
|
Jim_SetResult(interp, curObj);
|
||
|
return JIM_OK;
|
||
|
}
|
||
|
|
||
|
static int cursor_cmd_incr(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
|
||
|
{
|
||
|
Jim_Obj *curObj;
|
||
|
jim_wide offset;
|
||
|
|
||
|
if (argc == 1)
|
||
|
offset = 1;
|
||
|
else {
|
||
|
if (Jim_GetWide(interp, argv[1], &offset) != JIM_OK)
|
||
|
return JIM_ERR;
|
||
|
}
|
||
|
|
||
|
curObj = Jim_GetVariable(interp, argv[0], JIM_ERRMSG | JIM_UNSHARED);
|
||
|
if (curObj == NULL)
|
||
|
return JIM_ERR;
|
||
|
|
||
|
if (JimIncrCursor(interp, curObj, (int)offset) != JIM_OK)
|
||
|
return JIM_ERR;
|
||
|
|
||
|
Jim_SetResult(interp, curObj);
|
||
|
return JIM_OK;
|
||
|
}
|
||
|
|
||
|
/* Command table ----------------------------------------------------------- */
|
||
|
|
||
|
static const jim_subcmd_type cursor_command_table[] = {
|
||
|
|
||
|
/* Records */
|
||
|
|
||
|
{ "get", "cur ?-type? ?prop?",
|
||
|
cursor_cmd_get,
|
||
|
1, -1,
|
||
|
0,
|
||
|
/*"Get the whole record or a specific property at the cursor"*/
|
||
|
},
|
||
|
{ "set", "cur [dict | ?-type? field value ?...?]",
|
||
|
cursor_cmd_set,
|
||
|
1, -1,
|
||
|
0,
|
||
|
/*"Update the record at the cursor"*/
|
||
|
},
|
||
|
{ "insert", "cur ?count?",
|
||
|
cursor_cmd_insert,
|
||
|
1, 2,
|
||
|
0,
|
||
|
/*"Insert a specified number of empty rows at the cursor (default 1)"*/
|
||
|
},
|
||
|
{ "remove", "cur ?count?",
|
||
|
cursor_cmd_remove,
|
||
|
1, 2,
|
||
|
0,
|
||
|
/*"Remove a specified number of rows at the cursor (default 1)"*/
|
||
|
},
|
||
|
|
||
|
/* Attributes */
|
||
|
|
||
|
{ "view", "cur",
|
||
|
cursor_cmd_view,
|
||
|
1, 1,
|
||
|
0,
|
||
|
/*"Get the view the cursor points into"*/
|
||
|
},
|
||
|
|
||
|
/* Positioning */
|
||
|
|
||
|
{ "tell", "?-absolute? cur",
|
||
|
cursor_cmd_tell,
|
||
|
1, 2,
|
||
|
0,
|
||
|
/*"Get the position of the cursor"*/
|
||
|
},
|
||
|
{ "validfor", "?command? cur",
|
||
|
cursor_cmd_validfor,
|
||
|
1, 2,
|
||
|
0,
|
||
|
/*"Checks if the cursor is valid for get (default), set or insert commands"*/
|
||
|
},
|
||
|
{ "seek", "curVar index",
|
||
|
cursor_cmd_seek,
|
||
|
2, 2,
|
||
|
0,
|
||
|
/*"Seek to the specified index in the view"*/
|
||
|
},
|
||
|
{ "incr", "curVar ?offset?",
|
||
|
cursor_cmd_incr,
|
||
|
1, 2,
|
||
|
0,
|
||
|
/*"Move the cursor offset records from its current position (default 1)"*/
|
||
|
},
|
||
|
|
||
|
{ 0 }
|
||
|
};
|
||
|
|
||
|
static int JimCursorCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
|
||
|
{
|
||
|
Jim_Obj *cmdObj;
|
||
|
|
||
|
if (argc < 2) {
|
||
|
Jim_WrongNumArgs(interp, 1, argv, "command ...");
|
||
|
return JIM_ERR;
|
||
|
}
|
||
|
|
||
|
cmdObj = Jim_NewStringObj(interp, "cursor ", -1);
|
||
|
Jim_AppendObj(interp, cmdObj, argv[1]);
|
||
|
|
||
|
if (Jim_GetCommand(interp, cmdObj, 0) != NULL)
|
||
|
return Jim_EvalObjPrefix(interp, cmdObj, argc - 2, argv + 2);
|
||
|
else {
|
||
|
Jim_FreeNewObj(interp, cmdObj);
|
||
|
return Jim_CallSubCmd(interp,
|
||
|
Jim_ParseSubCmd(interp, cursor_command_table, argc, argv), argc, argv);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/* -------------------------------------------------------------------------
|
||
|
* View handle
|
||
|
* ------------------------------------------------------------------------- */
|
||
|
|
||
|
/* Views aren't really Jim objects; instead, they are Tk-style commands with
|
||
|
* oo.tcl-like lifetime management. Additionally, all views are initially
|
||
|
* created as one-shot, meaning that they die after one command. Call
|
||
|
* JimPinView to make a view object persistent.
|
||
|
*
|
||
|
* It is valid to rename a view in the Tcl land, but by doing this you take
|
||
|
* the responsibility of destroying the object when it's no longer needed.
|
||
|
* Any cursors that pointed into the view become invalid.
|
||
|
*/
|
||
|
|
||
|
static int JimViewSubCmdProc(Jim_Interp *interp, int argc, Jim_Obj *const *argv);
|
||
|
static int JimOneShotViewSubCmdProc(Jim_Interp *interp, int argc, Jim_Obj *const *argv);
|
||
|
|
||
|
/* Unary operations -------------------------------------------------------- */
|
||
|
|
||
|
#define UNOP(name, Method) \
|
||
|
static int view_cmd_##name(Jim_Interp *interp, int argc, Jim_Obj *const *argv) \
|
||
|
{ \
|
||
|
const c4_View *viewPtr = (const c4_View *)Jim_CmdPrivData(interp); \
|
||
|
\
|
||
|
Jim_SetResult(interp, JimNewViewObj(interp, viewPtr->Method())); \
|
||
|
return JIM_OK; \
|
||
|
}
|
||
|
|
||
|
UNOP(copy, Duplicate)
|
||
|
UNOP(clone, Clone)
|
||
|
UNOP(unique, Unique)
|
||
|
|
||
|
#undef UNOP
|
||
|
|
||
|
static int view_cmd_blocked(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
|
||
|
{
|
||
|
const c4_View *viewPtr = (const c4_View *)Jim_CmdPrivData(interp);
|
||
|
|
||
|
if (viewPtr->GetSize() != 1 ||
|
||
|
strcmp(viewPtr->NthProperty(0).Name(), "_B") != 0 ||
|
||
|
viewPtr->NthProperty(0).Type() != MK_PROPERTY_VIEW)
|
||
|
{
|
||
|
Jim_SetResultString(interp,
|
||
|
"blocked view must have exactly one subview property called _B", -1);
|
||
|
return JIM_ERR;
|
||
|
}
|
||
|
|
||
|
Jim_SetResult(interp, JimNewViewObj(interp, viewPtr->Blocked()));
|
||
|
return JIM_OK;
|
||
|
}
|
||
|
|
||
|
/* Binary operations ------------------------------------------------------- */
|
||
|
|
||
|
#define BINOP(name, Method) \
|
||
|
static int view_cmd_##name(Jim_Interp *interp, int argc, Jim_Obj *const *argv) \
|
||
|
{ \
|
||
|
const c4_View *viewPtr = (const c4_View *)Jim_CmdPrivData(interp); \
|
||
|
c4_View otherView; \
|
||
|
\
|
||
|
if (JimGetView(interp, argv[0], &otherView) != JIM_OK) \
|
||
|
return JIM_ERR; \
|
||
|
\
|
||
|
Jim_SetResult(interp, JimNewViewObj(interp, viewPtr->Method(otherView))); \
|
||
|
return JIM_OK; \
|
||
|
}
|
||
|
|
||
|
BINOP(pair, Pair)
|
||
|
BINOP(concat, Concat)
|
||
|
BINOP(product, Product)
|
||
|
|
||
|
BINOP(union, Union)
|
||
|
BINOP(intersect, Intersect)
|
||
|
BINOP(minus, Minus)
|
||
|
BINOP(different, Different)
|
||
|
|
||
|
#undef BINOP
|
||
|
|
||
|
/* Projections ------------------------------------------------------------- */
|
||
|
|
||
|
static int view_cmd_project(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
|
||
|
{
|
||
|
const c4_View *viewPtr = (const c4_View *)Jim_CmdPrivData(interp);
|
||
|
c4_View props;
|
||
|
|
||
|
if (JimGetProperties(interp, argc, argv, *viewPtr, &props) != JIM_OK)
|
||
|
return JIM_ERR;
|
||
|
|
||
|
Jim_SetResult(interp, JimNewViewObj(interp, viewPtr->Project(props)));
|
||
|
return JIM_OK;
|
||
|
}
|
||
|
|
||
|
static int view_cmd_without(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
|
||
|
{
|
||
|
const c4_View *viewPtr = (const c4_View *)Jim_CmdPrivData(interp);
|
||
|
c4_View props;
|
||
|
|
||
|
if (JimGetProperties(interp, argc, argv, *viewPtr, &props) != JIM_OK)
|
||
|
return JIM_ERR;
|
||
|
|
||
|
Jim_SetResult(interp, JimNewViewObj(interp, viewPtr->ProjectWithout(props)));
|
||
|
return JIM_OK;
|
||
|
}
|
||
|
|
||
|
static int view_cmd_range(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
|
||
|
{
|
||
|
const c4_View *viewPtr = (const c4_View *)Jim_CmdPrivData(interp);
|
||
|
int start, end;
|
||
|
jim_wide step;
|
||
|
|
||
|
if (JimGetPosition(interp, argv[0], *viewPtr, &start) != JIM_OK ||
|
||
|
JimGetPosition(interp, argv[1], *viewPtr, &end) != JIM_OK)
|
||
|
{
|
||
|
return JIM_ERR;
|
||
|
}
|
||
|
|
||
|
if (argc == 2)
|
||
|
step = 1;
|
||
|
else if (Jim_GetWide(interp, argv[2], &step) != JIM_OK)
|
||
|
return JIM_ERR;
|
||
|
|
||
|
Jim_SetResult(interp, JimNewViewObj(interp, viewPtr->Slice(start, end + 1, (int)step)));
|
||
|
return JIM_OK;
|
||
|
}
|
||
|
|
||
|
/* Ordering ---------------------------------------------------------------- */
|
||
|
|
||
|
static int view_cmd_sort(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
|
||
|
{
|
||
|
const c4_View *viewPtr = (const c4_View *)Jim_CmdPrivData(interp);
|
||
|
c4_View sortProps, revProps;
|
||
|
const c4_Property *propPtr;
|
||
|
int i, len;
|
||
|
|
||
|
const char *rep;
|
||
|
int reverse;
|
||
|
Jim_Obj *propObj;
|
||
|
|
||
|
/* Special case: property names may be preceded with a dash. Use
|
||
|
* a temporary object in this case.
|
||
|
*/
|
||
|
|
||
|
for (i = 0; i < argc; i++) {
|
||
|
propObj = argv[i];
|
||
|
|
||
|
rep = Jim_GetString(argv[i], &len);
|
||
|
reverse = (len > 0 && rep[0] == '-');
|
||
|
|
||
|
if (reverse)
|
||
|
propObj = Jim_NewStringObj(interp, rep + 1, len - 1);
|
||
|
|
||
|
if (JimGetProperty(interp, propObj, *viewPtr, NULL, &propPtr) != JIM_OK) {
|
||
|
if (reverse)
|
||
|
Jim_FreeNewObj(interp, propObj);
|
||
|
return JIM_ERR;
|
||
|
}
|
||
|
|
||
|
sortProps.AddProperty(*propPtr);
|
||
|
if (reverse) {
|
||
|
revProps.AddProperty(*propPtr);
|
||
|
Jim_FreeNewObj(interp, propObj);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if (sortProps.GetSize() == 0)
|
||
|
Jim_SetResult(interp, JimNewViewObj(interp, viewPtr->Sort()));
|
||
|
else if (revProps.GetSize() == 0)
|
||
|
Jim_SetResult(interp, JimNewViewObj(interp, viewPtr->SortOn(sortProps)));
|
||
|
else
|
||
|
Jim_SetResult(interp, JimNewViewObj(interp, viewPtr->SortOnReverse(sortProps, revProps)));
|
||
|
|
||
|
return JIM_OK;
|
||
|
}
|
||
|
|
||
|
/* Metakit core seems to be doing something similar for SortOn, but neither
|
||
|
* Ordered nor Hash use it, for unknown reason.
|
||
|
*/
|
||
|
|
||
|
static int BubbleProperties(Jim_Interp *interp, c4_View orig, int objc, Jim_Obj *const *objv, c4_View *projPtr)
|
||
|
{
|
||
|
c4_View proj;
|
||
|
const c4_Property *propPtr;
|
||
|
int i, count;
|
||
|
|
||
|
for (i = 0; i < objc; i++) {
|
||
|
if (JimGetProperty(interp, objv[i], orig, NULL, &propPtr) != JIM_OK)
|
||
|
return JIM_ERR;
|
||
|
proj.AddProperty(*propPtr);
|
||
|
}
|
||
|
|
||
|
count = orig.NumProperties();
|
||
|
for (i = 0; i < count; i++)
|
||
|
proj.AddProperty(orig.NthProperty(i));
|
||
|
|
||
|
*projPtr = proj;
|
||
|
return JIM_OK;
|
||
|
}
|
||
|
|
||
|
static int view_cmd_ordered(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
|
||
|
{
|
||
|
const c4_View *viewPtr = (const c4_View *)Jim_CmdPrivData(interp);
|
||
|
c4_View proj;
|
||
|
|
||
|
if (BubbleProperties(interp, *viewPtr, argc, argv, &proj) != JIM_OK)
|
||
|
return JIM_ERR;
|
||
|
|
||
|
Jim_SetResult(interp, JimNewViewObj(interp, proj.Ordered(argc)));
|
||
|
return JIM_OK;
|
||
|
}
|
||
|
|
||
|
static int view_cmd_hash(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
|
||
|
{
|
||
|
const c4_View *viewPtr = (const c4_View *)Jim_CmdPrivData(interp);
|
||
|
c4_View hash, proj;
|
||
|
|
||
|
if (JimGetView(interp, argv[0], &hash) != JIM_OK)
|
||
|
return JIM_ERR;
|
||
|
|
||
|
if (hash.GetSize() != 2 ||
|
||
|
strcmp(hash.NthProperty(0).Name(), "_H") != 0 ||
|
||
|
hash.NthProperty(0).Type() != MK_PROPERTY_INT ||
|
||
|
strcmp(hash.NthProperty(1).Name(), "_R") != 0 ||
|
||
|
hash.NthProperty(1).Type() != MK_PROPERTY_INT) /* Ouch. */
|
||
|
{
|
||
|
Jim_SetResultString(interp,
|
||
|
"hash view must be laid out as {_H integer _R integer}", -1);
|
||
|
return JIM_ERR;
|
||
|
}
|
||
|
|
||
|
if (BubbleProperties(interp, *viewPtr, argc - 1, argv + 1, &proj) != JIM_OK)
|
||
|
return JIM_ERR;
|
||
|
|
||
|
Jim_SetResult(interp, JimNewViewObj(interp, proj.Hash(hash, argc - 1)));
|
||
|
return JIM_OK;
|
||
|
}
|
||
|
|
||
|
/* Relational operations --------------------------------------------------- */
|
||
|
|
||
|
static int view_cmd_join(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
|
||
|
{
|
||
|
const c4_View *viewPtr = (const c4_View *)Jim_CmdPrivData(interp);
|
||
|
c4_View other, props;
|
||
|
int outer, off;
|
||
|
|
||
|
if (JimGetView(interp, argv[0], &other) != JIM_OK)
|
||
|
return JIM_ERR;
|
||
|
|
||
|
off = 1; outer = 0;
|
||
|
if (Jim_CompareStringImmediate(interp, argv[1], "-outer")) {
|
||
|
off++; outer = 1;
|
||
|
}
|
||
|
|
||
|
if (JimGetProperties(interp, argc - off, argv + off, *viewPtr, &props) != JIM_OK)
|
||
|
return JIM_ERR;
|
||
|
|
||
|
Jim_SetResult(interp, JimNewViewObj(interp, viewPtr->Join(props, other, outer)));
|
||
|
return JIM_OK;
|
||
|
}
|
||
|
|
||
|
static int view_cmd_group(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
|
||
|
{
|
||
|
const c4_View *viewPtr = (const c4_View *)Jim_CmdPrivData(interp);
|
||
|
const c4_Property *subviewPtr;
|
||
|
c4_View props;
|
||
|
|
||
|
if (JimGetPropertyTyped(interp, argv[0], MK_PROPERTY_VIEW, &subviewPtr) != JIM_OK)
|
||
|
return JIM_ERR;
|
||
|
|
||
|
if (JimGetProperties(interp, argc - 1, argv + 1, *viewPtr, &props) != JIM_OK)
|
||
|
return JIM_ERR;
|
||
|
|
||
|
Jim_SetResult(interp, JimNewViewObj(interp, viewPtr->GroupBy(props, *(c4_ViewProp *)subviewPtr)));
|
||
|
return JIM_OK;
|
||
|
}
|
||
|
|
||
|
static int view_cmd_flatten(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
|
||
|
{
|
||
|
const c4_View *viewPtr = (const c4_View *)Jim_CmdPrivData(interp);
|
||
|
const c4_Property *subviewPtr;
|
||
|
|
||
|
if (JimGetProperty(interp, argv[0], *viewPtr, NULL, &subviewPtr) != JIM_OK)
|
||
|
return JIM_ERR;
|
||
|
|
||
|
if (subviewPtr->Type() != MK_PROPERTY_VIEW) {
|
||
|
Jim_SetResultFormatted(interp, "expected a subview property but got %s one",
|
||
|
JimMkTypeName(subviewPtr->Type()));
|
||
|
return JIM_ERR;
|
||
|
}
|
||
|
|
||
|
Jim_SetResult(interp, JimNewViewObj(interp, viewPtr->JoinProp(*(c4_ViewProp *)subviewPtr)));
|
||
|
return JIM_OK;
|
||
|
}
|
||
|
|
||
|
/* View queries ------------------------------------------------------------ */
|
||
|
|
||
|
static int view_cmd_properties(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
|
||
|
{
|
||
|
const c4_View *viewPtr = (const c4_View *) Jim_CmdPrivData(interp);
|
||
|
Jim_SetResult(interp, JimViewPropertiesList(interp, *viewPtr));
|
||
|
return JIM_OK;
|
||
|
}
|
||
|
|
||
|
static int view_cmd_size(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
|
||
|
{
|
||
|
const c4_View *viewPtr = (const c4_View *) Jim_CmdPrivData(interp);
|
||
|
Jim_SetResultInt(interp, viewPtr->GetSize());
|
||
|
return JIM_OK;
|
||
|
}
|
||
|
|
||
|
static int view_cmd_resize(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
|
||
|
{
|
||
|
c4_View *view = (c4_View *) Jim_CmdPrivData(interp);
|
||
|
jim_wide size;
|
||
|
|
||
|
if (Jim_GetWide(interp, argv[0], &size) != JIM_OK)
|
||
|
return JIM_ERR;
|
||
|
if (size < 0 || size > INT_MAX) {
|
||
|
Jim_SetResultFormatted(interp,
|
||
|
"view size \"%#s\" is out of range", argv[0]);
|
||
|
return JIM_ERR;
|
||
|
}
|
||
|
|
||
|
view->SetSize((int)size);
|
||
|
Jim_SetResult(interp, argv[0]);
|
||
|
return JIM_OK;
|
||
|
}
|
||
|
|
||
|
static int view_cmd_type(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
|
||
|
{
|
||
|
const c4_View *viewPtr = (const c4_View *)Jim_CmdPrivData(interp);
|
||
|
|
||
|
if (argc == 1) {
|
||
|
const c4_Property *propPtr;
|
||
|
|
||
|
if (JimGetProperty(interp, argv[0], *viewPtr, NULL, &propPtr) != JIM_OK)
|
||
|
return JIM_ERR;
|
||
|
|
||
|
Jim_SetResultString(interp, JimMkTypeName(propPtr->Type()), -1);
|
||
|
}
|
||
|
else {
|
||
|
Jim_Obj *result;
|
||
|
int i, count;
|
||
|
|
||
|
result = Jim_NewListObj(interp, NULL, 0);
|
||
|
count = viewPtr->NumProperties();
|
||
|
|
||
|
for (i = 0; i < count; i++) {
|
||
|
c4_Property prop = viewPtr->NthProperty(i);
|
||
|
Jim_ListAppendElement(interp, result, JimNewPropertyObj(interp, prop));
|
||
|
Jim_ListAppendElement(interp, result,
|
||
|
Jim_NewStringObj(interp, JimMkTypeName(prop.Type()), -1));
|
||
|
}
|
||
|
|
||
|
Jim_SetResult(interp, result);
|
||
|
}
|
||
|
|
||
|
return JIM_OK;
|
||
|
}
|
||
|
|
||
|
/* View lifetime ----------------------------------------------------------- */
|
||
|
|
||
|
static int view_cmd_pin(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
|
||
|
{
|
||
|
JimPinView(interp, argv[0]);
|
||
|
Jim_SetResult(interp, argv[0]);
|
||
|
return JIM_OK;
|
||
|
}
|
||
|
|
||
|
static int view_cmd_as(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
|
||
|
{
|
||
|
JimPinView(interp, argv[0]);
|
||
|
Jim_SetVariable(interp, argv[2], argv[0]);
|
||
|
Jim_SetResult(interp, argv[0]);
|
||
|
return JIM_OK;
|
||
|
}
|
||
|
|
||
|
static int view_cmd_destroy(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
|
||
|
{
|
||
|
Jim_DeleteCommand(interp, argv[0]);
|
||
|
return JIM_OK;
|
||
|
}
|
||
|
|
||
|
/* Command table ----------------------------------------------------------- */
|
||
|
|
||
|
static const jim_subcmd_type view_command_table[] = {
|
||
|
|
||
|
/* Unary operations */
|
||
|
|
||
|
{ "copy", "",
|
||
|
view_cmd_copy,
|
||
|
0, 0,
|
||
|
0,
|
||
|
/*"Create a copy of the view with exactly the same data"*/
|
||
|
},
|
||
|
{ "clone", "",
|
||
|
view_cmd_clone,
|
||
|
0, 0,
|
||
|
0,
|
||
|
/*"Create an empty view with the same properties as this one"*/
|
||
|
},
|
||
|
{ "unique", "",
|
||
|
view_cmd_unique,
|
||
|
0, 0,
|
||
|
0,
|
||
|
/*"Derived view without any duplicate rows (read-only, no change notifications)"*/
|
||
|
},
|
||
|
{ "blocked", "",
|
||
|
view_cmd_blocked,
|
||
|
0, 0,
|
||
|
0,
|
||
|
/*"Build a scalable \"blocked\" out of a view with a single subview property called _B"*/
|
||
|
},
|
||
|
|
||
|
/* Binary operations */
|
||
|
|
||
|
#define BINOP(name, descr) \
|
||
|
{ #name, "otherView", \
|
||
|
view_cmd_##name, \
|
||
|
1, 1, 0, \
|
||
|
}
|
||
|
|
||
|
BINOP(pair, "Pairwise concatenation of two views"),
|
||
|
BINOP(concat, "Concatenation of two views; unlike union, doesn't remove duplicates"),
|
||
|
BINOP(product, "Cartesian product of two views, i.e. every row in view paired with every row in otherView"),
|
||
|
|
||
|
/* Set operations */
|
||
|
|
||
|
#define SETOP(name, descr) BINOP(name, descr "; works only if all the rows are unique")
|
||
|
|
||
|
SETOP(union, "Set union of two views (read-only, no change notifications)"),
|
||
|
SETOP(intersect, "Set intersection of two views"),
|
||
|
SETOP(different, "Symmetric difference of two views"),
|
||
|
SETOP(minus, "Set minus, i.e. all rows from view not in otherView"),
|
||
|
|
||
|
#undef SETOP
|
||
|
|
||
|
#undef BINOP
|
||
|
|
||
|
/* Projections and selections */
|
||
|
|
||
|
{ "project", "prop ?prop ...?",
|
||
|
view_cmd_project,
|
||
|
1, -1,
|
||
|
0,
|
||
|
/*"View projection: only the specified properties, in the specified order"*/
|
||
|
},
|
||
|
{ "without", "prop ?prop ...?",
|
||
|
view_cmd_without,
|
||
|
1, -1,
|
||
|
0,
|
||
|
/*"View projection: remove the specified properties"*/
|
||
|
},
|
||
|
{ "range", "first last ?step?",
|
||
|
view_cmd_range,
|
||
|
2, 3,
|
||
|
0,
|
||
|
/*"Range or slice of the view (read-write, no change notifications)"*/
|
||
|
},
|
||
|
|
||
|
/* Ordering */
|
||
|
|
||
|
{ "sort", "?[prop|-prop] ...?",
|
||
|
view_cmd_sort,
|
||
|
0, -1,
|
||
|
0,
|
||
|
/*"Derived view sorted on the specified properties (in order), or on all properties"*/
|
||
|
},
|
||
|
{ "ordered", "prop ?prop ...?",
|
||
|
view_cmd_ordered,
|
||
|
1, -1,
|
||
|
0,
|
||
|
/*"Consider the underlying view ordered on the specified properties"*/
|
||
|
},
|
||
|
{ "hash", "hashView prop ?prop ...?",
|
||
|
view_cmd_hash,
|
||
|
2, -1,
|
||
|
0,
|
||
|
/*"Mapped view maintaining a hash table on the key consisting of the specified properties"*/
|
||
|
},
|
||
|
|
||
|
/* Relational operations */
|
||
|
|
||
|
{ "join", "view ?-outer? prop ?prop ...?",
|
||
|
view_cmd_join,
|
||
|
2, -1,
|
||
|
0,
|
||
|
/*"Relational join with view on the specified properties"*/
|
||
|
},
|
||
|
{ "group", "subviewName prop ?prop ...?",
|
||
|
view_cmd_group,
|
||
|
1, -1,
|
||
|
0,
|
||
|
/*"Group rows with equal specified properties, move all other properties into subview"*/
|
||
|
},
|
||
|
{ "flatten", "subviewProp",
|
||
|
view_cmd_flatten,
|
||
|
1, 1,
|
||
|
0,
|
||
|
/*"Flatten the specified subview; the inverse of group"*/
|
||
|
},
|
||
|
|
||
|
/* Attributes */
|
||
|
|
||
|
{ "properties", "",
|
||
|
view_cmd_properties,
|
||
|
0, 0,
|
||
|
0,
|
||
|
/*"List the properties in this view"*/
|
||
|
},
|
||
|
{ "size", "",
|
||
|
view_cmd_size,
|
||
|
0, 0,
|
||
|
0,
|
||
|
/*"Return the number of records in the view"*/
|
||
|
},
|
||
|
{ "resize", "newSize",
|
||
|
view_cmd_resize,
|
||
|
1, 1,
|
||
|
0,
|
||
|
/*"Set the number of records in the view"*/
|
||
|
},
|
||
|
{ "type", "?prop?",
|
||
|
view_cmd_type,
|
||
|
0, 1,
|
||
|
0,
|
||
|
/*"Return the type of an existing property, or of all properties"*/
|
||
|
},
|
||
|
|
||
|
/* Lifetime management */
|
||
|
|
||
|
{ "pin", "",
|
||
|
view_cmd_pin,
|
||
|
0, 0,
|
||
|
JIM_MODFLAG_FULLARGV,
|
||
|
/*"Marks the view as persistent"*/
|
||
|
},
|
||
|
{ "as", "varName",
|
||
|
view_cmd_as,
|
||
|
1, 1,
|
||
|
JIM_MODFLAG_FULLARGV,
|
||
|
/*"Marks the view as persistent and assigns it to the given variable"*/
|
||
|
},
|
||
|
{ "destroy", "",
|
||
|
view_cmd_destroy,
|
||
|
0, 0,
|
||
|
JIM_MODFLAG_FULLARGV,
|
||
|
/*"Destroys the view explicitly"*/
|
||
|
},
|
||
|
|
||
|
{ 0 }
|
||
|
};
|
||
|
|
||
|
static void JimViewDelProc(Jim_Interp *interp, void *privData)
|
||
|
{
|
||
|
delete (c4_View *)privData;
|
||
|
}
|
||
|
|
||
|
static int JimViewSubCmdProc(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
|
||
|
{
|
||
|
int pipe, result;
|
||
|
Jim_Obj *cmdObj;
|
||
|
|
||
|
pipe = JimPipelineBoundary(argc, argv);
|
||
|
|
||
|
if (pipe < 1) {
|
||
|
Jim_WrongNumArgs(interp, 1, argv, "command ...");
|
||
|
return JIM_ERR;
|
||
|
}
|
||
|
|
||
|
/* Check for a Tcl command first, and try builtins afterwards.
|
||
|
* We have to do it in this order so that Jim_ParseSubCmd isn't too greedy
|
||
|
* about abbreviations, and still it can't now detect ambigous abbrevs
|
||
|
* properly :( Tcl commands cannot be abbreviated at all.
|
||
|
*/
|
||
|
|
||
|
cmdObj = Jim_NewStringObj(interp, "mk.view ", -1);
|
||
|
Jim_AppendObj(interp, cmdObj, argv[1]);
|
||
|
|
||
|
/* The command will be cached even though we discard the result */
|
||
|
if (Jim_GetCommand(interp, cmdObj, 0) != NULL) {
|
||
|
/* Shuffle the arguments: $view cmd args... => {mk.view cmd} $view args... */
|
||
|
|
||
|
Jim_Obj **objv = (Jim_Obj **)Jim_Alloc(pipe * sizeof(Jim_Obj *));
|
||
|
objv[0] = cmdObj;
|
||
|
objv[1] = argv[0];
|
||
|
memcpy(objv + 2, argv + 2, (pipe - 2) * sizeof(Jim_Obj *));
|
||
|
|
||
|
result = Jim_EvalObjVector(interp, pipe, objv);
|
||
|
|
||
|
Jim_Free(objv);
|
||
|
} else {
|
||
|
Jim_FreeNewObj(interp, cmdObj);
|
||
|
result = Jim_CallSubCmd(interp, Jim_ParseSubCmd(interp, view_command_table, pipe, argv), pipe, argv);
|
||
|
}
|
||
|
|
||
|
if (result != JIM_OK || pipe == argc)
|
||
|
return result;
|
||
|
else
|
||
|
return Jim_EvalObjPrefix(interp, Jim_GetResult(interp), argc - pipe - 1, argv + pipe + 1);
|
||
|
}
|
||
|
|
||
|
static int JimOneShotViewSubCmdProc(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
|
||
|
{
|
||
|
int result;
|
||
|
Jim_Cmd *cmd;
|
||
|
|
||
|
result = JimViewSubCmdProc(interp, argc, argv);
|
||
|
|
||
|
cmd = Jim_GetCommand(interp, argv[0], 0);
|
||
|
if (cmd && !cmd->isproc && cmd->u.native.cmdProc == JimOneShotViewSubCmdProc)
|
||
|
Jim_DeleteCommand(interp, argv[0]);
|
||
|
|
||
|
return result;
|
||
|
}
|
||
|
|
||
|
static int JimViewFinalizerProc(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
|
||
|
{
|
||
|
/* We won't succeed here if the user renamed the command, and this is right */
|
||
|
Jim_DeleteCommand(interp, argv[1]);
|
||
|
return JIM_OK;
|
||
|
}
|
||
|
|
||
|
static Jim_Obj *JimNewViewObj(Jim_Interp *interp, c4_View view) {
|
||
|
Jim_Obj *tag, *ref;
|
||
|
|
||
|
tag = Jim_NewStringObj(interp, "mk.view", -1);
|
||
|
ref = Jim_NewReference(interp, tag, tag, Jim_NewStringObj(interp, "mk.view.finalizer", -1));
|
||
|
Jim_CreateCommand(interp, Jim_String(ref),
|
||
|
JimOneShotViewSubCmdProc, new c4_View(view), JimViewDelProc);
|
||
|
|
||
|
return ref;
|
||
|
}
|
||
|
|
||
|
static int JimGetView(Jim_Interp *interp, Jim_Obj *obj, c4_View *viewPtr)
|
||
|
{
|
||
|
Jim_Cmd *cmd = Jim_GetCommand(interp, obj, 0);
|
||
|
|
||
|
if (cmd == NULL || cmd->isproc || cmd->u.native.delProc != JimViewDelProc) {
|
||
|
Jim_SetResultFormatted(interp, "invalid view object \"%#s\"", obj);
|
||
|
return JIM_ERR;
|
||
|
}
|
||
|
|
||
|
*viewPtr = *(c4_View *)cmd->u.native.privData;
|
||
|
return JIM_OK;
|
||
|
}
|
||
|
|
||
|
/* Only call this against known view objects. */
|
||
|
static void JimPinView(Jim_Interp *interp, Jim_Obj *obj)
|
||
|
{
|
||
|
Jim_Cmd *cmd = Jim_GetCommand(interp, obj, 0);
|
||
|
/* JimPanic((cmd == NULL, "JimPinView called against non-view"))
|
||
|
JimPanic((cmd->u.native.delProc != JimViewDelProc, "JimPinView called against non-view")) */
|
||
|
cmd->u.native.cmdProc = JimViewSubCmdProc;
|
||
|
}
|
||
|
|
||
|
static Jim_Obj *JimViewPropertiesList(Jim_Interp *interp, c4_View view)
|
||
|
{
|
||
|
int i, count;
|
||
|
Jim_Obj *result;
|
||
|
|
||
|
result = Jim_NewListObj(interp, NULL, 0);
|
||
|
count = view.NumProperties();
|
||
|
|
||
|
for (i = 0; i < count; i++) {
|
||
|
Jim_ListAppendElement(interp, result, Jim_NewStringObj(interp,
|
||
|
view.NthProperty(i).Name(), -1));
|
||
|
}
|
||
|
|
||
|
return result;
|
||
|
}
|
||
|
|
||
|
/* ----------------------------------------------------------------------------
|
||
|
* Storage handle
|
||
|
* ---------------------------------------------------------------------------- */
|
||
|
|
||
|
/* These are also commands, like views, but must be managed explicitly by the
|
||
|
* user. Quite like file handles, actually.
|
||
|
*/
|
||
|
|
||
|
typedef struct MkStorage {
|
||
|
unsigned flags;
|
||
|
Jim_Obj *filename;
|
||
|
c4_Storage storage;
|
||
|
c4_Cursor content;
|
||
|
} MkStorage;
|
||
|
|
||
|
#define JIM_MKFLAG_INMEMORY 0x0001
|
||
|
#define JIM_MKFLAG_READONLY 0x0002
|
||
|
#define JIM_MKFLAG_EXTEND 0x0004
|
||
|
#define JIM_MKFLAG_AUTOCOMMIT 0x0008
|
||
|
|
||
|
/* Attributes -------------------------------------------------------------- */
|
||
|
|
||
|
static int storage_cmd_autocommit(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
|
||
|
{
|
||
|
MkStorage *mk = (MkStorage *)Jim_CmdPrivData(interp);
|
||
|
|
||
|
if (argc == 1) {
|
||
|
jim_wide flag;
|
||
|
|
||
|
if (Jim_GetWide(interp, argv[0], &flag) != JIM_OK)
|
||
|
return JIM_ERR;
|
||
|
|
||
|
if (flag)
|
||
|
mk->flags |= JIM_MKFLAG_AUTOCOMMIT;
|
||
|
else
|
||
|
mk->flags &= ~JIM_MKFLAG_AUTOCOMMIT;
|
||
|
mk->storage.AutoCommit(flag);
|
||
|
}
|
||
|
|
||
|
Jim_SetResultBool(interp, (mk->flags & JIM_MKFLAG_AUTOCOMMIT) != 0);
|
||
|
return JIM_OK;
|
||
|
}
|
||
|
|
||
|
static int storage_cmd_readonly(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
|
||
|
{
|
||
|
MkStorage *mk = (MkStorage *)Jim_CmdPrivData(interp);
|
||
|
|
||
|
Jim_SetResultBool(interp, (mk->flags & JIM_MKFLAG_READONLY) != 0);
|
||
|
return JIM_OK;
|
||
|
}
|
||
|
|
||
|
/* Views ------------------------------------------------------------------- */
|
||
|
|
||
|
static int storage_cmd_views(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
|
||
|
{
|
||
|
MkStorage *mk = (MkStorage *)Jim_CmdPrivData(interp);
|
||
|
|
||
|
Jim_SetResult(interp, JimViewPropertiesList(interp, mk->storage));
|
||
|
return JIM_OK;
|
||
|
}
|
||
|
|
||
|
static int storage_cmd_view(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
|
||
|
{
|
||
|
MkStorage *mk = (MkStorage *)Jim_CmdPrivData(interp);
|
||
|
const c4_Property *propPtr;
|
||
|
|
||
|
if (JimGetProperty(interp, argv[0], mk->storage, "view", &propPtr) != JIM_OK)
|
||
|
return JIM_ERR;
|
||
|
Jim_SetResult(interp, JimGetMkValue(interp, mk->content, *propPtr));
|
||
|
|
||
|
if (argc == 1)
|
||
|
return JIM_OK;
|
||
|
else {
|
||
|
if (!Jim_CompareStringImmediate(interp, argv[1], "|")) {
|
||
|
Jim_SetResultFormatted(interp,
|
||
|
"expected start of a pipeline but got \"%#s\"", argv[1]);
|
||
|
return JIM_ERR;
|
||
|
}
|
||
|
return Jim_EvalObjPrefix(interp, Jim_GetResult(interp), argc - 2, argv + 2);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
static int storage_cmd_structure(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
|
||
|
{
|
||
|
MkStorage *mk = (MkStorage *)Jim_CmdPrivData(interp);
|
||
|
|
||
|
if (argc < 2) { /* Query */
|
||
|
const char *name;
|
||
|
|
||
|
if (argc == 0)
|
||
|
name = NULL;
|
||
|
else {
|
||
|
const c4_Property *propPtr;
|
||
|
|
||
|
if (JimGetProperty(interp, argv[0], mk->storage, "view", &propPtr) != JIM_OK)
|
||
|
return JIM_ERR;
|
||
|
name = propPtr->Name();
|
||
|
}
|
||
|
|
||
|
Jim_SetResult(interp, JimFromMkDescription(interp,
|
||
|
mk->storage.Description(name), NULL));
|
||
|
}
|
||
|
else { /* Modify */
|
||
|
char *descr;
|
||
|
const char *name;
|
||
|
int len, dlen;
|
||
|
|
||
|
if (JimCheckMkName(interp, argv[0], "view") != JIM_OK)
|
||
|
return JIM_ERR;
|
||
|
name = Jim_GetString(argv[0], &len);
|
||
|
|
||
|
if (JimToMkDescription(interp, argv[1], &descr) != JIM_OK)
|
||
|
return JIM_ERR;
|
||
|
dlen = strlen(descr);
|
||
|
|
||
|
descr = (char *)Jim_Realloc(descr, dlen + len + 2);
|
||
|
memmove(descr + len + 1, descr, dlen);
|
||
|
memcpy(descr, name, len);
|
||
|
descr[len] = '[';
|
||
|
descr[len + 1 + dlen] = ']';
|
||
|
descr[len + 1 + dlen + 1] = '\0';
|
||
|
|
||
|
mk->storage.GetAs(descr);
|
||
|
|
||
|
Jim_Free(descr);
|
||
|
}
|
||
|
|
||
|
return JIM_OK;
|
||
|
}
|
||
|
|
||
|
/* Store operations -------------------------------------------------------- */
|
||
|
|
||
|
static int storage_cmd_commit(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
|
||
|
{
|
||
|
MkStorage *mk = (MkStorage *)Jim_CmdPrivData(interp);
|
||
|
|
||
|
if (mk->flags & JIM_MKFLAG_INMEMORY) {
|
||
|
Jim_SetResultString(interp, "cannot commit an in-memory storage", -1);
|
||
|
return JIM_ERR;
|
||
|
}
|
||
|
else if (mk->flags & JIM_MKFLAG_READONLY) {
|
||
|
Jim_SetResultString(interp, "cannot commit a read-only storage", -1);
|
||
|
return JIM_ERR;
|
||
|
}
|
||
|
|
||
|
if (mk->storage.Commit(0)) {
|
||
|
Jim_SetEmptyResult(interp);
|
||
|
return JIM_OK;
|
||
|
}
|
||
|
else {
|
||
|
Jim_SetResultString(interp, "I/O error during commit", -1);
|
||
|
return JIM_ERR;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
static int storage_cmd_rollback(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
|
||
|
{
|
||
|
MkStorage *mk = (MkStorage *)Jim_CmdPrivData(interp);
|
||
|
|
||
|
if (mk->flags & JIM_MKFLAG_INMEMORY) {
|
||
|
Jim_SetResultString(interp, "cannot rollback an in-memory storage", -1);
|
||
|
return JIM_ERR;
|
||
|
}
|
||
|
|
||
|
if (mk->storage.Rollback(0)) {
|
||
|
Jim_SetEmptyResult(interp);
|
||
|
return JIM_OK;
|
||
|
}
|
||
|
else {
|
||
|
Jim_SetResultString(interp, "I/O error during rollback", -1);
|
||
|
return JIM_ERR;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
static int storage_cmd_close(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
|
||
|
{
|
||
|
return Jim_DeleteCommand(interp, argv[0]);
|
||
|
}
|
||
|
|
||
|
/* Command table ----------------------------------------------------------- */
|
||
|
|
||
|
static const jim_subcmd_type storage_command_table[] = {
|
||
|
|
||
|
/* Options */
|
||
|
|
||
|
{ "autocommit", "?value?",
|
||
|
storage_cmd_autocommit,
|
||
|
0, 1,
|
||
|
0,
|
||
|
/*"Query or modify the auto-commit option of this storage"*/
|
||
|
},
|
||
|
{ "readonly", "",
|
||
|
storage_cmd_readonly,
|
||
|
0, 0,
|
||
|
0,
|
||
|
/*"Returns the read-only status of this storage"*/
|
||
|
},
|
||
|
|
||
|
/* Views */
|
||
|
|
||
|
{ "views", "",
|
||
|
storage_cmd_views,
|
||
|
0, 0,
|
||
|
0,
|
||
|
/*"Returns the list of views stored here"*/
|
||
|
},
|
||
|
{ "view", "viewName",
|
||
|
storage_cmd_view,
|
||
|
1, -1,
|
||
|
0,
|
||
|
/*"Retrieve the view specified by viewName"*/
|
||
|
},
|
||
|
{ "structure", "?viewName? ?description?",
|
||
|
storage_cmd_structure,
|
||
|
0, 2,
|
||
|
0,
|
||
|
/*"Query or modify the structure of this storage"*/
|
||
|
},
|
||
|
|
||
|
/* Store operations */
|
||
|
|
||
|
{ "commit", "",
|
||
|
storage_cmd_commit,
|
||
|
0, 0,
|
||
|
0,
|
||
|
/*"Commit the changes to disk"*/
|
||
|
},
|
||
|
{ "rollback", "",
|
||
|
storage_cmd_rollback,
|
||
|
0, 0,
|
||
|
0,
|
||
|
/*"Revert to the saved state"*/
|
||
|
},
|
||
|
{ "close", "",
|
||
|
storage_cmd_close,
|
||
|
0, 0,
|
||
|
JIM_MODFLAG_FULLARGV,
|
||
|
/*"Close this storage"*/
|
||
|
},
|
||
|
|
||
|
{ 0 }
|
||
|
};
|
||
|
|
||
|
static void JimStorageDelProc(Jim_Interp *interp, void *privData)
|
||
|
{
|
||
|
MkStorage *mk = (MkStorage *)privData;
|
||
|
|
||
|
mk->storage.~c4_Storage();
|
||
|
mk->content.~c4_Cursor();
|
||
|
Jim_DecrRefCount(interp, mk->filename);
|
||
|
Jim_Free(mk);
|
||
|
}
|
||
|
|
||
|
static int JimStorageSubCmdProc(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
|
||
|
{
|
||
|
Jim_Obj *cmdObj;
|
||
|
|
||
|
cmdObj = Jim_NewStringObj(interp, "mk.storage ", -1);
|
||
|
Jim_AppendObj(interp, cmdObj, argv[1]);
|
||
|
|
||
|
if (Jim_GetCommand(interp, cmdObj, 0) != NULL) {
|
||
|
int result;
|
||
|
|
||
|
Jim_Obj **objv = (Jim_Obj **)Jim_Alloc(argc * sizeof(Jim_Obj *));
|
||
|
objv[0] = cmdObj;
|
||
|
objv[1] = argv[0];
|
||
|
memcpy(objv + 2, argv + 2, (argc - 2) * sizeof(Jim_Obj *));
|
||
|
|
||
|
result = Jim_EvalObjVector(interp, argc, objv);
|
||
|
|
||
|
Jim_Free(objv);
|
||
|
return result;
|
||
|
} else {
|
||
|
Jim_FreeNewObj(interp, cmdObj);
|
||
|
return Jim_CallSubCmd(interp, Jim_ParseSubCmd(interp,
|
||
|
storage_command_table, argc, argv), argc, argv);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/* -------------------------------------------------------------------------
|
||
|
* storage ?options? ?filename?
|
||
|
*
|
||
|
* Creates a new metakit storage object, optionally backed by a file.
|
||
|
*
|
||
|
* Options apply only when filename is given; these include:
|
||
|
*
|
||
|
* -readonly Open the file in read-only mode
|
||
|
* -original Open the file in read-only mode, discarding possible extends
|
||
|
* -extend Open the file in extend mode
|
||
|
* -nocommit Do not commit the changes when the storage is closed
|
||
|
* ------------------------------------------------------------------------- */
|
||
|
|
||
|
static int JimStorageCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
|
||
|
{
|
||
|
MkStorage *mk;
|
||
|
char buf[MK_CMD_LEN];
|
||
|
int i, mode;
|
||
|
|
||
|
static const char *const options[] = {
|
||
|
"-readonly",
|
||
|
"-original",
|
||
|
"-extend",
|
||
|
"-nocommit",
|
||
|
0
|
||
|
};
|
||
|
enum {
|
||
|
OPT_READONLY,
|
||
|
OPT_ORIGINAL,
|
||
|
OPT_EXTEND,
|
||
|
OPT_NOCOMMIT
|
||
|
};
|
||
|
int option;
|
||
|
|
||
|
mk = (MkStorage *)Jim_Alloc(sizeof(MkStorage));
|
||
|
mk->flags = JIM_MKFLAG_AUTOCOMMIT;
|
||
|
mode = MK_MODE_READWRITE;
|
||
|
for (i = 1; i < argc - 1; i++ ) {
|
||
|
if (Jim_GetEnum(interp, argv[i], options, &option, NULL, JIM_ERRMSG) != JIM_OK) {
|
||
|
Jim_Free(mk);
|
||
|
return JIM_ERR;
|
||
|
}
|
||
|
|
||
|
switch (option) {
|
||
|
case OPT_READONLY:
|
||
|
if (mode != MK_MODE_READWRITE)
|
||
|
goto modeconflict;
|
||
|
|
||
|
mode = MK_MODE_READONLY;
|
||
|
mk->flags |= JIM_MKFLAG_READONLY;
|
||
|
break;
|
||
|
|
||
|
case OPT_ORIGINAL:
|
||
|
if (mode != MK_MODE_READWRITE)
|
||
|
goto modeconflict;
|
||
|
|
||
|
mode = MK_MODE_ORIGINAL;
|
||
|
mk->flags |= JIM_MKFLAG_READONLY;
|
||
|
break;
|
||
|
|
||
|
case OPT_EXTEND:
|
||
|
if (mode != MK_MODE_READWRITE)
|
||
|
goto modeconflict;
|
||
|
|
||
|
mode = MK_MODE_EXTEND;
|
||
|
mk->flags |= JIM_MKFLAG_EXTEND;
|
||
|
break;
|
||
|
|
||
|
case OPT_NOCOMMIT:
|
||
|
mk->flags &= ~JIM_MKFLAG_AUTOCOMMIT;
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if (argc > 1) {
|
||
|
new(&mk->storage) c4_Storage(Jim_String(argv[argc-1]), mode);
|
||
|
|
||
|
if (!mk->storage.Strategy().IsValid()) {
|
||
|
mk->storage.~c4_Storage();
|
||
|
Jim_Free(mk);
|
||
|
Jim_SetResultFormatted(interp, "could not open storage \"%#s\"", argv[argc-1]);
|
||
|
return JIM_ERR;
|
||
|
}
|
||
|
|
||
|
mk->filename = argv[argc-1];
|
||
|
|
||
|
if ((mk->flags & JIM_MKFLAG_AUTOCOMMIT) && !(mk->flags & JIM_MKFLAG_READONLY))
|
||
|
mk->storage.AutoCommit(1);
|
||
|
}
|
||
|
else {
|
||
|
mk->flags |= JIM_MKFLAG_INMEMORY;
|
||
|
|
||
|
new(&mk->storage) c4_Storage();
|
||
|
mk->filename = Jim_NewEmptyStringObj(interp);
|
||
|
}
|
||
|
new(&mk->content) c4_Cursor(&mk->storage[0]);
|
||
|
Jim_IncrRefCount(mk->filename);
|
||
|
|
||
|
snprintf(buf, sizeof(buf), "mk.handle%ld", Jim_GetId(interp));
|
||
|
Jim_CreateCommand(interp, buf, JimStorageSubCmdProc, mk, JimStorageDelProc);
|
||
|
Jim_SetResultString(interp, buf, -1);
|
||
|
return JIM_OK;
|
||
|
|
||
|
modeconflict:
|
||
|
Jim_Free(mk);
|
||
|
Jim_SetResultString(interp, "only one of -readonly, -original and -extend may be specified", -1);
|
||
|
return JIM_ERR;
|
||
|
}
|
||
|
|
||
|
/* -------------------------------------------------------------------------
|
||
|
* Initialization code
|
||
|
* ------------------------------------------------------------------------- */
|
||
|
|
||
|
int Jim_mkInit(Jim_Interp *interp)
|
||
|
{
|
||
|
char version[MK_VERSION_SPACE];
|
||
|
|
||
|
snprintf(version, MK_VERSION_SPACE, "%d.%d.%d",
|
||
|
d4_MetakitLibraryVersion / 100,
|
||
|
d4_MetakitLibraryVersion % 100 / 10,
|
||
|
d4_MetakitLibraryVersion % 10);
|
||
|
|
||
|
if (Jim_PackageProvide(interp, "mk", version, JIM_ERRMSG))
|
||
|
return JIM_ERR;
|
||
|
|
||
|
Jim_CreateCommand(interp, "storage", JimStorageCommand, NULL, NULL);
|
||
|
Jim_CreateCommand(interp, "cursor", JimCursorCommand, NULL, NULL);
|
||
|
Jim_CreateCommand(interp, "mk.view.finalizer", JimViewFinalizerProc, NULL, NULL);
|
||
|
|
||
|
return JIM_OK;
|
||
|
}
|
||
|
|
||
|
} /* extern "C" */
|