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
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)) )
34 #define max(x, y) ((x) >= (y) ? (x) : (y))
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
);
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
);
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
);
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 /* -------------------------------------------------------------------------
82 * ------------------------------------------------------------------------- */
84 static int JimCheckMkName(Jim_Interp
*interp
, Jim_Obj
*name
, const char *type
)
89 s
= Jim_GetString(name
, &len
);
91 if (len
> 0 && s
[0] == '-')
93 for (i
= 0; i
< len
; i
++) {
101 Jim_SetResultFormatted(interp
, "expected %s name but got \"%#s\"", type
? type
: "property", name
);
105 static const char *const jim_mktype_options
[] = {
112 /* FIXME "-binary", */
116 static const char *const jim_mktype_names
[] = {
123 /* FIXME "binary", */
127 static const char jim_mktype_types
[] = {
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
)
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
)
155 result
= Jim_NewListObj(interp
, NULL
, 0);
161 else if (*descr
== '\0')
163 else if (*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));
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));
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
;
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); \
212 Jim_Obj
*name
, *struc
;
217 count
= Jim_ListLength(interp
, descrObj
);
219 Jim_SetResultString(interp
,
220 "view description must have an even number of elements", -1);
225 descr
= (char *)Jim_Alloc(bufSize
= JIM_MK_DESCR_LEN
);
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
)
236 rep
= Jim_GetString(name
, &len
);
237 ENLARGE(len
+ 3); /* At least :T, or [], */
238 memcpy(outPtr
, rep
, len
);
241 if (Jim_ListLength(interp
, struc
) == 1) {
244 if (Jim_GetEnum(interp
, struc
, jim_mktype_names
, &idx
,
245 "property type", JIM_ERRMSG
| JIM_ENUM_ABBREV
) != JIM_OK
)
249 *outPtr
++ = jim_mktype_types
[idx
];
254 if (JimToMkDescription(interp
, struc
, NULL
) != JIM_OK
)
257 ENLARGE(2); /* bracket, comma */
268 *descrPtr
= (char *)Jim_Realloc(descr
, strlen(descr
) + 1);
269 descr
= NULL
; /* Safety measure */
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
:
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
: {
312 if (Jim_GetWide(interp
, obj
, &value
) != JIM_OK
)
315 ((c4_IntProp
&)prop
).Set(*cur
, value
);
318 case MK_PROPERTY_LONG
: {
321 if (Jim_GetWide(interp
, obj
, &value
) != JIM_OK
)
324 ((c4_LongProp
&)prop
).Set(*cur
, value
);
327 case MK_PROPERTY_FLOAT
: {
330 if (Jim_GetDouble(interp
, obj
, &value
) != JIM_OK
)
333 ((c4_FloatProp
&)prop
).Set(*cur
, value
);
336 case MK_PROPERTY_DOUBLE
: {
339 if (Jim_GetDouble(interp
, obj
, &value
) != JIM_OK
)
342 ((c4_DoubleProp
&)prop
).Set(*cur
, value
);
345 case MK_PROPERTY_STRING
: {
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);
355 ((c4_StringProp
&)prop
).Set(*cur
, rep
);
358 case MK_PROPERTY_VIEW
: {
361 if (JimGetView(interp
, obj
, &value
) != JIM_OK
)
364 ((c4_ViewProp
&)prop
).Set(*cur
, value
);
366 case MK_PROPERTY_BINARY
:
369 Jim_SetResultString(interp
, "unsupported Metakit type", -1);
374 static int JimPipelineBoundary(int argc
, Jim_Obj
*const *argv
) {
378 for (pipe
= 0; pipe
< argc
; pipe
++) {
379 rep
= Jim_GetString(argv
[pipe
], &len
);
380 if (len
== 1 && rep
[0] == '|')
386 /* -------------------------------------------------------------------------
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);
413 static Jim_ObjType propertyObjType
= {
415 FreePropertyInternalRep
,
416 DupPropertyInternalRep
,
417 UpdateStringOfProperty
,
421 static int JimGetProperty(Jim_Interp
*interp
, Jim_Obj
*obj
, c4_View view
, const char *name
, const c4_Property
**propPtr
)
425 if (obj
->typePtr
== &propertyObjType
) {
426 index
= view
.FindProperty(JimPropertyValue(obj
)->GetId());
429 if (JimCheckMkName(interp
, obj
, name
) != JIM_OK
)
431 index
= view
.FindPropIndexByName(Jim_String(obj
));
435 *propPtr
= &view
.NthProperty(index
);
439 Jim_SetResultFormatted(interp
, "%s \"%#s\" does not exist",
440 name
? name
: "property", obj
);
445 static int JimGetPropertyTyped(Jim_Interp
*interp
, Jim_Obj
*obj
, char type
, const c4_Property
**propPtr
)
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
;
459 if (JimCheckMkName(interp
, obj
, NULL
) != JIM_OK
)
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
);
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
)
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()));
492 static int JimGetProperties(Jim_Interp
*interp
, int objc
, Jim_Obj
*const *objv
, c4_View view
, c4_View
*propsPtr
)
495 const c4_Property
*prop
;
498 for (i
= 0; i
< objc
; i
++) {
499 if (JimGetProperty(interp
, objv
[i
], view
, NULL
, &prop
) != JIM_OK
)
502 props
.AddProperty(*prop
);
509 static Jim_Obj
*JimNewPropertyObj(Jim_Interp
*interp
, c4_Property prop
)
513 obj
= Jim_NewObj(interp
);
514 obj
->typePtr
= &propertyObjType
;
516 obj
->internalRep
.ptr
= new c4_Property(prop
);
520 /* -------------------------------------------------------------------------
522 * ------------------------------------------------------------------------- */
524 /* Position ---------------------------------------------------------------- */
526 /* A normal position if endFlag == 0; otherwise an offset from end+1 (!) */
527 typedef struct 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
)
542 rep
= Jim_String(obj
);
544 if (strncmp(rep
, "end", 3) == 0) {
552 pos
.index
= strtol(rep
, &end
, 10);
559 while ((rep
[0] == '+') || (rep
[0] == '-')) {
560 sign
= (rep
[0] == '+' ? 1 : -1);
563 offset
= strtol(rep
, &end
, 10);
567 pos
.index
+= sign
* offset
;
571 while (isspace(UCHAR(*rep
)))
580 Jim_SetResultFormatted(interp
, "expected cursor position but got \"%#s\"", obj
);
584 static int PositionIndex(const MkPosition
*posPtr
, c4_View view
)
587 return view
.GetSize() + posPtr
->index
;
589 return posPtr
->index
;
592 static int JimGetPosition(Jim_Interp
*interp
, Jim_Obj
*obj
, c4_View view
, int *indexPtr
)
596 if (GetPosition(interp
, obj
, &pos
) != JIM_OK
)
599 *indexPtr
= PositionIndex(&pos
, view
);
603 /* Cursor type ------------------------------------------------------------- */
605 typedef struct 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
);
632 len
= snprintf(buf
, JIM_CURSOR_SPACE
+ 1, "%s!", Jim_String(curPtr
->viewObj
));
634 if (curPtr
->pos
.endFlag
) {
635 idx
= curPtr
->pos
.index
+ 1;
637 len
+= snprintf(buf
+ len
, JIM_CURSOR_SPACE
+ 1 - len
, "end");
639 len
+= snprintf(buf
+ len
, JIM_CURSOR_SPACE
+ 1 - len
, "end%+d", idx
);
642 len
+= snprintf(buf
+ len
, JIM_CURSOR_SPACE
+ 1 - len
, "%d",
646 obj
->bytes
= (char *)Jim_Alloc(len
+ 1);
647 memcpy(obj
->bytes
, buf
, len
+ 1);
651 static Jim_ObjType cursorObjType
= {
653 FreeCursorInternalRep
,
654 DupCursorInternalRep
,
655 UpdateStringOfCursor
,
659 static int SetCursorFromAny(Jim_Interp
*interp
, Jim_Obj
*obj
)
661 const char *rep
, *delim
;
666 rep
= Jim_GetString(obj
, &len
);
667 delim
= strrchr(rep
, '!');
670 Jim_SetResultFormatted(interp
, "expected cursor but got \"%#s\"", obj
);
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
);
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
;
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
)
701 *posObjPtr
= Jim_NewStringObj(interp
, strrchr(Jim_String(obj
), '!') + 1, -1);
705 static int JimGetCursorView(Jim_Interp
*interp
, Jim_Obj
*obj
, Jim_Obj
**viewObjPtr
)
707 if (obj
->typePtr
!= &cursorObjType
&& SetCursorFromAny(interp
, obj
) != JIM_OK
)
710 *viewObjPtr
= JimCursorValue(obj
)->viewObj
;
714 static int JimGetCursor(Jim_Interp
*interp
, Jim_Obj
*obj
, c4_Cursor
*curPtr
)
718 if (obj
->typePtr
!= &cursorObjType
&& SetCursorFromAny(interp
, obj
) != JIM_OK
)
720 if (JimGetView(interp
, JimCursorValue(obj
)->viewObj
, &view
) != JIM_OK
)
724 *curPtr
= &view
[PositionIndex(&JimCursorValue(obj
)->pos
, view
)];
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
)
735 Jim_InvalidateStringRep(obj
);
736 JimCursorValue(obj
)->pos
.index
+= offset
;
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
)
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];
758 if (JimGetCursor(interp
, curObj
, &cur
) != JIM_OK
)
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
);
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
);
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
);
787 /* Records ----------------------------------------------------------------- */
789 static int cursor_cmd_get(Jim_Interp
*interp
, int argc
, Jim_Obj
*const *argv
)
792 c4_Cursor cur
= &view
[0];
794 if (JimGetCursor(interp
, argv
[0], &cur
) != JIM_OK
)
796 if (JimCheckCursor(interp
, argv
[0], JIM_ERRMSG
| JIM_CURSOR_GET
) != JIM_OK
)
799 view
= (*cur
).Container();
801 if (argc
== 1) { /* Return all properties */
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
);
818 else { /* Return a single property */
819 const c4_Property
*propPtr
;
822 pipe
= JimPipelineBoundary(argc
, argv
);
824 /* No type annotation, existing property */
825 if (JimGetProperty(interp
, argv
[1], view
, NULL
, &propPtr
) != JIM_OK
)
828 else if (pipe
== 3) {
829 /* Explicit type annotation; the property may be new */
832 if (Jim_GetEnum(interp
, argv
[1], jim_mktype_options
, &idx
,
833 "property type", JIM_ERRMSG
| JIM_ENUM_ABBREV
) != JIM_OK
)
835 if (JimGetNewProperty(interp
, argv
[2], view
, jim_mktype_types
[idx
], &propPtr
) != JIM_OK
)
839 Jim_WrongNumArgs(interp
, 0, NULL
, "cursor get ?-type? ?prop?");
843 Jim_SetResult(interp
, JimGetMkValue(interp
, cur
, *propPtr
));
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
)
855 c4_Cursor cur
= &view
[0];
856 const c4_Property
*propPtr
;
859 if (JimGetCursor(interp
, argv
[0], &cur
) != JIM_OK
)
861 if (JimCheckCursor(interp
, argv
[0], JIM_ERRMSG
| JIM_CURSOR_SET
) != JIM_OK
)
864 view
= (*cur
).Container();
865 oldSize
= view
.GetSize();
867 if (cur
._index
>= oldSize
)
868 view
.SetSize(cur
._index
+ 1);
871 /* Update everything except subviews from a dictionary in argv[1].
872 * No new properties are permitted.
878 if (Jim_DictPairs(interp
, argv
[1], &objv
, &objc
) != JIM_OK
)
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
)
891 /* Update everything from argv[1..]. New properties are permitted if
895 for (i
= 1; i
< argc
; i
+= 2) {
896 if (Jim_String(argv
[i
])[0] == '-') {
900 Jim_WrongNumArgs(interp
, 2, argv
, "?-type? prop value ?...?");
904 if (Jim_GetEnum(interp
, argv
[i
], jim_mktype_options
, &idx
,
905 "property type", JIM_ERRMSG
| JIM_ENUM_ABBREV
) != JIM_OK
)
907 if (JimGetNewProperty(interp
, argv
[i
+1], view
, jim_mktype_types
[idx
], &propPtr
) != JIM_OK
)
913 Jim_WrongNumArgs(interp
, 2, argv
, "?-type? prop value ?...?");
917 if (JimGetProperty(interp
, argv
[i
], view
, NULL
, &propPtr
) != JIM_OK
)
921 if (JimSetMkValue(interp
, cur
, *propPtr
, argv
[i
+1]) != JIM_OK
)
929 view
.SetSize(oldSize
);
933 static int cursor_cmd_insert(Jim_Interp
*interp
, int argc
, Jim_Obj
*const *argv
)
936 c4_Cursor cur
= &view
[0];
939 if (JimGetCursor(interp
, argv
[0], &cur
) != JIM_OK
)
941 if (JimCheckCursor(interp
, argv
[0], JIM_ERRMSG
| JIM_CURSOR_INSERT
) != JIM_OK
)
944 view
= (*cur
).Container();
949 if (Jim_GetWide(interp
, argv
[1], &count
) != JIM_OK
)
955 view
.InsertAt(cur
._index
, empty
, (int)count
);
958 Jim_SetEmptyResult(interp
);
962 static int cursor_cmd_remove(Jim_Interp
*interp
, int argc
, Jim_Obj
*const *argv
)
965 c4_Cursor cur
= &view
[0];
969 if (JimGetCursor(interp
, argv
[0], &cur
) != JIM_OK
)
971 if (JimCheckCursor(interp
, argv
[0], JIM_ERRMSG
| JIM_CURSOR_SET
) != JIM_OK
)
974 view
= (*cur
).Container();
980 if (Jim_GetWide(interp
, argv
[1], &count
) != JIM_OK
)
984 if (pos
+ count
< view
.GetSize())
985 count
= view
.GetSize() - pos
;
987 if (pos
< view
.GetSize())
988 view
.RemoveAt(pos
, (int)count
);
993 /* Attributes -------------------------------------------------------------- */
995 static int cursor_cmd_view(Jim_Interp
*interp
, int argc
, Jim_Obj
*const *argv
)
999 if (JimGetCursorView(interp
, argv
[0], &viewObj
) != JIM_OK
)
1002 JimPinView(interp
, viewObj
);
1003 Jim_SetResult(interp
, viewObj
);
1007 /* Positioning ------------------------------------------------------------- */
1009 static int cursor_cmd_tell(Jim_Interp
*interp
, int argc
, Jim_Obj
*const *argv
)
1014 if (JimCursorPos(interp
, argv
[0], &result
) != JIM_OK
)
1016 Jim_SetResult(interp
, result
);
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]);
1028 if (JimGetCursor(interp
, argv
[1], &cur
) != JIM_OK
)
1031 Jim_SetResultInt(interp
, cur
._index
);
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
[] = {
1054 if (Jim_GetEnum(interp
, argv
[0], options
, &idx
, NULL
,
1055 JIM_ERRMSG
| JIM_ENUM_ABBREV
) != JIM_OK
)
1059 if (JimGetCursor(interp
, argv
[argc
-1], NULL
) != JIM_OK
)
1062 Jim_SetResultBool(interp
, JimCheckCursor(interp
, argv
[argc
-1], optflags
[idx
]) == JIM_OK
);
1066 static int cursor_cmd_seek(Jim_Interp
*interp
, int argc
, Jim_Obj
*const *argv
)
1070 curObj
= Jim_GetVariable(interp
, argv
[0], JIM_ERRMSG
| JIM_UNSHARED
);
1074 if (JimSeekCursor(interp
, curObj
, argv
[1]) != JIM_OK
)
1077 Jim_SetResult(interp
, curObj
);
1081 static int cursor_cmd_incr(Jim_Interp
*interp
, int argc
, Jim_Obj
*const *argv
)
1089 if (Jim_GetWide(interp
, argv
[1], &offset
) != JIM_OK
)
1093 curObj
= Jim_GetVariable(interp
, argv
[0], JIM_ERRMSG
| JIM_UNSHARED
);
1097 if (JimIncrCursor(interp
, curObj
, (int)offset
) != JIM_OK
)
1100 Jim_SetResult(interp
, curObj
);
1104 /* Command table ----------------------------------------------------------- */
1106 static const jim_subcmd_type cursor_command_table
[] = {
1110 { "get", "cur ?-type? ?prop?",
1114 /*"Get the whole record or a specific property at the cursor"*/
1116 { "set", "cur [dict | ?-type? field value ?...?]",
1120 /*"Update the record at the cursor"*/
1122 { "insert", "cur ?count?",
1126 /*"Insert a specified number of empty rows at the cursor (default 1)"*/
1128 { "remove", "cur ?count?",
1132 /*"Remove a specified number of rows at the cursor (default 1)"*/
1141 /*"Get the view the cursor points into"*/
1146 { "tell", "?-absolute? cur",
1150 /*"Get the position of the cursor"*/
1152 { "validfor", "?command? cur",
1153 cursor_cmd_validfor
,
1156 /*"Checks if the cursor is valid for get (default), set or insert commands"*/
1158 { "seek", "curVar index",
1162 /*"Seek to the specified index in the view"*/
1164 { "incr", "curVar ?offset?",
1168 /*"Move the cursor offset records from its current position (default 1)"*/
1174 static int JimCursorCommand(Jim_Interp
*interp
, int argc
, Jim_Obj
*const *argv
)
1179 Jim_WrongNumArgs(interp
, 1, argv
, "command ...");
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);
1189 Jim_FreeNewObj(interp
, cmdObj
);
1190 return Jim_CallSubCmd(interp
,
1191 Jim_ParseSubCmd(interp
, cursor_command_table
, argc
, argv
), argc
, argv
);
1195 /* -------------------------------------------------------------------------
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())); \
1223 UNOP(copy
, Duplicate
)
1225 UNOP(unique
, Unique
)
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);
1242 Jim_SetResult(interp
, JimNewViewObj(interp
, viewPtr
->Blocked()));
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) \
1257 Jim_SetResult(interp, JimNewViewObj(interp, viewPtr->Method(otherView))); \
1262 BINOP(concat
, Concat
)
1263 BINOP(product
, Product
)
1266 BINOP(intersect
, Intersect
)
1268 BINOP(different
, Different
)
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
);
1279 if (JimGetProperties(interp
, argc
, argv
, *viewPtr
, &props
) != JIM_OK
)
1282 Jim_SetResult(interp
, JimNewViewObj(interp
, viewPtr
->Project(props
)));
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
);
1291 if (JimGetProperties(interp
, argc
, argv
, *viewPtr
, &props
) != JIM_OK
)
1294 Jim_SetResult(interp
, JimNewViewObj(interp
, viewPtr
->ProjectWithout(props
)));
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
);
1304 if (JimGetPosition(interp
, argv
[0], *viewPtr
, &start
) != JIM_OK
||
1305 JimGetPosition(interp
, argv
[1], *viewPtr
, &end
) != JIM_OK
)
1312 else if (Jim_GetWide(interp
, argv
[2], &step
) != JIM_OK
)
1315 Jim_SetResult(interp
, JimNewViewObj(interp
, viewPtr
->Slice(start
, end
+ 1, (int)step
)));
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
;
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
++) {
1339 rep
= Jim_GetString(argv
[i
], &len
);
1340 reverse
= (len
> 0 && rep
[0] == '-');
1343 propObj
= Jim_NewStringObj(interp
, rep
+ 1, len
- 1);
1345 if (JimGetProperty(interp
, propObj
, *viewPtr
, NULL
, &propPtr
) != JIM_OK
) {
1347 Jim_FreeNewObj(interp
, propObj
);
1351 sortProps
.AddProperty(*propPtr
);
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
)));
1363 Jim_SetResult(interp
, JimNewViewObj(interp
, viewPtr
->SortOnReverse(sortProps
, revProps
)));
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
)
1375 const c4_Property
*propPtr
;
1378 for (i
= 0; i
< objc
; i
++) {
1379 if (JimGetProperty(interp
, objv
[i
], orig
, NULL
, &propPtr
) != JIM_OK
)
1381 proj
.AddProperty(*propPtr
);
1384 count
= orig
.NumProperties();
1385 for (i
= 0; i
< count
; i
++)
1386 proj
.AddProperty(orig
.NthProperty(i
));
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
);
1397 if (BubbleProperties(interp
, *viewPtr
, argc
, argv
, &proj
) != JIM_OK
)
1400 Jim_SetResult(interp
, JimNewViewObj(interp
, proj
.Ordered(argc
)));
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
);
1409 if (JimGetView(interp
, argv
[0], &hash
) != JIM_OK
)
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);
1423 if (BubbleProperties(interp
, *viewPtr
, argc
- 1, argv
+ 1, &proj
) != JIM_OK
)
1426 Jim_SetResult(interp
, JimNewViewObj(interp
, proj
.Hash(hash
, argc
- 1)));
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
;
1438 if (JimGetView(interp
, argv
[0], &other
) != JIM_OK
)
1442 if (Jim_CompareStringImmediate(interp
, argv
[1], "-outer")) {
1446 if (JimGetProperties(interp
, argc
- off
, argv
+ off
, *viewPtr
, &props
) != JIM_OK
)
1449 Jim_SetResult(interp
, JimNewViewObj(interp
, viewPtr
->Join(props
, other
, outer
)));
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
;
1459 if (JimGetPropertyTyped(interp
, argv
[0], MK_PROPERTY_VIEW
, &subviewPtr
) != JIM_OK
)
1462 if (JimGetProperties(interp
, argc
- 1, argv
+ 1, *viewPtr
, &props
) != JIM_OK
)
1465 Jim_SetResult(interp
, JimNewViewObj(interp
, viewPtr
->GroupBy(props
, *(c4_ViewProp
*)subviewPtr
)));
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
)
1477 if (subviewPtr
->Type() != MK_PROPERTY_VIEW
) {
1478 Jim_SetResultFormatted(interp
, "expected a subview property but got %s one",
1479 JimMkTypeName(subviewPtr
->Type()));
1483 Jim_SetResult(interp
, JimNewViewObj(interp
, viewPtr
->JoinProp(*(c4_ViewProp
*)subviewPtr
)));
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
));
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());
1503 static int view_cmd_resize(Jim_Interp
*interp
, int argc
, Jim_Obj
*const *argv
)
1505 c4_View
*view
= (c4_View
*) Jim_CmdPrivData(interp
);
1508 if (Jim_GetWide(interp
, argv
[0], &size
) != JIM_OK
)
1510 if (size
< 0 || size
> INT_MAX
) {
1511 Jim_SetResultFormatted(interp
,
1512 "view size \"%#s\" is out of range", argv
[0]);
1516 view
->SetSize((int)size
);
1517 Jim_SetResult(interp
, argv
[0]);
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
);
1526 const c4_Property
*propPtr
;
1528 if (JimGetProperty(interp
, argv
[0], *viewPtr
, NULL
, &propPtr
) != JIM_OK
)
1531 Jim_SetResultString(interp
, JimMkTypeName(propPtr
->Type()), -1);
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
);
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]);
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]);
1570 static int view_cmd_destroy(Jim_Interp
*interp
, int argc
, Jim_Obj
*const *argv
)
1572 Jim_DeleteCommand(interp
, Jim_String(argv
[0]));
1576 /* Command table ----------------------------------------------------------- */
1578 static const jim_subcmd_type view_command_table
[] = {
1580 /* Unary operations */
1586 /*"Create a copy of the view with exactly the same data"*/
1592 /*"Create an empty view with the same properties as this one"*/
1598 /*"Derived view without any duplicate rows (read-only, no change notifications)"*/
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", \
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"),
1632 /* Projections and selections */
1634 { "project", "prop ?prop ...?",
1638 /*"View projection: only the specified properties, in the specified order"*/
1640 { "without", "prop ?prop ...?",
1644 /*"View projection: remove the specified properties"*/
1646 { "range", "first last ?step?",
1650 /*"Range or slice of the view (read-write, no change notifications)"*/
1655 { "sort", "?[prop|-prop] ...?",
1659 /*"Derived view sorted on the specified properties (in order), or on all properties"*/
1661 { "ordered", "prop ?prop ...?",
1665 /*"Consider the underlying view ordered on the specified properties"*/
1667 { "hash", "hashView prop ?prop ...?",
1671 /*"Mapped view maintaining a hash table on the key consisting of the specified properties"*/
1674 /* Relational operations */
1676 { "join", "view ?-outer? prop ?prop ...?",
1680 /*"Relational join with view on the specified properties"*/
1682 { "group", "subviewName prop ?prop ...?",
1686 /*"Group rows with equal specified properties, move all other properties into subview"*/
1688 { "flatten", "subviewProp",
1692 /*"Flatten the specified subview; the inverse of group"*/
1698 view_cmd_properties
,
1701 /*"List the properties in this view"*/
1707 /*"Return the number of records in the view"*/
1709 { "resize", "newSize",
1713 /*"Set the number of records in the view"*/
1719 /*"Return the type of an existing property, or of all properties"*/
1722 /* Lifetime management */
1727 JIM_MODFLAG_FULLARGV
,
1728 /*"Marks the view as persistent"*/
1733 JIM_MODFLAG_FULLARGV
,
1734 /*"Marks the view as persistent and assigns it to the given variable"*/
1739 JIM_MODFLAG_FULLARGV
,
1740 /*"Destroys the view explicitly"*/
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
)
1756 pipe
= JimPipelineBoundary(argc
, argv
);
1759 Jim_WrongNumArgs(interp
, 1, argv
, "command ...");
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
*));
1779 memcpy(objv
+ 2, argv
+ 2, (pipe
- 2) * sizeof(Jim_Obj
*));
1781 result
= Jim_EvalObjVector(interp
, pipe
, objv
);
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
)
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
)
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]));
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]));
1816 static Jim_Obj
*JimNewViewObj(Jim_Interp
*interp
, c4_View view
) {
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
);
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
);
1836 *viewPtr
= *(c4_View
*)cmd
->u
.native
.privData
;
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
)
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));
1865 /* ----------------------------------------------------------------------------
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
{
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
);
1894 if (Jim_GetWide(interp
, argv
[0], &flag
) != JIM_OK
)
1898 mk
->flags
|= JIM_MKFLAG_AUTOCOMMIT
;
1900 mk
->flags
&= ~JIM_MKFLAG_AUTOCOMMIT
;
1901 mk
->storage
.AutoCommit(flag
);
1904 Jim_SetResultBool(interp
, (mk
->flags
& JIM_MKFLAG_AUTOCOMMIT
) != 0);
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);
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
));
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
)
1933 Jim_SetResult(interp
, JimGetMkValue(interp
, mk
->content
, *propPtr
));
1938 if (!Jim_CompareStringImmediate(interp
, argv
[1], "|")) {
1939 Jim_SetResultFormatted(interp
,
1940 "expected start of a pipeline but got \"%#s\"", argv
[1]);
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 */
1957 const c4_Property
*propPtr
;
1959 if (JimGetProperty(interp
, argv
[0], mk
->storage
, "view", &propPtr
) != JIM_OK
)
1961 name
= propPtr
->Name();
1964 Jim_SetResult(interp
, JimFromMkDescription(interp
,
1965 mk
->storage
.Description(name
), NULL
));
1972 if (JimCheckMkName(interp
, argv
[0], "view") != JIM_OK
)
1974 name
= Jim_GetString(argv
[0], &len
);
1976 if (JimToMkDescription(interp
, argv
[1], &descr
) != JIM_OK
)
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
);
1984 descr
[len
+ 1 + dlen
] = ']';
1985 descr
[len
+ 1 + dlen
+ 1] = '\0';
1987 mk
->storage
.GetAs(descr
);
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);
2005 else if (mk
->flags
& JIM_MKFLAG_READONLY
) {
2006 Jim_SetResultString(interp
, "cannot commit a read-only storage", -1);
2010 if (mk
->storage
.Commit(0)) {
2011 Jim_SetEmptyResult(interp
);
2015 Jim_SetResultString(interp
, "I/O error during commit", -1);
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);
2029 if (mk
->storage
.Rollback(0)) {
2030 Jim_SetEmptyResult(interp
);
2034 Jim_SetResultString(interp
, "I/O error during rollback", -1);
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
[] = {
2050 { "autocommit", "?value?",
2051 storage_cmd_autocommit
,
2054 /*"Query or modify the auto-commit option of this storage"*/
2057 storage_cmd_readonly
,
2060 /*"Returns the read-only status of this storage"*/
2069 /*"Returns the list of views stored here"*/
2071 { "view", "viewName",
2075 /*"Retrieve the view specified by viewName"*/
2077 { "structure", "?viewName? ?description?",
2078 storage_cmd_structure
,
2081 /*"Query or modify the structure of this storage"*/
2084 /* Store operations */
2090 /*"Commit the changes to disk"*/
2093 storage_cmd_rollback
,
2096 /*"Revert to the saved state"*/
2101 JIM_MODFLAG_FULLARGV
,
2102 /*"Close this storage"*/
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
);
2118 static int JimStorageSubCmdProc(Jim_Interp
*interp
, int argc
, Jim_Obj
*const *argv
)
2122 cmdObj
= Jim_NewStringObj(interp
, "mk.storage ", -1);
2123 Jim_AppendObj(interp
, cmdObj
, argv
[1]);
2125 if (Jim_GetCommand(interp
, cmdObj
, 0) != NULL
) {
2128 Jim_Obj
**objv
= (Jim_Obj
**)Jim_Alloc(argc
* sizeof(Jim_Obj
*));
2131 memcpy(objv
+ 2, argv
+ 2, (argc
- 2) * sizeof(Jim_Obj
*));
2133 result
= Jim_EvalObjVector(interp
, argc
, objv
);
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
)
2160 char buf
[MK_CMD_LEN
];
2163 static const char *const options
[] = {
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
) {
2189 if (mode
!= MK_MODE_READWRITE
)
2192 mode
= MK_MODE_READONLY
;
2193 mk
->flags
|= JIM_MKFLAG_READONLY
;
2197 if (mode
!= MK_MODE_READWRITE
)
2200 mode
= MK_MODE_ORIGINAL
;
2201 mk
->flags
|= JIM_MKFLAG_READONLY
;
2205 if (mode
!= MK_MODE_READWRITE
)
2208 mode
= MK_MODE_EXTEND
;
2209 mk
->flags
|= JIM_MKFLAG_EXTEND
;
2213 mk
->flags
&= ~JIM_MKFLAG_AUTOCOMMIT
;
2219 new(&mk
->storage
) c4_Storage(Jim_String(argv
[argc
-1]), mode
);
2221 if (!mk
->storage
.Strategy().IsValid()) {
2222 mk
->storage
.~c4_Storage();
2224 Jim_SetResultFormatted(interp
, "could not open storage \"%#s\"", argv
[argc
-1]);
2228 mk
->filename
= argv
[argc
-1];
2230 if ((mk
->flags
& JIM_MKFLAG_AUTOCOMMIT
) && !(mk
->flags
& JIM_MKFLAG_READONLY
))
2231 mk
->storage
.AutoCommit(1);
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);
2249 Jim_SetResultString(interp
, "only one of -readonly, -original and -extend may be specified", -1);
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
))
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
);