diff options
Diffstat (limited to 'win/tclWinPipe.c')
| -rw-r--r-- | win/tclWinPipe.c | 223 | 
1 files changed, 176 insertions, 47 deletions
| diff --git a/win/tclWinPipe.c b/win/tclWinPipe.c index 4b372a5..dd54a27 100644 --- a/win/tclWinPipe.c +++ b/win/tclWinPipe.c @@ -1289,7 +1289,7 @@ ApplicationType(      /*       * Look for the program as an external program. First try the name as it -     * is, then try adding .com, .exe, and .bat, in that order, to the name, +     * is, then try adding .com, .exe, .bat and .cmd, in that order, to the name,       * looking for an executable.       *       * Using the raw SearchPath() function doesn't do quite what is necessary. @@ -1408,7 +1408,7 @@ ApplicationType(  	return APPL_NONE;      } -    if ((applType == APPL_DOS) || (applType == APPL_WIN3X)) { +    if (applType == APPL_WIN3X) {  	/*  	 * Replace long path name of executable with short path name for  	 * 16-bit applications. Otherwise the application may not be able to @@ -1442,6 +1442,86 @@ ApplicationType(   *----------------------------------------------------------------------   */ +static const char * +BuildCmdLineBypassBS( +    const char *current, +    const char **bspos +) { +    /* mark first backslash possition */ +    if (!*bspos) { +	*bspos = current; +    } +    do { +	current++; +    } while (*current == '\\'); +    return current; +} + +static void +QuoteCmdLineBackslash( +    Tcl_DString *dsPtr, +    const char *start, +    const char *current, +    const char *bspos +) { +    if (!bspos) { +	if (current > start) { /* part before current (special) */ +	    Tcl_DStringAppend(dsPtr, start, (int) (current - start)); +	} +    } else { +    	if (bspos > start) { /* part before first backslash */ +	    Tcl_DStringAppend(dsPtr, start, (int) (bspos - start)); +	} +	while (bspos++ < current) { /* each backslash twice */ +	    TclDStringAppendLiteral(dsPtr, "\\\\"); +	} +    } +} + +static const char * +QuoteCmdLinePart( +    Tcl_DString *dsPtr, +    const char *start, +    const char *special, +    const char *specMetaChars, +    const char **bspos +) { +    if (!*bspos) { +	/* rest before special (before quote) */ +	QuoteCmdLineBackslash(dsPtr, start, special, NULL); +	start = special; +    } else { +	/* rest before first backslash and backslashes into new quoted block */ +	QuoteCmdLineBackslash(dsPtr, start, *bspos, NULL); +	start = *bspos; +    } +    /*  +     * escape all special chars enclosed in quotes like `"..."`, note that here we  +     * don't must escape `\` (with `\`), because it's outside of the main quotes, +     * so `\` remains `\`, but important - not at end of part, because results as  +     * before the quote,  so `%\%\` should be escaped as `"%\%"\\`). +     */ +    TclDStringAppendLiteral(dsPtr, "\""); /* opening escape quote-char */ +    do { +    	*bspos = NULL; +	special++; +	if (*special == '\\') { +	    /* bypass backslashes (and mark first backslash possition)*/ +	    special = BuildCmdLineBypassBS(special, bspos); +	    if (*special == '\0') break; +	} +    } while (*special && strchr(specMetaChars, *special)); +    if (!*bspos) { +	/* unescaped rest before quote */ +	QuoteCmdLineBackslash(dsPtr, start, special, NULL); +    } else { +	/* unescaped rest before first backslash (rather belongs to the main block) */ +	QuoteCmdLineBackslash(dsPtr, start, *bspos, NULL); +    } +    TclDStringAppendLiteral(dsPtr, "\""); /* closing escape quote-char */ +    return special; +} +  static void  BuildCommandLine(      const char *executable,	/* Full path of executable (including @@ -1451,10 +1531,22 @@ BuildCommandLine(      Tcl_DString *linePtr)	/* Initialized Tcl_DString that receives the  				 * command line (TCHAR). */  { -    const char *arg, *start, *special; -    int quote, i; +    const char *arg, *start, *special, *bspos; +    int quote = 0, i;      Tcl_DString ds; +    /* characters to enclose in quotes if unpaired quote flag set */ +    const static char *specMetaChars = "&|^<>!()%"; +    /* characters to enclose in quotes in any case (regardless unpaired-flag) */ +    const static char *specMetaChars2 = "%"; + +    /* Quote flags: +     *   CL_ESCAPE   - escape argument; +     *   CL_QUOTE    - enclose in quotes; +     *   CL_UNPAIRED - previous arguments chain contains unpaired quote-char; +     */ +    enum {CL_ESCAPE = 1, CL_QUOTE = 2, CL_UNPAIRED = 4}; +      Tcl_DStringInit(&ds);      /* @@ -1474,61 +1566,98 @@ BuildCommandLine(  	    TclDStringAppendLiteral(&ds, " ");  	} -	quote = 0; +	quote &= ~(CL_ESCAPE|CL_QUOTE); /* reset escape flags */ +	bspos = NULL;  	if (arg[0] == '\0') { -	    quote = 1; +	    quote = CL_QUOTE;  	} else {  	    int count; -	    Tcl_UniChar ch = 0; - -	    for (start = arg; *start != '\0'; start += count) { -		count = TclUtfToUniChar(start, &ch); -		if (Tcl_UniCharIsSpace(ch)) {	/* INTL: ISO space. */ -		    quote = 1; +	    Tcl_UniChar ch; +	    for (start = arg; +		*start != '\0' && +		    (quote & (CL_ESCAPE|CL_QUOTE)) != (CL_ESCAPE|CL_QUOTE); +		start += count +	    ) { +		count = Tcl_UtfToUniChar(start, &ch); +		if (count > 1) continue; +		if (Tcl_UniCharIsSpace(ch)) { +		    quote |= CL_QUOTE; /* quote only */ +		    if (bspos) { /* if backslash found - escape & quote */ +			quote |= CL_ESCAPE; +			break; +		    } +		    continue; +		} +		if (strchr(specMetaChars, *start)) { +		    quote |= (CL_ESCAPE|CL_QUOTE); /*escape & quote */  		    break;  		} +		if (*start == '"') { +		    quote |= CL_ESCAPE; /* escape only */ +		    continue; +		} +		if (*start == '\\') { +		    bspos = start; +		    if (quote & CL_QUOTE) { /* if quote - escape & quote */ +			quote |= CL_ESCAPE; +			break; +		    } +		    continue; +		}  	    } +	    bspos = NULL;  	} -	if (quote) { +	if (quote & CL_QUOTE) { +	    /* start of argument (main opening quote-char) */  	    TclDStringAppendLiteral(&ds, "\"");  	} -	start = arg; -	for (special = arg; ; ) { -	    if ((*special == '\\') && (special[1] == '\\' || -		    special[1] == '"' || (quote && special[1] == '\0'))) { -		Tcl_DStringAppend(&ds, start, (int) (special - start)); -		start = special; -		while (1) { -		    special++; -		    if (*special == '"' || (quote && *special == '\0')) { -			/* -			 * N backslashes followed a quote -> insert N * 2 + 1 -			 * backslashes then a quote. -			 */ - -			Tcl_DStringAppend(&ds, start, -				(int) (special - start)); -			break; -		    } -		    if (*special != '\\') { -			break; -		    } +	if (!(quote & CL_ESCAPE)) { +	    /* nothing to escape */ +	    Tcl_DStringAppend(&ds, arg, -1); +	} else { +	    start = arg; +	    for (special = arg; *special != '\0'; ) { +		/* position of `\` is important before quote or at end (equal `\"` because quoted) */ +		if (*special == '\\') { +		    /* bypass backslashes (and mark first backslash possition)*/ +		    special = BuildCmdLineBypassBS(special, &bspos); +		    if (*special == '\0') break;  		} -		Tcl_DStringAppend(&ds, start, (int) (special - start)); -		start = special; -	    } -	    if (*special == '"') { -		Tcl_DStringAppend(&ds, start, (int) (special - start)); -		TclDStringAppendLiteral(&ds, "\\\""); -		start = special + 1; -	    } -	    if (*special == '\0') { -		break; +		/* ["] */ +		if (*special == '"') { +		    quote ^= CL_UNPAIRED; /* invert unpaired flag - observe unpaired quotes */ +		    /* add part before (and escape backslashes before quote) */ +		    QuoteCmdLineBackslash(&ds, start, special, bspos); +		    bspos = NULL; +		    /* escape using backslash */ +		    TclDStringAppendLiteral(&ds, "\\\""); +		    start = ++special; +		    continue; +		} +		/* unpaired (escaped) quote causes special handling on meta-chars */ +		if ((quote & CL_UNPAIRED) && strchr(specMetaChars, *special)) { +		    special = QuoteCmdLinePart(&ds, start, special, specMetaChars, &bspos); +		    /* start to current or first backslash */ +		    start = !bspos ? special : bspos; +		    continue; +		} +		/* special case for % - should be enclosed always (paired also) */ +		if (strchr(specMetaChars2, *special)) { +		    special = QuoteCmdLinePart(&ds, start, special, specMetaChars2, &bspos); +		    /* start to current or first backslash */ +		    start = !bspos ? special : bspos; +		    continue; +		} +		/* other not special (and not meta) character */ +		bspos = NULL; /* reset last backslash possition (not interesting) */ +		special++;  	    } -	    special++; +	    /* rest of argument (and escape backslashes before closing main quote) */ +	    QuoteCmdLineBackslash(&ds, start, special,  +	    	(quote & CL_QUOTE) ? bspos : NULL);  	} -	Tcl_DStringAppend(&ds, start, (int) (special - start)); -	if (quote) { +	if (quote & CL_QUOTE) { +	    /* end of argument (main closing quote-char) */  	    TclDStringAppendLiteral(&ds, "\"");  	}      } | 
