1 #include <string.h>
2 #include <ctype.h>
3 #include <new>
4 #include <mk4.h>
5
6 #include "jim.h"
7 #include "jimautoconf.h"
8 #include "jim-subcmd.h"
9
10 extern "C" { /* The whole file is essentially C */
11
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'
19
20 #define MK_MODE_ORIGINAL -1
21 #define MK_MODE_READONLY 0
22 #define MK_MODE_READWRITE 1
23 #define MK_MODE_EXTEND 2
24
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 */
30
31 #define isnamech(c) ( (c) && !strchr(":,[^]!", (c)) )
32
33 #ifndef max
34 #define max(x, y) ((x) >= (y) ? (x) : (y))
35 #endif
36
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);
44
45 static int JimPipelineBoundary(int argc, Jim_Obj *const *argv);
46
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);
58
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);
67
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)
72
73 static int JimCheckCursor (Jim_Interp *interp, Jim_Obj *curObj, int flags);
74
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);
79
80 /* -------------------------------------------------------------------------
81 * Utilities
82 * ------------------------------------------------------------------------- */
83
JimCheckMkName(Jim_Interp * interp,Jim_Obj * name,const char * type)84 static int JimCheckMkName(Jim_Interp *interp, Jim_Obj *name, const char *type)
85 {
86 const char *s;
87 int i, len;
88
89 s = Jim_GetString(name, &len);
90
91 if (len > 0 && s[0] == '-')
92 goto err;
93 for (i = 0; i < len; i++) {
94 if (!isnamech(s[i]))
95 goto err;
96 }
97
98 return JIM_OK;
99
100 err:
101 Jim_SetResultFormatted(interp, "expected %s name but got \"%#s\"", type ? type : "property", name);
102 return JIM_ERR;
103 }
104
105 static const char *const jim_mktype_options[] = {
106 "-integer",
107 "-long",
108 "-float",
109 "-double",
110 "-string",
111 "-subview",
112 /* FIXME "-binary", */
113 0
114 };
115
116 static const char *const jim_mktype_names[] = {
117 "integer",
118 "long",
119 "float",
120 "double",
121 "string",
122 "subview",
123 /* FIXME "binary", */
124 0
125 };
126
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, */
135 };
136
137 #define JIM_MKTYPES ((int)(sizeof(jim_mktype_types) / sizeof(jim_mktype_types[0])))
138
JimMkTypeName(char type)139 static const char *JimMkTypeName(char type)
140 {
141 int i;
142
143 for (i = 0; i < JIM_MKTYPES; i++) {
144 if (type == jim_mktype_types[i])
145 return jim_mktype_names[i];
146 }
147 return "(unknown type)";
148 }
149
JimFromMkDescription(Jim_Interp * interp,const char * descr,const char ** endPtr)150 static Jim_Obj *JimFromMkDescription(Jim_Interp *interp, const char *descr, const char **endPtr)
151 {
152 Jim_Obj *result;
153 const char *delim;
154
155 result = Jim_NewListObj(interp, NULL, 0);
156 for (;;) {
157 if (*descr == ']') {
158 descr++;
159 break;
160 }
161 else if (*descr == '\0')
162 break;
163 else if (*descr == ',')
164 descr++;
165
166 delim = strpbrk(descr, ",:[]");
167 /* JimPanic((!delim, "Invalid Metakit description string")); */
168
169 Jim_ListAppendElement(interp, result,
170 Jim_NewStringObj(interp, descr, delim - descr));
171
172 if (delim[0] == '[') {
173 Jim_ListAppendElement(interp, result,
174 JimFromMkDescription(interp, delim + 1, &descr));
175 }
176 else if (delim[0] == ':') {
177 Jim_ListAppendElement(interp, result,
178 Jim_NewStringObj(interp, JimMkTypeName(delim[1]), -1));
179 descr = delim + 2;
180 }
181 else {
182 /* Seems that Metakit never generates descriptions without type
183 * tags, but let's handle this just to be safe
184 */
185
186 Jim_ListAppendElement(interp, result,
187 Jim_NewStringObj(interp, JimMkTypeName(MK_PROPERTY_STRING), -1));
188 }
189 }
190
191 if (endPtr)
192 *endPtr = descr;
193 return result;
194 }
195
196 /* This allocates the buffer once per user call and stores it in a static
197 * variable. Recursive calls are distinguished by descrPtr == NULL.
198 */
JimToMkDescription(Jim_Interp * interp,Jim_Obj * descrObj,char ** descrPtr)199 static int JimToMkDescription(Jim_Interp *interp, Jim_Obj *descrObj, char **descrPtr)
200 {
201 static char *descr, *outPtr;
202 static int bufSize;
203
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); \
208 } \
209 } while(0)
210
211 int i, count;
212 Jim_Obj *name, *struc;
213
214 const char *rep;
215 int len;
216
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;
222 }
223
224 if (descrPtr) {
225 descr = (char *)Jim_Alloc(bufSize = JIM_MK_DESCR_LEN);
226 outPtr = descr;
227 }
228
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);
232
233 if (JimCheckMkName(interp, name, NULL) != JIM_OK)
234 goto err;
235
236 rep = Jim_GetString(name, &len);
237 ENLARGE(len + 3); /* At least :T, or [], */
238 memcpy(outPtr, rep, len);
239 outPtr += len;
240
241 if (Jim_ListLength(interp, struc) == 1) {
242 int idx;
243
244 if (Jim_GetEnum(interp, struc, jim_mktype_names, &idx,
245 "property type", JIM_ERRMSG | JIM_ENUM_ABBREV) != JIM_OK)
246 goto err;
247
248 *outPtr++ = ':';
249 *outPtr++ = jim_mktype_types[idx];
250 }
251 else {
252 *outPtr++ = '[';
253
254 if (JimToMkDescription(interp, struc, NULL) != JIM_OK)
255 goto err;
256
257 ENLARGE(2); /* bracket, comma */
258 *outPtr++ = ']';
259 }
260
261 *outPtr++ = ',';
262 }
263 *(--outPtr) = '\0';
264
265 #undef ENLARGE
266
267 if (descrPtr) {
268 *descrPtr = (char *)Jim_Realloc(descr, strlen(descr) + 1);
269 descr = NULL; /* Safety measure */
270 }
271
272 return JIM_OK;
273
274 err:
275
276 if (descrPtr)
277 Jim_Free(descr);
278
279 return JIM_ERR;
280 }
281
JimGetMkValue(Jim_Interp * interp,c4_Cursor cur,const c4_Property & prop)282 static Jim_Obj *JimGetMkValue(Jim_Interp *interp, c4_Cursor cur, const c4_Property &prop)
283 {
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));
297
298 case MK_PROPERTY_BINARY:
299 /* FIXME */
300 default:
301 /* FIXME Something more meaningful here? */
302 return Jim_NewEmptyStringObj(interp);
303 }
304 }
305
JimSetMkValue(Jim_Interp * interp,c4_Cursor cur,const c4_Property & prop,Jim_Obj * obj)306 static int JimSetMkValue(Jim_Interp *interp, c4_Cursor cur, const c4_Property &prop, Jim_Obj *obj)
307 {
308 switch (prop.Type()) {
309 case MK_PROPERTY_INT: {
310 jim_wide value;
311
312 if (Jim_GetWide(interp, obj, &value) != JIM_OK)
313 return JIM_ERR;
314
315 ((c4_IntProp &)prop).Set(*cur, value);
316 return JIM_OK;
317 }
318 case MK_PROPERTY_LONG: {
319 jim_wide value;
320
321 if (Jim_GetWide(interp, obj, &value) != JIM_OK)
322 return JIM_ERR;
323
324 ((c4_LongProp &)prop).Set(*cur, value);
325 return JIM_OK;
326 }
327 case MK_PROPERTY_FLOAT: {
328 double value;
329
330 if (Jim_GetDouble(interp, obj, &value) != JIM_OK)
331 return JIM_ERR;
332
333 ((c4_FloatProp &)prop).Set(*cur, value);
334 return JIM_OK;
335 }
336 case MK_PROPERTY_DOUBLE: {
337 double value;
338
339 if (Jim_GetDouble(interp, obj, &value) != JIM_OK)
340 return JIM_ERR;
341
342 ((c4_DoubleProp &)prop).Set(*cur, value);
343 return JIM_OK;
344 }
345 case MK_PROPERTY_STRING: {
346 int len;
347 const char *rep;
348
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;
353 }
354
355 ((c4_StringProp &)prop).Set(*cur, rep);
356 return JIM_OK;
357 }
358 case MK_PROPERTY_VIEW: {
359 c4_View value;
360
361 if (JimGetView(interp, obj, &value) != JIM_OK)
362 return JIM_ERR;
363
364 ((c4_ViewProp &)prop).Set(*cur, value);
365 }
366 case MK_PROPERTY_BINARY:
367 /* FIXME */
368 default:
369 Jim_SetResultString(interp, "unsupported Metakit type", -1);
370 return JIM_ERR;
371 }
372 }
373
JimPipelineBoundary(int argc,Jim_Obj * const * argv)374 static int JimPipelineBoundary(int argc, Jim_Obj *const *argv) {
375 const char *rep;
376 int pipe, len;
377
378 for (pipe = 0; pipe < argc; pipe++) {
379 rep = Jim_GetString(argv[pipe], &len);
380 if (len == 1 && rep[0] == '|')
381 break;
382 }
383 return pipe;
384 }
385
386 /* -------------------------------------------------------------------------
387 * Property object
388 * ------------------------------------------------------------------------- */
389
390 #define JimPropertyValue(o) ((c4_Property *)((o)->internalRep.ptr))
391
FreePropertyInternalRep(Jim_Interp * interp,Jim_Obj * obj)392 static void FreePropertyInternalRep(Jim_Interp *interp, Jim_Obj *obj)
393 {
394 delete JimPropertyValue(obj);
395 }
396
DupPropertyInternalRep(Jim_Interp * interp,Jim_Obj * oldObj,Jim_Obj * newObj)397 static void DupPropertyInternalRep(Jim_Interp *interp, Jim_Obj *oldObj, Jim_Obj *newObj)
398 {
399 newObj->internalRep.ptr = new c4_Property(*JimPropertyValue(oldObj));
400 newObj->typePtr = oldObj->typePtr;
401 }
402
UpdateStringOfProperty(Jim_Obj * obj)403 static void UpdateStringOfProperty(Jim_Obj* obj)
404 {
405 const char *name = JimPropertyValue(obj)->Name();
406 int len = strlen(name);
407
408 obj->bytes = (char *) Jim_Alloc(len + 1);
409 memcpy(obj->bytes, name, len + 1);
410 obj->length = len;
411 }
412
413 static Jim_ObjType propertyObjType = {
414 "mk.property",
415 FreePropertyInternalRep,
416 DupPropertyInternalRep,
417 UpdateStringOfProperty,
418 JIM_TYPE_NONE
419 };
420
JimGetProperty(Jim_Interp * interp,Jim_Obj * obj,c4_View view,const char * name,const c4_Property ** propPtr)421 static int JimGetProperty(Jim_Interp *interp, Jim_Obj *obj, c4_View view, const char *name, const c4_Property **propPtr)
422 {
423 int index;
424
425 if (obj->typePtr == &propertyObjType) {
426 index = view.FindProperty(JimPropertyValue(obj)->GetId());
427 }
428 else {
429 if (JimCheckMkName(interp, obj, name) != JIM_OK)
430 return JIM_ERR;
431 index = view.FindPropIndexByName(Jim_String(obj));
432 }
433
434 if (index != -1) {
435 *propPtr = &view.NthProperty(index);
436 return JIM_OK;
437 }
438 else {
439 Jim_SetResultFormatted(interp, "%s \"%#s\" does not exist",
440 name ? name : "property", obj);
441 return JIM_ERR;
442 }
443 }
444
JimGetPropertyTyped(Jim_Interp * interp,Jim_Obj * obj,char type,const c4_Property ** propPtr)445 static int JimGetPropertyTyped(Jim_Interp *interp, Jim_Obj *obj, char type, const c4_Property **propPtr)
446 {
447 c4_Property *prop;
448
449 if (obj->typePtr == &propertyObjType) {
450 if (JimPropertyValue(obj)->Type() != type) {
451 /* coerce the property type */
452
453 prop = new c4_Property(type, JimPropertyValue(obj)->Name());
454 delete JimPropertyValue(obj);
455 obj->internalRep.ptr = prop;
456 }
457 }
458 else {
459 if (JimCheckMkName(interp, obj, NULL) != JIM_OK)
460 return JIM_ERR;
461
462 prop = new c4_Property(type, Jim_String(obj));
463
464 Jim_FreeIntRep(interp, obj);
465 obj->typePtr = &propertyObjType;
466 obj->internalRep.ptr = (void *)prop;
467 }
468
469 *propPtr = JimPropertyValue(obj);
470 return JIM_OK;
471 }
472
JimGetNewProperty(Jim_Interp * interp,Jim_Obj * obj,c4_View view,char type,const c4_Property ** propPtr)473 static int JimGetNewProperty(Jim_Interp *interp, Jim_Obj *obj, c4_View view, char type, const c4_Property **propPtr)
474 {
475 const c4_Property *newp, *prop;
476
477 if (JimGetPropertyTyped(interp, obj, type, &newp) != JIM_OK)
478 return JIM_ERR;
479
480 prop = &view.NthProperty(view.AddProperty(*newp));
481
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;
486 }
487
488 *propPtr = prop;
489 return JIM_OK;
490 }
491
JimGetProperties(Jim_Interp * interp,int objc,Jim_Obj * const * objv,c4_View view,c4_View * propsPtr)492 static int JimGetProperties(Jim_Interp *interp, int objc, Jim_Obj *const *objv, c4_View view, c4_View *propsPtr)
493 {
494 int i;
495 const c4_Property *prop;
496 c4_View props;
497
498 for (i = 0; i < objc; i++) {
499 if (JimGetProperty(interp, objv[i], view, NULL, &prop) != JIM_OK)
500 return JIM_ERR;
501
502 props.AddProperty(*prop);
503 }
504
505 *propsPtr = props;
506 return JIM_OK;
507 }
508
JimNewPropertyObj(Jim_Interp * interp,c4_Property prop)509 static Jim_Obj *JimNewPropertyObj(Jim_Interp *interp, c4_Property prop)
510 {
511 Jim_Obj *obj;
512
513 obj = Jim_NewObj(interp);
514 obj->typePtr = &propertyObjType;
515 obj->bytes = NULL;
516 obj->internalRep.ptr = new c4_Property(prop);
517 return obj;
518 }
519
520 /* -------------------------------------------------------------------------
521 * Cursor object
522 * ------------------------------------------------------------------------- */
523
524 /* Position ---------------------------------------------------------------- */
525
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;
531
532 /* This is mostly the same as SetIndexFromAny, but preserves more information
533 * and allows multiple [+-]integer parts.
534 */
GetPosition(Jim_Interp * interp,Jim_Obj * obj,MkPosition * posPtr)535 static int GetPosition(Jim_Interp *interp, Jim_Obj *obj, MkPosition *posPtr)
536 {
537 MkPosition pos;
538 const char *rep;
539 char *end;
540 int sign, offset;
541
542 rep = Jim_String(obj);
543
544 if (strncmp(rep, "end", 3) == 0) {
545 pos.endFlag = 1;
546 pos.index = -1;
547
548 rep += 3;
549 }
550 else {
551 pos.endFlag = 0;
552 pos.index = strtol(rep, &end, 10);
553 if (end == rep)
554 goto err;
555
556 rep = end;
557 }
558
559 while ((rep[0] == '+') || (rep[0] == '-')) {
560 sign = (rep[0] == '+' ? 1 : -1);
561 rep++;
562
563 offset = strtol(rep, &end, 10);
564 if (end == rep)
565 goto err;
566
567 pos.index += sign * offset;
568 rep = end;
569 }
570
571 while (isspace(UCHAR(*rep)))
572 rep++;
573 if (*rep != '\0')
574 goto err;
575
576 *posPtr = pos;
577 return JIM_OK;
578
579 err:
580 Jim_SetResultFormatted(interp, "expected cursor position but got \"%#s\"", obj);
581 return JIM_ERR;
582 }
583
PositionIndex(const MkPosition * posPtr,c4_View view)584 static int PositionIndex(const MkPosition *posPtr, c4_View view)
585 {
586 if (posPtr->endFlag)
587 return view.GetSize() + posPtr->index;
588 else
589 return posPtr->index;
590 }
591
JimGetPosition(Jim_Interp * interp,Jim_Obj * obj,c4_View view,int * indexPtr)592 static int JimGetPosition(Jim_Interp *interp, Jim_Obj *obj, c4_View view, int *indexPtr)
593 {
594 MkPosition pos;
595
596 if (GetPosition(interp, obj, &pos) != JIM_OK)
597 return JIM_ERR;
598
599 *indexPtr = PositionIndex(&pos, view);
600 return JIM_OK;
601 }
602
603 /* Cursor type ------------------------------------------------------------- */
604
605 typedef struct MkCursor {
606 MkPosition pos;
607 Jim_Obj *viewObj;
608 } MkCursor;
609
610 #define JimCursorValue(obj) ((MkCursor *)(obj->internalRep.ptr))
FreeCursorInternalRep(Jim_Interp * interp,Jim_Obj * obj)611 static void FreeCursorInternalRep(Jim_Interp *interp, Jim_Obj *obj)
612 {
613 Jim_DecrRefCount(interp, JimCursorValue(obj)->viewObj);
614 Jim_Free(obj->internalRep.ptr);
615 }
616
DupCursorInternalRep(Jim_Interp * interp,Jim_Obj * oldObj,Jim_Obj * newObj)617 static void DupCursorInternalRep(Jim_Interp *interp, Jim_Obj *oldObj, Jim_Obj *newObj)
618 {
619 newObj->internalRep.ptr = Jim_Alloc(sizeof(MkCursor));
620 *JimCursorValue(newObj) = *JimCursorValue(oldObj);
621 Jim_IncrRefCount(JimCursorValue(oldObj)->viewObj);
622
623 newObj->typePtr = oldObj->typePtr;
624 }
625
UpdateStringOfCursor(Jim_Obj * obj)626 static void UpdateStringOfCursor(Jim_Obj *obj)
627 {
628 char buf[JIM_CURSOR_SPACE + 1];
629 MkCursor *curPtr = JimCursorValue(obj);
630 int idx, len;
631
632 len = snprintf(buf, JIM_CURSOR_SPACE + 1, "%s!", Jim_String(curPtr->viewObj));
633
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);
640 }
641 else {
642 len += snprintf(buf + len, JIM_CURSOR_SPACE + 1 - len, "%d",
643 curPtr->pos.index);
644 }
645
646 obj->bytes = (char *)Jim_Alloc(len + 1);
647 memcpy(obj->bytes, buf, len + 1);
648 obj->length = len;
649 }
650
651 static Jim_ObjType cursorObjType = {
652 "mk.cursor",
653 FreeCursorInternalRep,
654 DupCursorInternalRep,
655 UpdateStringOfCursor,
656 JIM_TYPE_REFERENCES
657 };
658
SetCursorFromAny(Jim_Interp * interp,Jim_Obj * obj)659 static int SetCursorFromAny(Jim_Interp *interp, Jim_Obj *obj)
660 {
661 const char *rep, *delim;
662 int len;
663 Jim_Obj *posObj;
664 MkCursor cur;
665
666 rep = Jim_GetString(obj, &len);
667 delim = strrchr(rep, '!');
668
669 if (!delim) {
670 Jim_SetResultFormatted(interp, "expected cursor but got \"%#s\"", obj);
671 return JIM_ERR;
672 }
673
674 cur.viewObj = Jim_NewStringObj(interp, rep, delim - rep);
675 posObj = Jim_NewStringObj(interp, delim + 1, len - (delim - rep) - 1);
676
677 if (GetPosition(interp, posObj, &cur.pos) != JIM_OK) {
678 Jim_FreeNewObj(interp, posObj);
679 Jim_FreeNewObj(interp, cur.viewObj);
680 return JIM_ERR;
681 }
682
683 Jim_FreeIntRep(interp, obj);
684 Jim_FreeNewObj(interp, posObj);
685 Jim_IncrRefCount(cur.viewObj);
686
687 obj->typePtr = &cursorObjType;
688 obj->internalRep.ptr = Jim_Alloc(sizeof(MkCursor));
689 *JimCursorValue(obj) = cur;
690
691 return JIM_OK;
692 }
693
694 /* Functions --------------------------------------------------------------- */
695
JimCursorPos(Jim_Interp * interp,Jim_Obj * obj,Jim_Obj ** posObjPtr)696 static int JimCursorPos(Jim_Interp *interp, Jim_Obj *obj, Jim_Obj **posObjPtr)
697 {
698 if (obj->typePtr != &cursorObjType && SetCursorFromAny(interp, obj) != JIM_OK)
699 return JIM_ERR;
700
701 *posObjPtr = Jim_NewStringObj(interp, strrchr(Jim_String(obj), '!') + 1, -1);
702 return JIM_OK;
703 }
704
JimGetCursorView(Jim_Interp * interp,Jim_Obj * obj,Jim_Obj ** viewObjPtr)705 static int JimGetCursorView(Jim_Interp *interp, Jim_Obj *obj, Jim_Obj **viewObjPtr)
706 {
707 if (obj->typePtr != &cursorObjType && SetCursorFromAny(interp, obj) != JIM_OK)
708 return JIM_ERR;
709
710 *viewObjPtr = JimCursorValue(obj)->viewObj;
711 return JIM_OK;
712 }
713
JimGetCursor(Jim_Interp * interp,Jim_Obj * obj,c4_Cursor * curPtr)714 static int JimGetCursor(Jim_Interp *interp, Jim_Obj *obj, c4_Cursor *curPtr)
715 {
716 c4_View view;
717
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;
722
723 if (curPtr)
724 *curPtr = &view[PositionIndex(&JimCursorValue(obj)->pos, view)];
725 return JIM_OK;
726 }
727
JimIncrCursor(Jim_Interp * interp,Jim_Obj * obj,int offset)728 static int JimIncrCursor(Jim_Interp *interp, Jim_Obj *obj, int offset)
729 {
730 /* JimPanic((Jim_IsShared(obj), "JimIncrCursor called with shared object")) */
731
732 if (obj->typePtr != &cursorObjType && SetCursorFromAny(interp, obj) != JIM_OK)
733 return JIM_ERR;
734
735 Jim_InvalidateStringRep(obj);
736 JimCursorValue(obj)->pos.index += offset;
737 return JIM_OK;
738 }
739
JimSeekCursor(Jim_Interp * interp,Jim_Obj * obj,Jim_Obj * posObj)740 static int JimSeekCursor(Jim_Interp *interp, Jim_Obj *obj, Jim_Obj *posObj)
741 {
742 /* JimPanic((Jim_IsShared(obj), "JimSeekCursor called with shared object")) */
743
744 if (obj->typePtr != &cursorObjType && SetCursorFromAny(interp, obj) != JIM_OK)
745 return JIM_ERR;
746
747 Jim_InvalidateStringRep(obj);
748 return GetPosition(interp, posObj, &JimCursorValue(obj)->pos);
749 }
750
JimCheckCursor(Jim_Interp * interp,Jim_Obj * curObj,int flags)751 static int JimCheckCursor(Jim_Interp *interp, Jim_Obj *curObj, int flags)
752 {
753 static c4_View nullView;
754
755 c4_Cursor cur = &nullView[0];
756 int size;
757
758 if (JimGetCursor(interp, curObj, &cur) != JIM_OK)
759 return JIM_ERR;
760 size = (*cur).Container().GetSize();
761
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);
766 }
767 return JIM_ERR;
768 }
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);
773 }
774 return JIM_ERR;
775 }
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);
780 }
781 return JIM_ERR;
782 }
783
784 return JIM_OK;
785 }
786
787 /* Records ----------------------------------------------------------------- */
788
cursor_cmd_get(Jim_Interp * interp,int argc,Jim_Obj * const * argv)789 static int cursor_cmd_get(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
790 {
791 c4_View view;
792 c4_Cursor cur = &view[0];
793
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;
798
799 view = (*cur).Container();
800
801 if (argc == 1) { /* Return all properties */
802 int i, count;
803 Jim_Obj *result;
804
805 result = Jim_NewListObj(interp, NULL, 0);
806 count = view.NumProperties();
807
808 for (i = 0; i < count; i++) {
809 c4_Property prop = view.NthProperty(i);
810
811 Jim_ListAppendElement(interp, result, JimNewPropertyObj(interp, prop));
812 Jim_ListAppendElement(interp, result, JimGetMkValue(interp, cur, prop));
813 }
814
815 Jim_SetResult(interp, result);
816 return JIM_OK;
817 }
818 else { /* Return a single property */
819 const c4_Property *propPtr;
820 int pipe;
821
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;
827 }
828 else if (pipe == 3) {
829 /* Explicit type annotation; the property may be new */
830 int idx;
831
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;
837 }
838 else {
839 Jim_WrongNumArgs(interp, 0, NULL, "cursor get ?-type? ?prop?");
840 return JIM_ERR;
841 }
842
843 Jim_SetResult(interp, JimGetMkValue(interp, cur, *propPtr));
844
845 if (pipe == argc)
846 return JIM_OK;
847 else
848 return Jim_EvalObjPrefix(interp, Jim_GetResult(interp), argc - pipe - 1, argv + pipe + 1);
849 }
850 }
851
cursor_cmd_set(Jim_Interp * interp,int argc,Jim_Obj * const * argv)852 static int cursor_cmd_set(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
853 {
854 c4_View view;
855 c4_Cursor cur = &view[0];
856 const c4_Property *propPtr;
857 int i, oldSize;
858
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;
863
864 view = (*cur).Container();
865 oldSize = view.GetSize();
866
867 if (cur._index >= oldSize)
868 view.SetSize(cur._index + 1);
869
870 if (argc == 2) {
871 /* Update everything except subviews from a dictionary in argv[1].
872 * No new properties are permitted.
873 */
874
875 int objc;
876 Jim_Obj **objv;
877
878 if (Jim_DictPairs(interp, argv[1], &objv, &objc) != JIM_OK)
879 goto err;
880
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)
884 {
885 Jim_Free(objv);
886 goto err;
887 }
888 }
889 }
890 else {
891 /* Update everything from argv[1..]. New properties are permitted if
892 * explicitly typed.
893 */
894
895 for (i = 1; i < argc; i += 2) {
896 if (Jim_String(argv[i])[0] == '-') {
897 int idx;
898
899 if (i + 2 >= argc) {
900 Jim_WrongNumArgs(interp, 2, argv, "?-type? prop value ?...?");
901 goto err;
902 }
903
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++;
910 }
911 else {
912 if (i + 1 >= argc) {
913 Jim_WrongNumArgs(interp, 2, argv, "?-type? prop value ?...?");
914 goto err;
915 }
916
917 if (JimGetProperty(interp, argv[i], view, NULL, &propPtr) != JIM_OK)
918 goto err;
919 }
920
921 if (JimSetMkValue(interp, cur, *propPtr, argv[i+1]) != JIM_OK)
922 goto err;
923 }
924 }
925
926 return JIM_OK;
927
928 err:
929 view.SetSize(oldSize);
930 return JIM_ERR;
931 }
932
cursor_cmd_insert(Jim_Interp * interp,int argc,Jim_Obj * const * argv)933 static int cursor_cmd_insert(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
934 {
935 c4_View view;
936 c4_Cursor cur = &view[0];
937 jim_wide count;
938
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;
943
944 view = (*cur).Container();
945
946 if (argc == 1)
947 count = 1;
948 else {
949 if (Jim_GetWide(interp, argv[1], &count) != JIM_OK)
950 return JIM_ERR;
951 }
952
953 if (count > 0) {
954 c4_Row empty;
955 view.InsertAt(cur._index, empty, (int)count);
956 }
957
958 Jim_SetEmptyResult(interp);
959 return JIM_OK;
960 }
961
cursor_cmd_remove(Jim_Interp * interp,int argc,Jim_Obj * const * argv)962 static int cursor_cmd_remove(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
963 {
964 c4_View view;
965 c4_Cursor cur = &view[0];
966 int pos;
967 jim_wide count;
968
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;
973
974 view = (*cur).Container();
975 pos = cur._index;
976
977 if (argc == 1)
978 count = 1;
979 else {
980 if (Jim_GetWide(interp, argv[1], &count) != JIM_OK)
981 return JIM_ERR;
982 }
983
984 if (pos + count < view.GetSize())
985 count = view.GetSize() - pos;
986
987 if (pos < view.GetSize())
988 view.RemoveAt(pos, (int)count);
989
990 return JIM_OK;
991 }
992
993 /* Attributes -------------------------------------------------------------- */
994
cursor_cmd_view(Jim_Interp * interp,int argc,Jim_Obj * const * argv)995 static int cursor_cmd_view(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
996 {
997 Jim_Obj *viewObj;
998
999 if (JimGetCursorView(interp, argv[0], &viewObj) != JIM_OK)
1000 return JIM_ERR;
1001
1002 JimPinView(interp, viewObj);
1003 Jim_SetResult(interp, viewObj);
1004 return JIM_OK;
1005 }
1006
1007 /* Positioning ------------------------------------------------------------- */
1008
cursor_cmd_tell(Jim_Interp * interp,int argc,Jim_Obj * const * argv)1009 static int cursor_cmd_tell(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
1010 {
1011 if (argc == 1) {
1012 Jim_Obj *result;
1013
1014 if (JimCursorPos(interp, argv[0], &result) != JIM_OK)
1015 return JIM_ERR;
1016 Jim_SetResult(interp, result);
1017 }
1018 else {
1019 static c4_View nullView;
1020 c4_Cursor cur = &nullView[0];
1021
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;
1026 }
1027
1028 if (JimGetCursor(interp, argv[1], &cur) != JIM_OK)
1029 return JIM_ERR;
1030
1031 Jim_SetResultInt(interp, cur._index);
1032 }
1033
1034 return JIM_OK;
1035 }
1036
cursor_cmd_validfor(Jim_Interp * interp,int argc,Jim_Obj * const * argv)1037 static int cursor_cmd_validfor(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
1038 {
1039 static const char *options[] = {
1040 "get", "set", "insert", "remove", 0
1041 };
1042 static int optflags[] = {
1043 JIM_CURSOR_GET,
1044 JIM_CURSOR_SET,
1045 JIM_CURSOR_INSERT,
1046 JIM_CURSOR_SET
1047 };
1048
1049 int idx;
1050
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;
1057 }
1058
1059 if (JimGetCursor(interp, argv[argc-1], NULL) != JIM_OK)
1060 return JIM_ERR;
1061
1062 Jim_SetResultBool(interp, JimCheckCursor(interp, argv[argc-1], optflags[idx]) == JIM_OK);
1063 return JIM_OK;
1064 }
1065
cursor_cmd_seek(Jim_Interp * interp,int argc,Jim_Obj * const * argv)1066 static int cursor_cmd_seek(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
1067 {
1068 Jim_Obj *curObj;
1069
1070 curObj = Jim_GetVariable(interp, argv[0], JIM_ERRMSG | JIM_UNSHARED);
1071 if (curObj == NULL)
1072 return JIM_ERR;
1073
1074 if (JimSeekCursor(interp, curObj, argv[1]) != JIM_OK)
1075 return JIM_ERR;
1076
1077 Jim_SetResult(interp, curObj);
1078 return JIM_OK;
1079 }
1080
cursor_cmd_incr(Jim_Interp * interp,int argc,Jim_Obj * const * argv)1081 static int cursor_cmd_incr(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
1082 {
1083 Jim_Obj *curObj;
1084 jim_wide offset;
1085
1086 if (argc == 1)
1087 offset = 1;
1088 else {
1089 if (Jim_GetWide(interp, argv[1], &offset) != JIM_OK)
1090 return JIM_ERR;
1091 }
1092
1093 curObj = Jim_GetVariable(interp, argv[0], JIM_ERRMSG | JIM_UNSHARED);
1094 if (curObj == NULL)
1095 return JIM_ERR;
1096
1097 if (JimIncrCursor(interp, curObj, (int)offset) != JIM_OK)
1098 return JIM_ERR;
1099
1100 Jim_SetResult(interp, curObj);
1101 return JIM_OK;
1102 }
1103
1104 /* Command table ----------------------------------------------------------- */
1105
1106 static const jim_subcmd_type cursor_command_table[] = {
1107
1108 /* Records */
1109
1110 { "get", "cur ?-type? ?prop?",
1111 cursor_cmd_get,
1112 1, -1,
1113 0,
1114 /*"Get the whole record or a specific property at the cursor"*/
1115 },
1116 { "set", "cur [dict | ?-type? field value ?...?]",
1117 cursor_cmd_set,
1118 1, -1,
1119 0,
1120 /*"Update the record at the cursor"*/
1121 },
1122 { "insert", "cur ?count?",
1123 cursor_cmd_insert,
1124 1, 2,
1125 0,
1126 /*"Insert a specified number of empty rows at the cursor (default 1)"*/
1127 },
1128 { "remove", "cur ?count?",
1129 cursor_cmd_remove,
1130 1, 2,
1131 0,
1132 /*"Remove a specified number of rows at the cursor (default 1)"*/
1133 },
1134
1135 /* Attributes */
1136
1137 { "view", "cur",
1138 cursor_cmd_view,
1139 1, 1,
1140 0,
1141 /*"Get the view the cursor points into"*/
1142 },
1143
1144 /* Positioning */
1145
1146 { "tell", "?-absolute? cur",
1147 cursor_cmd_tell,
1148 1, 2,
1149 0,
1150 /*"Get the position of the cursor"*/
1151 },
1152 { "validfor", "?command? cur",
1153 cursor_cmd_validfor,
1154 1, 2,
1155 0,
1156 /*"Checks if the cursor is valid for get (default), set or insert commands"*/
1157 },
1158 { "seek", "curVar index",
1159 cursor_cmd_seek,
1160 2, 2,
1161 0,
1162 /*"Seek to the specified index in the view"*/
1163 },
1164 { "incr", "curVar ?offset?",
1165 cursor_cmd_incr,
1166 1, 2,
1167 0,
1168 /*"Move the cursor offset records from its current position (default 1)"*/
1169 },
1170
1171 { 0 }
1172 };
1173
JimCursorCommand(Jim_Interp * interp,int argc,Jim_Obj * const * argv)1174 static int JimCursorCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
1175 {
1176 Jim_Obj *cmdObj;
1177
1178 if (argc < 2) {
1179 Jim_WrongNumArgs(interp, 1, argv, "command ...");
1180 return JIM_ERR;
1181 }
1182
1183 cmdObj = Jim_NewStringObj(interp, "cursor ", -1);
1184 Jim_AppendObj(interp, cmdObj, argv[1]);
1185
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);
1192 }
1193 }
1194
1195 /* -------------------------------------------------------------------------
1196 * View handle
1197 * ------------------------------------------------------------------------- */
1198
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.
1203 *
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.
1207 */
1208
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);
1211
1212 /* Unary operations -------------------------------------------------------- */
1213
1214 #define UNOP(name, Method) \
1215 static int view_cmd_##name(Jim_Interp *interp, int argc, Jim_Obj *const *argv) \
1216 { \
1217 const c4_View *viewPtr = (const c4_View *)Jim_CmdPrivData(interp); \
1218 \
1219 Jim_SetResult(interp, JimNewViewObj(interp, viewPtr->Method())); \
1220 return JIM_OK; \
1221 }
1222
UNOP(copy,Duplicate)1223 UNOP(copy, Duplicate)
1224 UNOP(clone, Clone)
1225 UNOP(unique, Unique)
1226
1227 #undef UNOP
1228
1229 static int view_cmd_blocked(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
1230 {
1231 const c4_View *viewPtr = (const c4_View *)Jim_CmdPrivData(interp);
1232
1233 if (viewPtr->GetSize() != 1 ||
1234 strcmp(viewPtr->NthProperty(0).Name(), "_B") != 0 ||
1235 viewPtr->NthProperty(0).Type() != MK_PROPERTY_VIEW)
1236 {
1237 Jim_SetResultString(interp,
1238 "blocked view must have exactly one subview property called _B", -1);
1239 return JIM_ERR;
1240 }
1241
1242 Jim_SetResult(interp, JimNewViewObj(interp, viewPtr->Blocked()));
1243 return JIM_OK;
1244 }
1245
1246 /* Binary operations ------------------------------------------------------- */
1247
1248 #define BINOP(name, Method) \
1249 static int view_cmd_##name(Jim_Interp *interp, int argc, Jim_Obj *const *argv) \
1250 { \
1251 const c4_View *viewPtr = (const c4_View *)Jim_CmdPrivData(interp); \
1252 c4_View otherView; \
1253 \
1254 if (JimGetView(interp, argv[0], &otherView) != JIM_OK) \
1255 return JIM_ERR; \
1256 \
1257 Jim_SetResult(interp, JimNewViewObj(interp, viewPtr->Method(otherView))); \
1258 return JIM_OK; \
1259 }
1260
BINOP(pair,Pair)1261 BINOP(pair, Pair)
1262 BINOP(concat, Concat)
1263 BINOP(product, Product)
1264
1265 BINOP(union, Union)
1266 BINOP(intersect, Intersect)
1267 BINOP(minus, Minus)
1268 BINOP(different, Different)
1269
1270 #undef BINOP
1271
1272 /* Projections ------------------------------------------------------------- */
1273
1274 static int view_cmd_project(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
1275 {
1276 const c4_View *viewPtr = (const c4_View *)Jim_CmdPrivData(interp);
1277 c4_View props;
1278
1279 if (JimGetProperties(interp, argc, argv, *viewPtr, &props) != JIM_OK)
1280 return JIM_ERR;
1281
1282 Jim_SetResult(interp, JimNewViewObj(interp, viewPtr->Project(props)));
1283 return JIM_OK;
1284 }
1285
view_cmd_without(Jim_Interp * interp,int argc,Jim_Obj * const * argv)1286 static int view_cmd_without(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
1287 {
1288 const c4_View *viewPtr = (const c4_View *)Jim_CmdPrivData(interp);
1289 c4_View props;
1290
1291 if (JimGetProperties(interp, argc, argv, *viewPtr, &props) != JIM_OK)
1292 return JIM_ERR;
1293
1294 Jim_SetResult(interp, JimNewViewObj(interp, viewPtr->ProjectWithout(props)));
1295 return JIM_OK;
1296 }
1297
view_cmd_range(Jim_Interp * interp,int argc,Jim_Obj * const * argv)1298 static int view_cmd_range(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
1299 {
1300 const c4_View *viewPtr = (const c4_View *)Jim_CmdPrivData(interp);
1301 int start, end;
1302 jim_wide step;
1303
1304 if (JimGetPosition(interp, argv[0], *viewPtr, &start) != JIM_OK ||
1305 JimGetPosition(interp, argv[1], *viewPtr, &end) != JIM_OK)
1306 {
1307 return JIM_ERR;
1308 }
1309
1310 if (argc == 2)
1311 step = 1;
1312 else if (Jim_GetWide(interp, argv[2], &step) != JIM_OK)
1313 return JIM_ERR;
1314
1315 Jim_SetResult(interp, JimNewViewObj(interp, viewPtr->Slice(start, end + 1, (int)step)));
1316 return JIM_OK;
1317 }
1318
1319 /* Ordering ---------------------------------------------------------------- */
1320
view_cmd_sort(Jim_Interp * interp,int argc,Jim_Obj * const * argv)1321 static int view_cmd_sort(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
1322 {
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;
1327
1328 const char *rep;
1329 int reverse;
1330 Jim_Obj *propObj;
1331
1332 /* Special case: property names may be preceded with a dash. Use
1333 * a temporary object in this case.
1334 */
1335
1336 for (i = 0; i < argc; i++) {
1337 propObj = argv[i];
1338
1339 rep = Jim_GetString(argv[i], &len);
1340 reverse = (len > 0 && rep[0] == '-');
1341
1342 if (reverse)
1343 propObj = Jim_NewStringObj(interp, rep + 1, len - 1);
1344
1345 if (JimGetProperty(interp, propObj, *viewPtr, NULL, &propPtr) != JIM_OK) {
1346 if (reverse)
1347 Jim_FreeNewObj(interp, propObj);
1348 return JIM_ERR;
1349 }
1350
1351 sortProps.AddProperty(*propPtr);
1352 if (reverse) {
1353 revProps.AddProperty(*propPtr);
1354 Jim_FreeNewObj(interp, propObj);
1355 }
1356 }
1357
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)));
1364
1365 return JIM_OK;
1366 }
1367
1368 /* Metakit core seems to be doing something similar for SortOn, but neither
1369 * Ordered nor Hash use it, for unknown reason.
1370 */
1371
BubbleProperties(Jim_Interp * interp,c4_View orig,int objc,Jim_Obj * const * objv,c4_View * projPtr)1372 static int BubbleProperties(Jim_Interp *interp, c4_View orig, int objc, Jim_Obj *const *objv, c4_View *projPtr)
1373 {
1374 c4_View proj;
1375 const c4_Property *propPtr;
1376 int i, count;
1377
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);
1382 }
1383
1384 count = orig.NumProperties();
1385 for (i = 0; i < count; i++)
1386 proj.AddProperty(orig.NthProperty(i));
1387
1388 *projPtr = proj;
1389 return JIM_OK;
1390 }
1391
view_cmd_ordered(Jim_Interp * interp,int argc,Jim_Obj * const * argv)1392 static int view_cmd_ordered(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
1393 {
1394 const c4_View *viewPtr = (const c4_View *)Jim_CmdPrivData(interp);
1395 c4_View proj;
1396
1397 if (BubbleProperties(interp, *viewPtr, argc, argv, &proj) != JIM_OK)
1398 return JIM_ERR;
1399
1400 Jim_SetResult(interp, JimNewViewObj(interp, proj.Ordered(argc)));
1401 return JIM_OK;
1402 }
1403
view_cmd_hash(Jim_Interp * interp,int argc,Jim_Obj * const * argv)1404 static int view_cmd_hash(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
1405 {
1406 const c4_View *viewPtr = (const c4_View *)Jim_CmdPrivData(interp);
1407 c4_View hash, proj;
1408
1409 if (JimGetView(interp, argv[0], &hash) != JIM_OK)
1410 return JIM_ERR;
1411
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. */
1417 {
1418 Jim_SetResultString(interp,
1419 "hash view must be laid out as {_H integer _R integer}", -1);
1420 return JIM_ERR;
1421 }
1422
1423 if (BubbleProperties(interp, *viewPtr, argc - 1, argv + 1, &proj) != JIM_OK)
1424 return JIM_ERR;
1425
1426 Jim_SetResult(interp, JimNewViewObj(interp, proj.Hash(hash, argc - 1)));
1427 return JIM_OK;
1428 }
1429
1430 /* Relational operations --------------------------------------------------- */
1431
view_cmd_join(Jim_Interp * interp,int argc,Jim_Obj * const * argv)1432 static int view_cmd_join(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
1433 {
1434 const c4_View *viewPtr = (const c4_View *)Jim_CmdPrivData(interp);
1435 c4_View other, props;
1436 int outer, off;
1437
1438 if (JimGetView(interp, argv[0], &other) != JIM_OK)
1439 return JIM_ERR;
1440
1441 off = 1; outer = 0;
1442 if (Jim_CompareStringImmediate(interp, argv[1], "-outer")) {
1443 off++; outer = 1;
1444 }
1445
1446 if (JimGetProperties(interp, argc - off, argv + off, *viewPtr, &props) != JIM_OK)
1447 return JIM_ERR;
1448
1449 Jim_SetResult(interp, JimNewViewObj(interp, viewPtr->Join(props, other, outer)));
1450 return JIM_OK;
1451 }
1452
view_cmd_group(Jim_Interp * interp,int argc,Jim_Obj * const * argv)1453 static int view_cmd_group(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
1454 {
1455 const c4_View *viewPtr = (const c4_View *)Jim_CmdPrivData(interp);
1456 const c4_Property *subviewPtr;
1457 c4_View props;
1458
1459 if (JimGetPropertyTyped(interp, argv[0], MK_PROPERTY_VIEW, &subviewPtr) != JIM_OK)
1460 return JIM_ERR;
1461
1462 if (JimGetProperties(interp, argc - 1, argv + 1, *viewPtr, &props) != JIM_OK)
1463 return JIM_ERR;
1464
1465 Jim_SetResult(interp, JimNewViewObj(interp, viewPtr->GroupBy(props, *(c4_ViewProp *)subviewPtr)));
1466 return JIM_OK;
1467 }
1468
view_cmd_flatten(Jim_Interp * interp,int argc,Jim_Obj * const * argv)1469 static int view_cmd_flatten(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
1470 {
1471 const c4_View *viewPtr = (const c4_View *)Jim_CmdPrivData(interp);
1472 const c4_Property *subviewPtr;
1473
1474 if (JimGetProperty(interp, argv[0], *viewPtr, NULL, &subviewPtr) != JIM_OK)
1475 return JIM_ERR;
1476
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;
1481 }
1482
1483 Jim_SetResult(interp, JimNewViewObj(interp, viewPtr->JoinProp(*(c4_ViewProp *)subviewPtr)));
1484 return JIM_OK;
1485 }
1486
1487 /* View queries ------------------------------------------------------------ */
1488
view_cmd_properties(Jim_Interp * interp,int argc,Jim_Obj * const * argv)1489 static int view_cmd_properties(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
1490 {
1491 const c4_View *viewPtr = (const c4_View *) Jim_CmdPrivData(interp);
1492 Jim_SetResult(interp, JimViewPropertiesList(interp, *viewPtr));
1493 return JIM_OK;
1494 }
1495
view_cmd_size(Jim_Interp * interp,int argc,Jim_Obj * const * argv)1496 static int view_cmd_size(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
1497 {
1498 const c4_View *viewPtr = (const c4_View *) Jim_CmdPrivData(interp);
1499 Jim_SetResultInt(interp, viewPtr->GetSize());
1500 return JIM_OK;
1501 }
1502
view_cmd_resize(Jim_Interp * interp,int argc,Jim_Obj * const * argv)1503 static int view_cmd_resize(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
1504 {
1505 c4_View *view = (c4_View *) Jim_CmdPrivData(interp);
1506 jim_wide size;
1507
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;
1514 }
1515
1516 view->SetSize((int)size);
1517 Jim_SetResult(interp, argv[0]);
1518 return JIM_OK;
1519 }
1520
view_cmd_type(Jim_Interp * interp,int argc,Jim_Obj * const * argv)1521 static int view_cmd_type(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
1522 {
1523 const c4_View *viewPtr = (const c4_View *)Jim_CmdPrivData(interp);
1524
1525 if (argc == 1) {
1526 const c4_Property *propPtr;
1527
1528 if (JimGetProperty(interp, argv[0], *viewPtr, NULL, &propPtr) != JIM_OK)
1529 return JIM_ERR;
1530
1531 Jim_SetResultString(interp, JimMkTypeName(propPtr->Type()), -1);
1532 }
1533 else {
1534 Jim_Obj *result;
1535 int i, count;
1536
1537 result = Jim_NewListObj(interp, NULL, 0);
1538 count = viewPtr->NumProperties();
1539
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));
1545 }
1546
1547 Jim_SetResult(interp, result);
1548 }
1549
1550 return JIM_OK;
1551 }
1552
1553 /* View lifetime ----------------------------------------------------------- */
1554
view_cmd_pin(Jim_Interp * interp,int argc,Jim_Obj * const * argv)1555 static int view_cmd_pin(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
1556 {
1557 JimPinView(interp, argv[0]);
1558 Jim_SetResult(interp, argv[0]);
1559 return JIM_OK;
1560 }
1561
view_cmd_as(Jim_Interp * interp,int argc,Jim_Obj * const * argv)1562 static int view_cmd_as(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
1563 {
1564 JimPinView(interp, argv[0]);
1565 Jim_SetVariable(interp, argv[2], argv[0]);
1566 Jim_SetResult(interp, argv[0]);
1567 return JIM_OK;
1568 }
1569
view_cmd_destroy(Jim_Interp * interp,int argc,Jim_Obj * const * argv)1570 static int view_cmd_destroy(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
1571 {
1572 Jim_DeleteCommand(interp, Jim_String(argv[0]));
1573 return JIM_OK;
1574 }
1575
1576 /* Command table ----------------------------------------------------------- */
1577
1578 static const jim_subcmd_type view_command_table[] = {
1579
1580 /* Unary operations */
1581
1582 { "copy", "",
1583 view_cmd_copy,
1584 0, 0,
1585 0,
1586 /*"Create a copy of the view with exactly the same data"*/
1587 },
1588 { "clone", "",
1589 view_cmd_clone,
1590 0, 0,
1591 0,
1592 /*"Create an empty view with the same properties as this one"*/
1593 },
1594 { "unique", "",
1595 view_cmd_unique,
1596 0, 0,
1597 0,
1598 /*"Derived view without any duplicate rows (read-only, no change notifications)"*/
1599 },
1600 { "blocked", "",
1601 view_cmd_blocked,
1602 0, 0,
1603 0,
1604 /*"Build a scalable \"blocked\" out of a view with a single subview property called _B"*/
1605 },
1606
1607 /* Binary operations */
1608
1609 #define BINOP(name, descr) \
1610 { #name, "otherView", \
1611 view_cmd_##name, \
1612 1, 1, 0, \
1613 }
1614
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"),
1618
1619 /* Set operations */
1620
1621 #define SETOP(name, descr) BINOP(name, descr "; works only if all the rows are unique")
1622
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"),
1627
1628 #undef SETOP
1629
1630 #undef BINOP
1631
1632 /* Projections and selections */
1633
1634 { "project", "prop ?prop ...?",
1635 view_cmd_project,
1636 1, -1,
1637 0,
1638 /*"View projection: only the specified properties, in the specified order"*/
1639 },
1640 { "without", "prop ?prop ...?",
1641 view_cmd_without,
1642 1, -1,
1643 0,
1644 /*"View projection: remove the specified properties"*/
1645 },
1646 { "range", "first last ?step?",
1647 view_cmd_range,
1648 2, 3,
1649 0,
1650 /*"Range or slice of the view (read-write, no change notifications)"*/
1651 },
1652
1653 /* Ordering */
1654
1655 { "sort", "?[prop|-prop] ...?",
1656 view_cmd_sort,
1657 0, -1,
1658 0,
1659 /*"Derived view sorted on the specified properties (in order), or on all properties"*/
1660 },
1661 { "ordered", "prop ?prop ...?",
1662 view_cmd_ordered,
1663 1, -1,
1664 0,
1665 /*"Consider the underlying view ordered on the specified properties"*/
1666 },
1667 { "hash", "hashView prop ?prop ...?",
1668 view_cmd_hash,
1669 2, -1,
1670 0,
1671 /*"Mapped view maintaining a hash table on the key consisting of the specified properties"*/
1672 },
1673
1674 /* Relational operations */
1675
1676 { "join", "view ?-outer? prop ?prop ...?",
1677 view_cmd_join,
1678 2, -1,
1679 0,
1680 /*"Relational join with view on the specified properties"*/
1681 },
1682 { "group", "subviewName prop ?prop ...?",
1683 view_cmd_group,
1684 1, -1,
1685 0,
1686 /*"Group rows with equal specified properties, move all other properties into subview"*/
1687 },
1688 { "flatten", "subviewProp",
1689 view_cmd_flatten,
1690 1, 1,
1691 0,
1692 /*"Flatten the specified subview; the inverse of group"*/
1693 },
1694
1695 /* Attributes */
1696
1697 { "properties", "",
1698 view_cmd_properties,
1699 0, 0,
1700 0,
1701 /*"List the properties in this view"*/
1702 },
1703 { "size", "",
1704 view_cmd_size,
1705 0, 0,
1706 0,
1707 /*"Return the number of records in the view"*/
1708 },
1709 { "resize", "newSize",
1710 view_cmd_resize,
1711 1, 1,
1712 0,
1713 /*"Set the number of records in the view"*/
1714 },
1715 { "type", "?prop?",
1716 view_cmd_type,
1717 0, 1,
1718 0,
1719 /*"Return the type of an existing property, or of all properties"*/
1720 },
1721
1722 /* Lifetime management */
1723
1724 { "pin", "",
1725 view_cmd_pin,
1726 0, 0,
1727 JIM_MODFLAG_FULLARGV,
1728 /*"Marks the view as persistent"*/
1729 },
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"*/
1735 },
1736 { "destroy", "",
1737 view_cmd_destroy,
1738 0, 0,
1739 JIM_MODFLAG_FULLARGV,
1740 /*"Destroys the view explicitly"*/
1741 },
1742
1743 { 0 }
1744 };
1745
JimViewDelProc(Jim_Interp * interp,void * privData)1746 static void JimViewDelProc(Jim_Interp *interp, void *privData)
1747 {
1748 delete (c4_View *)privData;
1749 }
1750
JimViewSubCmdProc(Jim_Interp * interp,int argc,Jim_Obj * const * argv)1751 static int JimViewSubCmdProc(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
1752 {
1753 int pipe, result;
1754 Jim_Obj *cmdObj;
1755
1756 pipe = JimPipelineBoundary(argc, argv);
1757
1758 if (pipe < 1) {
1759 Jim_WrongNumArgs(interp, 1, argv, "command ...");
1760 return JIM_ERR;
1761 }
1762
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.
1767 */
1768
1769 cmdObj = Jim_NewStringObj(interp, "mk.view ", -1);
1770 Jim_AppendObj(interp, cmdObj, argv[1]);
1771
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... */
1775
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 *));
1780
1781 result = Jim_EvalObjVector(interp, pipe, objv);
1782
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);
1787 }
1788
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);
1793 }
1794
JimOneShotViewSubCmdProc(Jim_Interp * interp,int argc,Jim_Obj * const * argv)1795 static int JimOneShotViewSubCmdProc(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
1796 {
1797 int result;
1798 Jim_Cmd *cmd;
1799
1800 result = JimViewSubCmdProc(interp, argc, argv);
1801
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]));
1805
1806 return result;
1807 }
1808
JimViewFinalizerProc(Jim_Interp * interp,int argc,Jim_Obj * const * argv)1809 static int JimViewFinalizerProc(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
1810 {
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;
1814 }
1815
JimNewViewObj(Jim_Interp * interp,c4_View view)1816 static Jim_Obj *JimNewViewObj(Jim_Interp *interp, c4_View view) {
1817 Jim_Obj *tag, *ref;
1818
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);
1823
1824 return ref;
1825 }
1826
JimGetView(Jim_Interp * interp,Jim_Obj * obj,c4_View * viewPtr)1827 static int JimGetView(Jim_Interp *interp, Jim_Obj *obj, c4_View *viewPtr)
1828 {
1829 Jim_Cmd *cmd = Jim_GetCommand(interp, obj, 0);
1830
1831 if (cmd == NULL || cmd->isproc || cmd->u.native.delProc != JimViewDelProc) {
1832 Jim_SetResultFormatted(interp, "invalid view object \"%#s\"", obj);
1833 return JIM_ERR;
1834 }
1835
1836 *viewPtr = *(c4_View *)cmd->u.native.privData;
1837 return JIM_OK;
1838 }
1839
1840 /* Only call this against known view objects. */
JimPinView(Jim_Interp * interp,Jim_Obj * obj)1841 static void JimPinView(Jim_Interp *interp, Jim_Obj *obj)
1842 {
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;
1847 }
1848
JimViewPropertiesList(Jim_Interp * interp,c4_View view)1849 static Jim_Obj *JimViewPropertiesList(Jim_Interp *interp, c4_View view)
1850 {
1851 int i, count;
1852 Jim_Obj *result;
1853
1854 result = Jim_NewListObj(interp, NULL, 0);
1855 count = view.NumProperties();
1856
1857 for (i = 0; i < count; i++) {
1858 Jim_ListAppendElement(interp, result, Jim_NewStringObj(interp,
1859 view.NthProperty(i).Name(), -1));
1860 }
1861
1862 return result;
1863 }
1864
1865 /* ----------------------------------------------------------------------------
1866 * Storage handle
1867 * ---------------------------------------------------------------------------- */
1868
1869 /* These are also commands, like views, but must be managed explicitly by the
1870 * user. Quite like file handles, actually.
1871 */
1872
1873 typedef struct MkStorage {
1874 unsigned flags;
1875 Jim_Obj *filename;
1876 c4_Storage storage;
1877 c4_Cursor content;
1878 } MkStorage;
1879
1880 #define JIM_MKFLAG_INMEMORY 0x0001
1881 #define JIM_MKFLAG_READONLY 0x0002
1882 #define JIM_MKFLAG_EXTEND 0x0004
1883 #define JIM_MKFLAG_AUTOCOMMIT 0x0008
1884
1885 /* Attributes -------------------------------------------------------------- */
1886
storage_cmd_autocommit(Jim_Interp * interp,int argc,Jim_Obj * const * argv)1887 static int storage_cmd_autocommit(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
1888 {
1889 MkStorage *mk = (MkStorage *)Jim_CmdPrivData(interp);
1890
1891 if (argc == 1) {
1892 jim_wide flag;
1893
1894 if (Jim_GetWide(interp, argv[0], &flag) != JIM_OK)
1895 return JIM_ERR;
1896
1897 if (flag)
1898 mk->flags |= JIM_MKFLAG_AUTOCOMMIT;
1899 else
1900 mk->flags &= ~JIM_MKFLAG_AUTOCOMMIT;
1901 mk->storage.AutoCommit(flag);
1902 }
1903
1904 Jim_SetResultBool(interp, (mk->flags & JIM_MKFLAG_AUTOCOMMIT) != 0);
1905 return JIM_OK;
1906 }
1907
storage_cmd_readonly(Jim_Interp * interp,int argc,Jim_Obj * const * argv)1908 static int storage_cmd_readonly(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
1909 {
1910 MkStorage *mk = (MkStorage *)Jim_CmdPrivData(interp);
1911
1912 Jim_SetResultBool(interp, (mk->flags & JIM_MKFLAG_READONLY) != 0);
1913 return JIM_OK;
1914 }
1915
1916 /* Views ------------------------------------------------------------------- */
1917
storage_cmd_views(Jim_Interp * interp,int argc,Jim_Obj * const * argv)1918 static int storage_cmd_views(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
1919 {
1920 MkStorage *mk = (MkStorage *)Jim_CmdPrivData(interp);
1921
1922 Jim_SetResult(interp, JimViewPropertiesList(interp, mk->storage));
1923 return JIM_OK;
1924 }
1925
storage_cmd_view(Jim_Interp * interp,int argc,Jim_Obj * const * argv)1926 static int storage_cmd_view(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
1927 {
1928 MkStorage *mk = (MkStorage *)Jim_CmdPrivData(interp);
1929 const c4_Property *propPtr;
1930
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));
1934
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;
1942 }
1943 return Jim_EvalObjPrefix(interp, Jim_GetResult(interp), argc - 2, argv + 2);
1944 }
1945 }
1946
storage_cmd_structure(Jim_Interp * interp,int argc,Jim_Obj * const * argv)1947 static int storage_cmd_structure(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
1948 {
1949 MkStorage *mk = (MkStorage *)Jim_CmdPrivData(interp);
1950
1951 if (argc < 2) { /* Query */
1952 const char *name;
1953
1954 if (argc == 0)
1955 name = NULL;
1956 else {
1957 const c4_Property *propPtr;
1958
1959 if (JimGetProperty(interp, argv[0], mk->storage, "view", &propPtr) != JIM_OK)
1960 return JIM_ERR;
1961 name = propPtr->Name();
1962 }
1963
1964 Jim_SetResult(interp, JimFromMkDescription(interp,
1965 mk->storage.Description(name), NULL));
1966 }
1967 else { /* Modify */
1968 char *descr;
1969 const char *name;
1970 int len, dlen;
1971
1972 if (JimCheckMkName(interp, argv[0], "view") != JIM_OK)
1973 return JIM_ERR;
1974 name = Jim_GetString(argv[0], &len);
1975
1976 if (JimToMkDescription(interp, argv[1], &descr) != JIM_OK)
1977 return JIM_ERR;
1978 dlen = strlen(descr);
1979
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';
1986
1987 mk->storage.GetAs(descr);
1988
1989 Jim_Free(descr);
1990 }
1991
1992 return JIM_OK;
1993 }
1994
1995 /* Store operations -------------------------------------------------------- */
1996
storage_cmd_commit(Jim_Interp * interp,int argc,Jim_Obj * const * argv)1997 static int storage_cmd_commit(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
1998 {
1999 MkStorage *mk = (MkStorage *)Jim_CmdPrivData(interp);
2000
2001 if (mk->flags & JIM_MKFLAG_INMEMORY) {
2002 Jim_SetResultString(interp, "cannot commit an in-memory storage", -1);
2003 return JIM_ERR;
2004 }
2005 else if (mk->flags & JIM_MKFLAG_READONLY) {
2006 Jim_SetResultString(interp, "cannot commit a read-only storage", -1);
2007 return JIM_ERR;
2008 }
2009
2010 if (mk->storage.Commit(0)) {
2011 Jim_SetEmptyResult(interp);
2012 return JIM_OK;
2013 }
2014 else {
2015 Jim_SetResultString(interp, "I/O error during commit", -1);
2016 return JIM_ERR;
2017 }
2018 }
2019
storage_cmd_rollback(Jim_Interp * interp,int argc,Jim_Obj * const * argv)2020 static int storage_cmd_rollback(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
2021 {
2022 MkStorage *mk = (MkStorage *)Jim_CmdPrivData(interp);
2023
2024 if (mk->flags & JIM_MKFLAG_INMEMORY) {
2025 Jim_SetResultString(interp, "cannot rollback an in-memory storage", -1);
2026 return JIM_ERR;
2027 }
2028
2029 if (mk->storage.Rollback(0)) {
2030 Jim_SetEmptyResult(interp);
2031 return JIM_OK;
2032 }
2033 else {
2034 Jim_SetResultString(interp, "I/O error during rollback", -1);
2035 return JIM_ERR;
2036 }
2037 }
2038
storage_cmd_close(Jim_Interp * interp,int argc,Jim_Obj * const * argv)2039 static int storage_cmd_close(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
2040 {
2041 return Jim_DeleteCommand(interp, Jim_String(argv[0]));
2042 }
2043
2044 /* Command table ----------------------------------------------------------- */
2045
2046 static const jim_subcmd_type storage_command_table[] = {
2047
2048 /* Options */
2049
2050 { "autocommit", "?value?",
2051 storage_cmd_autocommit,
2052 0, 1,
2053 0,
2054 /*"Query or modify the auto-commit option of this storage"*/
2055 },
2056 { "readonly", "",
2057 storage_cmd_readonly,
2058 0, 0,
2059 0,
2060 /*"Returns the read-only status of this storage"*/
2061 },
2062
2063 /* Views */
2064
2065 { "views", "",
2066 storage_cmd_views,
2067 0, 0,
2068 0,
2069 /*"Returns the list of views stored here"*/
2070 },
2071 { "view", "viewName",
2072 storage_cmd_view,
2073 1, -1,
2074 0,
2075 /*"Retrieve the view specified by viewName"*/
2076 },
2077 { "structure", "?viewName? ?description?",
2078 storage_cmd_structure,
2079 0, 2,
2080 0,
2081 /*"Query or modify the structure of this storage"*/
2082 },
2083
2084 /* Store operations */
2085
2086 { "commit", "",
2087 storage_cmd_commit,
2088 0, 0,
2089 0,
2090 /*"Commit the changes to disk"*/
2091 },
2092 { "rollback", "",
2093 storage_cmd_rollback,
2094 0, 0,
2095 0,
2096 /*"Revert to the saved state"*/
2097 },
2098 { "close", "",
2099 storage_cmd_close,
2100 0, 0,
2101 JIM_MODFLAG_FULLARGV,
2102 /*"Close this storage"*/
2103 },
2104
2105 { 0 }
2106 };
2107
JimStorageDelProc(Jim_Interp * interp,void * privData)2108 static void JimStorageDelProc(Jim_Interp *interp, void *privData)
2109 {
2110 MkStorage *mk = (MkStorage *)privData;
2111
2112 mk->storage.~c4_Storage();
2113 mk->content.~c4_Cursor();
2114 Jim_DecrRefCount(interp, mk->filename);
2115 Jim_Free(mk);
2116 }
2117
JimStorageSubCmdProc(Jim_Interp * interp,int argc,Jim_Obj * const * argv)2118 static int JimStorageSubCmdProc(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
2119 {
2120 Jim_Obj *cmdObj;
2121
2122 cmdObj = Jim_NewStringObj(interp, "mk.storage ", -1);
2123 Jim_AppendObj(interp, cmdObj, argv[1]);
2124
2125 if (Jim_GetCommand(interp, cmdObj, 0) != NULL) {
2126 int result;
2127
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 *));
2132
2133 result = Jim_EvalObjVector(interp, argc, objv);
2134
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);
2141 }
2142 }
2143
2144 /* -------------------------------------------------------------------------
2145 * storage ?options? ?filename?
2146 *
2147 * Creates a new metakit storage object, optionally backed by a file.
2148 *
2149 * Options apply only when filename is given; these include:
2150 *
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 * ------------------------------------------------------------------------- */
2156
JimStorageCommand(Jim_Interp * interp,int argc,Jim_Obj * const * argv)2157 static int JimStorageCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
2158 {
2159 MkStorage *mk;
2160 char buf[MK_CMD_LEN];
2161 int i, mode;
2162
2163 static const char *const options[] = {
2164 "-readonly",
2165 "-original",
2166 "-extend",
2167 "-nocommit",
2168 0
2169 };
2170 enum {
2171 OPT_READONLY,
2172 OPT_ORIGINAL,
2173 OPT_EXTEND,
2174 OPT_NOCOMMIT
2175 };
2176 int option;
2177
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;
2185 }
2186
2187 switch (option) {
2188 case OPT_READONLY:
2189 if (mode != MK_MODE_READWRITE)
2190 goto modeconflict;
2191
2192 mode = MK_MODE_READONLY;
2193 mk->flags |= JIM_MKFLAG_READONLY;
2194 break;
2195
2196 case OPT_ORIGINAL:
2197 if (mode != MK_MODE_READWRITE)
2198 goto modeconflict;
2199
2200 mode = MK_MODE_ORIGINAL;
2201 mk->flags |= JIM_MKFLAG_READONLY;
2202 break;
2203
2204 case OPT_EXTEND:
2205 if (mode != MK_MODE_READWRITE)
2206 goto modeconflict;
2207
2208 mode = MK_MODE_EXTEND;
2209 mk->flags |= JIM_MKFLAG_EXTEND;
2210 break;
2211
2212 case OPT_NOCOMMIT:
2213 mk->flags &= ~JIM_MKFLAG_AUTOCOMMIT;
2214 break;
2215 }
2216 }
2217
2218 if (argc > 1) {
2219 new(&mk->storage) c4_Storage(Jim_String(argv[argc-1]), mode);
2220
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;
2226 }
2227
2228 mk->filename = argv[argc-1];
2229
2230 if ((mk->flags & JIM_MKFLAG_AUTOCOMMIT) && !(mk->flags & JIM_MKFLAG_READONLY))
2231 mk->storage.AutoCommit(1);
2232 }
2233 else {
2234 mk->flags |= JIM_MKFLAG_INMEMORY;
2235
2236 new(&mk->storage) c4_Storage();
2237 mk->filename = Jim_NewEmptyStringObj(interp);
2238 }
2239 new(&mk->content) c4_Cursor(&mk->storage[0]);
2240 Jim_IncrRefCount(mk->filename);
2241
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;
2246
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;
2251 }
2252
2253 /* -------------------------------------------------------------------------
2254 * Initialization code
2255 * ------------------------------------------------------------------------- */
2256
Jim_mkInit(Jim_Interp * interp)2257 int Jim_mkInit(Jim_Interp *interp)
2258 {
2259 char version[MK_VERSION_SPACE];
2260
2261 snprintf(version, MK_VERSION_SPACE, "%d.%d.%d",
2262 d4_MetakitLibraryVersion / 100,
2263 d4_MetakitLibraryVersion % 100 / 10,
2264 d4_MetakitLibraryVersion % 10);
2265
2266 if (Jim_PackageProvide(interp, "mk", version, JIM_ERRMSG))
2267 return JIM_ERR;
2268
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);
2272
2273 return JIM_OK;
2274 }
2275
2276 } /* extern "C" */
2277