• Skip to content
  • Skip to link menu
KDE 4.3 API Reference
  • KDE API Reference
  • kdelibs
  • Sitemap
  • Contact Us
 

KDECore

kshell_win.cpp

Go to the documentation of this file.
00001 /*
00002     This file is part of the KDE libraries
00003 
00004     Copyright (c) 2007 Bernhard Loos <nhuh.put@web.de>
00005     Copyright (c) 2007,2008 Oswald Buddenhagen <ossi@kde.org>
00006 
00007     This library is free software; you can redistribute it and/or
00008     modify it under the terms of the GNU Library General Public
00009     License as published by the Free Software Foundation; either
00010     version 2 of the License, or (at your option) any later version.
00011 
00012     This library is distributed in the hope that it will be useful,
00013     but WITHOUT ANY WARRANTY; without even the implied warranty of
00014     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
00015     Library General Public License for more details.
00016 
00017     You should have received a copy of the GNU Library General Public License
00018     along with this library; see the file COPYING.LIB.  If not, write to
00019     the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
00020     Boston, MA 02110-1301, USA. 
00021 */
00022 
00023 #include "kshell.h"
00024 #include "kshell_p.h"
00025 
00026 #include <kkernel_win.h>
00027 
00028 #include <QString>
00029 #include <QStringList>
00030 #include <QtCore/QDir>
00031 
00032 /*
00033  * A short introduction into cmd semantics:
00034  * - Variable expansion is done first, without regard to *any* escaping -
00035  *   if something looks like an existing variable, it is replaced.
00036  * - Then follows regular tokenization by the shell. &, &&, | and || are
00037  *   command delimiters. ( and ) are command grouping operators; they are
00038  *   recognized only a the start resp. end of a command; mismatched )s are
00039  *   an error if any (s are present. <, > are just like under UNIX - they can
00040  *   appear *anywhere* in a command, perform their function and are cut out.
00041  *   @ at the start of a command is eaten (local echo off - no function as
00042  *   far as cmd /c is concerned). : at the start of a command declares a label,
00043  *   which effectively means the remainder of the line is a comment - note that
00044  *   command separators are not recognized past that point.
00045  *   ^ is the escape char for everything including itself.
00046  *   cmd ignores *all* special chars between double quotes, so there is no
00047  *   way to escape the closing quote. Note that the quotes are *not* removed
00048  *   from the resulting command line.
00049  * - Then follows delayed variable expansion if it is enabled and at least
00050  *   one exclamation mark is present. This involves another layer of ^
00051  *   escaping, regardless of quotes. (Win2k+)
00052  * - Then follows argument splitting as described in
00053  *   http://msdn2.microsoft.com/en-us/library/ms880421.aspx .
00054  *   Note that this is done by the called application and therefore might
00055  *   be subject to completely different semantics, in fact.
00056  */
00057 
00058 inline static bool isMetaChar(ushort c)
00059 {
00060     static const uchar iqm[] = {
00061         0x00, 0x00, 0x00, 0x00, 0x40, 0x03, 0x00, 0x50,
00062         0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10
00063     }; // &()<>|
00064 
00065     return (c < sizeof(iqm) * 8) && (iqm[c / 8] & (1 << (c & 7)));
00066 }
00067 
00068 inline static bool isSpecialChar(ushort c)
00069 {
00070     // Chars that should be quoted (TM). This includes:
00071     // - control chars & space
00072     // - the shell meta chars &()<>^|
00073     // - the potential separators ,;=
00074     static const uchar iqm[] = {
00075         0xff, 0xff, 0xff, 0xff, 0x41, 0x13, 0x00, 0x78,
00076         0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x10
00077     };
00078 
00079     return (c < sizeof(iqm) * 8) && (iqm[c / 8] & (1 << (c & 7)));
00080 }
00081 
00082 inline static bool isWhiteSpace(ushort c)
00083 {
00084     return c == ' ' || c == '\t';
00085 }
00086 
00087 QStringList KShell::splitArgs(const QString &_args, Options flags, Errors *err)
00088 {
00089     QString args(_args);
00090     QStringList ret;
00091 
00092     const QLatin1Char bs('\\'), dq('\"');
00093 
00094     if (flags & AbortOnMeta) {
00095         args.remove(PERCENT_ESCAPE);
00096         if (args.indexOf('%') >= 0) {
00097             if (err)
00098                 *err = FoundMeta;
00099             return QStringList();
00100         }
00101 
00102         args = _args;
00103         args.replace(PERCENT_ESCAPE, "%");
00104 
00105         if (!args.isEmpty() && args[0].unicode() == '@')
00106             args.remove(0, 1);
00107 
00108         for (int p = 0; p < args.length(); p++) {
00109             ushort c = args[p].unicode();
00110             if (c == '^') {
00111                 args.remove(p, 1);
00112             } else if (c == '"') {
00113                 while (++p < args.length() && args[p].unicode() != '"')
00114                     ;
00115             } else if (isMetaChar(c)) {
00116                 if (err)
00117                     *err = FoundMeta;
00118                 return QStringList();
00119             }
00120         }
00121     }
00122 
00123     if (err)
00124         *err = NoError;
00125 
00126     int p = 0;
00127     const int length = args.length();
00128     forever {
00129         while (p < length && isWhiteSpace(args[p].unicode()))
00130             ++p;
00131         if (p == length)
00132             return ret;
00133 
00134         QString arg;
00135         bool inquote = false;
00136         forever {
00137             bool copy = true; // copy this char
00138             int bslashes = 0; // number of preceding backslashes to insert
00139             while (p < length && args[p] == bs) {
00140                 ++p;
00141                 ++bslashes;
00142             }
00143             if (p < length && args[p] == dq) {
00144                 if (bslashes % 2 == 0) {
00145                     // Even number of backslashes, so the quote is not escaped.
00146                     if (inquote) {
00147                         if (p + 1 < length && args[p + 1] == dq) {
00148                             // Two consecutive quotes make a literal quote.
00149                             // This is not documented on MSDN.
00150                             ++p;
00151                         } else {
00152                             // Closing quote
00153                             copy = false;
00154                             inquote = !inquote;
00155                         }
00156                     } else {
00157                         // Opening quote
00158                         copy = false;
00159                         inquote = !inquote;
00160                     }
00161                 }
00162                 bslashes /= 2;
00163             }
00164 
00165             while (--bslashes >= 0)
00166                 arg.append(bs);
00167 
00168             if (p == length || (!inquote && isWhiteSpace(args[p].unicode()))) {
00169                 ret.append(arg);
00170                 if (inquote) {
00171                     if (err)
00172                         *err = BadQuoting;
00173                     return QStringList();
00174                 }
00175                 break;
00176             }
00177 
00178             if (copy)
00179                 arg.append(args[p]);
00180             ++p;
00181         }
00182     }
00183     //not reached
00184 }
00185 
00186 QString KShell::quoteArgInternal(const QString &arg, bool _inquote)
00187 {
00188     // Escape quotes, preceding backslashes are doubled. Surround with quotes.
00189     // Note that cmd does not understand quote escapes in quoted strings,
00190     // so the quoting needs to be "suspended".
00191     const QLatin1Char bs('\\'), dq('\"');
00192     QString ret;
00193     bool inquote = _inquote;
00194     int bslashes = 0;
00195     for (int p = 0; p < arg.length(); p++) {
00196         if (arg[p] == bs) {
00197             bslashes++;
00198         } else if (arg[p] == dq) {
00199             if (inquote) {
00200                 ret.append(dq);
00201                 inquote = false;
00202             }
00203             for (; bslashes; bslashes--)
00204                 ret.append(QLatin1String("\\\\"));
00205             ret.append(QLatin1String("\\^\""));
00206         } else {
00207             if (!inquote) {
00208                 ret.append(dq);
00209                 inquote = true;
00210             }
00211             for (; bslashes; bslashes--)
00212                 ret.append(bs);
00213             ret.append(arg[p]);
00214         }
00215     }
00216     ret.replace('%', PERCENT_ESCAPE);
00217     if (bslashes) {
00218         // Ensure that we don't have directly trailing backslashes,
00219         // so concatenating with another string won't cause surprises.
00220         if (!inquote && !_inquote)
00221             ret.append(dq);
00222         for (; bslashes; bslashes--)
00223             ret.append(QLatin1String("\\\\"));
00224         ret.append(dq);
00225         if (inquote && _inquote)
00226             ret.append(dq);
00227     } else if (inquote != _inquote) {
00228         ret.append(dq);
00229     }
00230     return ret;
00231 }
00232 
00233 QString KShell::quoteArg(const QString &arg)
00234 {
00235     if (arg.isEmpty())
00236         return QString::fromLatin1("\"\"");
00237 
00238     // Ensure that we don't have directly trailing backslashes,
00239     // so concatenating with another string won't cause surprises.
00240     if (arg.endsWith(QLatin1Char('\\')))
00241         return quoteArgInternal(arg, false);
00242 
00243     for (int x = arg.length() - 1; x >= 0; --x)
00244         if (isSpecialChar(arg[x].unicode()))
00245             return quoteArgInternal(arg, false);
00246 
00247     // Escape quotes. Preceding backslashes are doubled.
00248     // Note that the remaining string is not quoted.
00249     QString ret(arg);
00250     ret.replace(QRegExp(QLatin1String("(\\\\*)\"")), QLatin1String("\\1\\1\\^\""));
00251     ret.replace('%', PERCENT_ESCAPE);
00252     return ret;
00253 }
00254 

KDECore

Skip menu "KDECore"
  • Main Page
  • Modules
  • Namespace List
  • Class Hierarchy
  • Alphabetical List
  • Class List
  • File List
  • Namespace Members
  • Class Members
  • Related Pages

kdelibs

Skip menu "kdelibs"
  • DNSSD
  • Interfaces
  •   KHexEdit
  •   KMediaPlayer
  •   KSpeech
  •   KTextEditor
  • Kate
  • kconf_update
  • KDE3Support
  •   KUnitTest
  • KDECore
  • KDED
  • KDEsu
  • KDEUI
  • KDocTools
  • KFile
  • KHTML
  • KImgIO
  • KInit
  • kio
  • KIOSlave
  • KJS
  •   KJS-API
  •   WTF
  • kjsembed
  • KNewStuff
  • KParts
  • KPty
  • Kross
  • KUtils
  • Nepomuk
  • Plasma
  • Solid
  • Sonnet
  • ThreadWeaver
Generated for kdelibs by doxygen 1.6.1
This website is maintained by Adriaan de Groot and Allen Winter.
KDE® and the K Desktop Environment® logo are registered trademarks of KDE e.V. | Legal