nis-util  1.0.D108
lib/rcstring/quote_shell.cc
Go to the documentation of this file.
00001 //
00002 // nis-util - NIS Administration Utilities
00003 // Copyright (C) 2012 Peter Miller
00004 //
00005 // This program is free software; you can redistribute it and/or modify
00006 // it under the terms of the GNU General Public License as published by
00007 // the Free Software Foundation; either version 2 of the License, or (at
00008 // your option) any later version.
00009 //
00010 // This program is distributed in the hope that it will be useful,
00011 // but WITHOUT ANY WARRANTY; without even the implied warranty of
00012 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
00013 // General Public License for more details.
00014 //
00015 // You should have received a copy of the GNU General Public License along
00016 // with this program. If not, see <http://www.gnu.org/licenses/>.
00017 //
00018 
00019 #include <lib/ac/ctype.h>
00020 
00021 #include <lib/rcstring.h>
00022 #include <lib/rcstring/accumulator.h>
00023 
00024 
00025 rcstring
00026 rcstring::quote_shell(void)
00027     const
00028 {
00029     //
00030     // Work out if the string needs quoting.
00031     // and how long the output will be.
00032     //
00033     const char *start = c_str();
00034     const char *end = start + size();
00035     bool single_quotes = false;
00036     bool double_quotes = false;
00037     unsigned char mode = '\0';
00038     for (const char *cp = start; cp < end; ++cp)
00039     {
00040         unsigned char c = *cp;
00041         switch (c)
00042         {
00043         default:
00044             if (!isprint(c))
00045             {
00046                 single_quotes = true;
00047                 if (!mode)
00048                     mode = '\'';
00049             }
00050             break;
00051 
00052 
00053         case '\'':
00054             double_quotes = true;
00055             if (!mode)
00056                 mode = '"';
00057             break;
00058 
00059         case '!':
00060             // The exclamation mark is special for bash and csh, and can
00061             // only be quoted with single quotes.
00062             //
00063             // Fall through...
00064 
00065         case '"': case '$': case '\\':
00066         case '`': case '~':
00067             single_quotes = true;
00068             if (!mode)
00069                 mode = '\'';
00070             break;
00071 
00072         case '\t': case ' ': case '#': case '&': case '(': case ')': case '*':
00073         case ':': case ';': case '<': case '=': case '>': case '?': case '[':
00074         case ']': case '^': case '{': case '|': case '}':
00075             single_quotes = true;
00076             double_quotes = true;
00077             break;
00078         }
00079     }
00080 
00081     //
00082     // If it doesn't need quoting, just pass it through.
00083     //
00084     if (!single_quotes && !double_quotes)
00085     {
00086         return rcstring(start, size());
00087     }
00088 
00089     //
00090     // If we have a choice, use single quote mode,
00091     // it's usually shorter and it's easier to read.
00092     //
00093     if (!mode)
00094     {
00095         // Only use double quotes if we have to.
00096         mode = (double_quotes ? '"' : '\'');
00097     }
00098 
00099     //
00100     // Form the quoted string, using the minimum number of escapes.
00101     //
00102     // The gotcha here is the backquote: the `blah` substitution is
00103     // still active within double quotes.  And so are a few others.
00104     //
00105     // Also, there are some difficulties: the single quote can't be
00106     // quoted within single quotes, and the exclamation mark can't
00107     // be quoted by anything *except* single quotes.  Sheesh.
00108     //
00109     // Also, the rules change depending on which style of quoting
00110     // is in force at the time.
00111     //
00112     rcstring_accumulator buf;
00113     buf.push_back(mode);
00114     for (const char *cp = start; cp < end; ++cp)
00115     {
00116         unsigned char c = *cp;
00117         if (mode == '\'')
00118         {
00119             // within single quotes
00120             if (c == '\'')
00121             {
00122                 //
00123                 // You can't quote a single quote within single quotes.
00124                 // Need to change to double quote mode.
00125                 //
00126                 buf.push_back("'\"'", 3);
00127                 mode = '"';
00128             }
00129             else
00130                 buf.push_back(c);
00131         }
00132         else
00133         {
00134             // within double quotes
00135             switch (c)
00136             {
00137             case '!':
00138                 //
00139                 // You can't quote an exclamation mark within double
00140                 // quotes.  Need to change to single quote mode.
00141                 //
00142                 buf.push_back("\"'!", 3);
00143                 mode = '\'';
00144                 break;
00145 
00146             case '\n':
00147             case '"':
00148             case '\\':
00149             case '`': // stop command substitutions
00150             case '$': // stop variable substitutions
00151                 buf.push_back('\\');
00152                 // fall through...
00153 
00154             default:
00155                 buf.push_back(c);
00156                 break;
00157             }
00158         }
00159     }
00160     buf.push_back(mode);
00161     return buf.mkstr();
00162 }
00163 
00164 
00165 // vim: set ts=8 sw=4 et :