Add tooltips test program.
[kdbg.git] / kdbg / xsldbgdriver.cpp
blob92b1901d7776e86a1a71bf37fb79f878b750c2b6
1 /*
2 * Copyright Johannes Sixt, Keith Isdale
3 * This file is licensed under the GNU General Public License Version 2.
4 * See the file COPYING in the toplevel directory of the source directory.
5 */
7 #include "xsldbgdriver.h"
8 #include "exprwnd.h"
9 #include <QFileInfo>
10 #include <QRegExp>
11 #include <QStringList>
12 #include <klocale.h> /* i18n */
13 #include <ctype.h>
14 #include <signal.h>
15 #include <stdlib.h> /* strtol, atoi */
16 #include <string.h> /* strcpy */
17 #include <kmessagebox.h>
19 #include "assert.h"
20 #include "mydebug.h"
23 static ExprValue *parseVar(const char *&s);
24 static bool parseName(const char *&s, QString & name,
25 VarTree::NameKind & kind);
26 static bool parseValue(const char *&s, ExprValue * variable);
27 static bool isErrorExpr(const char *output);
29 #define TERM_IO_ALLOWED 1
31 // TODO: make this cmd info stuff non-static to allow multiple
32 // simultaneous gdbs to run!
34 struct XsldbgCmdInfo {
35 DbgCommand cmd;
36 const char *fmt; /* format string */
37 enum Args {
38 argNone, argString, argNum,
39 argStringNum, argNumString,
40 argString2, argNum2
41 } argsNeeded;
45 * The following array of commands must be sorted by the DC* values,
46 * because they are used as indices.
48 static XsldbgCmdInfo cmds[] = {
49 {DCinitialize, "init\n", XsldbgCmdInfo::argNone},
50 {DCtty, "tty %s\n", XsldbgCmdInfo::argString},
51 {DCexecutable, "source %s\n", XsldbgCmdInfo::argString}, /* force a restart */
52 {DCtargetremote, "print 'target remote %s'\n", XsldbgCmdInfo::argString},
53 {DCcorefile, "data %s\n", XsldbgCmdInfo::argString}, /* force a restart */
54 {DCattach, "print 'attach %s'\n", XsldbgCmdInfo::argString},
55 {DCinfolinemain, "print 'info main line'\n", XsldbgCmdInfo::argNone},
56 {DCinfolocals, "locals -f\n", XsldbgCmdInfo::argNone},
57 {DCinforegisters, "print 'info reg'\n", XsldbgCmdInfo::argNone},
58 {DCexamine, "print 'x %s %s'\n", XsldbgCmdInfo::argString2},
59 {DCinfoline, "print 'templates %s:%d'\n", XsldbgCmdInfo::argStringNum},
60 {DCdisassemble, "print 'disassemble %s %s'\n", XsldbgCmdInfo::argString2},
61 {DCsetargs, "data %s\n", XsldbgCmdInfo::argString},
62 {DCsetenv, "addparam %s %s\n", XsldbgCmdInfo::argString2},
63 {DCunsetenv, "unset env %s\n", XsldbgCmdInfo::argString},
64 {DCsetoption, "setoption %s %d\n", XsldbgCmdInfo::argStringNum},
65 {DCcd, "chdir %s\n", XsldbgCmdInfo::argString},
66 {DCbt, "where\n", XsldbgCmdInfo::argNone},
67 {DCrun, "run\nsource\n", XsldbgCmdInfo::argNone}, /* Ensure that at the start
68 of executing XSLT we show the XSLT file */
69 {DCcont, "continue\n", XsldbgCmdInfo::argNone},
70 {DCstep, "step\n", XsldbgCmdInfo::argNone},
71 {DCstepi, "step\n", XsldbgCmdInfo::argNone},
72 {DCnext, "next\n", XsldbgCmdInfo::argNone},
73 {DCnexti, "next\n", XsldbgCmdInfo::argNone},
74 {DCfinish, "stepup\n", XsldbgCmdInfo::argNone},
75 {DCuntil, "continue %s:%d\n", XsldbgCmdInfo::argStringNum},
76 {DCkill, "quit\n", XsldbgCmdInfo::argNone},
77 {DCbreaktext, "break %s\n", XsldbgCmdInfo::argString},
78 {DCbreakline, "break -l %s %d\n", XsldbgCmdInfo::argStringNum},
79 {DCtbreakline, "break -l %s %d\n", XsldbgCmdInfo::argStringNum },
80 {DCbreakaddr, "print `break *%s`\n", XsldbgCmdInfo::argString },
81 {DCtbreakaddr, "print `tbreak *%s`\n", XsldbgCmdInfo::argString },
82 {DCwatchpoint, "print 'watch %s'\n", XsldbgCmdInfo::argString},
83 {DCdelete, "delete %d\n", XsldbgCmdInfo::argNum},
84 {DCenable, "enable %d\n", XsldbgCmdInfo::argNum},
85 {DCdisable, "disable %d\n", XsldbgCmdInfo::argNum},
86 {DCprint, "print %s\n", XsldbgCmdInfo::argString},
87 {DCprintDeref, "print 'print (*%s)'\n", XsldbgCmdInfo::argString},
88 {DCprintStruct, "print 'print %s'\n", XsldbgCmdInfo::argString},
89 {DCprintQStringStruct, "print 'print %s'\n", XsldbgCmdInfo::argString},
90 {DCprintPopup, "print %s\n", XsldbgCmdInfo::argString},
91 {DCframe, "frame %d\n", XsldbgCmdInfo::argNum},
92 {DCfindType, "print 'whatis %s'\n", XsldbgCmdInfo::argString},
93 {DCinfosharedlib, "stylesheets\n", XsldbgCmdInfo::argNone},
94 {DCthread, "print 'thread %d'\n", XsldbgCmdInfo::argNum},
95 {DCinfothreads, "print 'info threads'\n", XsldbgCmdInfo::argNone},
96 {DCinfobreak, "show\n", XsldbgCmdInfo::argNone},
97 {DCcondition, "print 'condition %d %s'\n", XsldbgCmdInfo::argNumString},
98 {DCsetpc, "print 'set variable $pc=%s'\n", XsldbgCmdInfo::argString},
99 {DCignore, "print 'ignore %d %d'\n", XsldbgCmdInfo::argNum2},
100 {DCprintWChar, "print 'ignore %s'\n", XsldbgCmdInfo::argString},
101 {DCsetvariable, "set %s %s\n", XsldbgCmdInfo::argString2},
104 #define NUM_CMDS (int(sizeof(cmds)/sizeof(cmds[0])))
105 #define MAX_FMTLEN 200
107 XsldbgDriver::XsldbgDriver():
108 DebuggerDriver()
110 m_haveDataFile = FALSE;
112 #ifndef NDEBUG
113 // check command info array
114 const char *perc;
116 for (int i = 0; i < NUM_CMDS; i++) {
117 // must be indexable by DbgCommand values, i.e. sorted by DbgCommand values
118 assert(i == cmds[i].cmd);
119 // a format string must be associated
120 assert(cmds[i].fmt != 0);
121 assert(strlen(cmds[i].fmt) <= MAX_FMTLEN);
122 // format string must match arg specification
123 switch (cmds[i].argsNeeded) {
124 case XsldbgCmdInfo::argNone:
125 assert(strchr(cmds[i].fmt, '%') == 0);
126 break;
127 case XsldbgCmdInfo::argString:
128 perc = strchr(cmds[i].fmt, '%');
129 assert(perc != 0 && perc[1] == 's');
130 assert(strchr(perc + 2, '%') == 0);
131 break;
132 case XsldbgCmdInfo::argNum:
133 perc = strchr(cmds[i].fmt, '%');
134 assert(perc != 0 && perc[1] == 'd');
135 assert(strchr(perc + 2, '%') == 0);
136 break;
137 case XsldbgCmdInfo::argStringNum:
138 perc = strchr(cmds[i].fmt, '%');
139 assert(perc != 0 && perc[1] == 's');
140 perc = strchr(perc + 2, '%');
141 assert(perc != 0 && perc[1] == 'd');
142 assert(strchr(perc + 2, '%') == 0);
143 break;
144 case XsldbgCmdInfo::argNumString:
145 perc = strchr(cmds[i].fmt, '%');
146 assert(perc != 0 && perc[1] == 'd');
147 perc = strchr(perc + 2, '%');
148 assert(perc != 0 && perc[1] == 's');
149 assert(strchr(perc + 2, '%') == 0);
150 break;
151 case XsldbgCmdInfo::argString2:
152 perc = strchr(cmds[i].fmt, '%');
153 assert(perc != 0 && perc[1] == 's');
154 perc = strchr(perc + 2, '%');
155 assert(perc != 0 && perc[1] == 's');
156 assert(strchr(perc + 2, '%') == 0);
157 break;
158 case XsldbgCmdInfo::argNum2:
159 perc = strchr(cmds[i].fmt, '%');
160 assert(perc != 0 && perc[1] == 'd');
161 perc = strchr(perc + 2, '%');
162 assert(perc != 0 && perc[1] == 'd');
163 assert(strchr(perc + 2, '%') == 0);
164 break;
167 #endif
170 XsldbgDriver::~XsldbgDriver()
175 QString
176 XsldbgDriver::driverName() const
178 return "XSLDBG";
181 QString
182 XsldbgDriver::defaultXsldbg()
184 return "xsldbg --lang en --shell --gdb";
187 QString
188 XsldbgDriver::defaultInvocation() const
190 return defaultXsldbg();
193 QStringList XsldbgDriver::boolOptionList() const
195 QStringList allOptions;
196 allOptions.append("verbose");
197 allOptions.append("repeat");
198 allOptions.append("debug");
199 allOptions.append("novalid");
200 allOptions.append("noout");
201 allOptions.append("html");
202 allOptions.append("docbook");
203 allOptions.append("nonet");
204 allOptions.append("catalogs");
205 allOptions.append("xinclude");
206 allOptions.append("profile");
207 return allOptions;
210 bool
211 XsldbgDriver::startup(QString cmdStr)
213 if (!DebuggerDriver::startup(cmdStr))
214 return false;
216 static const char xsldbgInitialize[] = "pwd\nsetoption gdb 2\n"; /* don't need to do anything else */
218 executeCmdString(DCinitialize, xsldbgInitialize, false);
220 return true;
223 void
224 XsldbgDriver::commandFinished(CmdQueueItem * cmd)
227 TRACE(__PRETTY_FUNCTION__);
228 // command string must be committed
229 if (!cmd->m_committed) {
230 // not commited!
231 TRACE("calling " +
232 (__PRETTY_FUNCTION__ +
233 (" with uncommited command:\n\t" + cmd->m_cmdString)));
234 return;
237 /* ok, the command is ready */
238 emit commandReceived(cmd, m_output.constData());
240 switch (cmd->m_cmd) {
241 case DCbt:
242 case DCinfolocals:
243 case DCrun:
244 case DCcont:
245 case DCstep:
246 case DCnext:
247 case DCfinish:{
248 if (!::isErrorExpr(m_output.constData()))
249 parseMarker();
250 else{
251 // This only shows an error for DCinfolocals
252 // need to update KDebugger::handleRunCommand ?
253 KMessageBox::sorry(0L, m_output);
256 break;
258 case DCinfolinemain:
259 if (!m_xslFile.isEmpty())
260 emit activateFileLine(m_xslFile, 0, DbgAddr());
261 break;
263 default:;
268 XsldbgDriver::findPrompt(const QByteArray& output) const
271 * If there's a prompt string in the collected output, it must be at
272 * the very end. We do a quick check whether the last characters of
273 * output are suitable and do the full search only if they are.
275 int len = output.length();
276 if (len < 11 || output[len-1] != ' ' || output[len-2] != '>')
277 return -1;
279 // There can be text between "(xsldbg) " and the "> " at the end
280 // since we do not know what that text is, we accept the former
281 // anywhere in the output.
282 return output.indexOf("(xsldbg) ");
285 void
286 XsldbgDriver::parseMarker()
288 char *p = m_output.data();
290 for (;;) {
291 if ((p == 0) || (*p == '\0')) {
292 m_output.clear();
293 return;
295 if (strncmp(p, "Breakpoint for file ", 20) == 0)
296 break;
297 // try to marker on next line !
298 p = strchr(p, '\n');
299 if ((p != 0) && (*p != '\0'))
300 p++;
304 // extract the marker
305 char *startMarker = p + 20;
307 //TRACE(QString("found marker:") + startMarker);
308 char *endMarker = strchr(startMarker, '\n');
310 if (endMarker == 0)
311 return;
313 *endMarker = '\0';
315 // extract filename and line number
316 static QRegExp MarkerRE(" at line (\\d+)");
318 int lineNoStart = MarkerRE.indexIn(startMarker);
320 if (lineNoStart >= 0) {
321 int lineNo = MarkerRE.cap(1).toInt();
323 DbgAddr address;
325 // now show the window
326 startMarker[lineNoStart-1] = '\0'; /* split off file name */
327 TRACE("Got file and line number");
328 startMarker++;
329 TRACE(QString(startMarker) + ": " + QString::number(lineNo));
330 emit activateFileLine(startMarker, lineNo - 1, address);
336 * Escapes characters that might lead to problems when they appear on gdb's
337 * command line.
339 static void
340 normalizeStringArg(QString & arg)
343 * Remove trailing backslashes. This approach is a little simplistic,
344 * but we know that there is at the moment no case where a trailing
345 * backslash would make sense.
347 while (!arg.isEmpty() && arg[arg.length() - 1] == '\\') {
348 arg = arg.left(arg.length() - 1);
353 QString
354 XsldbgDriver::makeCmdString(DbgCommand cmd, QString strArg)
356 assert(cmd >= 0 && cmd < NUM_CMDS);
357 assert(cmds[cmd].argsNeeded == XsldbgCmdInfo::argString);
359 normalizeStringArg(strArg);
361 if (cmd == DCcd) {
362 // need the working directory when parsing the output
363 m_programWD = strArg;
364 } else if (cmd == DCexecutable) {
365 // want to display the XSL file
366 m_xslFile = strArg;
369 QString cmdString;
370 cmdString.sprintf(cmds[cmd].fmt, strArg.toLatin1().constData());
371 return cmdString;
374 QString
375 XsldbgDriver::makeCmdString(DbgCommand cmd, int intArg)
377 assert(cmd >= 0 && cmd < NUM_CMDS);
378 assert(cmds[cmd].argsNeeded == XsldbgCmdInfo::argNum);
380 QString cmdString;
381 cmdString.sprintf(cmds[cmd].fmt, intArg);
382 return cmdString;
385 QString
386 XsldbgDriver::makeCmdString(DbgCommand cmd, QString strArg, int intArg)
388 assert(cmd >= 0 && cmd < NUM_CMDS);
389 assert(cmds[cmd].argsNeeded == XsldbgCmdInfo::argStringNum ||
390 cmds[cmd].argsNeeded == XsldbgCmdInfo::argNumString ||
391 cmd == DCexamine || cmd == DCtty);
393 normalizeStringArg(strArg);
395 QString cmdString;
397 if (cmd == DCtty) {
399 * intArg specifies which channels should be redirected to
400 * /dev/null. It is a value or'ed together from RDNstdin,
401 * RDNstdout, RDNstderr.
403 static const char *const runRedir[8] = {
405 " </dev/null",
406 " >/dev/null",
407 " </dev/null >/dev/null",
408 " 2>/dev/null",
409 " </dev/null 2>/dev/null",
410 " >/dev/null 2>&1",
411 " </dev/null >/dev/null 2>&1"
414 if (strArg.isEmpty())
415 intArg = 7; /* failsafe if no tty */
416 m_redirect = runRedir[intArg & 7];
418 return makeCmdString(DCtty, strArg); /* note: no problem if strArg empty */
421 if (cmd == DCexamine) {
422 // make a format specifier from the intArg
423 static const char size[16] = {
424 '\0', 'b', 'h', 'w', 'g'
426 static const char format[16] = {
427 '\0', 'x', 'd', 'u', 'o', 't',
428 'a', 'c', 'f', 's', 'i'
431 assert(MDTsizemask == 0xf); /* lowest 4 bits */
432 assert(MDTformatmask == 0xf0); /* next 4 bits */
433 int count = 16; /* number of entities to print */
434 char sizeSpec = size[intArg & MDTsizemask];
435 char formatSpec = format[(intArg & MDTformatmask) >> 4];
437 assert(sizeSpec != '\0');
438 assert(formatSpec != '\0');
439 // adjust count such that 16 lines are printed
440 switch (intArg & MDTformatmask) {
441 case MDTstring:
442 case MDTinsn:
443 break; /* no modification needed */
444 default:
445 // all cases drop through:
446 switch (intArg & MDTsizemask) {
447 case MDTbyte:
448 case MDThalfword:
449 count *= 2;
450 case MDTword:
451 count *= 2;
452 case MDTgiantword:
453 count *= 2;
455 break;
457 QString spec;
459 spec.sprintf("/%d%c%c", count, sizeSpec, formatSpec);
461 return makeCmdString(DCexamine, spec, strArg);
464 if (cmds[cmd].argsNeeded == XsldbgCmdInfo::argStringNum) {
465 // line numbers are zero-based
466 if (cmd == DCuntil || cmd == DCbreakline ||
467 cmd == DCtbreakline || cmd == DCinfoline) {
468 intArg++;
470 if (cmd == DCinfoline) {
471 // must split off file name part
472 strArg = QFileInfo(strArg).fileName();
474 cmdString.sprintf(cmds[cmd].fmt, strArg.toLatin1().constData(), intArg);
475 } else {
476 cmdString.sprintf(cmds[cmd].fmt, intArg, strArg.toLatin1().constData());
478 return cmdString;
481 QString
482 XsldbgDriver::makeCmdString(DbgCommand cmd, QString strArg1,
483 QString strArg2)
485 assert(cmd >= 0 && cmd < NUM_CMDS);
486 assert(cmds[cmd].argsNeeded == XsldbgCmdInfo::argString2);
488 normalizeStringArg(strArg1);
489 normalizeStringArg(strArg2);
491 QString cmdString;
492 cmdString.sprintf(cmds[cmd].fmt,
493 strArg1.toLatin1().constData(),
494 strArg2.toLatin1().constData());
495 return cmdString;
498 QString
499 XsldbgDriver::makeCmdString(DbgCommand cmd, int intArg1, int intArg2)
501 assert(cmd >= 0 && cmd < NUM_CMDS);
502 assert(cmds[cmd].argsNeeded == XsldbgCmdInfo::argNum2);
504 QString cmdString;
505 cmdString.sprintf(cmds[cmd].fmt, intArg1, intArg2);
506 return cmdString;
509 CmdQueueItem *
510 XsldbgDriver::executeCmd(DbgCommand cmd, bool clearLow)
512 assert(cmd >= 0 && cmd < NUM_CMDS);
513 assert(cmds[cmd].argsNeeded == XsldbgCmdInfo::argNone);
515 if (cmd == DCrun) {
516 m_haveCoreFile = false;
519 return executeCmdString(cmd, cmds[cmd].fmt, clearLow);
522 CmdQueueItem *
523 XsldbgDriver::executeCmd(DbgCommand cmd, QString strArg, bool clearLow)
525 return executeCmdString(cmd, makeCmdString(cmd, strArg), clearLow);
528 CmdQueueItem *
529 XsldbgDriver::executeCmd(DbgCommand cmd, int intArg, bool clearLow)
532 return executeCmdString(cmd, makeCmdString(cmd, intArg), clearLow);
535 CmdQueueItem *
536 XsldbgDriver::executeCmd(DbgCommand cmd, QString strArg, int intArg,
537 bool clearLow)
539 return executeCmdString(cmd, makeCmdString(cmd, strArg, intArg),
540 clearLow);
543 CmdQueueItem *
544 XsldbgDriver::executeCmd(DbgCommand cmd, QString strArg1, QString strArg2,
545 bool clearLow)
547 return executeCmdString(cmd, makeCmdString(cmd, strArg1, strArg2),
548 clearLow);
551 CmdQueueItem *
552 XsldbgDriver::executeCmd(DbgCommand cmd, int intArg1, int intArg2,
553 bool clearLow)
555 return executeCmdString(cmd, makeCmdString(cmd, intArg1, intArg2),
556 clearLow);
559 CmdQueueItem *
560 XsldbgDriver::queueCmd(DbgCommand cmd, QueueMode mode)
562 return queueCmdString(cmd, cmds[cmd].fmt, mode);
565 CmdQueueItem *
566 XsldbgDriver::queueCmd(DbgCommand cmd, QString strArg, QueueMode mode)
568 return queueCmdString(cmd, makeCmdString(cmd, strArg), mode);
571 CmdQueueItem *
572 XsldbgDriver::queueCmd(DbgCommand cmd, int intArg, QueueMode mode)
574 return queueCmdString(cmd, makeCmdString(cmd, intArg), mode);
577 CmdQueueItem *
578 XsldbgDriver::queueCmd(DbgCommand cmd, QString strArg, int intArg,
579 QueueMode mode)
581 return queueCmdString(cmd, makeCmdString(cmd, strArg, intArg), mode);
584 CmdQueueItem *
585 XsldbgDriver::queueCmd(DbgCommand cmd, QString strArg1, QString strArg2,
586 QueueMode mode)
588 return queueCmdString(cmd, makeCmdString(cmd, strArg1, strArg2), mode);
591 void
592 XsldbgDriver::terminate()
594 qDebug("XsldbgDriver::Terminate");
595 flushCommands();
596 executeCmdString(DCinitialize, "quit\n", true);
597 ::kill(pid(), SIGTERM);
598 m_state = DSidle;
601 void
602 XsldbgDriver::detachAndTerminate()
604 qDebug("XsldbgDriver::detachAndTerminate");
605 flushCommands();
606 executeCmdString(DCinitialize, "quit\n", true);
607 ::kill(pid(), SIGINT);
610 void
611 XsldbgDriver::interruptInferior()
613 // remove accidentally queued commands
614 qDebug("interruptInferior");
615 flushHiPriQueue();
616 ::kill(pid(), SIGINT);
619 static bool
620 isErrorExpr(const char *output)
622 int wordIndex;
623 bool result = false;
624 #define ERROR_WORD_COUNT 6
625 static const char *errorWords[ERROR_WORD_COUNT] = {
626 "Error:",
627 "error:", // libxslt error
628 "Unknown command",
629 "Warning:",
630 "warning:", // libxslt warning
631 "Information:" // xsldbg information
633 static int errorWordLength[ERROR_WORD_COUNT] = {
634 6, /* Error */
635 6, /* rror */
636 15, /* Unknown command*/
637 8, /* Warning */
638 8, /* warning */
639 12 /* Information */
642 for (wordIndex = 0; wordIndex < ERROR_WORD_COUNT; wordIndex++){
643 // ignore any warnings relating to local variables not being available
644 if (strncmp(output,
645 errorWords[wordIndex],
646 errorWordLength[wordIndex]) == 0 &&
647 (wordIndex == 0 && strstr(output, "try stepping past the xsl:param") == 0) ) {
648 result = true;
649 TRACE(QString("Error/Warning/Information from xsldbg ") + output);
650 break;
654 return result;
658 * Returns true if the output is an error message. If wantErrorValue is
659 * true, a new ExprValue object is created and filled with the error message.
661 static bool
662 parseErrorMessage(const char *output,
663 ExprValue * &variable, bool wantErrorValue)
665 if (isErrorExpr(output)) {
666 if (wantErrorValue) {
667 // put the error message as value in the variable
668 variable = new ExprValue(QString(), VarTree::NKplain);
669 const char *endMsg = strchr(output, '\n');
671 if (endMsg == 0)
672 endMsg = output + strlen(output);
673 variable->m_value = QString::fromLatin1(output, endMsg - output);
674 } else {
675 variable = 0;
677 return true;
679 return false;
683 void
684 XsldbgDriver::setPrintQStringDataCmd(const char* /*cmd*/)
688 ExprValue *
689 XsldbgDriver::parseQCharArray(const char */*output*/, bool /*wantErrorValue*/,
690 bool /*qt3like*/)
692 ExprValue *variable = 0;
694 TRACE("XsldbgDriver::parseQCharArray not implmented");
695 return variable;
698 static ExprValue *
699 parseVar(const char *&s)
701 const char *p = s;
702 bool foundLocalVar = false;
703 ExprValue *variable = 0L;
704 QString name;
706 VarTree::NameKind kind;
708 TRACE(__PRETTY_FUNCTION__);
709 TRACE(p);
711 if (parseErrorMessage(p, variable, false) == true) {
712 TRACE("Found error message");
713 return variable;
716 if (strncmp(p, " Local", 6) == 0) {
717 foundLocalVar = true;
718 /* skip " Local" */
719 p = p + 6;
720 TRACE("Found local variable");
721 } else if (strncmp(p, " Global", 7) == 0) {
722 /* skip " Global" */
723 p = p + 7;
724 TRACE("Found global variable");
725 } else if (strncmp(p, "= ", 2) == 0) {
726 /* we're processing the result of a "print command" */
727 /* find next line */
728 const char *nextLine = strchr(p, '\n');
730 TRACE("Found print expr");
731 if (nextLine) {
732 p = p + 2; /* skip the "= " */
733 name = QString::fromLatin1(p, nextLine - p);
734 kind = VarTree::NKplain;
735 p = nextLine + 1;
736 variable = new ExprValue(name, kind);
737 variable->m_varKind = VarTree::VKsimple;
738 parseValue(p, variable);
739 return variable;
741 } else
742 return variable; /* don't know what to do this this data abort!! */
744 // skip whitespace
745 while (isspace(*p))
746 p++;
748 if (*p != '='){
749 // No value provided just a name
750 TRACE(QString("Parse var: name") + p);
751 if (!parseName(p, name, kind)) {
752 return 0;
754 variable = new ExprValue(name, kind);
755 if (variable != 0L) {
756 variable->m_varKind = VarTree::VKsimple;
758 }else{
759 p++;
760 // skip whitespace
761 while (isspace(*p))
762 p++;
763 TRACE(QString("Parse var: name") + p);
764 if (!parseName(p, name, kind)) {
765 return 0;
767 variable = new ExprValue(name, kind);
768 if (variable != 0L) {
769 variable->m_varKind = VarTree::VKsimple;
771 if (*p == '\n')
772 p++;
773 if (!parseValue(p, variable)) {
774 delete variable;
775 return 0;
779 if (*p == '\n')
780 p++;
782 s = p;
783 return variable;
787 inline void
788 skipName(const char *&p)
790 // allow : (for enumeration values) and $ and . (for _vtbl.)
791 while (isalnum(*p) || *p == '_' || *p == ':' || *p == '$' || *p == '.')
792 p++;
795 static bool
796 parseName(const char *&s, QString & name, VarTree::NameKind & kind)
798 /* qDebug(__PRETTY_FUNCTION__); */
799 kind = VarTree::NKplain;
801 const char *p = s;
802 int len = 0;
804 // examples of names:
805 // help_cmd
807 while ((*p != '\n') && (*p != '\0')) {
808 len++;
809 p++;
813 name = QString::fromLatin1(s, len);
814 /* XSL variables will have a $ prefix to be evaluated
815 * properly */
816 //TRACE(QString("parseName got name" ) + name);
818 // return the new position
819 s = p;
820 return true;
823 static bool
824 parseValue(const char *&s, ExprValue * variable)
826 const char *start = s, *end = s;
827 ExprValue * childValue;
828 #define VALUE_END_MARKER_INDEX 0
830 /* This mark the end of a value */
831 static const char *marker[] = {
832 "\032\032", /* value end marker*/
833 "(xsldbg) ",
834 "Breakpoint at", /* stepped to next location */
835 "Breakpoint in", /* reached a set breakpoint */
836 "Reached ", /* reached template */
837 "Error:",
838 "Warning:",
839 "Information:",
840 "runtime error",
841 "xmlXPathEval:",
844 static char valueBuffer[2048];
845 int markerIndex = 0, foundEnd = 0;
846 size_t copySize;
848 if (variable == 0L)
849 return false; /* should never happen but .. */
851 while (start && (*start != '\0')) {
852 /* look for the next marker */
853 for (markerIndex = 0; marker[markerIndex] != 0; markerIndex++) {
854 foundEnd =
855 strncmp(start, marker[markerIndex],
856 strlen(marker[markerIndex])) == 0;
857 if (foundEnd)
858 break;
861 if (foundEnd)
862 break;
865 end = strchr(start, '\n');
866 if (end)
867 copySize = end - start;
868 else
869 copySize = strlen(start);
870 if (copySize >= sizeof(valueBuffer))
871 copySize = sizeof(valueBuffer)-1;
873 strncpy(valueBuffer, start, copySize);
874 valueBuffer[copySize] = '\0';
875 TRACE("Got value :");
876 TRACE(valueBuffer);
877 if ((variable->m_varKind == VarTree::VKsimple)) {
878 if (!variable->m_value.isEmpty()){
879 variable->m_varKind = VarTree::VKarray;
880 childValue = new ExprValue(variable->m_value, VarTree::NKplain);
881 variable->appendChild(childValue);
882 childValue = new ExprValue(valueBuffer, VarTree::NKplain);
883 variable->appendChild(childValue);
884 variable->m_value = "";
885 }else{
886 variable->m_value = valueBuffer;
888 }else{
889 childValue = new ExprValue(valueBuffer, VarTree::NKplain);
890 variable->appendChild(childValue);
893 if (*end =='\n'){
894 start = end + 1;
895 }else{
896 start = end + 1;
897 break;
901 if (foundEnd == 0)
902 TRACE(QString("Unable to find end on value near :") + start);
904 // If we've got something otherthan a end of value marker then
905 // advance to the end of this buffer
906 if (markerIndex != VALUE_END_MARKER_INDEX){
907 while (start && *start != '\0')
908 start++;
909 }else{
910 start = start + strlen(marker[0]);
913 s = start;
915 return true;
920 * Parses a stack frame.
922 static void
923 parseFrameInfo(const char *&s, QString & func,
924 QString & file, int &lineNo, DbgAddr & /*address*/)
926 const char *p = s, *endPos = s + strlen(s);
927 QString lineNoString;
929 TRACE("parseFrameInfo");
931 lineNo = -1;
933 /* skip 'template :\" */
934 p = p + 11;
935 // TRACE(p);
936 func = "";
937 while ((*p != '\"') && (*p != '\0')) {
938 func.append(*p);
939 p++;
941 while ((*p != '\0') && *p != '"')
942 p++;
943 if (*p != '\0')
944 p++;
945 ASSERT(p <= endPos);
946 if (p >= endPos) {
947 /* panic */
948 return;
951 /* skip mode :".*" */
952 while ((*p != '\0') && *p != '"')
953 p++;
954 if (*p != '\0')
955 p++;
956 while ((*p != '\0') && *p != '"')
957 p++;
959 /* skip '" in file ' */
960 p = p + 10;
961 if(*p == '"')
962 p++;
963 // TRACE(p);
964 file = "";
965 while (!isspace(*p) && (*p != '\"') && (*p != '\0')) {
966 file.append(*p);
967 p++;
969 if(*p == '"')
970 p++;
971 ASSERT(p <= endPos);
972 if (p >= endPos) {
973 /* panic */
974 return;
977 // TRACE(p);
978 /* skip ' : line '" */
979 p = p + 9;
980 // TRACE(p);
981 ASSERT(p <= endPos);
982 if (p >= endPos) {
983 /* panic */
984 return;
986 // TRACE(p);
987 if (isdigit(*p)) {
988 /* KDbg uses an offset of +1 for its line numbers */
989 lineNo = atoi(p) - 1;
990 lineNoString = QString::number(lineNo);
992 /* convert func into format needed */
993 func.append(" at ");
994 func.append(file);
995 func.append(':');
996 func.append(lineNoString);
998 /*advance to next line */
999 p = strchr(p, '\n');
1000 if (p)
1001 p++;
1002 s = p;
1006 #undef ISSPACE
1009 * Parses a stack frame including its frame number
1011 static bool
1012 parseFrame(const char *&s, int &frameNo, QString & func,
1013 QString & file, int &lineNo, DbgAddr & address)
1016 // TRACE("XsldbgDriver ::parseFrame");
1017 /* skip leading 'where' or 'frame <frame_no>' */
1018 if ((strncmp(s, "where", 5) == 0) || (strncmp(s, "frame", 5) == 0)) {
1019 s = strchr(s, '\n');
1020 if ((*s != '\0') && (*s != '#'))
1021 s++;
1023 // TRACE(s);
1025 // Example:
1026 //#1 template :"/" in file /home/keith/anon_CVS/xsldbg/docs/en/xsldoc.xsl : line 21
1027 // must start with a hash mark followed by number
1028 if (s[0] != '#' || !isdigit(s[1]))
1029 return false;
1031 //TRACE("XsldbgDriver ::parseFrame got #");
1032 s++; /* skip the hash mark */
1033 // frame number
1034 frameNo = atoi(s);
1035 while (isdigit(*s))
1036 s++;
1038 //TRACE(QString("Got frame ").append(QString::number(frameNo)));
1039 // space
1040 while (isspace(*s))
1041 s++;
1042 parseFrameInfo(s, func, file, lineNo, address);
1043 // TRACE("Will next look at ");
1044 // TRACE(s);
1045 return true;
1048 void
1049 XsldbgDriver::parseBackTrace(const char *output,
1050 std::list < StackFrame > &stack)
1052 QString func, file;
1053 int lineNo, frameNo;
1054 DbgAddr address;
1056 while (::parseFrame(output, frameNo, func, file, lineNo, address)) {
1057 stack.push_back(StackFrame());
1058 StackFrame* frm = &stack.back();
1060 frm->frameNo = frameNo;
1061 frm->fileName = file;
1062 frm->lineNo = lineNo;
1063 frm->address = address;
1064 frm->var = new ExprValue(func, VarTree::NKplain);
1068 bool
1069 XsldbgDriver::parseFrameChange(const char *output, int &frameNo,
1070 QString & file, int &lineNo,
1071 DbgAddr & address)
1073 QString func;
1075 return::parseFrame(output, frameNo, func, file, lineNo, address);
1079 bool
1080 XsldbgDriver::parseBreakList(const char *output,
1081 std::list < Breakpoint > &brks)
1083 TRACE("parseBreakList");
1084 /* skip the first blank line */
1085 const char *p;
1087 // split up a line
1088 Breakpoint bp;
1089 char *dummy;
1090 p = strchr(output, '\n');/* skip the first blank line*/
1092 while ((p != 0) && (*p != '\0')) {
1093 if (*p == '\n')
1094 p++;
1095 QString templateName;
1096 //qDebug("Looking at :%s", p);
1097 if (strncmp(p, " Breakpoint", 11) != 0)
1098 break;
1099 p = p + 11;
1100 if (*p == '\0')
1101 break;
1103 //TRACE(p);
1104 // get Num
1105 bp.id = strtol(p, &dummy, 10); /* don't care about overflows */
1107 p = dummy;
1108 if ((p == 0) || (p[1] == '\0'))
1109 break;
1110 p++;
1112 //TRACE(p);
1113 // Get breakpoint state ie enabled/disabled
1114 if (strncmp(p, "enabled", 7) == 0) {
1115 bp.enabled = true;
1116 p = p + 7;
1117 } else {
1118 if (strncmp(p, "disabled", 8) == 0) {
1119 p = p + 8;
1120 bp.enabled = false;
1121 } else{
1122 TRACE("Parse error in breakpoint list");
1123 TRACE(p);
1124 return false;
1128 //TRACE("Looking for template");
1129 //TRACE(p);
1130 if (strncmp(p, " for template: \"", 16) == 0){
1131 p = p + 16;
1132 //TRACE("Looking for template name near");
1133 //TRACE(p);
1134 /* get the template name */
1135 while (p && (*p != '\0') && (*p != '\"')){
1136 templateName.append(*p);
1137 p++;
1139 if (*p == '\"'){
1140 p++;
1141 }else{
1142 TRACE("Error missed \" near");
1143 TRACE(p);
1147 //TRACE("Looking for mode near");
1148 //TRACE(p);
1149 if (strncmp(p, " mode: \"", 8) == 0){
1150 p = p + 8;
1151 while (p && *p != '\"')
1152 p++;
1153 if (p)
1154 p++;
1157 if (strncmp(p, " in file ", 9) != 0){
1158 TRACE("Parse error in breakpoint list");
1159 TRACE(p);
1160 return false;
1164 /* skip ' in file ' */
1165 p = p + 9;
1166 // TRACE(p);
1168 if (*p == '\"')
1169 p++;
1170 /* grab file name */
1171 QString file;
1172 while ((*p != '\"') && !isspace(*p)) {
1173 file.append(*p);
1174 p++;
1176 if (*p == '\"')
1177 p++;
1178 if (*p == '\0')
1179 break;
1181 /* skip ' : line ' */
1182 p = p + 8;
1183 while (isspace(*p)) {
1184 p++;
1186 //TRACE(p);
1187 QString lineNo;
1188 while (isdigit(*p)) {
1189 lineNo.append(*p);
1190 p++;
1193 // bp.lineNo is zero-based
1194 bp.lineNo = lineNo.toInt() - 1;
1195 bp.location = QString("in %1 at %2:%3").arg(templateName, file, lineNo);
1196 bp.fileName = file;
1197 brks.push_back(bp);
1199 if (p != 0) {
1200 p = strchr(p, '\n');
1201 if (p)
1202 p++;
1205 return true;
1208 std::list<ThreadInfo>
1209 XsldbgDriver::parseThreadList(const char */*output*/)
1211 return std::list<ThreadInfo>();
1214 bool
1215 XsldbgDriver::parseBreakpoint(const char *output, int &id,
1216 QString &file, int &lineNo, QString &address)
1218 // check for errors
1219 if ( strncmp(output, "Error:", 6) == 0) {
1220 return false;
1223 char *dummy;
1224 if (strncmp(output, "Breakpoint ", 11) != 0)
1225 return false;
1227 output += 11;
1228 if (!isdigit(*output))
1229 return false;
1231 // get Num
1232 id = strtol(output, &dummy, 10); /* don't care about overflows */
1233 if (output == dummy)
1234 return false;
1236 // the file name + lineNo will be filled in later from the breakpoint list
1237 file = address = QString();
1238 lineNo = 0;
1239 return true;
1242 void
1243 XsldbgDriver::parseLocals(const char *output, std::list < ExprValue* > &newVars)
1246 /* keep going until error or xsldbg prompt is found */
1247 while (*output != '\0') {
1248 ExprValue *variable = parseVar(output);
1250 if (variable == 0) {
1251 break;
1253 // do not add duplicates
1254 for (std::list<ExprValue*>::iterator o = newVars.begin(); o != newVars.end(); ++o) {
1255 if ((*o)->m_name == variable->m_name) {
1256 delete variable;
1258 goto skipDuplicate;
1261 newVars.push_back(variable);
1262 skipDuplicate:;
1267 ExprValue *
1268 XsldbgDriver::parsePrintExpr(const char *output, bool wantErrorValue)
1270 ExprValue* var = 0;
1271 // check for error conditions
1272 if (!parseErrorMessage(output, var, wantErrorValue)) {
1273 // parse the variable
1274 var = parseVar(output);
1276 return var;
1279 bool
1280 XsldbgDriver::parseChangeWD(const char *output, QString & message)
1282 bool isGood = false;
1284 if (strncmp(output, "Change to directory", 20) == 0) {
1285 output = output + 20; /* skip 'Change to directory' */
1286 message = QString(output).simplified();
1287 if (message.isEmpty()) {
1288 message = i18n("New working directory: ") + m_programWD;
1289 isGood = true;
1292 return isGood;
1295 bool
1296 XsldbgDriver::parseChangeExecutable(const char *output, QString & message)
1298 message = output;
1299 TRACE(QString("XsldbgDriver::parseChangeExecutable :") + output);
1300 m_haveCoreFile = false;
1302 if (strstr(output, "Load of source deferred. Use the run command") != 0) {
1303 TRACE("Parsed stylesheet executable");
1304 message = QString();
1306 return message.isEmpty();
1309 bool
1310 XsldbgDriver::parseCoreFile(const char *output)
1312 TRACE("XsldbgDriver::parseCoreFile");
1313 TRACE(output);
1315 if (strstr(output, "Load of data file deferred. Use the run command") != 0) {
1316 m_haveCoreFile = true;
1317 TRACE("Parsed data file name");
1320 return m_haveCoreFile;
1323 uint
1324 XsldbgDriver::parseProgramStopped(const char *output, QString & message)
1326 /* Not sure about this function leave it here for the moment */
1328 * return DebuggerDriver::SFrefreshBreak & DebuggerDriver::SFprogramActive;
1331 // go through the output, line by line, checking what we have
1332 const char *start = output - 1;
1333 uint flags = SFprogramActive;
1335 message = QString();
1336 do {
1337 start++; /* skip '\n' */
1339 if (strncmp(start, "Finished stylesheet\n\032\032\n", 21) == 0){
1340 // flags &= ~SFprogramActive;
1341 break;
1344 // next line, please
1345 start = strchr(start, '\n');
1346 } while (start != 0);
1348 return flags;
1351 QStringList
1352 XsldbgDriver::parseSharedLibs(const char */*output*/)
1354 return QStringList();
1357 bool
1358 XsldbgDriver::parseFindType(const char */*output*/, QString & /*type*/)
1360 return true;
1363 std::list<RegisterInfo>
1364 XsldbgDriver::parseRegisters(const char */*output*/)
1366 return std::list<RegisterInfo>();
1369 bool
1370 XsldbgDriver::parseInfoLine(const char */*output*/, QString & /*addrFrom*/,
1371 QString & /*addrTo*/)
1373 return false;
1376 std::list<DisassembledCode>
1377 XsldbgDriver::parseDisassemble(const char */*output*/)
1379 return std::list<DisassembledCode>();
1382 QString
1383 XsldbgDriver::parseMemoryDump(const char */*output*/,
1384 std::list < MemoryDump > &/*memdump*/)
1386 return i18n("No memory dump available");
1389 QString
1390 XsldbgDriver::parseSetVariable(const char */*output*/)
1392 QString msg;
1393 return msg;
1397 #include "xsldbgdriver.moc"