Skip to content
Fetching contributors…
Cannot retrieve contributors at this time
2277 lines (1833 sloc) 63.6 KB
#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, Jim_String(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, Jim_String(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, Jim_String(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, Jim_String(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" */
Jump to Line
Something went wrong with that request. Please try again.