summaryrefslogtreecommitdiffstats
path: root/generic/tclUtil.c
diff options
context:
space:
mode:
authordgp <dgp@users.sourceforge.net>2011-04-14 16:12:54 (GMT)
committerdgp <dgp@users.sourceforge.net>2011-04-14 16:12:54 (GMT)
commit55499487fed3613d024c1ac04e226a6ab7e62a5d (patch)
treec4122e32f0b9ed8fee3b53bfd9cb40ddf1becd38 /generic/tclUtil.c
parent9400294f6cdf459c023e9baa1336f9016b0b4013 (diff)
parent341459596f22fe7e76ab600503bd3c826b7832e4 (diff)
downloadtcl-55499487fed3613d024c1ac04e226a6ab7e62a5d.zip
tcl-55499487fed3613d024c1ac04e226a6ab7e62a5d.tar.gz
tcl-55499487fed3613d024c1ac04e226a6ab7e62a5d.tar.bz2
More Tcl_Concat* and TclTrim* improvements.
Diffstat (limited to 'generic/tclUtil.c')
-rw-r--r--generic/tclUtil.c124
1 files changed, 78 insertions, 46 deletions
diff --git a/generic/tclUtil.c b/generic/tclUtil.c
index c67b6af..4fe1015 100644
--- a/generic/tclUtil.c
+++ b/generic/tclUtil.c
@@ -1002,12 +1002,12 @@ TclTrimRight(
if (bytesLeft == 0) {
/* No match; trim task done; *p is last non-trimmed char */
+ p += pInc;
break;
}
- pInc = 0;
} while (p > bytes);
- return numBytes - (p - bytes) - pInc;
+ return numBytes - (p - bytes);
}
/*
@@ -1019,8 +1019,7 @@ TclTrimRight(
* first string all characters found in the second string.
*
* Results:
- * An integer index into the first string, pointing to the first
- * character not to be trimmed.
+ * The number of bytes to be removed from the start of the string.
*
* Side effects:
* None.
@@ -1097,59 +1096,77 @@ TclTrimLeft(
*----------------------------------------------------------------------
*/
+/* The whitespace characters trimmed during [concat] operations */
+#define CONCAT_WS " \f\v\r\t\n"
+#define CONCAT_WS_SIZE (int) (sizeof(CONCAT_WS "") - 1)
+
char *
Tcl_Concat(
int argc, /* Number of strings to concatenate. */
const char *const *argv) /* Array of strings to concatenate. */
{
- int totalSize, i;
- char *p;
- char *result;
+ int i, needSpace = 0, bytesNeeded = 0;
+ char *result, *p;
+
+ /* Dispose of the empty result corner case first to simplify later code */
+ if (argc == 0) {
+ result = (char *) ckalloc(1);
+ result[0] = '\0';
+ return result;
+ }
- for (totalSize = 1, i = 0; i < argc; i++) {
- totalSize += strlen(argv[i]) + 1;
- if (totalSize <= 0) {
+ /* First allocate the result buffer at the size required */
+ for (i = 0; i < argc; i++) {
+ bytesNeeded += strlen(argv[i]);
+ if (bytesNeeded < 0) {
Tcl_Panic("Tcl_Concat: max size of Tcl value exceeded");
}
}
- result = ckalloc(totalSize);
- if (argc == 0) {
- *result = '\0';
- return result;
+ if (bytesNeeded + argc - 1 < 0) {
+ /*
+ * Panic test could be tighter, but not going to bother for
+ * this legacy routine.
+ */
+ Tcl_Panic("Tcl_Concat: max size of Tcl value exceeded");
}
- for (p = result, i = 0; i < argc; i++) {
+ /* All element bytes + (argc - 1) spaces + 1 terminating NULL */
+ result = (char *) ckalloc((unsigned) (bytesNeeded + argc));
+
+ for (p = result, i = 0; i < argc; i++) {
+ int trim, elemLength;
const char *element;
- int length;
+
+ element = argv[i];
+ elemLength = strlen(argv[i]);
+
+ /* Trim away the leading whitespace */
+ trim = TclTrimLeft(element, elemLength, CONCAT_WS, CONCAT_WS_SIZE);
+ element += trim;
+ elemLength -= trim;
/*
- * Clip white space off the front and back of the string to generate a
- * neater result, and ignore any empty elements.
+ * Trim away the trailing whitespace. Do not permit trimming
+ * to expose a final backslash character.
*/
- element = argv[i];
- while (isspace(UCHAR(*element))) { /* INTL: ISO space. */
- element++;
- }
- for (length = strlen(element);
- (length > 0)
- && (isspace(UCHAR(element[length-1]))) /* INTL: ISO space. */
- && ((length < 2) || (element[length-2] != '\\'));
- length--) {
- /* Null loop body. */
- }
- if (length == 0) {
+ trim = TclTrimRight(element, elemLength, CONCAT_WS, CONCAT_WS_SIZE);
+ trim -= trim && (element[elemLength - trim - 1] == '\\');
+ elemLength -= trim;
+
+ /* If we're left with empty element after trimming, do nothing */
+ if (elemLength == 0) {
continue;
}
- memcpy(p, element, (size_t) length);
- p += length;
- *p = ' ';
- p++;
- }
- if (p != result) {
- p[-1] = 0;
- } else {
- *p = 0;
+
+ /* Append to the result with space if needed */
+ if (needSpace) {
+ *p++ = ' ';
+ }
+ memcpy(p, element, (size_t) elemLength);
+ p += elemLength;
+ needSpace = 1;
}
+ *p = '\0';
return result;
}
@@ -1176,7 +1193,8 @@ Tcl_ConcatObj(
int objc, /* Number of objects to concatenate. */
Tcl_Obj *const objv[]) /* Array of objects to concatenate. */
{
- int i, needSpace = 0;
+ int i, elemLength, needSpace = 0, bytesNeeded = 0;
+ const char *element;
Tcl_Obj *objPtr, *resPtr;
/*
@@ -1241,16 +1259,30 @@ Tcl_ConcatObj(
* the slow way, using the string representations.
*/
+ /* First try to pre-allocate the size required */
+ for (i = 0; i < objc; i++) {
+ element = TclGetStringFromObj(objv[i], &elemLength);
+ bytesNeeded += elemLength;
+ if (bytesNeeded < 0) {
+ break;
+ }
+ }
+ /*
+ * Does not matter if this fails, will simply try later to build up
+ * the string with each Append reallocating as needed with the usual
+ * string append algorithm. When that fails it will report the error.
+ */
TclNewObj(resPtr);
+ Tcl_AttemptSetObjLength(resPtr, bytesNeeded + objc - 1);
+ Tcl_SetObjLength(resPtr, 0);
+
for (i = 0; i < objc; i++) {
- int trim, elemLength;
- const char *element;
+ int trim;
- objPtr = objv[i];
- element = TclGetStringFromObj(objPtr, &elemLength);
+ element = TclGetStringFromObj(objv[i], &elemLength);
/* Trim away the leading whitespace */
- trim = TclTrimLeft(element, elemLength, " \f\v\r\t\n", 6);
+ trim = TclTrimLeft(element, elemLength, CONCAT_WS, CONCAT_WS_SIZE);
element += trim;
elemLength -= trim;
@@ -1259,7 +1291,7 @@ Tcl_ConcatObj(
* to expose a final backslash character.
*/
- trim = TclTrimRight(element, elemLength, " \f\v\r\t\n", 6);
+ trim = TclTrimRight(element, elemLength, CONCAT_WS, CONCAT_WS_SIZE);
trim -= trim && (element[elemLength - trim - 1] == '\\');
elemLength -= trim;