summaryrefslogtreecommitdiffstats
path: root/generic/tclCmdIL.c
diff options
context:
space:
mode:
authordkf <donal.k.fellows@manchester.ac.uk>2019-03-30 10:36:58 (GMT)
committerdkf <donal.k.fellows@manchester.ac.uk>2019-03-30 10:36:58 (GMT)
commitc98bdffd8010a485a4800af86aa4059c4f2ed86b (patch)
tree83839fc0ecc8cf7434705398f85fe58f1928fe16 /generic/tclCmdIL.c
parent333c25eb420727e80f64a1e6d92269d35b3d8349 (diff)
downloadtcl-c98bdffd8010a485a4800af86aa4059c4f2ed86b.zip
tcl-c98bdffd8010a485a4800af86aa4059c4f2ed86b.tar.gz
tcl-c98bdffd8010a485a4800af86aa4059c4f2ed86b.tar.bz2
Implementation of [lremove].
Diffstat (limited to 'generic/tclCmdIL.c')
-rw-r--r--generic/tclCmdIL.c114
1 files changed, 114 insertions, 0 deletions
diff --git a/generic/tclCmdIL.c b/generic/tclCmdIL.c
index a1a7f3e..0e36455 100644
--- a/generic/tclCmdIL.c
+++ b/generic/tclCmdIL.c
@@ -2704,6 +2704,120 @@ Tcl_LrangeObjCmd(
/*
*----------------------------------------------------------------------
*
+ * Tcl_LremoveObjCmd --
+ *
+ * This procedure is invoked to process the "lremove" Tcl command. See the
+ * user documentation for details on what it does.
+ *
+ * Results:
+ * A standard Tcl object result.
+ *
+ * Side effects:
+ * See the user documentation.
+ *
+ *----------------------------------------------------------------------
+ */
+
+typedef int list_index_t;
+
+static int
+LremoveIndexCompare(
+ const void *el1Ptr,
+ const void *el2Ptr)
+{
+ list_index_t idx1 = *((const list_index_t *) el1Ptr);
+ list_index_t idx2 = *((const list_index_t *) el2Ptr);
+
+ /*
+ * This will put the larger element first.
+ */
+
+ return (idx1 < idx2) ? 1 : (idx1 > idx2) ? -1 : 0;
+}
+
+int
+Tcl_LremoveObjCmd(
+ ClientData notUsed, /* Not used. */
+ Tcl_Interp *interp, /* Current interpreter. */
+ int objc, /* Number of arguments. */
+ Tcl_Obj *const objv[]) /* Argument objects. */
+{
+ int i, idxc;
+ list_index_t listLen, *idxv, prevIdx;
+ Tcl_Obj *listObj;
+
+ if (objc < 2) {
+ Tcl_WrongNumArgs(interp, 1, objv, "list ?index ...?");
+ return TCL_ERROR;
+ }
+
+ listObj = objv[1];
+ if (TclListObjLength(interp, listObj, &listLen) != TCL_OK) {
+ return TCL_ERROR;
+ }
+
+ idxc = objc - 2;
+ if (idxc == 0) {
+ Tcl_SetObjResult(interp, listObj);
+ return TCL_OK;
+ }
+ idxv = ckalloc((objc - 2) * sizeof(list_index_t));
+ for (i = 2; i < objc; i++) {
+ if (TclGetIntForIndexM(interp, objv[i], /*endValue*/ listLen - 1,
+ &idxv[i - 2]) != TCL_OK) {
+ ckfree(idxv);
+ return TCL_ERROR;
+ }
+ }
+
+ /*
+ * Sort the indices, large to small so that when we remove an index we
+ * don't change the indices still to be processed.
+ */
+
+ if (idxc > 1) {
+ qsort(idxv, idxc, sizeof(list_index_t), LremoveIndexCompare);
+ }
+
+ /*
+ * Make our working copy, then do the actual removes piecemeal. It would
+ * be more efficient to do range coalescing; contributions accepted!
+ */
+
+ if (Tcl_IsShared(listObj)) {
+ listObj = TclListObjCopy(NULL, listObj);
+ }
+ for (i = 0, prevIdx = -1 ; i < idxc ; i++) {
+ list_index_t idx = idxv[i];
+
+ /*
+ * Repeated index and sanity check.
+ */
+
+ if (idx == prevIdx) {
+ continue;
+ }
+ prevIdx = idx;
+ if (idx < 0 || idx >= listLen) {
+ continue;
+ }
+
+ /*
+ * Note that this operation can't fail now; we know we have a list and
+ * we're only ever contracting that list.
+ */
+
+ (void) Tcl_ListObjReplace(interp, listObj, idx, 1, 0, NULL);
+ listLen--;
+ }
+ ckfree(idxv);
+ Tcl_SetObjResult(interp, listObj);
+ return TCL_OK;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
* Tcl_LrepeatObjCmd --
*
* This procedure is invoked to process the "lrepeat" Tcl command. See