//
// aegis - project change supervisor
// Copyright (C) 1999, 2001, 2002, 2004-2006, 2008, 2012 Peter Miller
//
// This program is free software; you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation; either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see
// .
//
#include
#include
#include
#include
string_ty *
str_quote_shell(string_ty *s)
{
const char *cp;
int needs_quoting;
static stracc_t buffer;
int mode;
//
// Convert the work list to a single string.
//
trace(("str_quote()\n{\n"));
//
// Work out if the string needs quoting.
//
// The empty string is not quoted, even though it could be argued
// that it needs to be. It has proven more useful in the present
// form, because it allows empty filename lists to pass through
// and remain empty.
//
needs_quoting = 0;
mode = 0;
for (cp = s->str_text; *cp; ++cp)
{
if (isspace((unsigned char)*cp))
{
needs_quoting = 1;
break;
}
switch (*cp)
{
default:
continue;
case '!':
// special for bash and csh
if (!mode)
mode = '\'';
needs_quoting = 1;
break;
case '$':
case '`':
// prefer single quotes to suppress substitutions
if (!mode)
mode = '\'';
needs_quoting = 1;
break;
case '\'':
if (!mode)
mode = '"';
needs_quoting = 1;
break;
case '"': case '#': case '&':
case '(': case ')': case '*': case ':': case ';': case '<':
case '=': case '>': case '?': case '[': case '\\': case ']':
case '^': case '{': case '|': case '}': case '~':
needs_quoting = 1;
break;
}
break;
}
//
// If it doesn't need quoting, return immediately.
//
if (!needs_quoting)
{
s = str_copy(s);
trace(("return %p;\n", s));
trace(("}\n"));
return s;
}
//
// If we have a choice, use single quote mode,
// it's shorter and easier to read.
//
if (!mode)
mode ='\'';
//
// Form the quoted string, using the minimum number of escapes.
//
// The gotcha here is the backquote: the `blah` substitution is
// still active within double quotes. And so are a few others.
//
// Also, there are some difficulties: the single quote can't be
// quoted within single quotes, and the exclamation mark can't
// be quoted by anything *except* single quotes. Sheesh.
//
// Also, the rules change depending on which style of quoting
// is in force at the time.
//
buffer.clear();
buffer.push_back(mode);
for (cp = s->str_text; *cp; ++cp)
{
if (mode == '\'')
{
// within single quotes
if (*cp == '\'')
{
//
// You can't quote a single quote within
// single quotes. Need to change to
// double quote mode.
//
buffer.push_back("'\"'", 3);
mode = '"';
}
else
buffer.push_back(*cp);
}
else
{
// within double quotes
switch (*cp)
{
case '!':
//
// You can't quote an exclamation mark
// within double quotes. Need to change
// to single quote mode.
//
buffer.push_back("\"'!", 3);
mode = '\'';
break;
case '\n':
case '"':
case '\\':
case '`': // stop command substitutions
case '$': // stop variable substitutions
buffer.push_back('\\');
// fall through...
default:
buffer.push_back(*cp);
break;
}
}
}
buffer.push_back(mode);
s = buffer.mkstr();
//
// all done
//
trace(("return %p;\n", s));
trace(("}\n"));
return s;
}
// vim: set ts=8 sw=4 et :