Document [string map]
[jimtcl.git] / jim-mk.cpp
blob637c2dd547c62a47743eff3806a59ab27e1b763c
1 #include <string.h>
2 #include <ctype.h>
3 #include <new>
4 #include <mk4.h>
6 #include "jim.h"
7 #include "jimautoconf.h"
8 #include "jim-subcmd.h"
10 extern "C" { /* The whole file is essentially C */
12 #define MK_PROPERTY_BINARY 'B'
13 #define MK_PROPERTY_INT 'I'
14 #define MK_PROPERTY_LONG 'L'
15 #define MK_PROPERTY_FLOAT 'F'
16 #define MK_PROPERTY_DOUBLE 'D'
17 #define MK_PROPERTY_STRING 'S'
18 #define MK_PROPERTY_VIEW 'V'
20 #define MK_MODE_ORIGINAL -1
21 #define MK_MODE_READONLY 0
22 #define MK_MODE_READWRITE 1
23 #define MK_MODE_EXTEND 2
25 #define MK_CMD_LEN 32
26 #define JIM_CURSOR_SPACE (35+JIM_REFERENCE_TAGLEN + 1 + 20)
27 #define JIM_POSITION_SPACE 32
28 #define MK_VERSION_SPACE 16
29 #define JIM_MK_DESCR_LEN 64 /* Default, will be reallocated if needed */
31 #define isnamech(c) ( (c) && !strchr(":,[^]!", (c)) )
33 #ifndef max
34 #define max(x, y) ((x) >= (y) ? (x) : (y))
35 #endif
37 /* utilities */
38 static int JimCheckMkName(Jim_Interp *interp, Jim_Obj *name, const char *type);
39 static const char *JimMkTypeName(char type);
40 static Jim_Obj *JimFromMkDescription(Jim_Interp *interp, const char *descr, const char **endPtr);
41 static int JimToMkDescription(Jim_Interp *interp, Jim_Obj *obj, char **descrPtr);
42 static Jim_Obj *JimGetMkValue(Jim_Interp *interp, c4_Cursor cur, const c4_Property &prop);
43 static int JimSetMkValue(Jim_Interp *interp, c4_Cursor cur, const c4_Property &prop, Jim_Obj *obj);
45 static int JimPipelineBoundary(int argc, Jim_Obj *const *argv);
47 /* property object */
48 static Jim_Obj *JimNewPropertyObj (Jim_Interp *interp, c4_Property prop);
49 static int JimGetProperty (Jim_Interp *interp, Jim_Obj *obj,
50 c4_View view, const char *what, const c4_Property **propPtr);
51 static int JimGetPropertyTyped (Jim_Interp *interp, Jim_Obj *obj,
52 char type, const c4_Property **propPtr);
53 static int JimGetNewProperty (Jim_Interp *interp, Jim_Obj *obj,
54 c4_View view, char type, const c4_Property **propPtr);
55 static int JimGetProperties (Jim_Interp *interp, int objc, Jim_Obj *const *objv,
56 c4_View view, c4_View *propsPtr);
57 static Jim_Obj *JimViewPropertiesList (Jim_Interp *interp, c4_View view);
59 /* cursor object */
60 static int JimGetPosition (Jim_Interp *interp, Jim_Obj *obj, c4_View view, int *indexPtr);
61 static int JimGetCursor (Jim_Interp *interp, Jim_Obj *obj, c4_Cursor *curPtr);
62 static int JimGetCursorView (Jim_Interp *interp, Jim_Obj *obj,
63 Jim_Obj **viewObjPtr);
64 static int JimCursorPos (Jim_Interp *interp, Jim_Obj *obj, Jim_Obj **posObjPtr);
65 static int JimIncrCursor (Jim_Interp *interp, Jim_Obj *obj, int offset);
66 static int JimSeekCursor (Jim_Interp *interp, Jim_Obj *obj, Jim_Obj *posObj);
68 /* Also accepts JIM_ERRMSG */
69 #define JIM_CURSOR_GET (1 << JIM_PRIV_FLAG_SHIFT)
70 #define JIM_CURSOR_SET (2 << JIM_PRIV_FLAG_SHIFT)
71 #define JIM_CURSOR_INSERT (4 << JIM_PRIV_FLAG_SHIFT)
73 static int JimCheckCursor (Jim_Interp *interp, Jim_Obj *curObj, int flags);
75 /* view handle */
76 static Jim_Obj *JimNewViewObj (Jim_Interp *interp, c4_View view);
77 static int JimGetView (Jim_Interp *interp, Jim_Obj *obj, c4_View *viewPtr);
78 static void JimPinView (Jim_Interp *interp, Jim_Obj *obj);
80 /* -------------------------------------------------------------------------
81 * Utilities
82 * ------------------------------------------------------------------------- */
84 static int JimCheckMkName(Jim_Interp *interp, Jim_Obj *name, const char *type)
86 const char *s;
87 int i, len;
89 s = Jim_GetString(name, &len);
91 if (len > 0 && s[0] == '-')
92 goto err;
93 for (i = 0; i < len; i++) {
94 if (!isnamech(s[i]))
95 goto err;
98 return JIM_OK;
100 err:
101 Jim_SetResultFormatted(interp, "expected %s name but got \"%#s\"", type ? type : "property", name);
102 return JIM_ERR;
105 static const char *const jim_mktype_options[] = {
106 "-integer",
107 "-long",
108 "-float",
109 "-double",
110 "-string",
111 "-subview",
112 /* FIXME "-binary", */
116 static const char *const jim_mktype_names[] = {
117 "integer",
118 "long",
119 "float",
120 "double",
121 "string",
122 "subview",
123 /* FIXME "binary", */
127 static const char jim_mktype_types[] = {
128 MK_PROPERTY_INT,
129 MK_PROPERTY_LONG,
130 MK_PROPERTY_FLOAT,
131 MK_PROPERTY_DOUBLE,
132 MK_PROPERTY_STRING,
133 MK_PROPERTY_VIEW,
134 /* MK_PROPERTY_BINARY, */
137 #define JIM_MKTYPES ((int)(sizeof(jim_mktype_types) / sizeof(jim_mktype_types[0])))
139 static const char *JimMkTypeName(char type)
141 int i;
143 for (i = 0; i < JIM_MKTYPES; i++) {
144 if (type == jim_mktype_types[i])
145 return jim_mktype_names[i];
147 return "(unknown type)";
150 static Jim_Obj *JimFromMkDescription(Jim_Interp *interp, const char *descr, const char **endPtr)
152 Jim_Obj *result;
153 const char *delim;
155 result = Jim_NewListObj(interp, NULL, 0);
156 for (;;) {
157 if (*descr == ']') {
158 descr++;
159 break;
161 else if (*descr == '\0')
162 break;
163 else if (*descr == ',')
164 descr++;
166 delim = strpbrk(descr, ",:[]");
167 /* JimPanic((!delim, "Invalid Metakit description string")); */
169 Jim_ListAppendElement(interp, result,
170 Jim_NewStringObj(interp, descr, delim - descr));
172 if (delim[0] == '[') {
173 Jim_ListAppendElement(interp, result,
174 JimFromMkDescription(interp, delim + 1, &descr));
176 else if (delim[0] == ':') {
177 Jim_ListAppendElement(interp, result,
178 Jim_NewStringObj(interp, JimMkTypeName(delim[1]), -1));
179 descr = delim + 2;
181 else {
182 /* Seems that Metakit never generates descriptions without type
183 * tags, but let's handle this just to be safe
186 Jim_ListAppendElement(interp, result,
187 Jim_NewStringObj(interp, JimMkTypeName(MK_PROPERTY_STRING), -1));
191 if (endPtr)
192 *endPtr = descr;
193 return result;
196 /* This allocates the buffer once per user call and stores it in a static
197 * variable. Recursive calls are distinguished by descrPtr == NULL.
199 static int JimToMkDescription(Jim_Interp *interp, Jim_Obj *descrObj, char **descrPtr)
201 static char *descr, *outPtr;
202 static int bufSize;
204 #define ENLARGE(size) do { \
205 if ((descr - outPtr) + (size) > bufSize) { \
206 bufSize = max(2*bufSize, (descr - outPtr) + (size)); \
207 descr = (char *)Jim_Realloc(descr, bufSize); \
209 } while(0)
211 int i, count;
212 Jim_Obj *name, *struc;
214 const char *rep;
215 int len;
217 count = Jim_ListLength(interp, descrObj);
218 if (count % 2) {
219 Jim_SetResultString(interp,
220 "view description must have an even number of elements", -1);
221 return JIM_ERR;
224 if (descrPtr) {
225 descr = (char *)Jim_Alloc(bufSize = JIM_MK_DESCR_LEN);
226 outPtr = descr;
229 for (i = 0; i < count; i += 2) {
230 Jim_ListIndex(interp, descrObj, i, &name, 0);
231 Jim_ListIndex(interp, descrObj, i + 1, &struc, 0);
233 if (JimCheckMkName(interp, name, NULL) != JIM_OK)
234 goto err;
236 rep = Jim_GetString(name, &len);
237 ENLARGE(len + 3); /* At least :T, or [], */
238 memcpy(outPtr, rep, len);
239 outPtr += len;
241 if (Jim_ListLength(interp, struc) == 1) {
242 int idx;
244 if (Jim_GetEnum(interp, struc, jim_mktype_names, &idx,
245 "property type", JIM_ERRMSG | JIM_ENUM_ABBREV) != JIM_OK)
246 goto err;
248 *outPtr++ = ':';
249 *outPtr++ = jim_mktype_types[idx];
251 else {
252 *outPtr++ = '[';
254 if (JimToMkDescription(interp, struc, NULL) != JIM_OK)
255 goto err;
257 ENLARGE(2); /* bracket, comma */
258 *outPtr++ = ']';
261 *outPtr++ = ',';
263 *(--outPtr) = '\0';
265 #undef ENLARGE
267 if (descrPtr) {
268 *descrPtr = (char *)Jim_Realloc(descr, strlen(descr) + 1);
269 descr = NULL; /* Safety measure */
272 return JIM_OK;
274 err:
276 if (descrPtr)
277 Jim_Free(descr);
279 return JIM_ERR;
282 static Jim_Obj *JimGetMkValue(Jim_Interp *interp, c4_Cursor cur, const c4_Property &prop)
284 switch (prop.Type()) {
285 case MK_PROPERTY_INT:
286 return Jim_NewIntObj(interp, ((c4_IntProp &)prop).Get(*cur));
287 case MK_PROPERTY_LONG:
288 return Jim_NewIntObj(interp, ((c4_LongProp &)prop).Get(*cur));
289 case MK_PROPERTY_FLOAT:
290 return Jim_NewDoubleObj(interp, ((c4_FloatProp &)prop).Get(*cur));
291 case MK_PROPERTY_DOUBLE:
292 return Jim_NewDoubleObj(interp, ((c4_DoubleProp &)prop).Get(*cur));
293 case MK_PROPERTY_STRING:
294 return Jim_NewStringObj(interp, ((c4_StringProp &)prop).Get(*cur), -1);
295 case MK_PROPERTY_VIEW:
296 return JimNewViewObj(interp, ((c4_ViewProp &)prop).Get(*cur));
298 case MK_PROPERTY_BINARY:
299 /* FIXME */
300 default:
301 /* FIXME Something more meaningful here? */
302 return Jim_NewEmptyStringObj(interp);
306 static int JimSetMkValue(Jim_Interp *interp, c4_Cursor cur, const c4_Property &prop, Jim_Obj *obj)
308 switch (prop.Type()) {
309 case MK_PROPERTY_INT: {
310 jim_wide value;
312 if (Jim_GetWide(interp, obj, &value) != JIM_OK)
313 return JIM_ERR;
315 ((c4_IntProp &)prop).Set(*cur, value);
316 return JIM_OK;
318 case MK_PROPERTY_LONG: {
319 jim_wide value;
321 if (Jim_GetWide(interp, obj, &value) != JIM_OK)
322 return JIM_ERR;
324 ((c4_LongProp &)prop).Set(*cur, value);
325 return JIM_OK;
327 case MK_PROPERTY_FLOAT: {
328 double value;
330 if (Jim_GetDouble(interp, obj, &value) != JIM_OK)
331 return JIM_ERR;
333 ((c4_FloatProp &)prop).Set(*cur, value);
334 return JIM_OK;
336 case MK_PROPERTY_DOUBLE: {
337 double value;
339 if (Jim_GetDouble(interp, obj, &value) != JIM_OK)
340 return JIM_ERR;
342 ((c4_DoubleProp &)prop).Set(*cur, value);
343 return JIM_OK;
345 case MK_PROPERTY_STRING: {
346 int len;
347 const char *rep;
349 rep = Jim_GetString(obj, &len);
350 if (len != (int)strlen(rep)) {
351 Jim_SetResultString(interp, "null characters are not allowed in Metakit strings", -1);
352 return JIM_ERR;
355 ((c4_StringProp &)prop).Set(*cur, rep);
356 return JIM_OK;
358 case MK_PROPERTY_VIEW: {
359 c4_View value;
361 if (JimGetView(interp, obj, &value) != JIM_OK)
362 return JIM_ERR;
364 ((c4_ViewProp &)prop).Set(*cur, value);
366 case MK_PROPERTY_BINARY:
367 /* FIXME */
368 default:
369 Jim_SetResultString(interp, "unsupported Metakit type", -1);
370 return JIM_ERR;
374 static int JimPipelineBoundary(int argc, Jim_Obj *const *argv) {
375 const char *rep;
376 int pipe, len;
378 for (pipe = 0; pipe < argc; pipe++) {
379 rep = Jim_GetString(argv[pipe], &len);
380 if (len == 1 && rep[0] == '|')
381 break;
383 return pipe;
386 /* -------------------------------------------------------------------------
387 * Property object
388 * ------------------------------------------------------------------------- */
390 #define JimPropertyValue(o) ((c4_Property *)((o)->internalRep.ptr))
392 static void FreePropertyInternalRep(Jim_Interp *interp, Jim_Obj *obj)
394 delete JimPropertyValue(obj);
397 static void DupPropertyInternalRep(Jim_Interp *interp, Jim_Obj *oldObj, Jim_Obj *newObj)
399 newObj->internalRep.ptr = new c4_Property(*JimPropertyValue(oldObj));
400 newObj->typePtr = oldObj->typePtr;
403 static void UpdateStringOfProperty(Jim_Obj* obj)
405 const char *name = JimPropertyValue(obj)->Name();
406 int len = strlen(name);
408 obj->bytes = (char *) Jim_Alloc(len + 1);
409 memcpy(obj->bytes, name, len + 1);
410 obj->length = len;
413 static Jim_ObjType propertyObjType = {
414 "mk.property",
415 FreePropertyInternalRep,
416 DupPropertyInternalRep,
417 UpdateStringOfProperty,
418 JIM_TYPE_NONE
421 static int JimGetProperty(Jim_Interp *interp, Jim_Obj *obj, c4_View view, const char *name, const c4_Property **propPtr)
423 int index;
425 if (obj->typePtr == &propertyObjType) {
426 index = view.FindProperty(JimPropertyValue(obj)->GetId());
428 else {
429 if (JimCheckMkName(interp, obj, name) != JIM_OK)
430 return JIM_ERR;
431 index = view.FindPropIndexByName(Jim_String(obj));
434 if (index != -1) {
435 *propPtr = &view.NthProperty(index);
436 return JIM_OK;
438 else {
439 Jim_SetResultFormatted(interp, "%s \"%#s\" does not exist",
440 name ? name : "property", obj);
441 return JIM_ERR;
445 static int JimGetPropertyTyped(Jim_Interp *interp, Jim_Obj *obj, char type, const c4_Property **propPtr)
447 c4_Property *prop;
449 if (obj->typePtr == &propertyObjType) {
450 if (JimPropertyValue(obj)->Type() != type) {
451 /* coerce the property type */
453 prop = new c4_Property(type, JimPropertyValue(obj)->Name());
454 delete JimPropertyValue(obj);
455 obj->internalRep.ptr = prop;
458 else {
459 if (JimCheckMkName(interp, obj, NULL) != JIM_OK)
460 return JIM_ERR;
462 prop = new c4_Property(type, Jim_String(obj));
464 Jim_FreeIntRep(interp, obj);
465 obj->typePtr = &propertyObjType;
466 obj->internalRep.ptr = (void *)prop;
469 *propPtr = JimPropertyValue(obj);
470 return JIM_OK;
473 static int JimGetNewProperty(Jim_Interp *interp, Jim_Obj *obj, c4_View view, char type, const c4_Property **propPtr)
475 const c4_Property *newp, *prop;
477 if (JimGetPropertyTyped(interp, obj, type, &newp) != JIM_OK)
478 return JIM_ERR;
480 prop = &view.NthProperty(view.AddProperty(*newp));
482 if (prop->Type() != newp->Type()) {
483 Jim_SetResultFormatted(interp, "property \"%#s\" is %s, not %s",
484 obj, JimMkTypeName(prop->Type()), JimMkTypeName(newp->Type()));
485 return JIM_ERR;
488 *propPtr = prop;
489 return JIM_OK;
492 static int JimGetProperties(Jim_Interp *interp, int objc, Jim_Obj *const *objv, c4_View view, c4_View *propsPtr)
494 int i;
495 const c4_Property *prop;
496 c4_View props;
498 for (i = 0; i < objc; i++) {
499 if (JimGetProperty(interp, objv[i], view, NULL, &prop) != JIM_OK)
500 return JIM_ERR;
502 props.AddProperty(*prop);
505 *propsPtr = props;
506 return JIM_OK;
509 static Jim_Obj *JimNewPropertyObj(Jim_Interp *interp, c4_Property prop)
511 Jim_Obj *obj;
513 obj = Jim_NewObj(interp);
514 obj->typePtr = &propertyObjType;
515 obj->bytes = NULL;
516 obj->internalRep.ptr = new c4_Property(prop);
517 return obj;
520 /* -------------------------------------------------------------------------
521 * Cursor object
522 * ------------------------------------------------------------------------- */
524 /* Position ---------------------------------------------------------------- */
526 /* A normal position if endFlag == 0; otherwise an offset from end+1 (!) */
527 typedef struct MkPosition {
528 int index;
529 int endFlag;
530 } MkPosition;
532 /* This is mostly the same as SetIndexFromAny, but preserves more information
533 * and allows multiple [+-]integer parts.
535 static int GetPosition(Jim_Interp *interp, Jim_Obj *obj, MkPosition *posPtr)
537 MkPosition pos;
538 const char *rep;
539 char *end;
540 int sign, offset;
542 rep = Jim_String(obj);
544 if (strncmp(rep, "end", 3) == 0) {
545 pos.endFlag = 1;
546 pos.index = -1;
548 rep += 3;
550 else {
551 pos.endFlag = 0;
552 pos.index = strtol(rep, &end, 10);
553 if (end == rep)
554 goto err;
556 rep = end;
559 while ((rep[0] == '+') || (rep[0] == '-')) {
560 sign = (rep[0] == '+' ? 1 : -1);
561 rep++;
563 offset = strtol(rep, &end, 10);
564 if (end == rep)
565 goto err;
567 pos.index += sign * offset;
568 rep = end;
571 while (isspace(UCHAR(*rep)))
572 rep++;
573 if (*rep != '\0')
574 goto err;
576 *posPtr = pos;
577 return JIM_OK;
579 err:
580 Jim_SetResultFormatted(interp, "expected cursor position but got \"%#s\"", obj);
581 return JIM_ERR;
584 static int PositionIndex(const MkPosition *posPtr, c4_View view)
586 if (posPtr->endFlag)
587 return view.GetSize() + posPtr->index;
588 else
589 return posPtr->index;
592 static int JimGetPosition(Jim_Interp *interp, Jim_Obj *obj, c4_View view, int *indexPtr)
594 MkPosition pos;
596 if (GetPosition(interp, obj, &pos) != JIM_OK)
597 return JIM_ERR;
599 *indexPtr = PositionIndex(&pos, view);
600 return JIM_OK;
603 /* Cursor type ------------------------------------------------------------- */
605 typedef struct MkCursor {
606 MkPosition pos;
607 Jim_Obj *viewObj;
608 } MkCursor;
610 #define JimCursorValue(obj) ((MkCursor *)(obj->internalRep.ptr))
611 static void FreeCursorInternalRep(Jim_Interp *interp, Jim_Obj *obj)
613 Jim_DecrRefCount(interp, JimCursorValue(obj)->viewObj);
614 Jim_Free(obj->internalRep.ptr);
617 static void DupCursorInternalRep(Jim_Interp *interp, Jim_Obj *oldObj, Jim_Obj *newObj)
619 newObj->internalRep.ptr = Jim_Alloc(sizeof(MkCursor));
620 *JimCursorValue(newObj) = *JimCursorValue(oldObj);
621 Jim_IncrRefCount(JimCursorValue(oldObj)->viewObj);
623 newObj->typePtr = oldObj->typePtr;
626 static void UpdateStringOfCursor(Jim_Obj *obj)
628 char buf[JIM_CURSOR_SPACE + 1];
629 MkCursor *curPtr = JimCursorValue(obj);
630 int idx, len;
632 len = snprintf(buf, JIM_CURSOR_SPACE + 1, "%s!", Jim_String(curPtr->viewObj));
634 if (curPtr->pos.endFlag) {
635 idx = curPtr->pos.index + 1;
636 if (idx == 0)
637 len += snprintf(buf + len, JIM_CURSOR_SPACE + 1 - len, "end");
638 else
639 len += snprintf(buf + len, JIM_CURSOR_SPACE + 1 - len, "end%+d", idx);
641 else {
642 len += snprintf(buf + len, JIM_CURSOR_SPACE + 1 - len, "%d",
643 curPtr->pos.index);
646 obj->bytes = (char *)Jim_Alloc(len + 1);
647 memcpy(obj->bytes, buf, len + 1);
648 obj->length = len;
651 static Jim_ObjType cursorObjType = {
652 "mk.cursor",
653 FreeCursorInternalRep,
654 DupCursorInternalRep,
655 UpdateStringOfCursor,
656 JIM_TYPE_REFERENCES
659 static int SetCursorFromAny(Jim_Interp *interp, Jim_Obj *obj)
661 const char *rep, *delim;
662 int len;
663 Jim_Obj *posObj;
664 MkCursor cur;
666 rep = Jim_GetString(obj, &len);
667 delim = strrchr(rep, '!');
669 if (!delim) {
670 Jim_SetResultFormatted(interp, "expected cursor but got \"%#s\"", obj);
671 return JIM_ERR;
674 cur.viewObj = Jim_NewStringObj(interp, rep, delim - rep);
675 posObj = Jim_NewStringObj(interp, delim + 1, len - (delim - rep) - 1);
677 if (GetPosition(interp, posObj, &cur.pos) != JIM_OK) {
678 Jim_FreeNewObj(interp, posObj);
679 Jim_FreeNewObj(interp, cur.viewObj);
680 return JIM_ERR;
683 Jim_FreeIntRep(interp, obj);
684 Jim_FreeNewObj(interp, posObj);
685 Jim_IncrRefCount(cur.viewObj);
687 obj->typePtr = &cursorObjType;
688 obj->internalRep.ptr = Jim_Alloc(sizeof(MkCursor));
689 *JimCursorValue(obj) = cur;
691 return JIM_OK;
694 /* Functions --------------------------------------------------------------- */
696 static int JimCursorPos(Jim_Interp *interp, Jim_Obj *obj, Jim_Obj **posObjPtr)
698 if (obj->typePtr != &cursorObjType && SetCursorFromAny(interp, obj) != JIM_OK)
699 return JIM_ERR;
701 *posObjPtr = Jim_NewStringObj(interp, strrchr(Jim_String(obj), '!') + 1, -1);
702 return JIM_OK;
705 static int JimGetCursorView(Jim_Interp *interp, Jim_Obj *obj, Jim_Obj **viewObjPtr)
707 if (obj->typePtr != &cursorObjType && SetCursorFromAny(interp, obj) != JIM_OK)
708 return JIM_ERR;
710 *viewObjPtr = JimCursorValue(obj)->viewObj;
711 return JIM_OK;
714 static int JimGetCursor(Jim_Interp *interp, Jim_Obj *obj, c4_Cursor *curPtr)
716 c4_View view;
718 if (obj->typePtr != &cursorObjType && SetCursorFromAny(interp, obj) != JIM_OK)
719 return JIM_ERR;
720 if (JimGetView(interp, JimCursorValue(obj)->viewObj, &view) != JIM_OK)
721 return JIM_ERR;
723 if (curPtr)
724 *curPtr = &view[PositionIndex(&JimCursorValue(obj)->pos, view)];
725 return JIM_OK;
728 static int JimIncrCursor(Jim_Interp *interp, Jim_Obj *obj, int offset)
730 /* JimPanic((Jim_IsShared(obj), "JimIncrCursor called with shared object")) */
732 if (obj->typePtr != &cursorObjType && SetCursorFromAny(interp, obj) != JIM_OK)
733 return JIM_ERR;
735 Jim_InvalidateStringRep(obj);
736 JimCursorValue(obj)->pos.index += offset;
737 return JIM_OK;
740 static int JimSeekCursor(Jim_Interp *interp, Jim_Obj *obj, Jim_Obj *posObj)
742 /* JimPanic((Jim_IsShared(obj), "JimSeekCursor called with shared object")) */
744 if (obj->typePtr != &cursorObjType && SetCursorFromAny(interp, obj) != JIM_OK)
745 return JIM_ERR;
747 Jim_InvalidateStringRep(obj);
748 return GetPosition(interp, posObj, &JimCursorValue(obj)->pos);
751 static int JimCheckCursor(Jim_Interp *interp, Jim_Obj *curObj, int flags)
753 static c4_View nullView;
755 c4_Cursor cur = &nullView[0];
756 int size;
758 if (JimGetCursor(interp, curObj, &cur) != JIM_OK)
759 return JIM_ERR;
760 size = (*cur).Container().GetSize();
762 if ((flags & JIM_CURSOR_GET) && (cur._index < 0 || cur._index >= size)) {
763 if (flags & JIM_ERRMSG) {
764 Jim_SetResultFormatted(interp,
765 "cursor \"%#s\" does not point to an existing row", curObj);
767 return JIM_ERR;
769 else if ((flags & JIM_CURSOR_SET) && cur._index < 0) {
770 if (flags & JIM_ERRMSG) {
771 Jim_SetResultFormatted(interp,
772 "cursor \"%#s\" points before start of view", curObj);
774 return JIM_ERR;
776 else if ((flags & JIM_CURSOR_INSERT) && (cur._index < 0 || cur._index > size)) {
777 if (flags & JIM_ERRMSG) {
778 Jim_SetResultFormatted(interp,
779 "cursor \"%#s\" does not point to a valid insert position", curObj);
781 return JIM_ERR;
784 return JIM_OK;
787 /* Records ----------------------------------------------------------------- */
789 static int cursor_cmd_get(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
791 c4_View view;
792 c4_Cursor cur = &view[0];
794 if (JimGetCursor(interp, argv[0], &cur) != JIM_OK)
795 return JIM_ERR;
796 if (JimCheckCursor(interp, argv[0], JIM_ERRMSG | JIM_CURSOR_GET) != JIM_OK)
797 return JIM_ERR;
799 view = (*cur).Container();
801 if (argc == 1) { /* Return all properties */
802 int i, count;
803 Jim_Obj *result;
805 result = Jim_NewListObj(interp, NULL, 0);
806 count = view.NumProperties();
808 for (i = 0; i < count; i++) {
809 c4_Property prop = view.NthProperty(i);
811 Jim_ListAppendElement(interp, result, JimNewPropertyObj(interp, prop));
812 Jim_ListAppendElement(interp, result, JimGetMkValue(interp, cur, prop));
815 Jim_SetResult(interp, result);
816 return JIM_OK;
818 else { /* Return a single property */
819 const c4_Property *propPtr;
820 int pipe;
822 pipe = JimPipelineBoundary(argc, argv);
823 if (pipe == 2) {
824 /* No type annotation, existing property */
825 if (JimGetProperty(interp, argv[1], view, NULL, &propPtr) != JIM_OK)
826 return JIM_ERR;
828 else if (pipe == 3) {
829 /* Explicit type annotation; the property may be new */
830 int idx;
832 if (Jim_GetEnum(interp, argv[1], jim_mktype_options, &idx,
833 "property type", JIM_ERRMSG | JIM_ENUM_ABBREV) != JIM_OK)
834 return JIM_ERR;
835 if (JimGetNewProperty(interp, argv[2], view, jim_mktype_types[idx], &propPtr) != JIM_OK)
836 return JIM_ERR;
838 else {
839 Jim_WrongNumArgs(interp, 0, NULL, "cursor get ?-type? ?prop?");
840 return JIM_ERR;
843 Jim_SetResult(interp, JimGetMkValue(interp, cur, *propPtr));
845 if (pipe == argc)
846 return JIM_OK;
847 else
848 return Jim_EvalObjPrefix(interp, Jim_GetResult(interp), argc - pipe - 1, argv + pipe + 1);
852 static int cursor_cmd_set(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
854 c4_View view;
855 c4_Cursor cur = &view[0];
856 const c4_Property *propPtr;
857 int i, oldSize;
859 if (JimGetCursor(interp, argv[0], &cur) != JIM_OK)
860 return JIM_ERR;
861 if (JimCheckCursor(interp, argv[0], JIM_ERRMSG | JIM_CURSOR_SET) != JIM_OK)
862 return JIM_ERR;
864 view = (*cur).Container();
865 oldSize = view.GetSize();
867 if (cur._index >= oldSize)
868 view.SetSize(cur._index + 1);
870 if (argc == 2) {
871 /* Update everything except subviews from a dictionary in argv[1].
872 * No new properties are permitted.
875 int objc;
876 Jim_Obj **objv;
878 if (Jim_DictPairs(interp, argv[1], &objv, &objc) != JIM_OK)
879 goto err;
881 for (i = 0; i < objc; i += 2) {
882 if (JimGetProperty(interp, objv[i], view, NULL, &propPtr) != JIM_OK ||
883 JimSetMkValue(interp, cur, *propPtr, objv[i+1]) != JIM_OK)
885 Jim_Free(objv);
886 goto err;
890 else {
891 /* Update everything from argv[1..]. New properties are permitted if
892 * explicitly typed.
895 for (i = 1; i < argc; i += 2) {
896 if (Jim_String(argv[i])[0] == '-') {
897 int idx;
899 if (i + 2 >= argc) {
900 Jim_WrongNumArgs(interp, 2, argv, "?-type? prop value ?...?");
901 goto err;
904 if (Jim_GetEnum(interp, argv[i], jim_mktype_options, &idx,
905 "property type", JIM_ERRMSG | JIM_ENUM_ABBREV) != JIM_OK)
906 goto err;
907 if (JimGetNewProperty(interp, argv[i+1], view, jim_mktype_types[idx], &propPtr) != JIM_OK)
908 goto err;
909 i++;
911 else {
912 if (i + 1 >= argc) {
913 Jim_WrongNumArgs(interp, 2, argv, "?-type? prop value ?...?");
914 goto err;
917 if (JimGetProperty(interp, argv[i], view, NULL, &propPtr) != JIM_OK)
918 goto err;
921 if (JimSetMkValue(interp, cur, *propPtr, argv[i+1]) != JIM_OK)
922 goto err;
926 return JIM_OK;
928 err:
929 view.SetSize(oldSize);
930 return JIM_ERR;
933 static int cursor_cmd_insert(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
935 c4_View view;
936 c4_Cursor cur = &view[0];
937 jim_wide count;
939 if (JimGetCursor(interp, argv[0], &cur) != JIM_OK)
940 return JIM_ERR;
941 if (JimCheckCursor(interp, argv[0], JIM_ERRMSG | JIM_CURSOR_INSERT) != JIM_OK)
942 return JIM_ERR;
944 view = (*cur).Container();
946 if (argc == 1)
947 count = 1;
948 else {
949 if (Jim_GetWide(interp, argv[1], &count) != JIM_OK)
950 return JIM_ERR;
953 if (count > 0) {
954 c4_Row empty;
955 view.InsertAt(cur._index, empty, (int)count);
958 Jim_SetEmptyResult(interp);
959 return JIM_OK;
962 static int cursor_cmd_remove(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
964 c4_View view;
965 c4_Cursor cur = &view[0];
966 int pos;
967 jim_wide count;
969 if (JimGetCursor(interp, argv[0], &cur) != JIM_OK)
970 return JIM_ERR;
971 if (JimCheckCursor(interp, argv[0], JIM_ERRMSG | JIM_CURSOR_SET) != JIM_OK)
972 return JIM_ERR;
974 view = (*cur).Container();
975 pos = cur._index;
977 if (argc == 1)
978 count = 1;
979 else {
980 if (Jim_GetWide(interp, argv[1], &count) != JIM_OK)
981 return JIM_ERR;
984 if (pos + count < view.GetSize())
985 count = view.GetSize() - pos;
987 if (pos < view.GetSize())
988 view.RemoveAt(pos, (int)count);
990 return JIM_OK;
993 /* Attributes -------------------------------------------------------------- */
995 static int cursor_cmd_view(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
997 Jim_Obj *viewObj;
999 if (JimGetCursorView(interp, argv[0], &viewObj) != JIM_OK)
1000 return JIM_ERR;
1002 JimPinView(interp, viewObj);
1003 Jim_SetResult(interp, viewObj);
1004 return JIM_OK;
1007 /* Positioning ------------------------------------------------------------- */
1009 static int cursor_cmd_tell(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
1011 if (argc == 1) {
1012 Jim_Obj *result;
1014 if (JimCursorPos(interp, argv[0], &result) != JIM_OK)
1015 return JIM_ERR;
1016 Jim_SetResult(interp, result);
1018 else {
1019 static c4_View nullView;
1020 c4_Cursor cur = &nullView[0];
1022 if (!Jim_CompareStringImmediate(interp, argv[0], "-absolute")) {
1023 Jim_SetResultFormatted(interp,
1024 "bad option \"%#s\": must be -absolute", argv[0]);
1025 return JIM_ERR;
1028 if (JimGetCursor(interp, argv[1], &cur) != JIM_OK)
1029 return JIM_ERR;
1031 Jim_SetResultInt(interp, cur._index);
1034 return JIM_OK;
1037 static int cursor_cmd_validfor(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
1039 static const char *options[] = {
1040 "get", "set", "insert", "remove", 0
1042 static int optflags[] = {
1043 JIM_CURSOR_GET,
1044 JIM_CURSOR_SET,
1045 JIM_CURSOR_INSERT,
1046 JIM_CURSOR_SET
1049 int idx;
1051 if (argc == 1)
1052 idx = 0;
1053 else {
1054 if (Jim_GetEnum(interp, argv[0], options, &idx, NULL,
1055 JIM_ERRMSG | JIM_ENUM_ABBREV) != JIM_OK)
1056 return JIM_ERR;
1059 if (JimGetCursor(interp, argv[argc-1], NULL) != JIM_OK)
1060 return JIM_ERR;
1062 Jim_SetResultBool(interp, JimCheckCursor(interp, argv[argc-1], optflags[idx]) == JIM_OK);
1063 return JIM_OK;
1066 static int cursor_cmd_seek(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
1068 Jim_Obj *curObj;
1070 curObj = Jim_GetVariable(interp, argv[0], JIM_ERRMSG | JIM_UNSHARED);
1071 if (curObj == NULL)
1072 return JIM_ERR;
1074 if (JimSeekCursor(interp, curObj, argv[1]) != JIM_OK)
1075 return JIM_ERR;
1077 Jim_SetResult(interp, curObj);
1078 return JIM_OK;
1081 static int cursor_cmd_incr(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
1083 Jim_Obj *curObj;
1084 jim_wide offset;
1086 if (argc == 1)
1087 offset = 1;
1088 else {
1089 if (Jim_GetWide(interp, argv[1], &offset) != JIM_OK)
1090 return JIM_ERR;
1093 curObj = Jim_GetVariable(interp, argv[0], JIM_ERRMSG | JIM_UNSHARED);
1094 if (curObj == NULL)
1095 return JIM_ERR;
1097 if (JimIncrCursor(interp, curObj, (int)offset) != JIM_OK)
1098 return JIM_ERR;
1100 Jim_SetResult(interp, curObj);
1101 return JIM_OK;
1104 /* Command table ----------------------------------------------------------- */
1106 static const jim_subcmd_type cursor_command_table[] = {
1108 /* Records */
1110 { "get", "cur ?-type? ?prop?",
1111 cursor_cmd_get,
1112 1, -1,
1114 "Get the whole record or a specific property at the cursor"
1116 { "set", "cur [dict | ?-type? field value ?...?]",
1117 cursor_cmd_set,
1118 1, -1,
1120 "Update the record at the cursor"
1122 { "insert", "cur ?count?",
1123 cursor_cmd_insert,
1124 1, 2,
1126 "Insert a specified number of empty rows at the cursor (default 1)"
1128 { "remove", "cur ?count?",
1129 cursor_cmd_remove,
1130 1, 2,
1132 "Remove a specified number of rows at the cursor (default 1)"
1135 /* Attributes */
1137 { "view", "cur",
1138 cursor_cmd_view,
1139 1, 1,
1141 "Get the view the cursor points into"
1144 /* Positioning */
1146 { "tell", "?-absolute? cur",
1147 cursor_cmd_tell,
1148 1, 2,
1150 "Get the position of the cursor"
1152 { "validfor", "?command? cur",
1153 cursor_cmd_validfor,
1154 1, 2,
1156 "Checks if the cursor is valid for get (default), set or insert commands"
1158 { "seek", "curVar index",
1159 cursor_cmd_seek,
1160 2, 2,
1162 "Seek to the specified index in the view"
1164 { "incr", "curVar ?offset?",
1165 cursor_cmd_incr,
1166 1, 2,
1168 "Move the cursor offset records from its current position (default 1)"
1171 { 0 }
1174 static int JimCursorCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
1176 Jim_Obj *cmdObj;
1178 if (argc < 2) {
1179 Jim_WrongNumArgs(interp, 1, argv, "command ...");
1180 return JIM_ERR;
1183 cmdObj = Jim_NewStringObj(interp, "cursor ", -1);
1184 Jim_AppendObj(interp, cmdObj, argv[1]);
1186 if (Jim_GetCommand(interp, cmdObj, 0) != NULL)
1187 return Jim_EvalObjPrefix(interp, cmdObj, argc - 2, argv + 2);
1188 else {
1189 Jim_FreeNewObj(interp, cmdObj);
1190 return Jim_CallSubCmd(interp,
1191 Jim_ParseSubCmd(interp, cursor_command_table, argc, argv), argc, argv);
1195 /* -------------------------------------------------------------------------
1196 * View handle
1197 * ------------------------------------------------------------------------- */
1199 /* Views aren't really Jim objects; instead, they are Tk-style commands with
1200 * oo.tcl-like lifetime management. Additionally, all views are initially
1201 * created as one-shot, meaning that they die after one command. Call
1202 * JimPinView to make a view object persistent.
1204 * It is valid to rename a view in the Tcl land, but by doing this you take
1205 * the responsibility of destroying the object when it's no longer needed.
1206 * Any cursors that pointed into the view become invalid.
1209 static int JimViewSubCmdProc(Jim_Interp *interp, int argc, Jim_Obj *const *argv);
1210 static int JimOneShotViewSubCmdProc(Jim_Interp *interp, int argc, Jim_Obj *const *argv);
1212 /* Unary operations -------------------------------------------------------- */
1214 #define UNOP(name, Method) \
1215 static int view_cmd_##name(Jim_Interp *interp, int argc, Jim_Obj *const *argv) \
1217 const c4_View *viewPtr = (const c4_View *)Jim_CmdPrivData(interp); \
1219 Jim_SetResult(interp, JimNewViewObj(interp, viewPtr->Method())); \
1220 return JIM_OK; \
1223 UNOP(copy, Duplicate)
1224 UNOP(clone, Clone)
1225 UNOP(unique, Unique)
1227 #undef UNOP
1229 static int view_cmd_blocked(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
1231 const c4_View *viewPtr = (const c4_View *)Jim_CmdPrivData(interp);
1233 if (viewPtr->GetSize() != 1 ||
1234 strcmp(viewPtr->NthProperty(0).Name(), "_B") != 0 ||
1235 viewPtr->NthProperty(0).Type() != MK_PROPERTY_VIEW)
1237 Jim_SetResultString(interp,
1238 "blocked view must have exactly one subview property called _B", -1);
1239 return JIM_ERR;
1242 Jim_SetResult(interp, JimNewViewObj(interp, viewPtr->Blocked()));
1243 return JIM_OK;
1246 /* Binary operations ------------------------------------------------------- */
1248 #define BINOP(name, Method) \
1249 static int view_cmd_##name(Jim_Interp *interp, int argc, Jim_Obj *const *argv) \
1251 const c4_View *viewPtr = (const c4_View *)Jim_CmdPrivData(interp); \
1252 c4_View otherView; \
1254 if (JimGetView(interp, argv[0], &otherView) != JIM_OK) \
1255 return JIM_ERR; \
1257 Jim_SetResult(interp, JimNewViewObj(interp, viewPtr->Method(otherView))); \
1258 return JIM_OK; \
1261 BINOP(pair, Pair)
1262 BINOP(concat, Concat)
1263 BINOP(product, Product)
1265 BINOP(union, Union)
1266 BINOP(intersect, Intersect)
1267 BINOP(minus, Minus)
1268 BINOP(different, Different)
1270 #undef BINOP
1272 /* Projections ------------------------------------------------------------- */
1274 static int view_cmd_project(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
1276 const c4_View *viewPtr = (const c4_View *)Jim_CmdPrivData(interp);
1277 c4_View props;
1279 if (JimGetProperties(interp, argc, argv, *viewPtr, &props) != JIM_OK)
1280 return JIM_ERR;
1282 Jim_SetResult(interp, JimNewViewObj(interp, viewPtr->Project(props)));
1283 return JIM_OK;
1286 static int view_cmd_without(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
1288 const c4_View *viewPtr = (const c4_View *)Jim_CmdPrivData(interp);
1289 c4_View props;
1291 if (JimGetProperties(interp, argc, argv, *viewPtr, &props) != JIM_OK)
1292 return JIM_ERR;
1294 Jim_SetResult(interp, JimNewViewObj(interp, viewPtr->ProjectWithout(props)));
1295 return JIM_OK;
1298 static int view_cmd_range(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
1300 const c4_View *viewPtr = (const c4_View *)Jim_CmdPrivData(interp);
1301 int start, end;
1302 jim_wide step;
1304 if (JimGetPosition(interp, argv[0], *viewPtr, &start) != JIM_OK ||
1305 JimGetPosition(interp, argv[1], *viewPtr, &end) != JIM_OK)
1307 return JIM_ERR;
1310 if (argc == 2)
1311 step = 1;
1312 else if (Jim_GetWide(interp, argv[2], &step) != JIM_OK)
1313 return JIM_ERR;
1315 Jim_SetResult(interp, JimNewViewObj(interp, viewPtr->Slice(start, end + 1, (int)step)));
1316 return JIM_OK;
1319 /* Ordering ---------------------------------------------------------------- */
1321 static int view_cmd_sort(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
1323 const c4_View *viewPtr = (const c4_View *)Jim_CmdPrivData(interp);
1324 c4_View sortProps, revProps;
1325 const c4_Property *propPtr;
1326 int i, len;
1328 const char *rep;
1329 int reverse;
1330 Jim_Obj *propObj;
1332 /* Special case: property names may be preceded with a dash. Use
1333 * a temporary object in this case.
1336 for (i = 0; i < argc; i++) {
1337 propObj = argv[i];
1339 rep = Jim_GetString(argv[i], &len);
1340 reverse = (len > 0 && rep[0] == '-');
1342 if (reverse)
1343 propObj = Jim_NewStringObj(interp, rep + 1, len - 1);
1345 if (JimGetProperty(interp, propObj, *viewPtr, NULL, &propPtr) != JIM_OK) {
1346 if (reverse)
1347 Jim_FreeNewObj(interp, propObj);
1348 return JIM_ERR;
1351 sortProps.AddProperty(*propPtr);
1352 if (reverse) {
1353 revProps.AddProperty(*propPtr);
1354 Jim_FreeNewObj(interp, propObj);
1358 if (sortProps.GetSize() == 0)
1359 Jim_SetResult(interp, JimNewViewObj(interp, viewPtr->Sort()));
1360 else if (revProps.GetSize() == 0)
1361 Jim_SetResult(interp, JimNewViewObj(interp, viewPtr->SortOn(sortProps)));
1362 else
1363 Jim_SetResult(interp, JimNewViewObj(interp, viewPtr->SortOnReverse(sortProps, revProps)));
1365 return JIM_OK;
1368 /* Metakit core seems to be doing something similar for SortOn, but neither
1369 * Ordered nor Hash use it, for unknown reason.
1372 static int BubbleProperties(Jim_Interp *interp, c4_View orig, int objc, Jim_Obj *const *objv, c4_View *projPtr)
1374 c4_View proj;
1375 const c4_Property *propPtr;
1376 int i, count;
1378 for (i = 0; i < objc; i++) {
1379 if (JimGetProperty(interp, objv[i], orig, NULL, &propPtr) != JIM_OK)
1380 return JIM_ERR;
1381 proj.AddProperty(*propPtr);
1384 count = orig.NumProperties();
1385 for (i = 0; i < count; i++)
1386 proj.AddProperty(orig.NthProperty(i));
1388 *projPtr = proj;
1389 return JIM_OK;
1392 static int view_cmd_ordered(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
1394 const c4_View *viewPtr = (const c4_View *)Jim_CmdPrivData(interp);
1395 c4_View proj;
1397 if (BubbleProperties(interp, *viewPtr, argc, argv, &proj) != JIM_OK)
1398 return JIM_ERR;
1400 Jim_SetResult(interp, JimNewViewObj(interp, proj.Ordered(argc)));
1401 return JIM_OK;
1404 static int view_cmd_hash(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
1406 const c4_View *viewPtr = (const c4_View *)Jim_CmdPrivData(interp);
1407 c4_View hash, proj;
1409 if (JimGetView(interp, argv[0], &hash) != JIM_OK)
1410 return JIM_ERR;
1412 if (hash.GetSize() != 2 ||
1413 strcmp(hash.NthProperty(0).Name(), "_H") != 0 ||
1414 hash.NthProperty(0).Type() != MK_PROPERTY_INT ||
1415 strcmp(hash.NthProperty(1).Name(), "_R") != 0 ||
1416 hash.NthProperty(1).Type() != MK_PROPERTY_INT) /* Ouch. */
1418 Jim_SetResultString(interp,
1419 "hash view must be laid out as {_H integer _R integer}", -1);
1420 return JIM_ERR;
1423 if (BubbleProperties(interp, *viewPtr, argc - 1, argv + 1, &proj) != JIM_OK)
1424 return JIM_ERR;
1426 Jim_SetResult(interp, JimNewViewObj(interp, proj.Hash(hash, argc - 1)));
1427 return JIM_OK;
1430 /* Relational operations --------------------------------------------------- */
1432 static int view_cmd_join(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
1434 const c4_View *viewPtr = (const c4_View *)Jim_CmdPrivData(interp);
1435 c4_View other, props;
1436 int outer, off;
1438 if (JimGetView(interp, argv[0], &other) != JIM_OK)
1439 return JIM_ERR;
1441 off = 1; outer = 0;
1442 if (Jim_CompareStringImmediate(interp, argv[1], "-outer")) {
1443 off++; outer = 1;
1446 if (JimGetProperties(interp, argc - off, argv + off, *viewPtr, &props) != JIM_OK)
1447 return JIM_ERR;
1449 Jim_SetResult(interp, JimNewViewObj(interp, viewPtr->Join(props, other, outer)));
1450 return JIM_OK;
1453 static int view_cmd_group(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
1455 const c4_View *viewPtr = (const c4_View *)Jim_CmdPrivData(interp);
1456 const c4_Property *subviewPtr;
1457 c4_View props;
1459 if (JimGetPropertyTyped(interp, argv[0], MK_PROPERTY_VIEW, &subviewPtr) != JIM_OK)
1460 return JIM_ERR;
1462 if (JimGetProperties(interp, argc - 1, argv + 1, *viewPtr, &props) != JIM_OK)
1463 return JIM_ERR;
1465 Jim_SetResult(interp, JimNewViewObj(interp, viewPtr->GroupBy(props, *(c4_ViewProp *)subviewPtr)));
1466 return JIM_OK;
1469 static int view_cmd_flatten(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
1471 const c4_View *viewPtr = (const c4_View *)Jim_CmdPrivData(interp);
1472 const c4_Property *subviewPtr;
1474 if (JimGetProperty(interp, argv[0], *viewPtr, NULL, &subviewPtr) != JIM_OK)
1475 return JIM_ERR;
1477 if (subviewPtr->Type() != MK_PROPERTY_VIEW) {
1478 Jim_SetResultFormatted(interp, "expected a subview property but got %s one",
1479 JimMkTypeName(subviewPtr->Type()));
1480 return JIM_ERR;
1483 Jim_SetResult(interp, JimNewViewObj(interp, viewPtr->JoinProp(*(c4_ViewProp *)subviewPtr)));
1484 return JIM_OK;
1487 /* View queries ------------------------------------------------------------ */
1489 static int view_cmd_properties(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
1491 const c4_View *viewPtr = (const c4_View *) Jim_CmdPrivData(interp);
1492 Jim_SetResult(interp, JimViewPropertiesList(interp, *viewPtr));
1493 return JIM_OK;
1496 static int view_cmd_size(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
1498 const c4_View *viewPtr = (const c4_View *) Jim_CmdPrivData(interp);
1499 Jim_SetResultInt(interp, viewPtr->GetSize());
1500 return JIM_OK;
1503 static int view_cmd_resize(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
1505 c4_View *view = (c4_View *) Jim_CmdPrivData(interp);
1506 jim_wide size;
1508 if (Jim_GetWide(interp, argv[0], &size) != JIM_OK)
1509 return JIM_ERR;
1510 if (size < 0 || size > INT_MAX) {
1511 Jim_SetResultFormatted(interp,
1512 "view size \"%#s\" is out of range", argv[0]);
1513 return JIM_ERR;
1516 view->SetSize((int)size);
1517 Jim_SetResult(interp, argv[0]);
1518 return JIM_OK;
1521 static int view_cmd_type(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
1523 const c4_View *viewPtr = (const c4_View *)Jim_CmdPrivData(interp);
1525 if (argc == 1) {
1526 const c4_Property *propPtr;
1528 if (JimGetProperty(interp, argv[0], *viewPtr, NULL, &propPtr) != JIM_OK)
1529 return JIM_ERR;
1531 Jim_SetResultString(interp, JimMkTypeName(propPtr->Type()), -1);
1533 else {
1534 Jim_Obj *result;
1535 int i, count;
1537 result = Jim_NewListObj(interp, NULL, 0);
1538 count = viewPtr->NumProperties();
1540 for (i = 0; i < count; i++) {
1541 c4_Property prop = viewPtr->NthProperty(i);
1542 Jim_ListAppendElement(interp, result, JimNewPropertyObj(interp, prop));
1543 Jim_ListAppendElement(interp, result,
1544 Jim_NewStringObj(interp, JimMkTypeName(prop.Type()), -1));
1547 Jim_SetResult(interp, result);
1550 return JIM_OK;
1553 /* View lifetime ----------------------------------------------------------- */
1555 static int view_cmd_pin(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
1557 JimPinView(interp, argv[0]);
1558 Jim_SetResult(interp, argv[0]);
1559 return JIM_OK;
1562 static int view_cmd_as(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
1564 JimPinView(interp, argv[0]);
1565 Jim_SetVariable(interp, argv[2], argv[0]);
1566 Jim_SetResult(interp, argv[0]);
1567 return JIM_OK;
1570 static int view_cmd_destroy(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
1572 Jim_DeleteCommand(interp, Jim_String(argv[0]));
1573 return JIM_OK;
1576 /* Command table ----------------------------------------------------------- */
1578 static const jim_subcmd_type view_command_table[] = {
1580 /* Unary operations */
1582 { "copy", "",
1583 view_cmd_copy,
1584 0, 0,
1586 "Create a copy of the view with exactly the same data"
1588 { "clone", "",
1589 view_cmd_clone,
1590 0, 0,
1592 "Create an empty view with the same properties as this one"
1594 { "unique", "",
1595 view_cmd_unique,
1596 0, 0,
1598 "Derived view without any duplicate rows (read-only, no change notifications)"
1600 { "blocked", "",
1601 view_cmd_blocked,
1602 0, 0,
1604 "Build a scalable \"blocked\" out of a view with a single subview property called _B"
1607 /* Binary operations */
1609 #define BINOP(name, descr) \
1610 { #name, "otherView", \
1611 view_cmd_##name, \
1612 1, 1, 0, \
1613 descr \
1616 BINOP(pair, "Pairwise concatenation of two views"),
1617 BINOP(concat, "Concatenation of two views; unlike union, doesn't remove duplicates"),
1618 BINOP(product, "Cartesian product of two views, i.e. every row in view paired with every row in otherView"),
1620 /* Set operations */
1622 #define SETOP(name, descr) BINOP(name, descr "; works only if all the rows are unique")
1624 SETOP(union, "Set union of two views (read-only, no change notifications)"),
1625 SETOP(intersect, "Set intersection of two views"),
1626 SETOP(different, "Symmetric difference of two views"),
1627 SETOP(minus, "Set minus, i.e. all rows from view not in otherView"),
1629 #undef SETOP
1631 #undef BINOP
1633 /* Projections and selections */
1635 { "project", "prop ?prop ...?",
1636 view_cmd_project,
1637 1, -1,
1639 "View projection: only the specified properties, in the specified order"
1641 { "without", "prop ?prop ...?",
1642 view_cmd_without,
1643 1, -1,
1645 "View projection: remove the specified properties"
1647 { "range", "first last ?step?",
1648 view_cmd_range,
1649 2, 3,
1651 "Range or slice of the view (read-write, no change notifications)"
1654 /* Ordering */
1656 { "sort", "?[prop|-prop] ...?",
1657 view_cmd_sort,
1658 0, -1,
1660 "Derived view sorted on the specified properties (in order), or on all properties"
1662 { "ordered", "prop ?prop ...?",
1663 view_cmd_ordered,
1664 1, -1,
1666 "Consider the underlying view ordered on the specified properties"
1668 { "hash", "hashView prop ?prop ...?",
1669 view_cmd_hash,
1670 2, -1,
1672 "Mapped view maintaining a hash table on the key consisting of the specified properties"
1675 /* Relational operations */
1677 { "join", "view ?-outer? prop ?prop ...?",
1678 view_cmd_join,
1679 2, -1,
1681 "Relational join with view on the specified properties"
1683 { "group", "subviewName prop ?prop ...?",
1684 view_cmd_group,
1685 1, -1,
1687 "Group rows with equal specified properties, move all other properties into subview"
1689 { "flatten", "subviewProp",
1690 view_cmd_flatten,
1691 1, 1,
1693 "Flatten the specified subview; the inverse of group"
1696 /* Attributes */
1698 { "properties", "",
1699 view_cmd_properties,
1700 0, 0,
1702 "List the properties in this view"
1704 { "size", "",
1705 view_cmd_size,
1706 0, 0,
1708 "Return the number of records in the view"
1710 { "resize", "newSize",
1711 view_cmd_resize,
1712 1, 1,
1714 "Set the number of records in the view"
1716 { "type", "?prop?",
1717 view_cmd_type,
1718 0, 1,
1720 "Return the type of an existing property, or of all properties"
1723 /* Lifetime management */
1725 { "pin", "",
1726 view_cmd_pin,
1727 0, 0,
1728 JIM_MODFLAG_FULLARGV,
1729 "Marks the view as persistent"
1731 { "as", "varName",
1732 view_cmd_as,
1733 1, 1,
1734 JIM_MODFLAG_FULLARGV,
1735 "Marks the view as persistent and assigns it to the given variable"
1737 { "destroy", "",
1738 view_cmd_destroy,
1739 0, 0,
1740 JIM_MODFLAG_FULLARGV,
1741 "Destroys the view explicitly"
1744 { 0 }
1747 static void JimViewDelProc(Jim_Interp *interp, void *privData)
1749 delete (c4_View *)privData;
1752 static int JimViewSubCmdProc(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
1754 int pipe, result;
1755 Jim_Obj *cmdObj;
1757 pipe = JimPipelineBoundary(argc, argv);
1759 if (pipe < 1) {
1760 Jim_WrongNumArgs(interp, 1, argv, "command ...");
1761 return JIM_ERR;
1764 /* Check for a Tcl command first, and try builtins afterwards.
1765 * We have to do it in this order so that Jim_ParseSubCmd isn't too greedy
1766 * about abbreviations, and still it can't now detect ambigous abbrevs
1767 * properly :( Tcl commands cannot be abbreviated at all.
1770 cmdObj = Jim_NewStringObj(interp, "mk.view ", -1);
1771 Jim_AppendObj(interp, cmdObj, argv[1]);
1773 /* The command will be cached even though we discard the result */
1774 if (Jim_GetCommand(interp, cmdObj, 0) != NULL) {
1775 /* Shuffle the arguments: $view cmd args... => {mk.view cmd} $view args... */
1777 Jim_Obj **objv = (Jim_Obj **)Jim_Alloc(pipe * sizeof(Jim_Obj *));
1778 objv[0] = cmdObj;
1779 objv[1] = argv[0];
1780 memcpy(objv + 2, argv + 2, (pipe - 2) * sizeof(Jim_Obj *));
1782 result = Jim_EvalObjVector(interp, pipe, objv);
1784 Jim_Free(objv);
1785 } else {
1786 Jim_FreeNewObj(interp, cmdObj);
1787 result = Jim_CallSubCmd(interp, Jim_ParseSubCmd(interp, view_command_table, pipe, argv), pipe, argv);
1790 if (result != JIM_OK || pipe == argc)
1791 return result;
1792 else
1793 return Jim_EvalObjPrefix(interp, Jim_GetResult(interp), argc - pipe - 1, argv + pipe + 1);
1796 static int JimOneShotViewSubCmdProc(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
1798 int result;
1799 Jim_Cmd *cmd;
1801 result = JimViewSubCmdProc(interp, argc, argv);
1803 cmd = Jim_GetCommand(interp, argv[0], 0);
1804 if (cmd && !cmd->isproc && cmd->u.native.cmdProc == JimOneShotViewSubCmdProc)
1805 Jim_DeleteCommand(interp, Jim_String(argv[0]));
1807 return result;
1810 static int JimViewFinalizerProc(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
1812 /* We won't succeed here if the user renamed the command, and this is right */
1813 Jim_DeleteCommand(interp, Jim_String(argv[1]));
1814 return JIM_OK;
1817 static Jim_Obj *JimNewViewObj(Jim_Interp *interp, c4_View view) {
1818 Jim_Obj *tag, *ref;
1820 tag = Jim_NewStringObj(interp, "mk.view", -1);
1821 ref = Jim_NewReference(interp, tag, tag, Jim_NewStringObj(interp, "mk.view.finalizer", -1));
1822 Jim_CreateCommand(interp, Jim_String(ref),
1823 JimOneShotViewSubCmdProc, new c4_View(view), JimViewDelProc);
1825 return ref;
1828 static int JimGetView(Jim_Interp *interp, Jim_Obj *obj, c4_View *viewPtr)
1830 Jim_Cmd *cmd = Jim_GetCommand(interp, obj, 0);
1832 if (cmd == NULL || cmd->isproc || cmd->u.native.delProc != JimViewDelProc) {
1833 Jim_SetResultFormatted(interp, "invalid view object \"%#s\"", obj);
1834 return JIM_ERR;
1837 *viewPtr = *(c4_View *)cmd->u.native.privData;
1838 return JIM_OK;
1841 /* Only call this against known view objects. */
1842 static void JimPinView(Jim_Interp *interp, Jim_Obj *obj)
1844 Jim_Cmd *cmd = Jim_GetCommand(interp, obj, 0);
1845 /* JimPanic((cmd == NULL, "JimPinView called against non-view"))
1846 JimPanic((cmd->u.native.delProc != JimViewDelProc, "JimPinView called against non-view")) */
1847 cmd->u.native.cmdProc = JimViewSubCmdProc;
1850 static Jim_Obj *JimViewPropertiesList(Jim_Interp *interp, c4_View view)
1852 int i, count;
1853 Jim_Obj *result;
1855 result = Jim_NewListObj(interp, NULL, 0);
1856 count = view.NumProperties();
1858 for (i = 0; i < count; i++) {
1859 Jim_ListAppendElement(interp, result, Jim_NewStringObj(interp,
1860 view.NthProperty(i).Name(), -1));
1863 return result;
1866 /* ----------------------------------------------------------------------------
1867 * Storage handle
1868 * ---------------------------------------------------------------------------- */
1870 /* These are also commands, like views, but must be managed explicitly by the
1871 * user. Quite like file handles, actually.
1874 typedef struct MkStorage {
1875 unsigned flags;
1876 Jim_Obj *filename;
1877 c4_Storage storage;
1878 c4_Cursor content;
1879 } MkStorage;
1881 #define JIM_MKFLAG_INMEMORY 0x0001
1882 #define JIM_MKFLAG_READONLY 0x0002
1883 #define JIM_MKFLAG_EXTEND 0x0004
1884 #define JIM_MKFLAG_AUTOCOMMIT 0x0008
1886 /* Attributes -------------------------------------------------------------- */
1888 static int storage_cmd_autocommit(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
1890 MkStorage *mk = (MkStorage *)Jim_CmdPrivData(interp);
1892 if (argc == 1) {
1893 jim_wide flag;
1895 if (Jim_GetWide(interp, argv[0], &flag) != JIM_OK)
1896 return JIM_ERR;
1898 if (flag)
1899 mk->flags |= JIM_MKFLAG_AUTOCOMMIT;
1900 else
1901 mk->flags &= ~JIM_MKFLAG_AUTOCOMMIT;
1902 mk->storage.AutoCommit(flag);
1905 Jim_SetResultBool(interp, (mk->flags & JIM_MKFLAG_AUTOCOMMIT) != 0);
1906 return JIM_OK;
1909 static int storage_cmd_readonly(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
1911 MkStorage *mk = (MkStorage *)Jim_CmdPrivData(interp);
1913 Jim_SetResultBool(interp, (mk->flags & JIM_MKFLAG_READONLY) != 0);
1914 return JIM_OK;
1917 /* Views ------------------------------------------------------------------- */
1919 static int storage_cmd_views(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
1921 MkStorage *mk = (MkStorage *)Jim_CmdPrivData(interp);
1923 Jim_SetResult(interp, JimViewPropertiesList(interp, mk->storage));
1924 return JIM_OK;
1927 static int storage_cmd_view(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
1929 MkStorage *mk = (MkStorage *)Jim_CmdPrivData(interp);
1930 const c4_Property *propPtr;
1932 if (JimGetProperty(interp, argv[0], mk->storage, "view", &propPtr) != JIM_OK)
1933 return JIM_ERR;
1934 Jim_SetResult(interp, JimGetMkValue(interp, mk->content, *propPtr));
1936 if (argc == 1)
1937 return JIM_OK;
1938 else {
1939 if (!Jim_CompareStringImmediate(interp, argv[1], "|")) {
1940 Jim_SetResultFormatted(interp,
1941 "expected start of a pipeline but got \"%#s\"", argv[1]);
1942 return JIM_ERR;
1944 return Jim_EvalObjPrefix(interp, Jim_GetResult(interp), argc - 2, argv + 2);
1948 static int storage_cmd_structure(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
1950 MkStorage *mk = (MkStorage *)Jim_CmdPrivData(interp);
1952 if (argc < 2) { /* Query */
1953 const char *name;
1955 if (argc == 0)
1956 name = NULL;
1957 else {
1958 const c4_Property *propPtr;
1960 if (JimGetProperty(interp, argv[0], mk->storage, "view", &propPtr) != JIM_OK)
1961 return JIM_ERR;
1962 name = propPtr->Name();
1965 Jim_SetResult(interp, JimFromMkDescription(interp,
1966 mk->storage.Description(name), NULL));
1968 else { /* Modify */
1969 char *descr;
1970 const char *name;
1971 int len, dlen;
1973 if (JimCheckMkName(interp, argv[0], "view") != JIM_OK)
1974 return JIM_ERR;
1975 name = Jim_GetString(argv[0], &len);
1977 if (JimToMkDescription(interp, argv[1], &descr) != JIM_OK)
1978 return JIM_ERR;
1979 dlen = strlen(descr);
1981 descr = (char *)Jim_Realloc(descr, dlen + len + 2);
1982 memmove(descr + len + 1, descr, dlen);
1983 memcpy(descr, name, len);
1984 descr[len] = '[';
1985 descr[len + 1 + dlen] = ']';
1986 descr[len + 1 + dlen + 1] = '\0';
1988 mk->storage.GetAs(descr);
1990 Jim_Free(descr);
1993 return JIM_OK;
1996 /* Store operations -------------------------------------------------------- */
1998 static int storage_cmd_commit(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
2000 MkStorage *mk = (MkStorage *)Jim_CmdPrivData(interp);
2002 if (mk->flags & JIM_MKFLAG_INMEMORY) {
2003 Jim_SetResultString(interp, "cannot commit an in-memory storage", -1);
2004 return JIM_ERR;
2006 else if (mk->flags & JIM_MKFLAG_READONLY) {
2007 Jim_SetResultString(interp, "cannot commit a read-only storage", -1);
2008 return JIM_ERR;
2011 if (mk->storage.Commit(0)) {
2012 Jim_SetEmptyResult(interp);
2013 return JIM_OK;
2015 else {
2016 Jim_SetResultString(interp, "I/O error during commit", -1);
2017 return JIM_ERR;
2021 static int storage_cmd_rollback(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
2023 MkStorage *mk = (MkStorage *)Jim_CmdPrivData(interp);
2025 if (mk->flags & JIM_MKFLAG_INMEMORY) {
2026 Jim_SetResultString(interp, "cannot rollback an in-memory storage", -1);
2027 return JIM_ERR;
2030 if (mk->storage.Rollback(0)) {
2031 Jim_SetEmptyResult(interp);
2032 return JIM_OK;
2034 else {
2035 Jim_SetResultString(interp, "I/O error during rollback", -1);
2036 return JIM_ERR;
2040 static int storage_cmd_close(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
2042 return Jim_DeleteCommand(interp, Jim_String(argv[0]));
2045 /* Command table ----------------------------------------------------------- */
2047 static const jim_subcmd_type storage_command_table[] = {
2049 /* Options */
2051 { "autocommit", "?value?",
2052 storage_cmd_autocommit,
2053 0, 1,
2055 "Query or modify the auto-commit option of this storage"
2057 { "readonly", "",
2058 storage_cmd_readonly,
2059 0, 0,
2061 "Returns the read-only status of this storage"
2064 /* Views */
2066 { "views", "",
2067 storage_cmd_views,
2068 0, 0,
2070 "Returns the list of views stored here"
2072 { "view", "viewName",
2073 storage_cmd_view,
2074 1, -1,
2076 "Retrieve the view specified by viewName"
2078 { "structure", "?viewName? ?description?",
2079 storage_cmd_structure,
2080 0, 2,
2082 "Query or modify the structure of this storage"
2085 /* Store operations */
2087 { "commit", "",
2088 storage_cmd_commit,
2089 0, 0,
2091 "Commit the changes to disk"
2093 { "rollback", "",
2094 storage_cmd_rollback,
2095 0, 0,
2097 "Revert to the saved state"
2099 { "close", "",
2100 storage_cmd_close,
2101 0, 0,
2102 JIM_MODFLAG_FULLARGV,
2103 "Close this storage"
2106 { 0 }
2109 static void JimStorageDelProc(Jim_Interp *interp, void *privData)
2111 MkStorage *mk = (MkStorage *)privData;
2113 mk->storage.~c4_Storage();
2114 mk->content.~c4_Cursor();
2115 Jim_DecrRefCount(interp, mk->filename);
2116 Jim_Free(mk);
2119 static int JimStorageSubCmdProc(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
2121 Jim_Obj *cmdObj;
2123 cmdObj = Jim_NewStringObj(interp, "mk.storage ", -1);
2124 Jim_AppendObj(interp, cmdObj, argv[1]);
2126 if (Jim_GetCommand(interp, cmdObj, 0) != NULL) {
2127 int result;
2129 Jim_Obj **objv = (Jim_Obj **)Jim_Alloc(argc * sizeof(Jim_Obj *));
2130 objv[0] = cmdObj;
2131 objv[1] = argv[0];
2132 memcpy(objv + 2, argv + 2, (argc - 2) * sizeof(Jim_Obj *));
2134 result = Jim_EvalObjVector(interp, argc, objv);
2136 Jim_Free(objv);
2137 return result;
2138 } else {
2139 Jim_FreeNewObj(interp, cmdObj);
2140 return Jim_CallSubCmd(interp, Jim_ParseSubCmd(interp,
2141 storage_command_table, argc, argv), argc, argv);
2145 /* -------------------------------------------------------------------------
2146 * storage ?options? ?filename?
2148 * Creates a new metakit storage object, optionally backed by a file.
2150 * Options apply only when filename is given; these include:
2152 * -readonly Open the file in read-only mode
2153 * -original Open the file in read-only mode, discarding possible extends
2154 * -extend Open the file in extend mode
2155 * -nocommit Do not commit the changes when the storage is closed
2156 * ------------------------------------------------------------------------- */
2158 static int JimStorageCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
2160 MkStorage *mk;
2161 char buf[MK_CMD_LEN];
2162 int i, mode;
2164 static const char *const options[] = {
2165 "-readonly",
2166 "-original",
2167 "-extend",
2168 "-nocommit",
2171 enum {
2172 OPT_READONLY,
2173 OPT_ORIGINAL,
2174 OPT_EXTEND,
2175 OPT_NOCOMMIT
2177 int option;
2179 mk = (MkStorage *)Jim_Alloc(sizeof(MkStorage));
2180 mk->flags = JIM_MKFLAG_AUTOCOMMIT;
2181 mode = MK_MODE_READWRITE;
2182 for (i = 1; i < argc - 1; i++ ) {
2183 if (Jim_GetEnum(interp, argv[i], options, &option, NULL, JIM_ERRMSG) != JIM_OK) {
2184 Jim_Free(mk);
2185 return JIM_ERR;
2188 switch (option) {
2189 case OPT_READONLY:
2190 if (mode != MK_MODE_READWRITE)
2191 goto modeconflict;
2193 mode = MK_MODE_READONLY;
2194 mk->flags |= JIM_MKFLAG_READONLY;
2195 break;
2197 case OPT_ORIGINAL:
2198 if (mode != MK_MODE_READWRITE)
2199 goto modeconflict;
2201 mode = MK_MODE_ORIGINAL;
2202 mk->flags |= JIM_MKFLAG_READONLY;
2203 break;
2205 case OPT_EXTEND:
2206 if (mode != MK_MODE_READWRITE)
2207 goto modeconflict;
2209 mode = MK_MODE_EXTEND;
2210 mk->flags |= JIM_MKFLAG_EXTEND;
2211 break;
2213 case OPT_NOCOMMIT:
2214 mk->flags &= ~JIM_MKFLAG_AUTOCOMMIT;
2215 break;
2219 if (argc > 1) {
2220 new(&mk->storage) c4_Storage(Jim_String(argv[argc-1]), mode);
2222 if (!mk->storage.Strategy().IsValid()) {
2223 mk->storage.~c4_Storage();
2224 Jim_Free(mk);
2225 Jim_SetResultFormatted(interp, "could not open storage \"%#s\"", argv[argc-1]);
2226 return JIM_ERR;
2229 mk->filename = argv[argc-1];
2231 if ((mk->flags & JIM_MKFLAG_AUTOCOMMIT) && !(mk->flags & JIM_MKFLAG_READONLY))
2232 mk->storage.AutoCommit(1);
2234 else {
2235 mk->flags |= JIM_MKFLAG_INMEMORY;
2237 new(&mk->storage) c4_Storage();
2238 mk->filename = Jim_NewEmptyStringObj(interp);
2240 new(&mk->content) c4_Cursor(&mk->storage[0]);
2241 Jim_IncrRefCount(mk->filename);
2243 snprintf(buf, sizeof(buf), "mk.handle%ld", Jim_GetId(interp));
2244 Jim_CreateCommand(interp, buf, JimStorageSubCmdProc, mk, JimStorageDelProc);
2245 Jim_SetResultString(interp, buf, -1);
2246 return JIM_OK;
2248 modeconflict:
2249 Jim_Free(mk);
2250 Jim_SetResultString(interp, "only one of -readonly, -original and -extend may be specified", -1);
2251 return JIM_ERR;
2254 /* -------------------------------------------------------------------------
2255 * Initialization code
2256 * ------------------------------------------------------------------------- */
2258 int Jim_mkInit(Jim_Interp *interp)
2260 char version[MK_VERSION_SPACE];
2262 snprintf(version, MK_VERSION_SPACE, "%d.%d.%d",
2263 d4_MetakitLibraryVersion / 100,
2264 d4_MetakitLibraryVersion % 100 / 10,
2265 d4_MetakitLibraryVersion % 10);
2267 if (Jim_PackageProvide(interp, "mk", version, JIM_ERRMSG))
2268 return JIM_ERR;
2270 Jim_CreateCommand(interp, "storage", JimStorageCommand, NULL, NULL);
2271 Jim_CreateCommand(interp, "cursor", JimCursorCommand, NULL, NULL);
2272 Jim_CreateCommand(interp, "mk.view.finalizer", JimViewFinalizerProc, NULL, NULL);
2274 return JIM_OK;
2277 } /* extern "C" */