00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022 #include <kshell.h>
00023
00024 #include <qfile.h>
00025
00026 #include <stdlib.h>
00027 #include <pwd.h>
00028 #include <sys/types.h>
00029
00030 static int fromHex( QChar c )
00031 {
00032 if (c >= '0' && c <= '9')
00033 return c - '0';
00034 else if (c >= 'A' && c <= 'F')
00035 return c - 'A' + 10;
00036 else if (c >= 'a' && c <= 'f')
00037 return c - 'a' + 10;
00038 return -1;
00039 }
00040
00041 inline static bool isQuoteMeta( uint c )
00042 {
00043 #if 0 // it's not worth it, especially after seeing gcc's asm output ...
00044 static const uchar iqm[] = {
00045 0x00, 0x00, 0x00, 0x00, 0x94, 0x00, 0x00, 0x00,
00046 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00
00047 };
00048
00049 return (c < sizeof(iqm) * 8) && (iqm[c / 8] & (1 << (c & 7)));
00050 #else
00051 return c == '\\' || c == '\'' || c == '"' || c == '$';
00052 #endif
00053 }
00054
00055 inline static bool isMeta( uint c )
00056 {
00057 static const uchar iqm[] = {
00058 0x00, 0x00, 0x00, 0x00, 0xdc, 0x07, 0x00, 0xd8,
00059 0x00, 0x00, 0x00, 0x10, 0x01, 0x00, 0x00, 0x38
00060 };
00061
00062 return (c < sizeof(iqm) * 8) && (iqm[c / 8] & (1 << (c & 7)));
00063 }
00064
00065 QStringList KShell::splitArgs( const QString &args, int flags, int *err )
00066 {
00067 QStringList ret;
00068 bool firstword = flags & AbortOnMeta;
00069
00070 for (uint pos = 0; ; ) {
00071 QChar c;
00072 do {
00073 if (pos >= args.length())
00074 goto okret;
00075 c = args.unicode()[pos++];
00076 } while (c.isSpace());
00077 QString cret;
00078 if ((flags & TildeExpand) && c == '~') {
00079 uint opos = pos;
00080 for (; ; pos++) {
00081 if (pos >= args.length())
00082 break;
00083 c = args.unicode()[pos];
00084 if (c == '/' || c.isSpace())
00085 break;
00086 if (isQuoteMeta( c )) {
00087 pos = opos;
00088 c = '~';
00089 goto notilde;
00090 }
00091 if ((flags & AbortOnMeta) && isMeta( c ))
00092 goto metaerr;
00093 }
00094 QString ccret = homeDir( QConstString( args.unicode() + opos, pos - opos ).string() );
00095 if (ccret.isEmpty()) {
00096 pos = opos;
00097 c = '~';
00098 goto notilde;
00099 }
00100 if (pos >= args.length()) {
00101 ret += ccret;
00102 goto okret;
00103 }
00104 pos++;
00105 if (c.isSpace()) {
00106 ret += ccret;
00107 firstword = false;
00108 continue;
00109 }
00110 cret = ccret;
00111 }
00112
00113 if (firstword) {
00114 if (c == '_' || (c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z')) {
00115 uint pos2 = pos;
00116 QChar cc;
00117 do
00118 cc = args[pos2++];
00119 while (cc == '_' || (cc >= 'A' && cc <= 'Z') ||
00120 (cc >= 'a' && cc <= 'z') || (cc >= '0' && cc <= '9'));
00121 if (cc == '=')
00122 goto metaerr;
00123 }
00124 }
00125 notilde:
00126 do {
00127 if (c == '\'') {
00128 uint spos = pos;
00129 do {
00130 if (pos >= args.length())
00131 goto quoteerr;
00132 c = args.unicode()[pos++];
00133 } while (c != '\'');
00134 cret += QConstString( args.unicode() + spos, pos - spos - 1 ).string();
00135 } else if (c == '"') {
00136 for (;;) {
00137 if (pos >= args.length())
00138 goto quoteerr;
00139 c = args.unicode()[pos++];
00140 if (c == '"')
00141 break;
00142 if (c == '\\') {
00143 if (pos >= args.length())
00144 goto quoteerr;
00145 c = args.unicode()[pos++];
00146 if (c != '"' && c != '\\' &&
00147 !((flags & AbortOnMeta) && (c == '$' || c == '`')))
00148 cret += '\\';
00149 } else if ((flags & AbortOnMeta) && (c == '$' || c == '`'))
00150 goto metaerr;
00151 cret += c;
00152 }
00153 } else if (c == '$' && args[pos] == '\'') {
00154 pos++;
00155 for (;;) {
00156 if (pos >= args.length())
00157 goto quoteerr;
00158 c = args.unicode()[pos++];
00159 if (c == '\'')
00160 break;
00161 if (c == '\\') {
00162 if (pos >= args.length())
00163 goto quoteerr;
00164 c = args.unicode()[pos++];
00165 switch (c) {
00166 case 'a': cret += '\a'; break;
00167 case 'b': cret += '\b'; break;
00168 case 'e': cret += '\033'; break;
00169 case 'f': cret += '\f'; break;
00170 case 'n': cret += '\n'; break;
00171 case 'r': cret += '\r'; break;
00172 case 't': cret += '\t'; break;
00173 case '\\': cret += '\\'; break;
00174 case '\'': cret += '\''; break;
00175 case 'c': cret += args[pos++] & 31; break;
00176 case 'x':
00177 {
00178 int hv = fromHex( args[pos] );
00179 if (hv < 0) {
00180 cret += "\\x";
00181 } else {
00182 int hhv = fromHex( args[++pos] );
00183 if (hhv > 0) {
00184 hv = hv * 16 + hhv;
00185 pos++;
00186 }
00187 cret += QChar( hv );
00188 }
00189 break;
00190 }
00191 default:
00192 if (c >= '0' && c <= '7') {
00193 int hv = c - '0';
00194 for (int i = 0; i < 2; i++) {
00195 c = args[pos];
00196 if (c < '0' || c > '7')
00197 break;
00198 hv = hv * 8 + (c - '0');
00199 pos++;
00200 }
00201 cret += QChar( hv );
00202 } else {
00203 cret += '\\';
00204 cret += c;
00205 }
00206 break;
00207 }
00208 } else
00209 cret += c;
00210 }
00211 } else {
00212 if (c == '\\') {
00213 if (pos >= args.length())
00214 goto quoteerr;
00215 c = args.unicode()[pos++];
00216 if (!c.isSpace() &&
00217 !((flags & AbortOnMeta) ? isMeta( c ) : isQuoteMeta( c )))
00218 cret += '\\';
00219 } else if ((flags & AbortOnMeta) && isMeta( c ))
00220 goto metaerr;
00221 cret += c;
00222 }
00223 if (pos >= args.length())
00224 break;
00225 c = args.unicode()[pos++];
00226 } while (!c.isSpace());
00227 ret += cret;
00228 firstword = false;
00229 }
00230
00231 okret:
00232 if (err)
00233 *err = NoError;
00234 return ret;
00235
00236 quoteerr:
00237 if (err)
00238 *err = BadQuoting;
00239 return QStringList();
00240
00241 metaerr:
00242 if (err)
00243 *err = FoundMeta;
00244 return QStringList();
00245 }
00246
00247 inline static bool isSpecial( uint c )
00248 {
00249 static const uchar iqm[] = {
00250 0xff, 0xff, 0xff, 0xff, 0xdd, 0x07, 0x00, 0xd8,
00251 0x00, 0x00, 0x00, 0x10, 0x01, 0x00, 0x00, 0x38
00252 };
00253
00254 return (c < sizeof(iqm) * 8) && (iqm[c / 8] & (1 << (c & 7)));
00255 }
00256
00257 QString KShell::joinArgs( const QStringList &args )
00258 {
00259 QChar q( '\'' );
00260 QString ret;
00261 for (QStringList::ConstIterator it = args.begin(); it != args.end(); ++it) {
00262 if (!ret.isEmpty())
00263 ret += ' ';
00264 if (!(*it).length())
00265 ret.append( q ).append( q );
00266 else {
00267 for (uint i = 0; i < (*it).length(); i++)
00268 if (isSpecial((*it).unicode()[i])) {
00269 QString tmp(*it);
00270 tmp.replace( q, "'\\''" );
00271 ret += q;
00272 tmp += q;
00273 ret += tmp;
00274 goto ex;
00275 }
00276 ret += *it;
00277 ex: ;
00278 }
00279 }
00280 return ret;
00281 }
00282
00283 QString KShell::joinArgs( const char * const *args, int nargs )
00284 {
00285 if (!args)
00286 return QString::null;
00287 QChar q( '\'' );
00288 QString ret;
00289 for (const char * const *argp = args; nargs && *argp; argp++, nargs--) {
00290 if (!ret.isEmpty())
00291 ret += ' ';
00292 if (!**argp)
00293 ret.append( q ).append( q );
00294 else {
00295 QString tmp( QFile::decodeName( *argp ) );
00296 for (uint i = 0; i < tmp.length(); i++)
00297 if (isSpecial(tmp.unicode()[i])) {
00298 tmp.replace( q, "'\\''" );
00299 ret += q;
00300 tmp += q;
00301 ret += tmp;
00302 goto ex;
00303 }
00304 ret += tmp;
00305 ex: ;
00306 }
00307 }
00308 return ret;
00309 }
00310
00311 QString KShell::joinArgsDQ( const QStringList &args )
00312 {
00313 QChar q( '\'' ), sp( ' ' ), bs( '\\' );
00314 QString ret;
00315 for (QStringList::ConstIterator it = args.begin(); it != args.end(); ++it) {
00316 if (!ret.isEmpty())
00317 ret += sp;
00318 if (!(*it).length())
00319 ret.append( q ).append( q );
00320 else {
00321 for (uint i = 0; i < (*it).length(); i++)
00322 if (isSpecial((*it).unicode()[i])) {
00323 ret.append( '$' ).append( q );
00324 for (uint pos = 0; pos < (*it).length(); pos++) {
00325 int c = (*it).unicode()[pos];
00326 if (c < 32) {
00327 ret += bs;
00328 switch (c) {
00329 case '\a': ret += 'a'; break;
00330 case '\b': ret += 'b'; break;
00331 case '\033': ret += 'e'; break;
00332 case '\f': ret += 'f'; break;
00333 case '\n': ret += 'n'; break;
00334 case '\r': ret += 'r'; break;
00335 case '\t': ret += 't'; break;
00336 case '\034': ret += 'c'; ret += '|'; break;
00337 default: ret += 'c'; ret += c + '@'; break;
00338 }
00339 } else {
00340 if (c == '\'' || c == '\\')
00341 ret += bs;
00342 ret += c;
00343 }
00344 }
00345 ret.append( q );
00346 goto ex;
00347 }
00348 ret += *it;
00349 ex: ;
00350 }
00351 }
00352 return ret;
00353 }
00354
00355 QString KShell::tildeExpand( const QString &fname )
00356 {
00357 if (fname[0] == '~') {
00358 int pos = fname.find( '/' );
00359 if (pos < 0)
00360 return homeDir( QConstString( fname.unicode() + 1, fname.length() - 1 ).string() );
00361 QString ret = homeDir( QConstString( fname.unicode() + 1, pos - 1 ).string() );
00362 if (!ret.isNull())
00363 ret += QConstString( fname.unicode() + pos, fname.length() - pos ).string();
00364 return ret;
00365 }
00366 return fname;
00367 }
00368
00369 QString KShell::homeDir( const QString &user )
00370 {
00371 if (user.isEmpty())
00372 return QFile::decodeName( getenv( "HOME" ) );
00373 struct passwd *pw = getpwnam( QFile::encodeName( user ).data() );
00374 if (!pw)
00375 return QString::null;
00376 return QFile::decodeName( pw->pw_dir );
00377 }