tools/vsexplorer/vsexplorer.cpp
changeset 0 876b1a06bc25
equal deleted inserted replaced
-1:000000000000 0:876b1a06bc25
       
     1 /****************************************************************************
       
     2 **
       
     3 ** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies).
       
     4 ** All rights reserved.
       
     5 ** Contact: Nokia Corporation (qt-info@nokia.com)
       
     6 **
       
     7 ** This file is part of the Qt Mobility Components.
       
     8 **
       
     9 ** $QT_BEGIN_LICENSE:LGPL$
       
    10 ** No Commercial Usage
       
    11 ** This file contains pre-release code and may not be distributed.
       
    12 ** You may use this file in accordance with the terms and conditions
       
    13 ** contained in the Technology Preview License Agreement accompanying
       
    14 ** this package.
       
    15 **
       
    16 ** GNU Lesser General Public License Usage
       
    17 ** Alternatively, this file may be used under the terms of the GNU Lesser
       
    18 ** General Public License version 2.1 as published by the Free Software
       
    19 ** Foundation and appearing in the file LICENSE.LGPL included in the
       
    20 ** packaging of this file.  Please review the following information to
       
    21 ** ensure the GNU Lesser General Public License version 2.1 requirements
       
    22 ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
       
    23 **
       
    24 ** In addition, as a special exception, Nokia gives you certain additional
       
    25 ** rights.  These rights are described in the Nokia Qt LGPL Exception
       
    26 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
       
    27 **
       
    28 ** If you have questions regarding the use of this file, please contact
       
    29 ** Nokia at qt-info@nokia.com.
       
    30 **
       
    31 **
       
    32 **
       
    33 **
       
    34 **
       
    35 **
       
    36 **
       
    37 **
       
    38 ** $QT_END_LICENSE$
       
    39 **
       
    40 ****************************************************************************/
       
    41 
       
    42 #include <QApplication>
       
    43 #include <QObject>
       
    44 #include <QTextStream>
       
    45 #include <QFile>
       
    46 #include <QSocketNotifier>
       
    47 #include <cstdio>
       
    48 #include <cstdlib>
       
    49 #include <QMap>
       
    50 #include <QDir>
       
    51 #include <QStringList>
       
    52 #include <QSet>
       
    53 
       
    54 #include <qvaluespace.h>
       
    55 #include <qvaluespacesubscriber.h>
       
    56 #include <qvaluespacepublisher.h>
       
    57 
       
    58 #ifdef USE_READLINE
       
    59 #include <stdio.h>
       
    60 #include <readline/readline.h>
       
    61 #include <readline/history.h>
       
    62 #include <QThread>
       
    63 #include <QMutex>
       
    64 #include <QEvent>
       
    65 #include <QWaitCondition>
       
    66 #include <pthread.h>
       
    67 #endif
       
    68 
       
    69 #ifdef Q_OS_WIN
       
    70 #include <QThread>
       
    71 #endif
       
    72 
       
    73 QTM_USE_NAMESPACE
       
    74 
       
    75 static bool terminateRequested = false;
       
    76 
       
    77 class VSExplorer : public QObject
       
    78 {
       
    79 Q_OBJECT
       
    80 public:
       
    81     VSExplorer();
       
    82 
       
    83     void printError();
       
    84     void printHelp();
       
    85     void ls();
       
    86     void dump();
       
    87     void pwdCmd();
       
    88     void ls(const QString &abs, bool);
       
    89     void subscribe();
       
    90     void unsubscribe();
       
    91     void quit();
       
    92     void suppress();
       
    93     void listwatchers();
       
    94     void watch(const QString &);
       
    95     void unwatch(const QString &);
       
    96     void set(const QString &, const QString &);
       
    97     void clear(const QString &);
       
    98     void subscriptions();
       
    99 
       
   100     QString path() const
       
   101     {
       
   102         return pwd.path();
       
   103     }
       
   104 
       
   105 public slots:
       
   106     void processLine(const QString &);
       
   107 
       
   108 private slots:
       
   109     void contentsChanged();
       
   110     void interestChanged(const QString &attribute, bool interested);
       
   111 
       
   112 private:
       
   113     void lsPath(QValueSpaceSubscriber *, int = 0, bool = false);
       
   114 
       
   115     bool isSuppress;
       
   116     QValueSpaceSubscriber pwd;
       
   117     QValueSpacePublisher prov;
       
   118     QSet<QValueSpaceSubscriber *> subs;
       
   119     QSet<QValueSpacePublisher *> watchers;
       
   120 };
       
   121 static VSExplorer * vse = 0;
       
   122 
       
   123 class LineInput :
       
   124 #if defined(USE_READLINE) || defined(Q_OS_WIN)
       
   125     public QThread
       
   126 #else
       
   127     public QObject
       
   128 #endif
       
   129 {
       
   130     Q_OBJECT
       
   131 
       
   132 public:
       
   133     LineInput();
       
   134 
       
   135 signals:
       
   136     void line(const QString &);
       
   137 
       
   138 #if defined(USE_READLINE)
       
   139 protected:
       
   140     virtual bool event(QEvent *);
       
   141     virtual void run();
       
   142 
       
   143 private:
       
   144     QMutex mutex;
       
   145     QWaitCondition wait;
       
   146 #elif defined(Q_OS_WIN)
       
   147 protected:
       
   148     void run();
       
   149 
       
   150 private:
       
   151     QFile ts;
       
   152 #else
       
   153 private slots:
       
   154     void readyRead();
       
   155 
       
   156 private:
       
   157     QFile ts;
       
   158     QSocketNotifier *sock;
       
   159 #endif
       
   160 };
       
   161 
       
   162 VSExplorer::VSExplorer()
       
   163 : isSuppress(false), pwd(QLatin1String("/")), prov(QLatin1String("/"))
       
   164 {
       
   165 }
       
   166 
       
   167 void VSExplorer::interestChanged(const QString &attribute, bool interested)
       
   168 {
       
   169     Q_ASSERT(sender());
       
   170 
       
   171     if (!isSuppress) {
       
   172         QValueSpacePublisher *obj = static_cast<QValueSpacePublisher *>(sender());
       
   173         fprintf(stdout, "\nInterest Changed: %s ... %s %d\n",
       
   174                 qPrintable(obj->path()), qPrintable(attribute), interested);
       
   175     }
       
   176 }
       
   177 
       
   178 static QString variantToString( const QVariant& var )
       
   179 {
       
   180     if ( var.type() == QVariant::StringList )
       
   181         return var.toStringList().join(QLatin1String(", "));
       
   182     else
       
   183         return var.toString();
       
   184 }
       
   185 
       
   186 void VSExplorer::contentsChanged()
       
   187 {
       
   188     Q_ASSERT(sender());
       
   189 
       
   190     if(!isSuppress) {
       
   191         QValueSpaceSubscriber *subscriber = static_cast<QValueSpaceSubscriber *>(sender());
       
   192         fprintf(stdout, "\nChanged: %s\n", subscriber->path().toAscii().constData());
       
   193     }
       
   194 }
       
   195 
       
   196 void VSExplorer::printError()
       
   197 {
       
   198     fprintf(stdout, "Come again?\n");
       
   199     fflush(stdout);
       
   200 }
       
   201 
       
   202 void VSExplorer::printHelp()
       
   203 {
       
   204     fprintf(stdout, "help/?: Print this message\n");
       
   205     fprintf(stdout, "quit: Exit VSExplorer\n");
       
   206     fprintf(stdout, "ls: List contents of path\n");
       
   207     fprintf(stdout, "pwd: Print current working directory\n");
       
   208     fprintf(stdout, "subscribe: Subscribe to path\n");
       
   209     fprintf(stdout, "unsubscribe: Unsubscribe from path\n");
       
   210     fprintf(stdout, "suppress: Toggle suppression of publish messages\n");
       
   211     fprintf(stdout, "subscriptions: List current subscriptions\n");
       
   212     fprintf(stdout, "set <key> <value>: Set app layer<key> to <value>\n");
       
   213     fprintf(stdout, "clear <key>: Clear app layer <key>\n");
       
   214     fprintf(stdout, "cd <path>: Change working path\n");
       
   215     fprintf(stdout, "watch <path>: Add a watch for the path\n");
       
   216     fprintf(stdout, "unwatch <path>: Remove a watch for the path\n");
       
   217     fprintf(stdout, "watchers: List paths for which a watch is active\n");
       
   218     fflush(stdout);
       
   219 }
       
   220 
       
   221 void VSExplorer::ls()
       
   222 {
       
   223     lsPath(&pwd);
       
   224     fflush(stdout);
       
   225 }
       
   226 
       
   227 void VSExplorer::ls(const QString &abs, bool all)
       
   228 {
       
   229     QValueSpaceSubscriber subscriber(abs);
       
   230     lsPath(&subscriber, 0, all);
       
   231     fflush(stdout);
       
   232 }
       
   233 
       
   234 
       
   235 void VSExplorer::lsPath(QValueSpaceSubscriber * p, int indent, bool showHidden)
       
   236 {
       
   237     QVariant var = p->value();
       
   238     bool spaceRequired = false;
       
   239     if(!var.isNull()) {
       
   240         fprintf(stdout, "Value: '%s' (%s)\n",
       
   241                 variantToString(var).toAscii().constData(), var.typeName());
       
   242         spaceRequired = true;
       
   243     }
       
   244 
       
   245     foreach (const QString &path, p->subPaths()) {
       
   246         if (!showHidden && path.startsWith(QLatin1Char('.')))
       
   247             continue;
       
   248 
       
   249         if(spaceRequired) {
       
   250             fprintf(stdout, "\n");
       
   251             spaceRequired = false;
       
   252         }
       
   253         for(int ii = 0; ii < indent; ++ii) fprintf(stdout, "\t");
       
   254         fprintf(stdout, "%s/", path.toAscii().constData());
       
   255         QVariant value = p->value(path);
       
   256         if(!value.isNull()) {
       
   257             if(path.length() < 30) {
       
   258                 for(int ii = 0; ii < 30 - path.length(); ++ii)
       
   259                     fprintf(stdout, " ");
       
   260             } else {
       
   261                 fprintf(stdout, "    ");
       
   262             }
       
   263 
       
   264             fprintf(stdout, " '%s' (%s)",
       
   265                     variantToString(value).toAscii().constData(), value.typeName());
       
   266         }
       
   267         fprintf(stdout, "\n");
       
   268     }
       
   269 }
       
   270 
       
   271 void VSExplorer::listwatchers()
       
   272 {
       
   273     if(watchers.isEmpty()) {
       
   274         fprintf(stdout, "No watchers.\n");
       
   275     } else {
       
   276         fprintf(stdout, "Current watchers:\n");
       
   277         foreach (QValueSpacePublisher *obj, watchers)
       
   278             fprintf(stdout, "\t%s\n", obj->path().toAscii().constData());
       
   279     }
       
   280 
       
   281     fflush(stdout);
       
   282 }
       
   283 
       
   284 void VSExplorer::subscriptions()
       
   285 {
       
   286     if(subs.isEmpty()) {
       
   287         fprintf(stdout, "No subscriptions.\n");
       
   288     } else {
       
   289         fprintf(stdout, "Current subscriptions:\n");
       
   290 
       
   291         foreach (QValueSpaceSubscriber *subscriber, subs)
       
   292             fprintf(stdout, "\t%s\n", subscriber->path().toAscii().constData());
       
   293     }
       
   294 
       
   295     fflush(stdout);
       
   296 }
       
   297 
       
   298 void VSExplorer::subscribe()
       
   299 {
       
   300     QValueSpaceSubscriber *subscriber = new QValueSpaceSubscriber;
       
   301     subscriber->setPath(&pwd);
       
   302     QObject::connect(subscriber, SIGNAL(contentsChanged()),
       
   303                      this, SLOT(contentsChanged()));
       
   304     subs.insert(subscriber);
       
   305 
       
   306     fprintf(stdout, "OK\n");
       
   307     fflush(stdout);
       
   308 }
       
   309 
       
   310 void VSExplorer::unsubscribe()
       
   311 {
       
   312     foreach (QValueSpaceSubscriber *subscriber, subs) {
       
   313         if (subscriber->path() == pwd.path()) {
       
   314             subs.remove(subscriber);
       
   315             delete subscriber;
       
   316             fprintf(stdout, "OK\n");
       
   317             fflush(stdout);
       
   318             return;
       
   319         }
       
   320     }
       
   321 
       
   322     fprintf(stdout, "No subscription.\n");
       
   323     fflush(stdout);
       
   324 }
       
   325 
       
   326 void VSExplorer::quit()
       
   327 {
       
   328     fprintf(stdout, "Bye, bye.\n");
       
   329     fflush(stdout);
       
   330     terminateRequested = true;
       
   331 }
       
   332 
       
   333 void VSExplorer::watch(const QString &path)
       
   334 {
       
   335     foreach (QValueSpacePublisher *obj, watchers) {
       
   336         if (obj->path() == path)
       
   337             return;
       
   338     }
       
   339 
       
   340     QValueSpacePublisher * newObject = new QValueSpacePublisher(path);
       
   341     watchers.insert(newObject);
       
   342     QObject::connect(newObject, SIGNAL(interestChanged(QString,bool)),
       
   343                      this, SLOT(interestChanged(QString,bool)));
       
   344 }
       
   345 
       
   346 void VSExplorer::unwatch(const QString &path)
       
   347 {
       
   348     foreach (QValueSpacePublisher *obj, watchers) {
       
   349         if (obj->path() == path) {
       
   350             watchers.remove(obj);
       
   351             delete obj;
       
   352             return;
       
   353         }
       
   354     }
       
   355 }
       
   356 
       
   357 void VSExplorer::suppress()
       
   358 {
       
   359     if (isSuppress) {
       
   360         isSuppress = false;
       
   361         fprintf(stdout, "Suppression off.\n");
       
   362     } else {
       
   363         isSuppress = true;
       
   364         fprintf(stdout, "Suppression on.\n");
       
   365     }
       
   366     fflush(stdout);
       
   367 }
       
   368 
       
   369 void VSExplorer::set(const QString &name, const QString &value)
       
   370 {
       
   371     if (name.startsWith(QLatin1Char('/')))
       
   372         prov.setValue(name, value);
       
   373     else if (pwd.path().endsWith(QLatin1Char('/')))
       
   374         prov.setValue(pwd.path() + name, value);
       
   375     else
       
   376         prov.setValue(pwd.path() + QLatin1Char('/') + name, value);
       
   377 }
       
   378 
       
   379 void VSExplorer::clear(const QString &name)
       
   380 {
       
   381     if (name.startsWith(QLatin1Char('/')))
       
   382         prov.resetValue(name);
       
   383     else if (pwd.path().endsWith(QLatin1Char('/')))
       
   384         prov.resetValue(pwd.path() + name);
       
   385     else
       
   386         prov.resetValue(pwd.path() + QLatin1Char('/') + name);
       
   387 }
       
   388 
       
   389 void VSExplorer::processLine(const QString &line)
       
   390 {
       
   391     QStringList cmds = line.trimmed().split(QLatin1Char(' '));
       
   392 
       
   393     if(cmds.isEmpty()) {
       
   394         return;
       
   395     }
       
   396 
       
   397     const QString & cmd = cmds.at(0);
       
   398     if (cmd.isEmpty()) {
       
   399 
       
   400     } else if (cmd == QLatin1String("ls") && 1 == cmds.count()) {
       
   401         ls();
       
   402     } else if (cmd == QLatin1String("dump")) {
       
   403         dump();
       
   404     } else if (cmd == QLatin1String("pwd")) {
       
   405         pwdCmd();
       
   406     } else if (cmd == QLatin1String("ls") && 2 <= cmds.count()) {
       
   407         QStringList newCmds = cmds;
       
   408         newCmds.removeFirst();
       
   409         bool lsAll = false;
       
   410         if (newCmds.first() == QLatin1String("-a")) {
       
   411             lsAll = true;
       
   412             newCmds.removeFirst();
       
   413         }
       
   414         QString newPath = newCmds.join(QLatin1String(" "));
       
   415         if (newPath.startsWith(QLatin1Char('"')) && newPath.endsWith(QLatin1Char('"'))) {
       
   416             newPath = newPath.mid(1);
       
   417             newPath = newPath.left(newPath.length() - 1);
       
   418         }
       
   419         if(newPath.isEmpty()) {
       
   420             ls(pwd.path(), lsAll);
       
   421         } else if (newPath.startsWith(QLatin1Char('/'))) {
       
   422             ls(newPath, lsAll);
       
   423         } else {
       
   424             QString oldPath = pwd.path();
       
   425             if (!oldPath.endsWith(QLatin1Char('/')))
       
   426                 oldPath.append(QLatin1Char('/'));
       
   427             oldPath.append(newPath);
       
   428             oldPath = QDir::cleanPath(oldPath);
       
   429             ls(oldPath, lsAll);
       
   430         }
       
   431 
       
   432     } else if (cmd == QLatin1String("cd") && 2 <= cmds.count()) {
       
   433         QStringList newCmds = cmds;
       
   434         newCmds.removeFirst();
       
   435         QString newPath = newCmds.join(QLatin1String(" "));
       
   436         if (newPath.startsWith(QLatin1Char('"')) && newPath.endsWith(QLatin1Char('"'))) {
       
   437             newPath = newPath.mid(1);
       
   438             newPath = newPath.left(newPath.length() - 1);
       
   439         }
       
   440         if (newPath.startsWith(QLatin1Char('/'))) {
       
   441             pwd.setPath(newPath);
       
   442         } else {
       
   443             QString oldPath = pwd.path();
       
   444             if (!oldPath.endsWith(QLatin1Char('/')))
       
   445                 oldPath.append(QLatin1Char('/'));
       
   446             oldPath.append(newPath);
       
   447             oldPath = QDir::cleanPath(oldPath);
       
   448             pwd.setPath(oldPath);
       
   449         }
       
   450     } else if (cmd == QLatin1String("unwatch") && 2 <= cmds.count()) {
       
   451         QStringList newCmds = cmds;
       
   452         newCmds.removeFirst();
       
   453         QString newPath = newCmds.join(QLatin1String(" "));
       
   454         QString finalPath;
       
   455         if (newPath.startsWith(QLatin1Char('"')) && newPath.endsWith(QLatin1Char('"'))) {
       
   456             newPath = newPath.mid(1);
       
   457             newPath = newPath.left(newPath.length() - 1);
       
   458         }
       
   459         if (newPath.startsWith(QLatin1Char('/'))) {
       
   460             finalPath = QValueSpaceSubscriber(newPath).path();
       
   461         } else {
       
   462             QString oldPath = pwd.path();
       
   463             if (!oldPath.endsWith(QLatin1Char('/')))
       
   464                 oldPath.append(QLatin1Char('/'));
       
   465             oldPath.append(newPath);
       
   466             oldPath = QDir::cleanPath(oldPath);
       
   467             finalPath = QValueSpaceSubscriber(oldPath).path();
       
   468         }
       
   469         unwatch(finalPath);
       
   470     } else if (cmd == QLatin1String("watch") && 2 <= cmds.count()) {
       
   471         QStringList newCmds = cmds;
       
   472         newCmds.removeFirst();
       
   473         QString newPath = newCmds.join(QLatin1String(" "));
       
   474         QString finalPath;
       
   475         if (newPath.startsWith(QLatin1Char('"')) && newPath.endsWith(QLatin1Char('"'))) {
       
   476             newPath = newPath.mid(1);
       
   477             newPath = newPath.left(newPath.length() - 1);
       
   478         }
       
   479         if (newPath.startsWith(QLatin1Char('/'))) {
       
   480             finalPath = QValueSpaceSubscriber(newPath).path();
       
   481         } else {
       
   482             QString oldPath = pwd.path();
       
   483             if (!oldPath.endsWith(QLatin1Char('/')))
       
   484                 oldPath.append(QLatin1Char('/'));
       
   485             oldPath.append(newPath);
       
   486             oldPath = QDir::cleanPath(oldPath);
       
   487             finalPath = QValueSpaceSubscriber(oldPath).path();
       
   488         }
       
   489         watch(finalPath);
       
   490     } else if (cmd == QLatin1String("set") && 3 == cmds.count()) {
       
   491         set(cmds.at(1).trimmed(), cmds.at(2).trimmed());
       
   492     } else if (cmd == QLatin1String("clear") && 2 == cmds.count()) {
       
   493         clear(cmds.at(1).trimmed());
       
   494     } else if ((cmd == QLatin1String("subscribe") || cmd == QLatin1String("sub")) &&
       
   495                1 == cmds.count()) {
       
   496         subscribe();
       
   497     } else if ((cmd == QLatin1String("unsubscribe") || cmd == QLatin1String("unsub")) &&
       
   498                1 == cmds.count()) {
       
   499         unsubscribe();
       
   500     } else if ((cmd == QLatin1String("?") || cmd == QLatin1String("help")) && 1 == cmds.count()) {
       
   501         printHelp();
       
   502     } else if ((cmd == QLatin1String("quit") || cmd == QLatin1String("exit")) &&
       
   503                1 == cmds.count()) {
       
   504         quit();
       
   505     } else if (cmd == QLatin1String("suppress") && 1 == cmds.count()) {
       
   506         suppress();
       
   507     } else if (cmd == QLatin1String("watchers") && 1 == cmds.count()) {
       
   508         listwatchers();
       
   509     } else if (cmd == QLatin1String("subscriptions") && 1 == cmds.count()) {
       
   510         subscriptions();
       
   511     } else {
       
   512         printError();
       
   513     }
       
   514 }
       
   515 
       
   516 #ifdef USE_READLINE
       
   517 extern "C" {
       
   518     char * item_generator(const char * text, int num);
       
   519     char * command_generator(const char * text, int num);
       
   520     char ** item_completion(const char * text, int start, int end);
       
   521 }
       
   522 #endif
       
   523 
       
   524 LineInput::LineInput()
       
   525 {
       
   526 #if defined(USE_READLINE)
       
   527     rl_completion_append_character = '\0';
       
   528     rl_attempted_completion_function = item_completion;
       
   529     rl_completer_quote_characters = "\"";
       
   530     rl_basic_word_break_characters = " \t\n\"\\'`@$><=;|&(";
       
   531     rl_filename_quote_characters = " ";
       
   532     QObject::connect(this, SIGNAL(finished()), qApp, SLOT(quit()));
       
   533     QObject::connect(this, SIGNAL(terminated()), qApp, SLOT(quit()));
       
   534     start();
       
   535 #elif defined(Q_OS_WIN)
       
   536     ts.open(stdin, QIODevice::ReadOnly);
       
   537     QObject::connect(this, SIGNAL(finished()), qApp, SLOT(quit()));
       
   538     QObject::connect(this, SIGNAL(terminated()), qApp, SLOT(quit()));
       
   539     start();
       
   540 
       
   541     fprintf(stdout, "%s > ", vse->path().constData());
       
   542     fflush(stdout);
       
   543 #else
       
   544     ts.open(stdin, QIODevice::ReadOnly);
       
   545     sock = new QSocketNotifier(ts.handle(), QSocketNotifier::Read, this);
       
   546     QObject::connect(sock, SIGNAL(activated(int)), this, SLOT(readyRead()));
       
   547 
       
   548     fprintf(stdout, "%s > ", qPrintable(vse->path()));
       
   549     fflush(stdout);
       
   550 #endif
       
   551 }
       
   552 
       
   553 #if !defined(USE_READLINE) && !defined(Q_OS_WIN)
       
   554 void LineInput::readyRead()
       
   555 {
       
   556     QByteArray line = ts.readLine();
       
   557 
       
   558     emit this->line(QString::fromLocal8Bit(line));
       
   559 
       
   560     if(terminateRequested)
       
   561         exit(0);
       
   562 
       
   563     fprintf(stdout, "%s > ", qPrintable(vse->path()));
       
   564     fflush(stdout);
       
   565 }
       
   566 #endif
       
   567 
       
   568 #ifdef USE_READLINE
       
   569 #define TEXTEVENTTYPE (QEvent::User + 10)
       
   570 struct TextEvent : public QEvent
       
   571 {
       
   572     TextEvent(char *line)
       
   573         : QEvent((QEvent::Type)TEXTEVENTTYPE), data(line) {}
       
   574 
       
   575     char * data;
       
   576 };
       
   577 
       
   578 bool LineInput::event(QEvent *e)
       
   579 {
       
   580     if(e->type() == TEXTEVENTTYPE) {
       
   581         TextEvent * te = static_cast<TextEvent *>(e);
       
   582         emit line(te->data);
       
   583         free(te->data);
       
   584 
       
   585         mutex.lock();
       
   586         wait.wakeAll();
       
   587         mutex.unlock();
       
   588 
       
   589         return true;
       
   590     } else {
       
   591         return QThread::event(e);
       
   592     }
       
   593 }
       
   594 
       
   595 char ** item_completion(const char * text, int start, int end)
       
   596 {
       
   597     char ** matches = (char **)NULL;
       
   598     const char * non_space = text;
       
   599     while(*non_space == ' ') ++non_space;
       
   600 
       
   601 
       
   602     if(start == non_space - text)
       
   603         matches = rl_completion_matches(text, command_generator);
       
   604     else
       
   605         matches = rl_completion_matches(text, item_generator);
       
   606 
       
   607     rl_attempted_completion_over = 1;
       
   608     return (matches);
       
   609 }
       
   610 
       
   611 char * command_generator(const char * t, int num)
       
   612 {
       
   613     static QList<QByteArray> children;
       
   614 
       
   615     if(0 == num) {
       
   616         children.clear();
       
   617 
       
   618         // Command
       
   619         static const char * commands[] = { "help ",
       
   620                                            "quit ",
       
   621                                            "pwd ",
       
   622                                            "ls ",
       
   623                                            "subscribe ",
       
   624                                            "unsubscribe ",
       
   625                                            "suppress ",
       
   626                                            "subscriptions ",
       
   627                                            "set ",
       
   628                                            "clear ",
       
   629                                            "cd " };
       
   630 
       
   631         for(unsigned int ii = 0; ii < sizeof(commands) / sizeof(char *); ++ii)
       
   632             if(0 == ::strncmp(commands[ii], t, strlen(t)))
       
   633                 children.append(commands[ii]);
       
   634     }
       
   635 
       
   636     if(children.isEmpty())
       
   637         return 0;
       
   638 
       
   639     char * rv = (char *)malloc(children.at(0).length() + 1);
       
   640     ::memcpy(rv, children.at(0).constData(), children.at(0).length() + 1);
       
   641     children.removeFirst();
       
   642 
       
   643     return rv;
       
   644 }
       
   645 
       
   646 char * item_generator(const char * t, int num)
       
   647 {
       
   648     static QStringList children;
       
   649 
       
   650     rl_filename_completion_desired = 1;
       
   651     rl_filename_quoting_desired = 1;
       
   652     if(0 == num) {
       
   653 
       
   654         children.clear();
       
   655 
       
   656         // Path
       
   657         QString text = QString::fromLocal8Bit(t);
       
   658         QString textExt;
       
   659         QString textBase;
       
   660 
       
   661         int last = text.lastIndexOf('/');
       
   662         if(-1 == last) {
       
   663             textExt = text;
       
   664         } else {
       
   665             textBase = text.left(last);
       
   666             textExt = text.mid(last + 1);
       
   667         }
       
   668 
       
   669         QString vsBase;
       
   670 
       
   671         if (!textBase.startsWith(QLatin1Char('/'))) {
       
   672             QString in = vse->path();
       
   673             if (!in.endsWith(QLatin1Char('/')))
       
   674                 vsBase = in + QLatin1Char('/') + textBase;
       
   675             else
       
   676                 vsBase = in + textBase;
       
   677         } else {
       
   678             vsBase = textBase;
       
   679         }
       
   680 
       
   681         QValueSpaceSubscriber subscriber(vsBase);
       
   682 
       
   683         QStringList schildren = subscriber.subPaths();
       
   684 
       
   685         foreach(QString child, schildren) {
       
   686             if(child.startsWith(textExt)) {
       
   687                 QString completion;
       
   688                 completion.append(textBase);
       
   689                 if(!completion.isEmpty())
       
   690                     completion.append(QLatin1Char('/'));
       
   691                 completion.append(child.toAscii());
       
   692                 completion.append(QLatin1Char('/'));
       
   693                 children.append(completion);
       
   694             }
       
   695         }
       
   696     }
       
   697 
       
   698     if(children.isEmpty())
       
   699         return 0;
       
   700 
       
   701     QByteArray child = children.takeFirst().toLocal8Bit();
       
   702     char *rv = (char *)malloc(child.length() + 1);
       
   703     ::memcpy(rv, child.constData(), child.length() + 1);
       
   704 
       
   705     return rv;
       
   706 }
       
   707 
       
   708 
       
   709 void LineInput::run()
       
   710 {
       
   711     while(true) {
       
   712         /* Get a line from the user. */
       
   713         mutex.lock();
       
   714         QString prompt = vse->path();
       
   715         prompt.append(" > ");
       
   716         mutex.unlock();
       
   717         char *line_read = readline (prompt.toLocal8Bit().constData());
       
   718 
       
   719         /* If the line has any text in it,
       
   720            save it on the history. */
       
   721         if (line_read && *line_read)
       
   722             add_history (line_read);
       
   723 
       
   724         mutex.lock();
       
   725         TextEvent * e = new TextEvent(line_read);
       
   726         QApplication::postEvent(this, e);
       
   727         wait.wait(&mutex);
       
   728         if(terminateRequested) {
       
   729             mutex.unlock();
       
   730             return;
       
   731         } else {
       
   732             mutex.unlock();
       
   733         }
       
   734     }
       
   735 }
       
   736 #endif
       
   737 
       
   738 #ifdef Q_OS_WIN
       
   739 void LineInput::run()
       
   740 {
       
   741     while (!terminateRequested) {
       
   742         fprintf(stdout, "%s > ", vse->path().constData());
       
   743         fflush(stdout);
       
   744 
       
   745         QByteArray l = ts.readLine();
       
   746         emit line(QString::fromLocal8Bit(l.constData(), l.length()));
       
   747     }
       
   748 }
       
   749 
       
   750 #endif
       
   751 
       
   752 void usage(char * app)
       
   753 {
       
   754     fprintf(stderr, "Usage: %s [-s] [-d]\n", app);
       
   755     fprintf(stderr, "   -s     a valuespace manager instance is created\n");
       
   756     fprintf(stderr, "   -d     the tree content is dumped to command line\n");
       
   757     exit(-1);
       
   758 }
       
   759 
       
   760 void dodump(QValueSpaceSubscriber *subscriber)
       
   761 {
       
   762     foreach (const QString &child, subscriber->subPaths()) {
       
   763         if (child.isEmpty())
       
   764             continue;
       
   765 
       
   766         QValueSpaceSubscriber subItem;
       
   767         subItem.setPath(subscriber);
       
   768         subItem.cd(child);
       
   769         dodump(&subItem);
       
   770     }
       
   771 
       
   772     QVariant var = subscriber->value();
       
   773     fprintf(stdout, "%s '%s' %s\n",
       
   774             subscriber->path().toAscii().constData(),
       
   775             variantToString(var).toAscii().constData(),
       
   776             var.typeName());
       
   777 }
       
   778 
       
   779 void VSExplorer::dump()
       
   780 {
       
   781     QValueSpaceSubscriber subscriber;
       
   782     subscriber.setPath(&pwd);
       
   783     dodump(&subscriber);
       
   784     fflush(stdout);
       
   785 }
       
   786 
       
   787 void VSExplorer::pwdCmd()
       
   788 {
       
   789     fprintf(stdout, "Working directory: %s\n", pwd.path().toLatin1().constData());
       
   790     fflush(stdout);
       
   791 }
       
   792 
       
   793 
       
   794 int main(int argc, char ** argv)
       
   795 {
       
   796     QApplication app(argc, argv, true);
       
   797 
       
   798     bool manager = false;
       
   799     bool dump = false;
       
   800     for(int ii = 1; ii < argc; ++ii) {
       
   801         if(0 == ::strcmp(argv[ii], "-s"))
       
   802             manager = true;
       
   803         else if(0 == ::strcmp(argv[ii], "-d"))
       
   804             dump = true;
       
   805         else
       
   806             usage(argv[0]);
       
   807     }
       
   808 
       
   809     if(manager)
       
   810         QValueSpace::initValueSpaceServer();
       
   811 
       
   812     if(dump) {
       
   813         QValueSpaceSubscriber subscriber(QLatin1String("/"));
       
   814         dodump(&subscriber);
       
   815         return 0;
       
   816     } else {
       
   817         vse = new VSExplorer;
       
   818         LineInput li;
       
   819 
       
   820 
       
   821 #ifdef Q_OS_WIN
       
   822         QObject::connect(&li, SIGNAL(line(QString)),
       
   823                          vse, SLOT(processLine(QString)), Qt::BlockingQueuedConnection);
       
   824 #else
       
   825         QObject::connect(&li, SIGNAL(line(QString)),
       
   826                          vse, SLOT(processLine(QString)));
       
   827 #endif
       
   828 
       
   829         int rv = app.exec();
       
   830         delete vse;
       
   831         return rv;
       
   832     }
       
   833 }
       
   834 
       
   835 #include "vsexplorer.moc"