tcltest: do a better job of cleanup up after tests
[jimtcl.git] / jim-mk.cpp
blob8ff1b32954444511c22cfc239de4b8aa0fe5ed38
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, \
1615 BINOP(pair, "Pairwise concatenation of two views"),
1616 BINOP(concat, "Concatenation of two views; unlike union, doesn't remove duplicates"),
1617 BINOP(product, "Cartesian product of two views, i.e. every row in view paired with every row in otherView"),
1619 /* Set operations */
1621 #define SETOP(name, descr) BINOP(name, descr "; works only if all the rows are unique")
1623 SETOP(union, "Set union of two views (read-only, no change notifications)"),
1624 SETOP(intersect, "Set intersection of two views"),
1625 SETOP(different, "Symmetric difference of two views"),
1626 SETOP(minus, "Set minus, i.e. all rows from view not in otherView"),
1628 #undef SETOP
1630 #undef BINOP
1632 /* Projections and selections */
1634 { "project", "prop ?prop ...?",
1635 view_cmd_project,
1636 1, -1,
1638 /*"View projection: only the specified properties, in the specified order"*/
1640 { "without", "prop ?prop ...?",
1641 view_cmd_without,
1642 1, -1,
1644 /*"View projection: remove the specified properties"*/
1646 { "range", "first last ?step?",
1647 view_cmd_range,
1648 2, 3,
1650 /*"Range or slice of the view (read-write, no change notifications)"*/
1653 /* Ordering */
1655 { "sort", "?[prop|-prop] ...?",
1656 view_cmd_sort,
1657 0, -1,
1659 /*"Derived view sorted on the specified properties (in order), or on all properties"*/
1661 { "ordered", "prop ?prop ...?",
1662 view_cmd_ordered,
1663 1, -1,
1665 /*"Consider the underlying view ordered on the specified properties"*/
1667 { "hash", "hashView prop ?prop ...?",
1668 view_cmd_hash,
1669 2, -1,
1671 /*"Mapped view maintaining a hash table on the key consisting of the specified properties"*/
1674 /* Relational operations */
1676 { "join", "view ?-outer? prop ?prop ...?",
1677 view_cmd_join,
1678 2, -1,
1680 /*"Relational join with view on the specified properties"*/
1682 { "group", "subviewName prop ?prop ...?",
1683 view_cmd_group,
1684 1, -1,
1686 /*"Group rows with equal specified properties, move all other properties into subview"*/
1688 { "flatten", "subviewProp",
1689 view_cmd_flatten,
1690 1, 1,
1692 /*"Flatten the specified subview; the inverse of group"*/
1695 /* Attributes */
1697 { "properties", "",
1698 view_cmd_properties,
1699 0, 0,
1701 /*"List the properties in this view"*/
1703 { "size", "",
1704 view_cmd_size,
1705 0, 0,
1707 /*"Return the number of records in the view"*/
1709 { "resize", "newSize",
1710 view_cmd_resize,
1711 1, 1,
1713 /*"Set the number of records in the view"*/
1715 { "type", "?prop?",
1716 view_cmd_type,
1717 0, 1,
1719 /*"Return the type of an existing property, or of all properties"*/
1722 /* Lifetime management */
1724 { "pin", "",
1725 view_cmd_pin,
1726 0, 0,
1727 JIM_MODFLAG_FULLARGV,
1728 /*"Marks the view as persistent"*/
1730 { "as", "varName",
1731 view_cmd_as,
1732 1, 1,
1733 JIM_MODFLAG_FULLARGV,
1734 /*"Marks the view as persistent and assigns it to the given variable"*/
1736 { "destroy", "",
1737 view_cmd_destroy,
1738 0, 0,
1739 JIM_MODFLAG_FULLARGV,
1740 /*"Destroys the view explicitly"*/
1743 { 0 }
1746 static void JimViewDelProc(Jim_Interp *interp, void *privData)
1748 delete (c4_View *)privData;
1751 static int JimViewSubCmdProc(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
1753 int pipe, result;
1754 Jim_Obj *cmdObj;
1756 pipe = JimPipelineBoundary(argc, argv);
1758 if (pipe < 1) {
1759 Jim_WrongNumArgs(interp, 1, argv, "command ...");
1760 return JIM_ERR;
1763 /* Check for a Tcl command first, and try builtins afterwards.
1764 * We have to do it in this order so that Jim_ParseSubCmd isn't too greedy
1765 * about abbreviations, and still it can't now detect ambigous abbrevs
1766 * properly :( Tcl commands cannot be abbreviated at all.
1769 cmdObj = Jim_NewStringObj(interp, "mk.view ", -1);
1770 Jim_AppendObj(interp, cmdObj, argv[1]);
1772 /* The command will be cached even though we discard the result */
1773 if (Jim_GetCommand(interp, cmdObj, 0) != NULL) {
1774 /* Shuffle the arguments: $view cmd args... => {mk.view cmd} $view args... */
1776 Jim_Obj **objv = (Jim_Obj **)Jim_Alloc(pipe * sizeof(Jim_Obj *));
1777 objv[0] = cmdObj;
1778 objv[1] = argv[0];
1779 memcpy(objv + 2, argv + 2, (pipe - 2) * sizeof(Jim_Obj *));
1781 result = Jim_EvalObjVector(interp, pipe, objv);
1783 Jim_Free(objv);
1784 } else {
1785 Jim_FreeNewObj(interp, cmdObj);
1786 result = Jim_CallSubCmd(interp, Jim_ParseSubCmd(interp, view_command_table, pipe, argv), pipe, argv);
1789 if (result != JIM_OK || pipe == argc)
1790 return result;
1791 else
1792 return Jim_EvalObjPrefix(interp, Jim_GetResult(interp), argc - pipe - 1, argv + pipe + 1);
1795 static int JimOneShotViewSubCmdProc(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
1797 int result;
1798 Jim_Cmd *cmd;
1800 result = JimViewSubCmdProc(interp, argc, argv);
1802 cmd = Jim_GetCommand(interp, argv[0], 0);
1803 if (cmd && !cmd->isproc && cmd->u.native.cmdProc == JimOneShotViewSubCmdProc)
1804 Jim_DeleteCommand(interp, Jim_String(argv[0]));
1806 return result;
1809 static int JimViewFinalizerProc(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
1811 /* We won't succeed here if the user renamed the command, and this is right */
1812 Jim_DeleteCommand(interp, Jim_String(argv[1]));
1813 return JIM_OK;
1816 static Jim_Obj *JimNewViewObj(Jim_Interp *interp, c4_View view) {
1817 Jim_Obj *tag, *ref;
1819 tag = Jim_NewStringObj(interp, "mk.view", -1);
1820 ref = Jim_NewReference(interp, tag, tag, Jim_NewStringObj(interp, "mk.view.finalizer", -1));
1821 Jim_CreateCommand(interp, Jim_String(ref),
1822 JimOneShotViewSubCmdProc, new c4_View(view), JimViewDelProc);
1824 return ref;
1827 static int JimGetView(Jim_Interp *interp, Jim_Obj *obj, c4_View *viewPtr)
1829 Jim_Cmd *cmd = Jim_GetCommand(interp, obj, 0);
1831 if (cmd == NULL || cmd->isproc || cmd->u.native.delProc != JimViewDelProc) {
1832 Jim_SetResultFormatted(interp, "invalid view object \"%#s\"", obj);
1833 return JIM_ERR;
1836 *viewPtr = *(c4_View *)cmd->u.native.privData;
1837 return JIM_OK;
1840 /* Only call this against known view objects. */
1841 static void JimPinView(Jim_Interp *interp, Jim_Obj *obj)
1843 Jim_Cmd *cmd = Jim_GetCommand(interp, obj, 0);
1844 /* JimPanic((cmd == NULL, "JimPinView called against non-view"))
1845 JimPanic((cmd->u.native.delProc != JimViewDelProc, "JimPinView called against non-view")) */
1846 cmd->u.native.cmdProc = JimViewSubCmdProc;
1849 static Jim_Obj *JimViewPropertiesList(Jim_Interp *interp, c4_View view)
1851 int i, count;
1852 Jim_Obj *result;
1854 result = Jim_NewListObj(interp, NULL, 0);
1855 count = view.NumProperties();
1857 for (i = 0; i < count; i++) {
1858 Jim_ListAppendElement(interp, result, Jim_NewStringObj(interp,
1859 view.NthProperty(i).Name(), -1));
1862 return result;
1865 /* ----------------------------------------------------------------------------
1866 * Storage handle
1867 * ---------------------------------------------------------------------------- */
1869 /* These are also commands, like views, but must be managed explicitly by the
1870 * user. Quite like file handles, actually.
1873 typedef struct MkStorage {
1874 unsigned flags;
1875 Jim_Obj *filename;
1876 c4_Storage storage;
1877 c4_Cursor content;
1878 } MkStorage;
1880 #define JIM_MKFLAG_INMEMORY 0x0001
1881 #define JIM_MKFLAG_READONLY 0x0002
1882 #define JIM_MKFLAG_EXTEND 0x0004
1883 #define JIM_MKFLAG_AUTOCOMMIT 0x0008
1885 /* Attributes -------------------------------------------------------------- */
1887 static int storage_cmd_autocommit(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
1889 MkStorage *mk = (MkStorage *)Jim_CmdPrivData(interp);
1891 if (argc == 1) {
1892 jim_wide flag;
1894 if (Jim_GetWide(interp, argv[0], &flag) != JIM_OK)
1895 return JIM_ERR;
1897 if (flag)
1898 mk->flags |= JIM_MKFLAG_AUTOCOMMIT;
1899 else
1900 mk->flags &= ~JIM_MKFLAG_AUTOCOMMIT;
1901 mk->storage.AutoCommit(flag);
1904 Jim_SetResultBool(interp, (mk->flags & JIM_MKFLAG_AUTOCOMMIT) != 0);
1905 return JIM_OK;
1908 static int storage_cmd_readonly(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
1910 MkStorage *mk = (MkStorage *)Jim_CmdPrivData(interp);
1912 Jim_SetResultBool(interp, (mk->flags & JIM_MKFLAG_READONLY) != 0);
1913 return JIM_OK;
1916 /* Views ------------------------------------------------------------------- */
1918 static int storage_cmd_views(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
1920 MkStorage *mk = (MkStorage *)Jim_CmdPrivData(interp);
1922 Jim_SetResult(interp, JimViewPropertiesList(interp, mk->storage));
1923 return JIM_OK;
1926 static int storage_cmd_view(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
1928 MkStorage *mk = (MkStorage *)Jim_CmdPrivData(interp);
1929 const c4_Property *propPtr;
1931 if (JimGetProperty(interp, argv[0], mk->storage, "view", &propPtr) != JIM_OK)
1932 return JIM_ERR;
1933 Jim_SetResult(interp, JimGetMkValue(interp, mk->content, *propPtr));
1935 if (argc == 1)
1936 return JIM_OK;
1937 else {
1938 if (!Jim_CompareStringImmediate(interp, argv[1], "|")) {
1939 Jim_SetResultFormatted(interp,
1940 "expected start of a pipeline but got \"%#s\"", argv[1]);
1941 return JIM_ERR;
1943 return Jim_EvalObjPrefix(interp, Jim_GetResult(interp), argc - 2, argv + 2);
1947 static int storage_cmd_structure(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
1949 MkStorage *mk = (MkStorage *)Jim_CmdPrivData(interp);
1951 if (argc < 2) { /* Query */
1952 const char *name;
1954 if (argc == 0)
1955 name = NULL;
1956 else {
1957 const c4_Property *propPtr;
1959 if (JimGetProperty(interp, argv[0], mk->storage, "view", &propPtr) != JIM_OK)
1960 return JIM_ERR;
1961 name = propPtr->Name();
1964 Jim_SetResult(interp, JimFromMkDescription(interp,
1965 mk->storage.Description(name), NULL));
1967 else { /* Modify */
1968 char *descr;
1969 const char *name;
1970 int len, dlen;
1972 if (JimCheckMkName(interp, argv[0], "view") != JIM_OK)
1973 return JIM_ERR;
1974 name = Jim_GetString(argv[0], &len);
1976 if (JimToMkDescription(interp, argv[1], &descr) != JIM_OK)
1977 return JIM_ERR;
1978 dlen = strlen(descr);
1980 descr = (char *)Jim_Realloc(descr, dlen + len + 2);
1981 memmove(descr + len + 1, descr, dlen);
1982 memcpy(descr, name, len);
1983 descr[len] = '[';
1984 descr[len + 1 + dlen] = ']';
1985 descr[len + 1 + dlen + 1] = '\0';
1987 mk->storage.GetAs(descr);
1989 Jim_Free(descr);
1992 return JIM_OK;
1995 /* Store operations -------------------------------------------------------- */
1997 static int storage_cmd_commit(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
1999 MkStorage *mk = (MkStorage *)Jim_CmdPrivData(interp);
2001 if (mk->flags & JIM_MKFLAG_INMEMORY) {
2002 Jim_SetResultString(interp, "cannot commit an in-memory storage", -1);
2003 return JIM_ERR;
2005 else if (mk->flags & JIM_MKFLAG_READONLY) {
2006 Jim_SetResultString(interp, "cannot commit a read-only storage", -1);
2007 return JIM_ERR;
2010 if (mk->storage.Commit(0)) {
2011 Jim_SetEmptyResult(interp);
2012 return JIM_OK;
2014 else {
2015 Jim_SetResultString(interp, "I/O error during commit", -1);
2016 return JIM_ERR;
2020 static int storage_cmd_rollback(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
2022 MkStorage *mk = (MkStorage *)Jim_CmdPrivData(interp);
2024 if (mk->flags & JIM_MKFLAG_INMEMORY) {
2025 Jim_SetResultString(interp, "cannot rollback an in-memory storage", -1);
2026 return JIM_ERR;
2029 if (mk->storage.Rollback(0)) {
2030 Jim_SetEmptyResult(interp);
2031 return JIM_OK;
2033 else {
2034 Jim_SetResultString(interp, "I/O error during rollback", -1);
2035 return JIM_ERR;
2039 static int storage_cmd_close(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
2041 return Jim_DeleteCommand(interp, Jim_String(argv[0]));
2044 /* Command table ----------------------------------------------------------- */
2046 static const jim_subcmd_type storage_command_table[] = {
2048 /* Options */
2050 { "autocommit", "?value?",
2051 storage_cmd_autocommit,
2052 0, 1,
2054 /*"Query or modify the auto-commit option of this storage"*/
2056 { "readonly", "",
2057 storage_cmd_readonly,
2058 0, 0,
2060 /*"Returns the read-only status of this storage"*/
2063 /* Views */
2065 { "views", "",
2066 storage_cmd_views,
2067 0, 0,
2069 /*"Returns the list of views stored here"*/
2071 { "view", "viewName",
2072 storage_cmd_view,
2073 1, -1,
2075 /*"Retrieve the view specified by viewName"*/
2077 { "structure", "?viewName? ?description?",
2078 storage_cmd_structure,
2079 0, 2,
2081 /*"Query or modify the structure of this storage"*/
2084 /* Store operations */
2086 { "commit", "",
2087 storage_cmd_commit,
2088 0, 0,
2090 /*"Commit the changes to disk"*/
2092 { "rollback", "",
2093 storage_cmd_rollback,
2094 0, 0,
2096 /*"Revert to the saved state"*/
2098 { "close", "",
2099 storage_cmd_close,
2100 0, 0,
2101 JIM_MODFLAG_FULLARGV,
2102 /*"Close this storage"*/
2105 { 0 }
2108 static void JimStorageDelProc(Jim_Interp *interp, void *privData)
2110 MkStorage *mk = (MkStorage *)privData;
2112 mk->storage.~c4_Storage();
2113 mk->content.~c4_Cursor();
2114 Jim_DecrRefCount(interp, mk->filename);
2115 Jim_Free(mk);
2118 static int JimStorageSubCmdProc(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
2120 Jim_Obj *cmdObj;
2122 cmdObj = Jim_NewStringObj(interp, "mk.storage ", -1);
2123 Jim_AppendObj(interp, cmdObj, argv[1]);
2125 if (Jim_GetCommand(interp, cmdObj, 0) != NULL) {
2126 int result;
2128 Jim_Obj **objv = (Jim_Obj **)Jim_Alloc(argc * sizeof(Jim_Obj *));
2129 objv[0] = cmdObj;
2130 objv[1] = argv[0];
2131 memcpy(objv + 2, argv + 2, (argc - 2) * sizeof(Jim_Obj *));
2133 result = Jim_EvalObjVector(interp, argc, objv);
2135 Jim_Free(objv);
2136 return result;
2137 } else {
2138 Jim_FreeNewObj(interp, cmdObj);
2139 return Jim_CallSubCmd(interp, Jim_ParseSubCmd(interp,
2140 storage_command_table, argc, argv), argc, argv);
2144 /* -------------------------------------------------------------------------
2145 * storage ?options? ?filename?
2147 * Creates a new metakit storage object, optionally backed by a file.
2149 * Options apply only when filename is given; these include:
2151 * -readonly Open the file in read-only mode
2152 * -original Open the file in read-only mode, discarding possible extends
2153 * -extend Open the file in extend mode
2154 * -nocommit Do not commit the changes when the storage is closed
2155 * ------------------------------------------------------------------------- */
2157 static int JimStorageCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
2159 MkStorage *mk;
2160 char buf[MK_CMD_LEN];
2161 int i, mode;
2163 static const char *const options[] = {
2164 "-readonly",
2165 "-original",
2166 "-extend",
2167 "-nocommit",
2170 enum {
2171 OPT_READONLY,
2172 OPT_ORIGINAL,
2173 OPT_EXTEND,
2174 OPT_NOCOMMIT
2176 int option;
2178 mk = (MkStorage *)Jim_Alloc(sizeof(MkStorage));
2179 mk->flags = JIM_MKFLAG_AUTOCOMMIT;
2180 mode = MK_MODE_READWRITE;
2181 for (i = 1; i < argc - 1; i++ ) {
2182 if (Jim_GetEnum(interp, argv[i], options, &option, NULL, JIM_ERRMSG) != JIM_OK) {
2183 Jim_Free(mk);
2184 return JIM_ERR;
2187 switch (option) {
2188 case OPT_READONLY:
2189 if (mode != MK_MODE_READWRITE)
2190 goto modeconflict;
2192 mode = MK_MODE_READONLY;
2193 mk->flags |= JIM_MKFLAG_READONLY;
2194 break;
2196 case OPT_ORIGINAL:
2197 if (mode != MK_MODE_READWRITE)
2198 goto modeconflict;
2200 mode = MK_MODE_ORIGINAL;
2201 mk->flags |= JIM_MKFLAG_READONLY;
2202 break;
2204 case OPT_EXTEND:
2205 if (mode != MK_MODE_READWRITE)
2206 goto modeconflict;
2208 mode = MK_MODE_EXTEND;
2209 mk->flags |= JIM_MKFLAG_EXTEND;
2210 break;
2212 case OPT_NOCOMMIT:
2213 mk->flags &= ~JIM_MKFLAG_AUTOCOMMIT;
2214 break;
2218 if (argc > 1) {
2219 new(&mk->storage) c4_Storage(Jim_String(argv[argc-1]), mode);
2221 if (!mk->storage.Strategy().IsValid()) {
2222 mk->storage.~c4_Storage();
2223 Jim_Free(mk);
2224 Jim_SetResultFormatted(interp, "could not open storage \"%#s\"", argv[argc-1]);
2225 return JIM_ERR;
2228 mk->filename = argv[argc-1];
2230 if ((mk->flags & JIM_MKFLAG_AUTOCOMMIT) && !(mk->flags & JIM_MKFLAG_READONLY))
2231 mk->storage.AutoCommit(1);
2233 else {
2234 mk->flags |= JIM_MKFLAG_INMEMORY;
2236 new(&mk->storage) c4_Storage();
2237 mk->filename = Jim_NewEmptyStringObj(interp);
2239 new(&mk->content) c4_Cursor(&mk->storage[0]);
2240 Jim_IncrRefCount(mk->filename);
2242 snprintf(buf, sizeof(buf), "mk.handle%ld", Jim_GetId(interp));
2243 Jim_CreateCommand(interp, buf, JimStorageSubCmdProc, mk, JimStorageDelProc);
2244 Jim_SetResultString(interp, buf, -1);
2245 return JIM_OK;
2247 modeconflict:
2248 Jim_Free(mk);
2249 Jim_SetResultString(interp, "only one of -readonly, -original and -extend may be specified", -1);
2250 return JIM_ERR;
2253 /* -------------------------------------------------------------------------
2254 * Initialization code
2255 * ------------------------------------------------------------------------- */
2257 int Jim_mkInit(Jim_Interp *interp)
2259 char version[MK_VERSION_SPACE];
2261 snprintf(version, MK_VERSION_SPACE, "%d.%d.%d",
2262 d4_MetakitLibraryVersion / 100,
2263 d4_MetakitLibraryVersion % 100 / 10,
2264 d4_MetakitLibraryVersion % 10);
2266 if (Jim_PackageProvide(interp, "mk", version, JIM_ERRMSG))
2267 return JIM_ERR;
2269 Jim_CreateCommand(interp, "storage", JimStorageCommand, NULL, NULL);
2270 Jim_CreateCommand(interp, "cursor", JimCursorCommand, NULL, NULL);
2271 Jim_CreateCommand(interp, "mk.view.finalizer", JimViewFinalizerProc, NULL, NULL);
2273 return JIM_OK;
2276 } /* extern "C" */