about summary refs log tree commit diff
path: root/Src
diff options
context:
space:
mode:
authorPeter Stephenson <pws@users.sourceforge.net>2009-01-05 21:56:53 +0000
committerPeter Stephenson <pws@users.sourceforge.net>2009-01-05 21:56:53 +0000
commitd388995eed7a13eae05dc3236908c96a1ccc7016 (patch)
treea633d448022ba8eb8688c5609956a897d746e160 /Src
parenta8ca1046132aef61d0d6cf88d9b749bc5d87dab8 (diff)
downloadzsh-d388995eed7a13eae05dc3236908c96a1ccc7016.tar.gz
zsh-d388995eed7a13eae05dc3236908c96a1ccc7016.tar.xz
zsh-d388995eed7a13eae05dc3236908c96a1ccc7016.zip
26249: remove arbitrary string length limit in unmeta()
Diffstat (limited to 'Src')
-rw-r--r--Src/utils.c61
1 files changed, 51 insertions, 10 deletions
diff --git a/Src/utils.c b/Src/utils.c
index d66de8d2f..05732abb7 100644
--- a/Src/utils.c
+++ b/Src/utils.c
@@ -3704,26 +3704,67 @@ metalen(const char *s, int len)
     return mlen;
 }
 
-/* This function converts a zsh internal string to a form which can be *
- * passed to a system call as a filename.  The result is stored in a   *
- * single static area.  NULL returned if the result is longer than     *
- * 4 * PATH_MAX.                                                       */
+/*
+ * This function converts a zsh internal string to a form which can be
+ * passed to a system call as a filename.  The result is stored in a
+ * single static area, sized to fit.  If there is no Meta character
+ * the original string is returned.
+ */
 
 /**/
 mod_export char *
 unmeta(const char *file_name)
 {
-    static char fn[4 * PATH_MAX];
+    static char *fn;
+    static int sz;
     char *p;
     const char *t;
+    int newsz, meta;
+    
+    meta = 0;
+    for (t = file_name; *t; t++) {
+	if (*t == Meta)
+	    meta = 1;
+    }
+    if (!meta) {
+	/*
+	 * don't need allocation... free if it's long, see below
+	 */
+	if (sz > 4 * PATH_MAX) {
+	    zfree(fn, sz);
+	    fn = NULL;
+	    sz = 0;
+	}
+	return (char *) file_name;
+    }
+
+    newsz = (t - file_name) + 1;
+    /*
+     * Optimisation: don't resize if we don't have to.
+     * We need a new allocation if
+     * - nothing was allocated before
+     * - the new string is larger than the old one
+     * - the old string was larger than an arbitrary limit but the
+     *   new string isn't so that we free up significant space by resizing.
+     */
+    if (!fn || newsz > sz || (sz > 4 * PATH_MAX && newsz <= 4 * PATH_MAX))
+    {
+	if (fn)
+	    zfree(fn, sz);
+	sz = newsz;
+	fn = (char *)zalloc(sz);
+	if (!fn) {
+	    sz = 0;
+	    /*
+	     * will quite likely crash in the caller anyway...
+	     */
+	    return NULL;
+	}
+    }
 
-    for (t = file_name, p = fn; *t && p < fn + 4 * PATH_MAX - 1; p++)
+    for (t = file_name, p = fn; *t; p++)
 	if ((*p = *t++) == Meta)
 	    *p = *t++ ^ 32;
-    if (*t)
-	return NULL;
-    if (p - fn == t - file_name)
-	return (char *) file_name;
     *p = '\0';
     return fn;
 }