1// mk4too.cpp -- Tcl object command interface to Metakit
2// $Id: mk4too.cpp 4452 2008-12-10 22:57:54Z patthoyts $
3// This is part of Metakit, see http://www.equi4.com/metakit.html
4// Copyright (C) 2000-2004 by Matt Newman and Jean-Claude Wippler.
5
6#include "mk4tcl.h"
7#include <stdio.h>
8#include <string.h>
9
10#if 10 * TCL_MAJOR_VERSION + TCL_MINOR_VERSION < 86
11#define Tcl_GetErrorLine(interp) (interp)->errorLine
12#endif
13
14///////////////////////////////////////////////////////////////////////////////
15// Defined in this file:
16
17class MkView;
18
19///////////////////////////////////////////////////////////////////////////////
20// The MkView class adds Metakit-specific utilities and all the command procs.
21
22int MkView::Dispatcher(ClientData cd, Tcl_Interp *ip, int oc, Tcl_Obj *const *
23  ov) {
24    MkView *self = (MkView*)cd;
25
26    if (self == 0 || self->interp != ip) {
27        Tcl_SetResult(ip, "Initialization error in dispatcher", TCL_STATIC);
28        return TCL_ERROR;
29    }
30    return self->Execute(oc, ov);
31}
32
33void MkView::DeleteProc(ClientData cd) {
34  MkView *self = (MkView*)cd;
35  delete self;
36}
37
38MkView::MkView(Tcl_Interp *ip_, c4_View view_, const char *name): Tcl(ip_),
39  work(*(MkWorkspace*)Tcl_GetAssocData(interp, "mk4tcl", 0)), view(view_) {
40  Register(name);
41}
42
43MkView::MkView(Tcl_Interp *ip_, const char *name): Tcl(ip_), work(*
44  (MkWorkspace*)Tcl_GetAssocData(interp, "mk4tcl", 0)) {
45  Register(name);
46}
47
48MkView::~MkView(){}
49
50void MkView::Register(const char *name) {
51  static int uid = 0;
52  char buf[32];
53
54  if (name == 0 ||  *name == 0) {
55    sprintf(buf, "%d", uid++);
56    cmd = "view" + (c4_String)buf;
57  } else {
58    cmd = name;
59  }
60  // save token... so I can delete cmd later (even if renamed)
61  cmdToken = Tcl_CreateObjCommand(interp, (char*)(const char*)cmd, MkView
62    ::Dispatcher, this, MkView::DeleteProc);
63}
64
65c4_View MkView::View(Tcl_Interp *interp, Tcl_Obj *obj) {
66  const char *name = Tcl_GetStringFromObj(obj, 0);
67  Tcl_CmdInfo ci;
68
69  if (!Tcl_GetCommandInfo(interp, (char*)name, &ci) || ci.objProc != MkView
70    ::Dispatcher) {
71    //Fail("no such view");
72    c4_View temp;
73    return temp;
74  } else {
75    MkView *v = (MkView*)ci.objClientData;
76    return v->view;
77  }
78}
79
80int MkView::asIndex(c4_View &view, Tcl_Obj *obj_, bool mayExceed_) {
81  int size = view.GetSize();
82  int index;
83
84  if (Tcl_GetIntFromObj(interp, obj_, &index) != TCL_OK) {
85    const char *step = Tcl_GetStringFromObj(obj_, 0);
86    if (step != 0 && strcmp(step, "end") == 0) {
87      index = !mayExceed_ ? size - 1: size;
88      Tcl_ResetResult(interp); // clear error
89      _error = TCL_OK;
90    } else {
91      index =  - 1;
92    }
93  }
94
95  if (mayExceed_) {
96    if (index > size)
97      Fail("view index is too large");
98    else if (index < 0)
99      Fail("view index is negative");
100  } else if (index < 0 || index >= size)
101    Fail("view index is out of range");
102
103  return index;
104}
105
106int MkView::SetValues(const c4_RowRef &row_, int objc, Tcl_Obj *const * objv,
107  c4_View &view_) {
108  if (objc % 2)
109    Fail("bad args: must be prop value pairs");
110
111  while (objc > 0 && !_error) {
112    _error = SetAsObj(interp, row_, AsProperty(objv[0], view_), objv[1]);
113
114    objc -= 2;
115    objv += 2;
116  }
117
118  return _error;
119}
120
121int MkView::Execute(int oc, Tcl_Obj *const * ov) {
122  struct CmdDef {
123    int(MkView:: *proc)();
124    int min;
125    int max;
126    const char *desc;
127  };
128
129  static const char *subCmds[] =  {
130    "close", "delete", "exists", "find", "get", "properties", "insert", "open",
131      "search", "select", "set", "size", "loop", "view", "info",
132      // will be deprecated (use "properties" instead)
133    0
134  };
135  static CmdDef defTab[] =  {
136    // the "&MkView::" stuff is required for Mac cwpro2
137     {
138       &MkView::CloseCmd, 2, 2, "close"
139    }
140    ,  {
141       &MkView::DeleteCmd, 3, 4, "delete cursor ?cursor2?"
142    }
143    ,  {
144       &MkView::ExistsCmd, 3, 0, "exists cursor ?prop ...?"
145    }
146    ,  {
147       &MkView::FindCmd, 2, 0, "find ?prop value ...?"
148    }
149    ,  {
150       &MkView::GetCmd, 3, 0, "get cursor ?prop ...?"
151    }
152    ,  {
153       &MkView::InfoCmd, 2, 2, "properties"
154    }
155    ,  {
156       &MkView::InsertCmd, 3, 0, "insert cursor ?prop ...?"
157    }
158    ,  {
159       &MkView::OpenCmd, 4, 4, "open cursor prop"
160    }
161    ,  {
162       &MkView::SearchCmd, 4, 4, "search prop value"
163    }
164    ,  {
165       &MkView::SelectCmd, 2, 0, "select ?..?"
166    }
167    ,  {
168       &MkView::SetCmd, 3, 0, "set cursor prop ?value prop value ...?"
169    }
170    ,  {
171       &MkView::SizeCmd, 2, 3, "size ?newsize?"
172    }
173    ,  {
174       &MkView::LoopCmd, 3, 0, "loop cursor ?first? ?limit? ?step? body"
175    }
176    ,  {
177       &MkView::ViewCmd, 3, 0, "view option ?args?"
178    }
179    ,  {
180       &MkView::InfoCmd, 2, 2, "info"
181    }
182    ,  {
183      0, 0, 0, 0
184    }
185    ,
186  };
187  _error = TCL_OK;
188
189  int id = tcl_GetIndexFromObj(ov[1], subCmds);
190
191  if (id ==  - 1)
192    return TCL_ERROR;
193
194  CmdDef &cd = defTab[id];
195
196  objc = oc;
197  objv = ov;
198
199  if (oc < cd.min || (cd.max > 0 && oc > cd.max)) {
200    msg = "wrong # args: should be \"$obj ";
201    msg += cd.desc;
202    msg += "\"";
203
204    return Fail(msg);
205  }
206
207  return (this->*cd.proc)();
208}
209
210//
211// Tcl command methods
212//
213
214int MkView::CloseCmd() {
215  // remove command instance... this will call delete...
216  Tcl_DeleteCommandFromToken(interp, cmdToken);
217  return TCL_OK;
218}
219
220int MkView::DeleteCmd() {
221  int count = 1;
222  int index = asIndex(view, objv[2], true);
223
224  if (_error)
225    return _error;
226
227  if (objc > 3) {
228    int index2 = asIndex(view, objv[3], true);
229
230    if (_error)
231      return _error;
232
233    count = index2 - index + 1;
234  }
235
236  if (count > view.GetSize() - index)
237    count = view.GetSize() - index;
238
239  if (count >= 1) {
240    view.RemoveAt(index, count);
241  }
242  return TCL_OK;
243}
244
245int MkView::ExistsCmd() {
246  asIndex(view, objv[2], false);
247  int r = _error ? 0 : 1;
248  _error = 0;
249  return tcl_SetObjResult(Tcl_NewIntObj(r));
250}
251
252int MkView::FindCmd() {
253  c4_Row row;
254  int idx = 2;
255
256  while (idx < objc && !_error) {
257    _error = SetAsObj(interp, row, AsProperty(objv[idx], view), objv[idx + 1]);
258    idx += 2;
259  }
260  if (_error)
261    return _error;
262
263  idx = view.Find(row, 0);
264  if (idx ==  - 1) {
265    Fail("not found");
266    return TCL_ERROR;
267  }
268  return tcl_SetObjResult(Tcl_NewIntObj(idx));
269}
270
271int MkView::GetCmd() {
272  int index = asIndex(view, objv[2], false);
273  if (_error)
274    return _error;
275
276  Tcl_Obj *result = tcl_GetObjResult();
277  c4_RowRef row = view[index];
278
279  if (objc < 4) {
280    for (int i = 0; i < view.NumProperties() && !_error; ++i) {
281      const c4_Property &prop = view.NthProperty(i);
282      c4_String name = prop.Name();
283
284      if (prop.Type() == 'V')
285        continue;
286      // omit subviews
287
288      tcl_ListObjAppendElement(result, tcl_NewStringObj(name));
289      tcl_ListObjAppendElement(result, GetValue(row, prop));
290    }
291  } else if (objc == 4) {
292    GetValue(row, AsProperty(objv[3], view), result);
293  } else {
294    for (int i = 3; i < objc && !_error; ++i) {
295      const c4_Property &prop = AsProperty(objv[i], view);
296      tcl_ListObjAppendElement(result, GetValue(row, prop));
297    }
298  }
299  return _error;
300}
301
302int MkView::InfoCmd() {
303  Tcl_Obj *result = tcl_GetObjResult();
304
305  for (int i = 0; i < view.NumProperties() && !_error; ++i) {
306    const c4_Property &prop = view.NthProperty(i);
307
308    c4_String s = prop.Name();
309    if (prop.Type() != 'S') {
310      s += ":";
311      s += prop.Type();
312    }
313
314    tcl_ListObjAppendElement(result, tcl_NewStringObj(s));
315  }
316  return tcl_SetObjResult(result);
317}
318
319int MkView::InsertCmd() {
320  int index = asIndex(view, objv[2], true);
321  if (_error)
322    return _error;
323
324  c4_Row temp;
325  SetValues(temp, objc - 3, objv + 3, view);
326  view.InsertAt(index, temp, 1);
327  //SetValues(view[index], objc - 3, objv + 3);
328
329  if (_error) {
330    view.RemoveAt(index, 1); // remove new row on errors
331    return _error;
332  }
333  return tcl_SetObjResult(Tcl_NewIntObj(index));
334}
335
336int MkView::OpenCmd() {
337  int index = asIndex(view, objv[2], false);
338
339  if (_error)
340    return _error;
341
342  const c4_Property &prop = AsProperty(objv[3], view);
343  if (_error)
344    return _error;
345
346  if (prop.Type() != 'V') {
347    Fail("bad property: must be a view");
348    return TCL_ERROR;
349  }
350  MkView *ncmd = new MkView(interp, ((const c4_ViewProp &)prop)(view[index]));
351
352  return tcl_SetObjResult(tcl_NewStringObj(ncmd->CmdName()));
353}
354
355int MkView::SearchCmd() {
356  Tcl_Obj *obj_ = objv[3];
357  const c4_Property &prop = AsProperty(objv[2], view);
358  char type = prop.Type();
359  double dblVal = 0, dtmp;
360  long longVal = 0;
361#ifdef TCL_WIDE_INT_TYPE
362  Tcl_WideInt wideVal = 0, wtmp;
363#endif
364  c4_String strVal;
365
366  int size = view.GetSize();
367  int first = 0, last = size;
368  int row, rc, e;
369
370  switch (type) {
371    case 'S':
372       {
373        strVal = Tcl_GetStringFromObj(obj_, 0);
374      }
375      break;
376
377    case 'F':
378    case 'D':
379       {
380        e = Tcl_GetDoubleFromObj(interp, obj_, &dblVal);
381        if (e != TCL_OK)
382          return e;
383      }
384      break;
385
386#ifdef TCL_WIDE_INT_TYPE
387    case 'L':
388       {
389        e = Tcl_GetWideIntFromObj(interp, obj_, &wideVal);
390        if (e != TCL_OK)
391          return e;
392      }
393      break;
394#endif
395
396    case 'I':
397       {
398        e = Tcl_GetLongFromObj(interp, obj_, &longVal);
399        if (e != TCL_OK)
400          return e;
401      }
402      break;
403
404    default:
405      Tcl_SetResult(interp, "unsupported property type", TCL_STATIC);
406      return TCL_ERROR;
407  }
408
409  while (first <= last) {
410    row = (first + last) / 2;
411
412    if (row >= size)
413      break;
414
415    switch (type) {
416      case 'S':
417        rc = strVal.CompareNoCase(((c4_StringProp &)prop)(view[row]));
418        break;
419      case 'F':
420        dtmp = dblVal - ((c4_FloatProp &)prop)(view[row]);
421        rc = (dtmp < 0 ?  - 1: (dtmp > 0));
422        break;
423      case 'D':
424        dtmp = dblVal - ((c4_DoubleProp &)prop)(view[row]);
425        rc = (dtmp < 0 ?  - 1: (dtmp > 0));
426        break;
427#ifdef TCL_WIDE_INT_TYPE
428      case 'L':
429        wtmp = wideVal - ((c4_LongProp &)prop)(view[row]);
430        rc = (wtmp < 0 ?  - 1: (wtmp > 0));
431        break;
432#endif
433      case 'I':
434        rc = longVal - ((c4_IntProp &)prop)(view[row]);
435        break;
436      default:
437        rc = 0; // 27-09-2001, to satisfy MSVC6 warn level 4
438    }
439
440    if (rc == 0) {
441      goto done;
442    } else if (rc > 0) {
443      first = row + 1;
444    } else {
445      last = row - 1;
446    }
447  }
448  // Not found
449  row =  - 1;
450  done: return tcl_SetObjResult(Tcl_NewIntObj(row));
451}
452
453int MkView::SelectCmd() {
454  TclSelector sel(interp, view);
455
456  static const char *opts[] =  {
457    "-min",  // 0
458    "-max",  // 1
459    "-exact",  // 2
460    "-glob",  // 3
461    "-regexp",  // 4
462    "-keyword",  // 5
463    "-first",  // 6
464    "-count",  // 7
465    "-sort",  // 8
466    "-rsort",  // 9
467    "-globnc",  // 10
468    0
469  };
470
471  while (objc >= 4) {
472    objc -= 2; // gobble next two arguments
473    objv += 2;
474
475    // at this point, *objv is the next option, and objc >= 2
476
477    int id =  - 1;
478
479    const char *p = Tcl_GetStringFromObj(*objv, 0);
480    if (p &&  *p == '-') {
481      id = tcl_GetIndexFromObj(*objv, opts);
482      if (id < 0)
483        return _error;
484    }
485
486    switch (id) {
487      case  - 1:  { // prop value : case-insensitive match
488        _error = sel.AddCondition( - 1, objv[0], objv[1]);
489      }
490      break;
491
492      case 0:
493        // -min prop value : property must be greater or equal to value
494      case 1:
495        // -max prop value : property must be less or equal to value
496      case 2:
497        // -exact prop value : exact case-sensitive match
498      case 3:
499        // -glob prop pattern : match "glob" expression wildcard
500      case 4:
501        // -regexp prop pattern : match specified regular expression
502      case 5:
503        // -keyword prop prefix : match keyword in given property
504      case 10:
505         { // -globnc prop pattern : match "glob", but ignore case
506          if (objc < 3)
507            return Fail("not enough arguments");
508
509          _error = sel.AddCondition(id, objv[1], objv[2]);
510
511          --objc; // gobble a third argument
512          ++objv;
513        }
514        break;
515
516      case 6:
517        // -first pos : searching starts at specified row index
518      case 7:
519         { // -count num : return no more than this many results
520          int n = tcl_GetIntFromObj(objv[1]);
521          if (_error)
522            return _error;
523
524          if (id == 6)
525            sel._first = n;
526          else
527            sel._count = n;
528        }
529        break;
530
531      case 8:
532        // -sort prop : sort on one or more properties, ascending
533      case 9:
534         { // -rsort prop : sort on one or more properties, descending
535          c4_View props = sel.GetAsProps(objv[1]);
536          for (int i = 0; i < props.NumProperties(); ++i) {
537            const c4_Property &prop = props.NthProperty(i);
538
539            sel._sortProps.AddProperty(prop);
540            if (id == 9)
541              sel._sortRevProps.AddProperty(prop);
542          }
543        }
544        break;
545    }
546  }
547
548  if (_error)
549    return _error;
550
551  c4_View nview;
552  sel.DoSelect(0, &nview);
553  MkView *ncmd = new MkView(interp, nview);
554  return tcl_SetObjResult(tcl_NewStringObj(ncmd->CmdName()));
555}
556
557int MkView::SetCmd() {
558  if (objc < 4)
559    return GetCmd();
560
561  int index = asIndex(view, objv[2], false);
562  if (_error)
563    return _error;
564
565  return SetValues(view[index], objc - 3, objv + 3, view);
566}
567
568int MkView::SizeCmd() {
569  if (objc > 2) {
570    int i = tcl_GetIntFromObj(objv[2]);
571    if (_error)
572      return _error;
573    view.SetSize(i);
574  }
575
576  return tcl_SetObjResult(Tcl_NewIntObj(view.GetSize()));
577}
578
579int MkView::LoopCmd() {
580  long first = 0;
581  long limit = view.GetSize();
582  long incr = 1;
583
584  if (objc >= 5)
585    first = tcl_ExprLongObj(objv[3]);
586
587  if (objc >= 6)
588    limit = tcl_ExprLongObj(objv[4]);
589
590  if (objc >= 7) {
591    incr = tcl_ExprLongObj(objv[5]);
592    if (incr == 0)
593      Fail("increment has to be nonzero");
594  }
595
596  if (_error)
597    return _error;
598
599  Tcl_Obj *vname = objv[2];
600  Tcl_Obj *cmd = objv[objc - 1];
601
602  for (int i = first; i < limit && incr > 0 || i > limit && incr < 0; i += incr)
603    {
604    Tcl_Obj *var = Tcl_ObjSetVar2(interp, vname, 0, Tcl_NewIntObj(i),
605      TCL_LEAVE_ERR_MSG);
606    if (var == 0)
607      return Fail();
608
609    _error = Mk_EvalObj(interp, cmd);
610
611    if (_error) {
612      if (_error == TCL_CONTINUE)
613        _error = TCL_OK;
614      else {
615        if (_error == TCL_BREAK)
616          _error = TCL_OK;
617        else if (_error == TCL_ERROR) {
618          char msg[100];
619          sprintf(msg, "\n  (\"mk::loop\" body line %d)", Tcl_GetErrorLine(interp));
620          Tcl_AddObjErrorInfo(interp, msg,  - 1);
621        }
622        break;
623      }
624    }
625  }
626
627  if (_error == TCL_OK)
628    Tcl_ResetResult(interp);
629
630  return _error;
631}
632
633int MkView::ViewCmd() {
634  struct CmdDef {
635    int(MkView:: *proc)();
636    int min;
637    int max;
638    const char *desc;
639  };
640
641  static const char *subCmds[] =  {
642    "blocked", "clone", "concat", "copy", "different", "dup", "flatten",
643      "groupby", "hash", "indexed", "intersect", "join", "map", "minus",
644      "ordered", "pair", "product", "project", "range", "readonly", "rename",
645      "restrict", "union", "unique",
646#if 0
647    "==", "!=", "<", ">", "<=", ">=",
648#endif
649    0
650  };
651  static CmdDef defTab[] =  {
652    // the "&MkView::" stuff is required for Mac cwpro2
653     {
654       &MkView::BlockedCmd, 2, 2, "blocked"
655    }
656    ,  {
657       &MkView::CloneCmd, 2, 2, "clone"
658    }
659    ,  {
660       &MkView::ConcatCmd, 3, 3, "concat view"
661    }
662    ,  {
663       &MkView::CopyCmd, 2, 3, "copy"
664    }
665    ,  {
666       &MkView::DifferentCmd, 3, 3, "different view"
667    }
668    ,  {
669       &MkView::DupCmd, 2, 2, "dup"
670    }
671    ,  {
672       &MkView::FlattenCmd, 3, 3, "flatten prop"
673    }
674    ,  {
675       &MkView::GroupByCmd, 4, 0, "groupby subview prop ?prop ...?"
676    }
677    ,  {
678       &MkView::HashCmd, 3, 4, "hash map ?numkeys?"
679    }
680    ,  {
681       &MkView::IndexedCmd, 5, 0, "indexed map unique prop ?prop ...?"
682    }
683    ,  {
684       &MkView::IntersectCmd, 3, 3, "intersect view"
685    }
686    ,  {
687       &MkView::JoinCmd, 4, 0, "join view prop ?prop ...?"
688    }
689    ,  {
690       &MkView::MapCmd, 3, 3, "map view"
691    }
692    ,  {
693       &MkView::MinusCmd, 3, 3, "minus view"
694    }
695    ,  {
696       &MkView::OrderedCmd, 2, 3, "ordered ?numkeys?"
697    }
698    ,  {
699       &MkView::PairCmd, 3, 3, "pair view"
700    }
701    ,  {
702       &MkView::ProductCmd, 3, 3, "product view"
703    }
704    ,  {
705       &MkView::ProjectCmd, 3, 0, "project prop ?prop ...?"
706    }
707    ,  {
708       &MkView::RangeCmd, 4, 0, "range start finish ?step?"
709    }
710    ,  {
711       &MkView::ReadOnlyCmd, 2, 2, "readonly"
712    }
713    ,  {
714       &MkView::RenameCmd, 4, 4, "rename oprop nprop"
715    }
716    ,  {
717       &MkView::RestrictCmd, 2, 0, "restrict...."
718    }
719    ,  {
720       &MkView::UnionCmd, 3, 3, "union view"
721    }
722    ,  {
723       &MkView::UniqueCmd, 2, 2, "unique"
724    }
725    ,
726#if 0
727     {
728       &MkView::OperatorCmd, 3, 3, "== view"
729    }
730    ,  {
731       &MkView::OperatorCmd, 3, 3, "!= view"
732    }
733    ,  {
734       &MkView::OperatorCmd, 3, 3, "< view"
735    }
736    ,  {
737       &MkView::OperatorCmd, 3, 3, "> view"
738    }
739    ,  {
740       &MkView::OperatorCmd, 3, 3, "<= view"
741    }
742    ,  {
743       &MkView::OperatorCmd, 3, 3, ">= view"
744    }
745    ,
746#endif
747     {
748      0, 0, 0, 0
749    }
750    ,
751  };
752  _error = TCL_OK;
753
754  objc--;
755  objv++;
756
757  int id = tcl_GetIndexFromObj(objv[1], subCmds);
758
759  if (id ==  - 1)
760    return TCL_ERROR;
761
762  CmdDef &cd = defTab[id];
763
764  if (objc < cd.min || (cd.max > 0 && objc > cd.max)) {
765    msg = "wrong # args: should be \"$obj view ";
766    msg += cd.desc;
767    msg += "\"";
768
769    return Fail(msg);
770  }
771
772  return (this->*cd.proc)();
773}
774
775//
776// View-based methods (typically return a new view)
777//
778int MkView::BlockedCmd() {
779  MkView *ncmd = new MkView(interp, view.Blocked());
780
781  return tcl_SetObjResult(tcl_NewStringObj(ncmd->CmdName()));
782}
783
784int MkView::DupCmd() {
785  MkView *cmd = new MkView(interp, view);
786
787  return tcl_SetObjResult(tcl_NewStringObj(cmd->CmdName()));
788}
789
790int MkView::CloneCmd() {
791  MkView *cmd = new MkView(interp, view.Clone());
792
793  return tcl_SetObjResult(tcl_NewStringObj(cmd->CmdName()));
794}
795
796int MkView::ConcatCmd() {
797  c4_View nview = View(interp, objv[2]);
798  MkView *ncmd = new MkView(interp, view.Concat(nview));
799
800  return tcl_SetObjResult(tcl_NewStringObj(ncmd->CmdName()));
801}
802
803int MkView::DifferentCmd() {
804  c4_View nview = View(interp, objv[2]);
805  MkView *ncmd = new MkView(interp, view.Different(nview));
806
807  return tcl_SetObjResult(tcl_NewStringObj(ncmd->CmdName()));
808}
809
810int MkView::CopyCmd() {
811  MkView *cmd = new MkView(interp, view.Duplicate());
812
813  return tcl_SetObjResult(tcl_NewStringObj(cmd->CmdName()));
814}
815
816int MkView::FlattenCmd() {
817  c4_View nview;
818
819  const c4_Property &prop = AsProperty(objv[2], view);
820  if (_error)
821    return _error;
822
823  if (prop.Type() != 'V') {
824    Fail("bad property: must be a view");
825    return TCL_ERROR;
826  }
827  MkView *ncmd = new MkView(interp, view.JoinProp((const c4_ViewProp &)prop));
828
829  return tcl_SetObjResult(tcl_NewStringObj(ncmd->CmdName()));
830}
831
832int MkView::GroupByCmd() {
833  const c4_Property &prop = AsProperty(objv[2], view);
834  if (_error)
835    return _error;
836
837  if (prop.Type() != 'V') {
838    Fail("bad property: must be a view");
839    return TCL_ERROR;
840  }
841  c4_View nview;
842
843  for (int i = 3; i < objc && !_error; ++i) {
844    const c4_Property &prop = AsProperty(objv[i], view);
845    nview.AddProperty(prop);
846  }
847  if (_error)
848    return _error;
849
850  MkView *ncmd = new MkView(interp, view.GroupBy(nview, (const c4_ViewProp &)
851    prop));
852
853  return tcl_SetObjResult(tcl_NewStringObj(ncmd->CmdName()));
854}
855
856int MkView::HashCmd() {
857  c4_View nview = View(interp, objv[2]);
858  int nkeys = objc > 3 ? tcl_GetIntFromObj(objv[3]): 1;
859  MkView *ncmd = new MkView(interp, view.Hash(nview, nkeys));
860
861  return tcl_SetObjResult(tcl_NewStringObj(ncmd->CmdName()));
862}
863
864int MkView::IndexedCmd() {
865  c4_View map = View(interp, objv[2]);
866  bool unique = tcl_GetIntFromObj(objv[3]) != 0;
867
868  c4_View props;
869  for (int i = 4; i < objc && !_error; ++i) {
870    const c4_Property &prop = AsProperty(objv[i], view);
871    props.AddProperty(prop);
872  }
873  if (_error)
874    return _error;
875
876  MkView *ncmd = new MkView(interp, view.Indexed(map, props, unique));
877
878  return tcl_SetObjResult(tcl_NewStringObj(ncmd->CmdName()));
879}
880
881int MkView::IntersectCmd() {
882  c4_View nview = View(interp, objv[2]);
883  MkView *ncmd = new MkView(interp, view.Intersect(nview));
884
885  return tcl_SetObjResult(tcl_NewStringObj(ncmd->CmdName()));
886}
887
888int MkView::JoinCmd() {
889  c4_View nview = View(interp, objv[2]);
890  c4_View props;
891
892  for (int i = 3; i < objc && !_error; ++i) {
893    const c4_Property &prop = AsProperty(objv[i], view);
894    props.AddProperty(prop);
895  }
896  if (_error)
897    return _error;
898
899  MkView *ncmd = new MkView(interp, view.Join(props, nview));
900
901  return tcl_SetObjResult(tcl_NewStringObj(ncmd->CmdName()));
902}
903
904int MkView::MapCmd() {
905  c4_View nview = View(interp, objv[2]);
906  MkView *ncmd = new MkView(interp, view.RemapWith(nview));
907
908  return tcl_SetObjResult(tcl_NewStringObj(ncmd->CmdName()));
909}
910
911int MkView::MinusCmd() {
912  c4_View nview = View(interp, objv[2]);
913  MkView *ncmd = new MkView(interp, view.Minus(nview));
914
915  return tcl_SetObjResult(tcl_NewStringObj(ncmd->CmdName()));
916}
917
918#if 0
919int MkView::OperatorCmd() {
920  c4_String op = (const char*)Tcl_GetStringFromObj(objv[1], 0);
921  c4_View nview = View(interp, objv[2]);
922  bool rc;
923
924  if (op == "==")
925    rc = (view == nview);
926  else if (op == "!=")
927    rc = (view != nview);
928  else if (op == "<")
929    rc = (view < nview);
930  else if (op == ">")
931    rc = (view > nview);
932  else if (op == ">=")
933    rc = (view >= nview);
934  else if (op == "<=")
935    rc = (view <= nview);
936  else
937    return Fail("bad operator: must be one of ==, !=, <, >, <=, >=");
938
939  return tcl_SetObjResult(Tcl_NewBooleanObj(rc ? 1 : 0));
940}
941
942#endif
943
944int MkView::OrderedCmd() {
945  int nkeys = objc > 2 ? tcl_GetIntFromObj(objv[2]): 1;
946  MkView *ncmd = new MkView(interp, view.Ordered(nkeys));
947
948  return tcl_SetObjResult(tcl_NewStringObj(ncmd->CmdName()));
949}
950
951int MkView::PairCmd() {
952  c4_View nview = View(interp, objv[2]);
953
954  MkView *ncmd = new MkView(interp, view.Pair(nview));
955
956  return tcl_SetObjResult(tcl_NewStringObj(ncmd->CmdName()));
957}
958
959int MkView::ProductCmd() {
960  c4_View nview = View(interp, objv[2]);
961  MkView *ncmd = new MkView(interp, view.Product(nview));
962
963  return tcl_SetObjResult(tcl_NewStringObj(ncmd->CmdName()));
964}
965
966int MkView::ProjectCmd() {
967  c4_View nview;
968
969  for (int i = 2; i < objc; i++) {
970    const c4_Property &prop = AsProperty(objv[i], view);
971
972    nview.AddProperty(prop);
973  }
974  MkView *ncmd = new MkView(interp, view.Project(nview));
975
976  return tcl_SetObjResult(tcl_NewStringObj(ncmd->CmdName()));
977}
978
979int MkView::RangeCmd() {
980  int start = asIndex(view, objv[2], false);
981  if (_error)
982    return _error;
983
984  int finish = objc > 3 ? asIndex(view, objv[3], false) + 1: start + 1;
985  if (_error)
986    return _error;
987
988  int step = objc > 4 ? tcl_GetIntFromObj(objv[4]): 1;
989  if (_error)
990    return _error;
991
992  MkView *ncmd = new MkView(interp, view.Slice(start, finish, step));
993
994  return tcl_SetObjResult(tcl_NewStringObj(ncmd->CmdName()));
995}
996
997int MkView::ReadOnlyCmd() {
998  MkView *ncmd = new MkView(interp, view.ReadOnly());
999
1000  return tcl_SetObjResult(tcl_NewStringObj(ncmd->CmdName()));
1001}
1002
1003int MkView::RenameCmd() {
1004  const c4_Property &oprop = AsProperty(objv[2], view);
1005  if (_error)
1006    return _error;
1007
1008  const c4_Property &nprop = AsProperty(objv[3], view);
1009  if (_error)
1010    return _error;
1011
1012  MkView *ncmd = new MkView(interp, view.Rename(oprop, nprop));
1013
1014  return tcl_SetObjResult(tcl_NewStringObj(ncmd->CmdName()));
1015}
1016
1017int MkView::RestrictCmd() {
1018  int index = asIndex(view, objv[2], false);
1019  int pos = tcl_GetIntFromObj(objv[3]);
1020  int count = tcl_GetIntFromObj(objv[4]);
1021
1022  int result = view.RestrictSearch(view[index], pos, count);
1023
1024  Tcl_Obj *r = tcl_GetObjResult();
1025  tcl_ListObjAppendElement(r, Tcl_NewIntObj(result));
1026  tcl_ListObjAppendElement(r, Tcl_NewIntObj(pos));
1027  tcl_ListObjAppendElement(r, Tcl_NewIntObj(count));
1028  return _error;
1029}
1030
1031int MkView::UnionCmd() {
1032  c4_View nview = View(interp, objv[2]);
1033  MkView *ncmd = new MkView(interp, view.Union(nview));
1034
1035  return tcl_SetObjResult(tcl_NewStringObj(ncmd->CmdName()));
1036}
1037
1038int MkView::UniqueCmd() {
1039  MkView *ncmd = new MkView(interp, view.Unique());
1040
1041  return tcl_SetObjResult(tcl_NewStringObj(ncmd->CmdName()));
1042}
1043