[prev in list] [next in list] [prev in thread] [next in thread]
List: kde-devel
Subject: KShell: argument splitting and tilde expansion
From: Oswald Buddenhagen <ossi () kde ! org>
Date: 2003-03-01 19:45:43
[Download RAW message or body]
moin,
due to the incredible demand for such functionality (there seem to be
roughly as many (more or less broken) implementations out there as kde
apps), i've implemented a class with a few static functions that cover
some basic ((posix) shell)/bash functionality.
tildeExpand is self-explaining, i think.
splitArgs performs bash-like argument splitting. the following
quotings prevent word-splitting:
- backslash.
- single quotes. everything between them is taken literally.
- double quotes. the backslash can be used to escape the double quote
(and itself).
- bash-like $'' quoting. read the bash man page for details.
note that backslashes preceeding non-special chars are treated literally.
splitArgs can optionally perform tilde expansion, too. note that this is
the only place where this is allowed, as
- before splitting you don't know where words begin
- after splitting the quotes are gone, so you don't know whether tilde
expansion was intended by the user
possible todo:
- simple substitutions ($var, ${var})
- complex substitutions (${var:-foo})
- command substitutions ($(echo hallo), `echo hallo`)
- pipes, redirections ($(echo foo|clobber 2>/dev/null))
- command lists ($(echo foo; echo bar))
- complex commands (conditions, loops, grouping, assignments, functions)
- smoke weaker shit to get the feet back on the ground
i provided joinArgs just in case somebody needs it, even though i didn't
find a place where it would be needed, yet.
comments welcome.
greetings
--
Hi! I'm a .signature virus! Copy me into your ~/.signature, please!
--
Chaos, panic, and disorder - my work here is done.
["kshell.h" (text/x-chdr)]
/*
This file is part of the KDE libraries
Copyright (c) 2003 Oswald Buddenhagen <ossi@kde.org>
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Library General Public
License as published by the Free Software Foundation; either
version 2 of the License, or (at your option) any later version.
This library 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
Library General Public License for more details.
You should have received a copy of the GNU Library General Public License
along with this library; see the file COPYING.LIB. If not, write to
the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
Boston, MA 02111-1307, USA.
*/
#ifndef _KSHELL_H
#define _KSHELL_H
#include <qstring.h>
#include <qstringlist.h>
//struct KShellPrivate;
class KShell {
public:
enum Options { NoOpts = 0, TildeExpand = 1 };
static QStringList splitArgs( const QString &args, int flags = 0 );
static QString joinArgs( const QStringList &args );
static QString tildeExpand( const QString &fname );
private:
static QString getHome( const QString &user );
KShell();
KShell(const KShell &);
KShell &operator=(const KShell &);
// KShellPrivate *d;
};
#endif /* _KSHELL_H */
["kshell.cpp" (text/x-c++src)]
/*
This file is part of the KDE libraries
Copyright (c) 2003 Oswald Buddenhagen <ossi@kde.org>
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Library General Public
License as published by the Free Software Foundation; either
version 2 of the License, or (at your option) any later version.
This library 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
Library General Public License for more details.
You should have received a copy of the GNU Library General Public License
along with this library; see the file COPYING.LIB. If not, write to
the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
Boston, MA 02111-1307, USA.
*/
#include <kshell.h>
#include <qfile.h>
#include <stdlib.h>
#include <pwd.h>
#include <sys/types.h>
static int fromHex( QChar c )
{
if (c >= '0' && c <= '9')
return c - '0';
else if (c >= 'A' && c <= 'F')
return c - 'A' + 10;
else if (c >= 'a' && c <= 'f')
return c - 'a' + 10;
return -1;
}
QStringList KShell::splitArgs( const QString &args, int flags )
{
QStringList ret;
for (uint pos = 0; ; ) {
QChar c;
do {
if (pos >= args.length())
return ret;
c = args.unicode()[pos++];
} while (c.isSpace());
QString cret;
if ((flags & TildeExpand) && c == '~') {
uint opos = pos;
for (; ; pos++) {
if (pos >= args.length())
break;
c = args.unicode()[pos];
if (c == '/' || c.isSpace())
break;
if (c == '\'' || c == '"' || c == '$' || c == '\\') {
pos = opos;
c = '~';
goto notilde;
}
}
QString ccret = getHome( QConstString( args.unicode() + opos, pos - opos ).string() );
if (ccret.isEmpty()) {
pos = opos;
c = '~';
goto notilde;
}
if (pos >= args.length()) {
ret += ccret;
return ret;
}
pos++;
if (c.isSpace()) {
ret += ccret;
continue;
}
cret = ccret;
}
notilde:
do {
if (c == '\'') {
uint spos = pos;
do {
if (pos >= args.length())
return QStringList();
c = args.unicode()[pos++];
} while (c != '\'');
cret += QConstString( args.unicode() + spos, pos - spos - 1 ).string();
} else if (c == '"') {
for (;;) {
if (pos >= args.length())
return QStringList();
c = args.unicode()[pos++];
if (c == '"')
break;
if (c == '\\') {
if (pos >= args.length())
return QStringList();
c = args.unicode()[pos++];
if (c != '"' && c != '\\')
cret += '\\';
}
cret += c;
}
} else if (c == '$' && args[pos] == '\'') {
pos++;
for (;;) {
if (pos >= args.length())
return QStringList();
c = args.unicode()[pos++];
if (c == '\'')
break;
if (c == '\\') {
if (pos >= args.length())
return QStringList();
c = args.unicode()[pos++];
switch (c) {
case 'a': cret += '\a'; break;
case 'b': cret += '\b'; break;
case 'e': cret += '\033'; break;
case 'f': cret += '\f'; break;
case 'n': cret += '\n'; break;
case 'r': cret += '\r'; break;
case 't': cret += '\t'; break;
case '\\': cret += '\\'; break;
case '\'': cret += '\''; break;
case 'c': cret += args[pos++] & 31; break;
case 'x':
{
int hv = fromHex( args[pos] );
if (hv < 0) {
cret += "\\x";
} else {
int hhv = fromHex( args[++pos] );
if (hhv > 0) {
hv = hv * 16 + hhv;
pos++;
}
cret += QChar( hv );
}
break;
}
default:
if (c >= '0' && c <= '7') {
int hv = c - '0';
for (int i = 0; i < 2; i++) {
c = args[pos];
if (c < '0' || c > '7')
break;
hv = hv * 8 + (c - '0');
pos++;
}
cret += QChar( hv );
} else {
cret += '\\';
cret += c;
}
break;
}
} else
cret += c;
}
} else {
if (c == '\\') {
if (pos >= args.length())
return QStringList();
c = args.unicode()[pos++];
if (!c.isSpace() && c != '\'' && c != '"' && c != '$' && c != '\\')
cret += '\\';
}
cret += c;
}
if (pos >= args.length())
break;
c = args.unicode()[pos++];
} while (!c.isSpace());
ret += cret;
}
}
QString KShell::joinArgs( const QStringList &args )
{
QString ret("");
for (QStringList::ConstIterator it = args.begin(); it != args.end(); ++it) {
ret += ret.isEmpty() ? "$'" : " $'";
for (uint pos = 0; pos < (*it).length(); pos++) {
int c = (*it).unicode()[pos];
if (c < 32) {
ret += '\\';
switch (c) {
case '\a': ret += 'a'; break;
case '\b': ret += 'b'; break;
case '\033': ret += 'e'; break;
case '\f': ret += 'f'; break;
case '\n': ret += 'n'; break;
case '\r': ret += 'r'; break;
case '\t': ret += 't'; break;
case '\034': ret += "c|"; break;
default: ret += 'c'; ret += c + '@'; break;
}
} else {
if (c == '\'' || c == '\\')
ret += '\\';
ret += c;
}
}
ret += '\'';
}
return ret;
}
QString KShell::tildeExpand( const QString &fname )
{
if (fname[0] == '~') {
int pos = fname.find( '/' );
if (pos < 0)
return getHome( QConstString( fname.unicode() + 1, fname.length() - 1 ).string() );
QString ret = getHome( QConstString( fname.unicode() + 1, fname.length() - pos - 1 ).string() );
if (!ret.isNull())
ret += QConstString( fname.unicode() + pos, fname.length() - pos ).string();
return ret;
}
return fname;
}
QString KShell::getHome( const QString &user )
{
if (user.isEmpty())
return QFile::decodeName( getenv( "HOME" ) );
struct passwd *pw = getpwnam( QFile::encodeName( user ) );
if (!pw)
return QString::null;
return QFile::decodeName( pw->pw_dir );
}
["kshelltest.cpp" (text/x-c++src)]
#include <kshell.h>
#include <iostream>
int main()
{
#if 0
std::cout << KShell::tildeExpand("~") << std::endl;
std::cout << KShell::tildeExpand("~/sulli") << std::endl;
std::cout << KShell::tildeExpand("~root") << std::endl;
std::cout << KShell::tildeExpand("~root/sulli") << std::endl;
std::cout << KShell::tildeExpand("~sulli") << std::endl;
#endif
#if 0
QStringList lst;
lst << "this" << "is" << "text";
std::cout << KShell::joinArgs(lst) << std::endl;
#endif
std::cout << KShell::joinArgs(KShell::splitArgs("\"~sulli\" 'text' 'jo'\"jo\" $'crap' \
$'\\\\\\'\\ca\\e\\x21' ha\\ lo ")) << std::endl; std::cout << \
KShell::joinArgs(KShell::splitArgs("\"~sulli\" 'text'", KShell::TildeExpand)) << std::endl; std::cout << \
KShell::joinArgs(KShell::splitArgs("~\"sulli\" 'text'", KShell::TildeExpand)) << std::endl; std::cout << \
KShell::joinArgs(KShell::splitArgs("~/\"sulli\" 'text'", KShell::TildeExpand)) << std::endl; std::cout \
<< KShell::joinArgs(KShell::splitArgs("~ 'text' ~", KShell::TildeExpand)) << std::endl; std::cout << \
KShell::joinArgs(KShell::splitArgs("~sulli ~root", KShell::TildeExpand)) << std::endl; }
CFLAGS := -O2 -march=$(shell uname -m) -g -W -Wall
CXXFLAGS := $(CFLAGS) -fno-exceptions
QTDIR := /usr/local/kde
KSHELLDIR := .
test: kshelltest
./kshelltest
kshelltest: kshelltest.o kshell.o
$(CXX) -o $@ $^ -L$(QTDIR)/lib -lqt-mt
kshelltest.o: kshelltest.cpp $(KSHELLDIR)/kshell.h
$(CXX) $(CXXFLAGS) -o $@ -c $< -I$(QTDIR)/include -I$(KSHELLDIR)
kshell.o: $(KSHELLDIR)/kshell.cpp $(KSHELLDIR)/kshell.h
$(CXX) $(CXXFLAGS) -o $@ -c $< -I$(QTDIR)/include -I$(KSHELLDIR)
clean:
rm -f *.o *.s kshelltest
>> Visit http://mail.kde.org/mailman/listinfo/kde-devel#unsub to unsubscribe <<
[prev in list] [next in list] [prev in thread] [next in thread]
Configure |
About |
News |
Add a list |
Sponsored by KoreLogic