// mk4too.cpp -- Tcl object command interface to Metakit // $Id: mk4too.cpp,v 1.26 2004/01/21 01:46:06 jcw Exp $ // This is part of Metakit, see http://www.equi4.com/metakit/ // Copyright (C) 2000-2004 by Matt Newman and Jean-Claude Wippler. #include "mk4tcl.h" #include /////////////////////////////////////////////////////////////////////////////// // Defined in this file: class MkView; /////////////////////////////////////////////////////////////////////////////// // The MkView class adds Metakit-specific utilities and all the command procs. int MkView::Dispatcher(ClientData cd, Tcl_Interp* ip, int oc, Tcl_Obj* const* ov) { MkView* self = (MkView*) cd; if (self == 0 || self->interp != ip) { Tcl_SetResult(ip, "Initialization error in dispatcher", TCL_STATIC); return TCL_ERROR; } return self->Execute(oc, ov); } void MkView::DeleteProc(ClientData cd) { MkView* self = (MkView*) cd; delete self; } MkView::MkView (Tcl_Interp* ip_, c4_View view_, const char *name) : Tcl (ip_), work (*(MkWorkspace*) Tcl_GetAssocData(interp, "mk4tcl", 0)), view (view_) { Register(name); } MkView::MkView (Tcl_Interp* ip_, const char *name) : Tcl (ip_), work (*(MkWorkspace*) Tcl_GetAssocData(interp, "mk4tcl", 0)) { Register(name); } MkView::~MkView () { } void MkView::Register(const char *name) { static int uid = 0; char buf[32]; if (name == 0 || *name == 0) { sprintf(buf, "%d", uid++); cmd = "view" + (c4_String)buf; } else { cmd = name; } // save token... so I can delete cmd later (even if renamed) cmdToken = Tcl_CreateObjCommand(interp, (char *)(const char*) cmd, MkView::Dispatcher, this, MkView::DeleteProc); } c4_View MkView::View(Tcl_Interp *interp, Tcl_Obj *obj) { const char *name = Tcl_GetStringFromObj(obj,0); Tcl_CmdInfo ci; if ( !Tcl_GetCommandInfo( interp, (char*) name, &ci) || ci.objProc != MkView::Dispatcher) { //Fail("no such view"); c4_View temp; return temp; } else { MkView *v = (MkView*)ci.objClientData; return v->view; } } int MkView::asIndex(c4_View& view, Tcl_Obj* obj_, bool mayExceed_) { int size = view.GetSize(); int index; if (Tcl_GetIntFromObj(interp, obj_, &index) != TCL_OK) { const char* step = Tcl_GetStringFromObj(obj_, 0); if (step != 0 && strcmp(step, "end") == 0) { index = !mayExceed_ ? size - 1 : size; Tcl_ResetResult(interp); // clear error _error = TCL_OK; } else { index = -1; } } if (mayExceed_) { if (index > size) Fail("view index is too large"); else if (index < 0) Fail("view index is negative"); } else if (index < 0 || index >= size) Fail("view index is out of range"); return index; } int MkView::SetValues(const c4_RowRef& row_, int objc, Tcl_Obj* const* objv, c4_View& view_) { if (objc % 2) Fail("bad args: must be prop value pairs"); while (objc > 0 && !_error) { _error = SetAsObj(interp, row_, AsProperty(objv[0], view_), objv[1]); objc -= 2; objv += 2; } return _error; } int MkView::Execute(int oc, Tcl_Obj* const* ov) { struct CmdDef { int (MkView::*proc)(); int min; int max; const char* desc; }; static const char *subCmds [] = { "close", "delete", "exists", "find", "get", "properties", "insert", "open", "search", "select", "set", "size", "loop", "view", "info", // will be deprecated (use "properties" instead) 0 }; static CmdDef defTab [] = { // the "&MkView::" stuff is required for Mac cwpro2 { &MkView::CloseCmd, 2, 2, "close" }, { &MkView::DeleteCmd, 3, 4, "delete cursor ?cursor2?" }, { &MkView::ExistsCmd, 3, 0, "exists cursor ?prop ...?" }, { &MkView::FindCmd, 2, 0, "find ?prop value ...?" }, { &MkView::GetCmd, 3, 0, "get cursor ?prop ...?" }, { &MkView::InfoCmd, 2, 2, "properties" }, { &MkView::InsertCmd, 3, 0, "insert cursor ?prop ...?" }, { &MkView::OpenCmd, 4, 4, "open cursor prop" }, { &MkView::SearchCmd, 4, 4, "search prop value" }, { &MkView::SelectCmd, 2, 0, "select ?..?" }, { &MkView::SetCmd, 3, 0, "set cursor prop ?value prop value ...?" }, { &MkView::SizeCmd, 2, 3, "size ?newsize?" }, { &MkView::LoopCmd, 3, 0, "loop cursor ?first? ?limit? ?step? body" }, { &MkView::ViewCmd, 3, 0, "view option ?args?" }, { &MkView::InfoCmd, 2, 2, "info" }, { 0, 0, 0, 0 }, }; _error = TCL_OK; int id = tcl_GetIndexFromObj( ov[1], subCmds); if (id == -1) return TCL_ERROR; CmdDef& cd = defTab[id]; objc = oc; objv = ov; if (oc < cd.min || (cd.max > 0 && oc > cd.max)) { msg = "wrong # args: should be \"$obj "; msg += cd.desc; msg += "\""; return Fail(msg); } return (this->*cd.proc)(); } // // Tcl command methods // int MkView::CloseCmd() { // remove command instance... this will call delete... Tcl_DeleteCommandFromToken(interp, cmdToken); return TCL_OK; } int MkView::DeleteCmd() { int count = 1; int index = asIndex(view, objv[2], true); if (_error) return _error; if (objc > 3) { int index2 = asIndex(view, objv[3], true); if (_error) return _error; count = index2 - index + 1; } if (count > view.GetSize() - index) count = view.GetSize() - index; if (count >= 1) { view.RemoveAt(index, count); } return TCL_OK; } int MkView::ExistsCmd() { asIndex(view, objv[2], false); int r = _error ? 0 : 1; _error = 0; return tcl_SetObjResult(Tcl_NewIntObj(r)); } int MkView::FindCmd() { c4_Row row; int idx = 2; while (idx < objc && !_error) { _error = SetAsObj(interp, row, AsProperty(objv[idx], view), objv[idx+1]); idx += 2; } if (_error) return _error; idx = view.Find(row, 0); if (idx == -1) { Fail("not found"); return TCL_ERROR; } return tcl_SetObjResult(Tcl_NewIntObj(idx)); } int MkView::GetCmd() { int index = asIndex(view, objv[2], false); if (_error) return _error; Tcl_Obj* result = tcl_GetObjResult(); c4_RowRef row = view[index]; if (objc < 4) { for (int i = 0; i < view.NumProperties() && !_error; ++i) { const c4_Property& prop = view.NthProperty(i); c4_String name = prop.Name(); if (prop.Type() == 'V') continue; // omit subviews tcl_ListObjAppendElement(result, tcl_NewStringObj(name)); tcl_ListObjAppendElement(result, GetValue(row, prop)); } } else if (objc == 4) { GetValue(row, AsProperty(objv[3], view), result); } else { for (int i = 3; i < objc && !_error; ++i) { const c4_Property& prop = AsProperty(objv[i], view); tcl_ListObjAppendElement(result, GetValue(row, prop)); } } return _error; } int MkView::InfoCmd() { Tcl_Obj* result = tcl_GetObjResult(); for (int i = 0; i < view.NumProperties() && !_error; ++i) { const c4_Property& prop = view.NthProperty(i); c4_String s = prop.Name(); if (prop.Type() != 'S') { s += ":"; s += prop.Type(); } tcl_ListObjAppendElement(result, tcl_NewStringObj(s)); } return tcl_SetObjResult(result); } int MkView::InsertCmd() { int index = asIndex(view, objv[2], true); if (_error) return _error; c4_Row temp; SetValues(temp, objc - 3, objv + 3, view); view.InsertAt(index, temp, 1); //SetValues(view[index], objc - 3, objv + 3); if (_error) { view.RemoveAt(index, 1); // remove new row on errors return _error; } return tcl_SetObjResult(Tcl_NewIntObj(index)); } int MkView::OpenCmd() { int index = asIndex(view, objv[2], false); if (_error) return _error; const c4_Property& prop = AsProperty(objv[3], view); if (_error) return _error; if (prop.Type() != 'V') { Fail("bad property: must be a view"); return TCL_ERROR; } MkView *ncmd = new MkView( interp, ((const c4_ViewProp&)prop)(view[index]) ); return tcl_SetObjResult(tcl_NewStringObj(ncmd->CmdName())); } int MkView::SearchCmd() { Tcl_Obj *obj_ = objv[3]; const c4_Property& prop = AsProperty(objv[2], view); char type = prop.Type(); double dblVal = 0, dtmp; long longVal = 0; #ifdef TCL_WIDE_INT_TYPE Tcl_WideInt wideVal = 0, wtmp; #endif c4_String strVal; int size = view.GetSize(); int first = 0, last = size; int row, rc, e; switch (type) { case 'S': { strVal = Tcl_GetStringFromObj(obj_, 0); } break; case 'F': case 'D': { e = Tcl_GetDoubleFromObj(interp, obj_, &dblVal); if (e != TCL_OK) return e; } break; #ifdef TCL_WIDE_INT_TYPE case 'L': { e = Tcl_GetWideIntFromObj(interp, obj_, &wideVal); if (e != TCL_OK) return e; } break; #endif case 'I': { e = Tcl_GetLongFromObj(interp, obj_, &longVal); if (e != TCL_OK) return e; } break; default: Tcl_SetResult(interp, "unsupported property type", TCL_STATIC); return TCL_ERROR; } while (first <= last) { row = (first + last)/2; if (row >= size) break; switch (type) { case 'S': rc = strVal.CompareNoCase(((c4_StringProp&) prop)(view[row])); break; case 'F': dtmp = dblVal - ((c4_FloatProp&) prop)(view[row]); rc = (dtmp < 0 ? -1 : (dtmp > 0)); break; case 'D': dtmp = dblVal - ((c4_DoubleProp&) prop)(view[row]); rc = (dtmp < 0 ? -1 : (dtmp > 0)); break; #ifdef TCL_WIDE_INT_TYPE case 'L': wtmp = wideVal - ((c4_LongProp&) prop)(view[row]); rc = (wtmp < 0 ? -1 : (wtmp > 0)); break; #endif case 'I': rc = longVal - ((c4_IntProp&) prop)(view[row]); break; default: rc = 0; // 27-09-2001, to satisfy MSVC6 warn level 4 } if (rc == 0) { goto done; } else if (rc > 0) { first = row + 1; } else { last = row - 1; } } // Not found row = -1; done: return tcl_SetObjResult(Tcl_NewIntObj(row)); } int MkView::SelectCmd() { TclSelector sel (interp, view); static const char* opts [] = { "-min", // 0 "-max", // 1 "-exact", // 2 "-glob", // 3 "-regexp", // 4 "-keyword", // 5 "-first", // 6 "-count", // 7 "-sort", // 8 "-rsort", // 9 "-globnc", // 10 0 }; while (objc >= 4) { objc -= 2; // gobble next two arguments objv += 2; // at this point, *objv is the next option, and objc >= 2 int id = -1; const char* p = Tcl_GetStringFromObj(*objv, 0); if (p && *p == '-') { id = tcl_GetIndexFromObj(*objv, opts); if (id < 0) return _error; } switch (id) { case -1: // prop value : case-insensitive match { _error = sel.AddCondition(-1, objv[0], objv[1]); } break; case 0: // -min prop value : property must be greater or equal to value case 1: // -max prop value : property must be less or equal to value case 2: // -exact prop value : exact case-sensitive match case 3: // -glob prop pattern : match "glob" expression wildcard case 4: // -regexp prop pattern : match specified regular expression case 5: // -keyword prop prefix : match keyword in given property case 10: // -globnc prop pattern : match "glob", but ignore case { if (objc < 3) return Fail("not enough arguments"); _error = sel.AddCondition(id, objv[1], objv[2]); --objc; // gobble a third argument ++objv; } break; case 6: // -first pos : searching starts at specified row index case 7: // -count num : return no more than this many results { int n = tcl_GetIntFromObj(objv[1]); if (_error) return _error; if (id == 6) sel._first = n; else sel._count = n; } break; case 8: // -sort prop : sort on one or more properties, ascending case 9: // -rsort prop : sort on one or more properties, descending { c4_View props = sel.GetAsProps(objv[1]); for (int i = 0; i < props.NumProperties(); ++i) { const c4_Property& prop = props.NthProperty(i); sel._sortProps.AddProperty(prop); if (id == 9) sel._sortRevProps.AddProperty(prop); } } break; } } if (_error) return _error; c4_View nview; sel.DoSelect(0, &nview); MkView *ncmd = new MkView (interp, nview); return tcl_SetObjResult(tcl_NewStringObj(ncmd->CmdName())); } int MkView::SetCmd() { if (objc < 4) return GetCmd(); int index = asIndex(view, objv[2], false); if (_error) return _error; return SetValues(view[index], objc - 3, objv + 3, view); } int MkView::SizeCmd() { if (objc > 2) { int i = tcl_GetIntFromObj(objv[2]); if (_error) return _error; view.SetSize(i); } return tcl_SetObjResult(Tcl_NewIntObj(view.GetSize())); } int MkView::LoopCmd() { long first = 0; long limit = view.GetSize(); long incr = 1; if (objc >= 5) first = tcl_ExprLongObj (objv [3]); if (objc >= 6) limit = tcl_ExprLongObj (objv [4]); if (objc >= 7) { incr = tcl_ExprLongObj (objv [5]); if (incr == 0) Fail("increment has to be nonzero"); } if (_error) return _error; Tcl_Obj* vname = objv[2]; Tcl_Obj* cmd = objv[objc-1]; for (int i = first; i < limit && incr > 0 || i > limit && incr < 0; i += incr) { Tcl_Obj *var = Tcl_ObjSetVar2(interp, vname, 0, Tcl_NewIntObj(i), TCL_LEAVE_ERR_MSG); if (var == 0) return Fail(); _error = Mk_EvalObj(interp, cmd); if (_error) { if (_error == TCL_CONTINUE) _error = TCL_OK; else { if (_error == TCL_BREAK) _error = TCL_OK; else if (_error == TCL_ERROR) { char msg[100]; sprintf(msg, "\n (\"mk::loop\" body line %d)", interp->errorLine); Tcl_AddObjErrorInfo(interp, msg, -1); } break; } } } if (_error == TCL_OK) Tcl_ResetResult(interp); return _error; } int MkView::ViewCmd() { struct CmdDef { int (MkView::*proc)(); int min; int max; const char* desc; }; static const char *subCmds [] = { "blocked", "clone", "concat", "copy", "different", "dup", "flatten", "groupby", "hash", "indexed", "intersect", "join", "map", "minus", "ordered", "pair", "product", "project", "range", "readonly", "rename", "restrict", "union", "unique", #if 0 "==", "!=", "<", ">", "<=", ">=", #endif 0 }; static CmdDef defTab [] = { // the "&MkView::" stuff is required for Mac cwpro2 { &MkView::BlockedCmd, 2, 2, "blocked" }, { &MkView::CloneCmd, 2, 2, "clone" }, { &MkView::ConcatCmd, 3, 3, "concat view" }, { &MkView::CopyCmd, 2, 3, "copy" }, { &MkView::DifferentCmd, 3, 3, "different view" }, { &MkView::DupCmd, 2, 2, "dup" }, { &MkView::FlattenCmd, 3, 3, "flatten prop" }, { &MkView::GroupByCmd, 4, 0, "groupby subview prop ?prop ...?" }, { &MkView::HashCmd, 3, 4, "hash map ?numkeys?" }, { &MkView::IndexedCmd, 5, 0, "indexed map unique prop ?prop ...?" }, { &MkView::IntersectCmd, 3, 3, "intersect view" }, { &MkView::JoinCmd, 4, 0, "join view prop ?prop ...?" }, { &MkView::MapCmd, 3, 3, "map view" }, { &MkView::MinusCmd, 3, 3, "minus view" }, { &MkView::OrderedCmd, 2, 3, "ordered ?numkeys?" }, { &MkView::PairCmd, 3, 3, "pair view" }, { &MkView::ProductCmd, 3, 3, "product view" }, { &MkView::ProjectCmd, 3, 0, "project prop ?prop ...?" }, { &MkView::RangeCmd, 4, 0, "range start finish ?step?" }, { &MkView::ReadOnlyCmd, 2, 2, "readonly" }, { &MkView::RenameCmd, 4, 4, "rename oprop nprop" }, { &MkView::RestrictCmd, 2, 0, "restrict...." }, { &MkView::UnionCmd, 3, 3, "union view" }, { &MkView::UniqueCmd, 2, 2, "unique" }, #if 0 { &MkView::OperatorCmd, 3, 3, "== view" }, { &MkView::OperatorCmd, 3, 3, "!= view" }, { &MkView::OperatorCmd, 3, 3, "< view" }, { &MkView::OperatorCmd, 3, 3, "> view" }, { &MkView::OperatorCmd, 3, 3, "<= view" }, { &MkView::OperatorCmd, 3, 3, ">= view" }, #endif { 0, 0, 0, 0 }, }; _error = TCL_OK; objc--; objv++; int id = tcl_GetIndexFromObj( objv[1], subCmds); if (id == -1) return TCL_ERROR; CmdDef& cd = defTab[id]; if (objc < cd.min || (cd.max > 0 && objc > cd.max)) { msg = "wrong # args: should be \"$obj view "; msg += cd.desc; msg += "\""; return Fail(msg); } return (this->*cd.proc)(); } // // View-based methods (typically return a new view) // int MkView::BlockedCmd() { MkView *ncmd = new MkView( interp, view.Blocked()); return tcl_SetObjResult(tcl_NewStringObj(ncmd->CmdName())); } int MkView::DupCmd() { MkView *cmd = new MkView( interp, view); return tcl_SetObjResult(tcl_NewStringObj(cmd->CmdName())); } int MkView::CloneCmd() { MkView *cmd = new MkView( interp, view.Clone()); return tcl_SetObjResult(tcl_NewStringObj(cmd->CmdName())); } int MkView::ConcatCmd() { c4_View nview = View(interp, objv[2]); MkView *ncmd = new MkView( interp, view.Concat(nview)); return tcl_SetObjResult(tcl_NewStringObj(ncmd->CmdName())); } int MkView::DifferentCmd() { c4_View nview = View(interp, objv[2]); MkView *ncmd = new MkView( interp, view.Different(nview)); return tcl_SetObjResult(tcl_NewStringObj(ncmd->CmdName())); } int MkView::CopyCmd() { MkView *cmd = new MkView( interp, view.Duplicate()); return tcl_SetObjResult(tcl_NewStringObj(cmd->CmdName())); } int MkView::FlattenCmd() { c4_View nview; const c4_Property& prop = AsProperty(objv[2], view); if (_error) return _error; if (prop.Type() != 'V') { Fail("bad property: must be a view"); return TCL_ERROR; } MkView *ncmd = new MkView( interp, view.JoinProp((const c4_ViewProp&)prop)); return tcl_SetObjResult(tcl_NewStringObj(ncmd->CmdName())); } int MkView::GroupByCmd() { const c4_Property& prop = AsProperty(objv[2], view); if (_error) return _error; if (prop.Type() != 'V') { Fail("bad property: must be a view"); return TCL_ERROR; } c4_View nview; for (int i = 3; i < objc && !_error; ++i) { const c4_Property& prop = AsProperty(objv[i], view); nview.AddProperty(prop); } if (_error) return _error; MkView *ncmd = new MkView( interp, view.GroupBy(nview, (const c4_ViewProp&)prop)); return tcl_SetObjResult(tcl_NewStringObj(ncmd->CmdName())); } int MkView::HashCmd() { c4_View nview = View(interp, objv[2]); int nkeys = objc > 3 ? tcl_GetIntFromObj(objv[3]) : 1; MkView *ncmd = new MkView( interp, view.Hash(nview,nkeys)); return tcl_SetObjResult(tcl_NewStringObj(ncmd->CmdName())); } int MkView::IndexedCmd() { c4_View map = View(interp, objv[2]); bool unique = tcl_GetIntFromObj(objv[3]) != 0; c4_View props; for (int i = 4; i < objc && !_error; ++i) { const c4_Property& prop = AsProperty(objv[i], view); props.AddProperty(prop); } if (_error) return _error; MkView *ncmd = new MkView( interp, view.Indexed(map, props, unique)); return tcl_SetObjResult(tcl_NewStringObj(ncmd->CmdName())); } int MkView::IntersectCmd() { c4_View nview = View(interp, objv[2]); MkView *ncmd = new MkView( interp, view.Intersect(nview)); return tcl_SetObjResult(tcl_NewStringObj(ncmd->CmdName())); } int MkView::JoinCmd() { c4_View nview = View(interp, objv[2]); c4_View props; for (int i = 3; i < objc && !_error; ++i) { const c4_Property& prop = AsProperty(objv[i], view); props.AddProperty(prop); } if (_error) return _error; MkView *ncmd = new MkView( interp, view.Join(props, nview)); return tcl_SetObjResult(tcl_NewStringObj(ncmd->CmdName())); } int MkView::MapCmd() { c4_View nview = View(interp, objv[2]); MkView *ncmd = new MkView( interp, view.RemapWith(nview)); return tcl_SetObjResult(tcl_NewStringObj(ncmd->CmdName())); } int MkView::MinusCmd() { c4_View nview = View(interp, objv[2]); MkView *ncmd = new MkView( interp, view.Minus(nview)); return tcl_SetObjResult(tcl_NewStringObj(ncmd->CmdName())); } #if 0 int MkView::OperatorCmd() { c4_String op = (const char *)Tcl_GetStringFromObj(objv[1], 0); c4_View nview = View(interp, objv[2]); bool rc; if (op == "==") rc = (view == nview); else if (op == "!=") rc = (view != nview); else if (op == "<") rc = (view < nview); else if (op == ">") rc = (view > nview); else if (op == ">=") rc = (view >= nview); else if (op == "<=") rc = (view <= nview); else return Fail("bad operator: must be one of ==, !=, <, >, <=, >="); return tcl_SetObjResult(Tcl_NewBooleanObj(rc ? 1 : 0)); } #endif int MkView::OrderedCmd() { int nkeys = objc > 2 ? tcl_GetIntFromObj(objv[2]) : 1; MkView *ncmd = new MkView( interp, view.Ordered(nkeys)); return tcl_SetObjResult(tcl_NewStringObj(ncmd->CmdName())); } int MkView::PairCmd() { c4_View nview = View(interp, objv[2]); MkView *ncmd = new MkView( interp, view.Pair(nview)); return tcl_SetObjResult(tcl_NewStringObj(ncmd->CmdName())); } int MkView::ProductCmd() { c4_View nview = View(interp, objv[2]); MkView *ncmd = new MkView( interp, view.Product(nview)); return tcl_SetObjResult(tcl_NewStringObj(ncmd->CmdName())); } int MkView::ProjectCmd() { c4_View nview; for (int i = 2; i < objc; i++) { const c4_Property& prop = AsProperty(objv[i], view); nview.AddProperty(prop); } MkView *ncmd = new MkView( interp, view.Project(nview)); return tcl_SetObjResult(tcl_NewStringObj(ncmd->CmdName())); } int MkView::RangeCmd() { int start = asIndex(view, objv[2], false); if (_error) return _error; int finish = objc > 3 ? asIndex(view, objv[3], false)+1 : start+1; if (_error) return _error; int step = objc > 4 ? tcl_GetIntFromObj(objv[4]) : 1; if (_error) return _error; MkView *ncmd = new MkView( interp, view.Slice(start, finish, step)); return tcl_SetObjResult(tcl_NewStringObj(ncmd->CmdName())); } int MkView::ReadOnlyCmd() { MkView *ncmd = new MkView( interp, view.ReadOnly()); return tcl_SetObjResult(tcl_NewStringObj(ncmd->CmdName())); } int MkView::RenameCmd() { const c4_Property& oprop = AsProperty(objv[2], view); if (_error) return _error; const c4_Property& nprop = AsProperty(objv[3], view); if (_error) return _error; MkView *ncmd = new MkView( interp, view.Rename(oprop, nprop)); return tcl_SetObjResult(tcl_NewStringObj(ncmd->CmdName())); } int MkView::RestrictCmd() { int index = asIndex(view, objv[2], false); int pos = tcl_GetIntFromObj(objv[3]); int count = tcl_GetIntFromObj(objv[4]); int result = view.RestrictSearch(view[index], pos, count); Tcl_Obj* r = tcl_GetObjResult(); tcl_ListObjAppendElement(r, Tcl_NewIntObj(result)); tcl_ListObjAppendElement(r, Tcl_NewIntObj(pos)); tcl_ListObjAppendElement(r, Tcl_NewIntObj(count)); return _error; } int MkView::UnionCmd() { c4_View nview = View(interp, objv[2]); MkView *ncmd = new MkView( interp, view.Union(nview)); return tcl_SetObjResult(tcl_NewStringObj(ncmd->CmdName())); } int MkView::UniqueCmd() { MkView *ncmd = new MkView( interp, view.Unique()); return tcl_SetObjResult(tcl_NewStringObj(ncmd->CmdName())); }