Merge branch 'maint'
[kdbg.git] / kdbg / xsldbgdriver.cpp
blobebdba6a1379a8d7d6b4e3745038c80f9d425a238
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.toUtf8().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.toUtf8().constData(), intArg);
475 } else {
476 cmdString.sprintf(cmds[cmd].fmt, intArg, strArg.toUtf8().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.toUtf8().constData(),
494 strArg2.toUtf8().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 ExprValue *variable = 0L;
703 QString name;
705 VarTree::NameKind kind;
707 TRACE(__PRETTY_FUNCTION__);
708 TRACE(p);
710 if (parseErrorMessage(p, variable, false) == true) {
711 TRACE("Found error message");
712 return variable;
715 if (strncmp(p, " Local", 6) == 0) {
716 /* skip " Local" */
717 p = p + 6;
718 TRACE("Found local variable");
719 } else if (strncmp(p, " Global", 7) == 0) {
720 /* skip " Global" */
721 p = p + 7;
722 TRACE("Found global variable");
723 } else if (strncmp(p, "= ", 2) == 0) {
724 /* we're processing the result of a "print command" */
725 /* find next line */
726 const char *nextLine = strchr(p, '\n');
728 TRACE("Found print expr");
729 if (nextLine) {
730 p = p + 2; /* skip the "= " */
731 name = QString::fromLatin1(p, nextLine - p);
732 kind = VarTree::NKplain;
733 p = nextLine + 1;
734 variable = new ExprValue(name, kind);
735 variable->m_varKind = VarTree::VKsimple;
736 parseValue(p, variable);
737 return variable;
739 } else
740 return variable; /* don't know what to do this this data abort!! */
742 // skip whitespace
743 while (isspace(*p))
744 p++;
746 if (*p != '='){
747 // No value provided just a name
748 TRACE(QString("Parse var: name") + p);
749 if (!parseName(p, name, kind)) {
750 return 0;
752 variable = new ExprValue(name, kind);
753 if (variable != 0L) {
754 variable->m_varKind = VarTree::VKsimple;
756 }else{
757 p++;
758 // skip whitespace
759 while (isspace(*p))
760 p++;
761 TRACE(QString("Parse var: name") + p);
762 if (!parseName(p, name, kind)) {
763 return 0;
765 variable = new ExprValue(name, kind);
766 if (variable != 0L) {
767 variable->m_varKind = VarTree::VKsimple;
769 if (*p == '\n')
770 p++;
771 if (!parseValue(p, variable)) {
772 delete variable;
773 return 0;
777 if (*p == '\n')
778 p++;
780 s = p;
781 return variable;
785 inline void
786 skipName(const char *&p)
788 // allow : (for enumeration values) and $ and . (for _vtbl.)
789 while (isalnum(*p) || *p == '_' || *p == ':' || *p == '$' || *p == '.')
790 p++;
793 static bool
794 parseName(const char *&s, QString & name, VarTree::NameKind & kind)
796 /* qDebug(__PRETTY_FUNCTION__); */
797 kind = VarTree::NKplain;
799 const char *p = s;
800 int len = 0;
802 // examples of names:
803 // help_cmd
805 while ((*p != '\n') && (*p != '\0')) {
806 len++;
807 p++;
811 name = QString::fromLatin1(s, len);
812 /* XSL variables will have a $ prefix to be evaluated
813 * properly */
814 //TRACE(QString("parseName got name" ) + name);
816 // return the new position
817 s = p;
818 return true;
821 static bool
822 parseValue(const char *&s, ExprValue * variable)
824 const char *start = s, *end = s;
825 ExprValue * childValue;
826 #define VALUE_END_MARKER_INDEX 0
828 /* This mark the end of a value */
829 static const char *marker[] = {
830 "\032\032", /* value end marker*/
831 "(xsldbg) ",
832 "Breakpoint at", /* stepped to next location */
833 "Breakpoint in", /* reached a set breakpoint */
834 "Reached ", /* reached template */
835 "Error:",
836 "Warning:",
837 "Information:",
838 "runtime error",
839 "xmlXPathEval:",
842 static char valueBuffer[2048];
843 int markerIndex = 0, foundEnd = 0;
844 size_t copySize;
846 if (variable == 0L)
847 return false; /* should never happen but .. */
849 while (start && (*start != '\0')) {
850 /* look for the next marker */
851 for (markerIndex = 0; marker[markerIndex] != 0; markerIndex++) {
852 foundEnd =
853 strncmp(start, marker[markerIndex],
854 strlen(marker[markerIndex])) == 0;
855 if (foundEnd)
856 break;
859 if (foundEnd)
860 break;
863 end = strchr(start, '\n');
864 if (end)
865 copySize = end - start;
866 else
867 copySize = strlen(start);
868 if (copySize >= sizeof(valueBuffer))
869 copySize = sizeof(valueBuffer)-1;
871 strncpy(valueBuffer, start, copySize);
872 valueBuffer[copySize] = '\0';
873 TRACE("Got value :");
874 TRACE(valueBuffer);
875 if ((variable->m_varKind == VarTree::VKsimple)) {
876 if (!variable->m_value.isEmpty()){
877 variable->m_varKind = VarTree::VKarray;
878 childValue = new ExprValue(variable->m_value, VarTree::NKplain);
879 variable->appendChild(childValue);
880 childValue = new ExprValue(valueBuffer, VarTree::NKplain);
881 variable->appendChild(childValue);
882 variable->m_value = "";
883 }else{
884 variable->m_value = valueBuffer;
886 }else{
887 childValue = new ExprValue(valueBuffer, VarTree::NKplain);
888 variable->appendChild(childValue);
891 if (*end =='\n'){
892 start = end + 1;
893 }else{
894 start = end + 1;
895 break;
899 if (foundEnd == 0)
900 TRACE(QString("Unable to find end on value near :") + start);
902 // If we've got something otherthan a end of value marker then
903 // advance to the end of this buffer
904 if (markerIndex != VALUE_END_MARKER_INDEX){
905 while (start && *start != '\0')
906 start++;
907 }else{
908 start = start + strlen(marker[0]);
911 s = start;
913 return true;
918 * Parses a stack frame.
920 static void
921 parseFrameInfo(const char *&s, QString & func,
922 QString & file, int &lineNo, DbgAddr & /*address*/)
924 const char *p = s, *endPos = s + strlen(s);
925 QString lineNoString;
927 TRACE("parseFrameInfo");
929 lineNo = -1;
931 /* skip 'template :\" */
932 p = p + 11;
933 // TRACE(p);
934 func = "";
935 while ((*p != '\"') && (*p != '\0')) {
936 func.append(*p);
937 p++;
939 while ((*p != '\0') && *p != '"')
940 p++;
941 if (*p != '\0')
942 p++;
943 ASSERT(p <= endPos);
944 if (p >= endPos) {
945 /* panic */
946 return;
949 /* skip mode :".*" */
950 while ((*p != '\0') && *p != '"')
951 p++;
952 if (*p != '\0')
953 p++;
954 while ((*p != '\0') && *p != '"')
955 p++;
957 /* skip '" in file ' */
958 p = p + 10;
959 if(*p == '"')
960 p++;
961 // TRACE(p);
962 file = "";
963 while (!isspace(*p) && (*p != '\"') && (*p != '\0')) {
964 file.append(*p);
965 p++;
967 if(*p == '"')
968 p++;
969 ASSERT(p <= endPos);
970 if (p >= endPos) {
971 /* panic */
972 return;
975 // TRACE(p);
976 /* skip ' : line '" */
977 p = p + 9;
978 // TRACE(p);
979 ASSERT(p <= endPos);
980 if (p >= endPos) {
981 /* panic */
982 return;
984 // TRACE(p);
985 if (isdigit(*p)) {
986 /* KDbg uses an offset of +1 for its line numbers */
987 lineNo = atoi(p) - 1;
988 lineNoString = QString::number(lineNo);
990 /* convert func into format needed */
991 func.append(" at ");
992 func.append(file);
993 func.append(':');
994 func.append(lineNoString);
996 /*advance to next line */
997 p = strchr(p, '\n');
998 if (p)
999 p++;
1000 s = p;
1004 #undef ISSPACE
1007 * Parses a stack frame including its frame number
1009 static bool
1010 parseFrame(const char *&s, int &frameNo, QString & func,
1011 QString & file, int &lineNo, DbgAddr & address)
1014 // TRACE("XsldbgDriver ::parseFrame");
1015 /* skip leading 'where' or 'frame <frame_no>' */
1016 if ((strncmp(s, "where", 5) == 0) || (strncmp(s, "frame", 5) == 0)) {
1017 s = strchr(s, '\n');
1018 if ((*s != '\0') && (*s != '#'))
1019 s++;
1021 // TRACE(s);
1023 // Example:
1024 //#1 template :"/" in file /home/keith/anon_CVS/xsldbg/docs/en/xsldoc.xsl : line 21
1025 // must start with a hash mark followed by number
1026 if (s[0] != '#' || !isdigit(s[1]))
1027 return false;
1029 //TRACE("XsldbgDriver ::parseFrame got #");
1030 s++; /* skip the hash mark */
1031 // frame number
1032 frameNo = atoi(s);
1033 while (isdigit(*s))
1034 s++;
1036 //TRACE(QString("Got frame ").append(QString::number(frameNo)));
1037 // space
1038 while (isspace(*s))
1039 s++;
1040 parseFrameInfo(s, func, file, lineNo, address);
1041 // TRACE("Will next look at ");
1042 // TRACE(s);
1043 return true;
1046 void
1047 XsldbgDriver::parseBackTrace(const char *output,
1048 std::list < StackFrame > &stack)
1050 QString func, file;
1051 int lineNo, frameNo;
1052 DbgAddr address;
1054 while (::parseFrame(output, frameNo, func, file, lineNo, address)) {
1055 stack.push_back(StackFrame());
1056 StackFrame* frm = &stack.back();
1058 frm->frameNo = frameNo;
1059 frm->fileName = file;
1060 frm->lineNo = lineNo;
1061 frm->address = address;
1062 frm->var = new ExprValue(func, VarTree::NKplain);
1066 bool
1067 XsldbgDriver::parseFrameChange(const char *output, int &frameNo,
1068 QString & file, int &lineNo,
1069 DbgAddr & address)
1071 QString func;
1073 return::parseFrame(output, frameNo, func, file, lineNo, address);
1077 bool
1078 XsldbgDriver::parseBreakList(const char *output,
1079 std::list < Breakpoint > &brks)
1081 TRACE("parseBreakList");
1082 /* skip the first blank line */
1083 const char *p;
1085 // split up a line
1086 Breakpoint bp;
1087 char *dummy;
1088 p = strchr(output, '\n');/* skip the first blank line*/
1090 while ((p != 0) && (*p != '\0')) {
1091 if (*p == '\n')
1092 p++;
1093 QString templateName;
1094 //qDebug("Looking at :%s", p);
1095 if (strncmp(p, " Breakpoint", 11) != 0)
1096 break;
1097 p = p + 11;
1098 if (*p == '\0')
1099 break;
1101 //TRACE(p);
1102 // get Num
1103 bp.id = strtol(p, &dummy, 10); /* don't care about overflows */
1105 p = dummy;
1106 if ((p == 0) || (p[1] == '\0'))
1107 break;
1108 p++;
1110 //TRACE(p);
1111 // Get breakpoint state ie enabled/disabled
1112 if (strncmp(p, "enabled", 7) == 0) {
1113 bp.enabled = true;
1114 p = p + 7;
1115 } else {
1116 if (strncmp(p, "disabled", 8) == 0) {
1117 p = p + 8;
1118 bp.enabled = false;
1119 } else{
1120 TRACE("Parse error in breakpoint list");
1121 TRACE(p);
1122 return false;
1126 //TRACE("Looking for template");
1127 //TRACE(p);
1128 if (strncmp(p, " for template: \"", 16) == 0){
1129 p = p + 16;
1130 //TRACE("Looking for template name near");
1131 //TRACE(p);
1132 /* get the template name */
1133 while (p && (*p != '\0') && (*p != '\"')){
1134 templateName.append(*p);
1135 p++;
1137 if (*p == '\"'){
1138 p++;
1139 }else{
1140 TRACE("Error missed \" near");
1141 TRACE(p);
1145 //TRACE("Looking for mode near");
1146 //TRACE(p);
1147 if (strncmp(p, " mode: \"", 8) == 0){
1148 p = p + 8;
1149 while (p && *p != '\"')
1150 p++;
1151 if (p)
1152 p++;
1155 if (strncmp(p, " in file ", 9) != 0){
1156 TRACE("Parse error in breakpoint list");
1157 TRACE(p);
1158 return false;
1162 /* skip ' in file ' */
1163 p = p + 9;
1164 // TRACE(p);
1166 if (*p == '\"')
1167 p++;
1168 /* grab file name */
1169 QString file;
1170 while ((*p != '\"') && !isspace(*p)) {
1171 file.append(*p);
1172 p++;
1174 if (*p == '\"')
1175 p++;
1176 if (*p == '\0')
1177 break;
1179 /* skip ' : line ' */
1180 p = p + 8;
1181 while (isspace(*p)) {
1182 p++;
1184 //TRACE(p);
1185 QString lineNo;
1186 while (isdigit(*p)) {
1187 lineNo.append(*p);
1188 p++;
1191 // bp.lineNo is zero-based
1192 bp.lineNo = lineNo.toInt() - 1;
1193 bp.location = QString("in %1 at %2:%3").arg(templateName, file, lineNo);
1194 bp.fileName = file;
1195 brks.push_back(bp);
1197 if (p != 0) {
1198 p = strchr(p, '\n');
1199 if (p)
1200 p++;
1203 return true;
1206 std::list<ThreadInfo>
1207 XsldbgDriver::parseThreadList(const char */*output*/)
1209 return std::list<ThreadInfo>();
1212 bool
1213 XsldbgDriver::parseBreakpoint(const char *output, int &id,
1214 QString &file, int &lineNo, QString &address)
1216 // check for errors
1217 if ( strncmp(output, "Error:", 6) == 0) {
1218 return false;
1221 char *dummy;
1222 if (strncmp(output, "Breakpoint ", 11) != 0)
1223 return false;
1225 output += 11;
1226 if (!isdigit(*output))
1227 return false;
1229 // get Num
1230 id = strtol(output, &dummy, 10); /* don't care about overflows */
1231 if (output == dummy)
1232 return false;
1234 // the file name + lineNo will be filled in later from the breakpoint list
1235 file = address = QString();
1236 lineNo = 0;
1237 return true;
1240 void
1241 XsldbgDriver::parseLocals(const char *output, std::list < ExprValue* > &newVars)
1244 /* keep going until error or xsldbg prompt is found */
1245 while (*output != '\0') {
1246 ExprValue *variable = parseVar(output);
1248 if (variable == 0) {
1249 break;
1251 // do not add duplicates
1252 for (std::list<ExprValue*>::iterator o = newVars.begin(); o != newVars.end(); ++o) {
1253 if ((*o)->m_name == variable->m_name) {
1254 delete variable;
1256 goto skipDuplicate;
1259 newVars.push_back(variable);
1260 skipDuplicate:;
1265 ExprValue *
1266 XsldbgDriver::parsePrintExpr(const char *output, bool wantErrorValue)
1268 ExprValue* var = 0;
1269 // check for error conditions
1270 if (!parseErrorMessage(output, var, wantErrorValue)) {
1271 // parse the variable
1272 var = parseVar(output);
1274 return var;
1277 bool
1278 XsldbgDriver::parseChangeWD(const char *output, QString & message)
1280 bool isGood = false;
1282 if (strncmp(output, "Change to directory", 20) == 0) {
1283 output = output + 20; /* skip 'Change to directory' */
1284 message = QString(output).simplified();
1285 if (message.isEmpty()) {
1286 message = i18n("New working directory: ") + m_programWD;
1287 isGood = true;
1290 return isGood;
1293 bool
1294 XsldbgDriver::parseChangeExecutable(const char *output, QString & message)
1296 message = output;
1297 TRACE(QString("XsldbgDriver::parseChangeExecutable :") + output);
1298 m_haveCoreFile = false;
1300 if (strstr(output, "Load of source deferred. Use the run command") != 0) {
1301 TRACE("Parsed stylesheet executable");
1302 message = QString();
1304 return message.isEmpty();
1307 bool
1308 XsldbgDriver::parseCoreFile(const char *output)
1310 TRACE("XsldbgDriver::parseCoreFile");
1311 TRACE(output);
1313 if (strstr(output, "Load of data file deferred. Use the run command") != 0) {
1314 m_haveCoreFile = true;
1315 TRACE("Parsed data file name");
1318 return m_haveCoreFile;
1321 uint
1322 XsldbgDriver::parseProgramStopped(const char *output, QString & message)
1324 /* Not sure about this function leave it here for the moment */
1326 * return DebuggerDriver::SFrefreshBreak & DebuggerDriver::SFprogramActive;
1329 // go through the output, line by line, checking what we have
1330 const char *start = output - 1;
1331 uint flags = SFprogramActive;
1333 message = QString();
1334 do {
1335 start++; /* skip '\n' */
1337 if (strncmp(start, "Finished stylesheet\n\032\032\n", 21) == 0){
1338 // flags &= ~SFprogramActive;
1339 break;
1342 // next line, please
1343 start = strchr(start, '\n');
1344 } while (start != 0);
1346 return flags;
1349 QStringList
1350 XsldbgDriver::parseSharedLibs(const char */*output*/)
1352 return QStringList();
1355 bool
1356 XsldbgDriver::parseFindType(const char */*output*/, QString & /*type*/)
1358 return true;
1361 std::list<RegisterInfo>
1362 XsldbgDriver::parseRegisters(const char */*output*/)
1364 return std::list<RegisterInfo>();
1367 bool
1368 XsldbgDriver::parseInfoLine(const char */*output*/, QString & /*addrFrom*/,
1369 QString & /*addrTo*/)
1371 return false;
1374 std::list<DisassembledCode>
1375 XsldbgDriver::parseDisassemble(const char */*output*/)
1377 return std::list<DisassembledCode>();
1380 QString
1381 XsldbgDriver::parseMemoryDump(const char */*output*/,
1382 std::list < MemoryDump > &/*memdump*/)
1384 return i18n("No memory dump available");
1387 QString
1388 XsldbgDriver::parseSetVariable(const char */*output*/)
1390 QString msg;
1391 return msg;
1395 #include "xsldbgdriver.moc"