|
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" |