KDbg 2.5.5.
[kdbg.git] / kdbg / xsldbgdriver.cpp
blobc919e110e8b82af75b9692c7944d1132f128495f
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 {DCframe, "frame %d\n", XsldbgCmdInfo::argNum},
91 {DCfindType, "print 'whatis %s'\n", XsldbgCmdInfo::argString},
92 {DCinfosharedlib, "stylesheets\n", XsldbgCmdInfo::argNone},
93 {DCthread, "print 'thread %d'\n", XsldbgCmdInfo::argNum},
94 {DCinfothreads, "print 'info threads'\n", XsldbgCmdInfo::argNone},
95 {DCinfobreak, "show\n", XsldbgCmdInfo::argNone},
96 {DCcondition, "print 'condition %d %s'\n", XsldbgCmdInfo::argNumString},
97 {DCsetpc, "print 'set variable $pc=%s'\n", XsldbgCmdInfo::argString},
98 {DCignore, "print 'ignore %d %d'\n", XsldbgCmdInfo::argNum2},
99 {DCprintWChar, "print 'ignore %s'\n", XsldbgCmdInfo::argString},
100 {DCsetvariable, "set %s %s\n", XsldbgCmdInfo::argString2},
103 #define NUM_CMDS (int(sizeof(cmds)/sizeof(cmds[0])))
104 #define MAX_FMTLEN 200
106 XsldbgDriver::XsldbgDriver():
107 DebuggerDriver()
109 m_haveDataFile = FALSE;
111 #ifndef NDEBUG
112 // check command info array
113 const char *perc;
115 for (int i = 0; i < NUM_CMDS; i++) {
116 // must be indexable by DbgCommand values, i.e. sorted by DbgCommand values
117 assert(i == cmds[i].cmd);
118 // a format string must be associated
119 assert(cmds[i].fmt != 0);
120 assert(strlen(cmds[i].fmt) <= MAX_FMTLEN);
121 // format string must match arg specification
122 switch (cmds[i].argsNeeded) {
123 case XsldbgCmdInfo::argNone:
124 assert(strchr(cmds[i].fmt, '%') == 0);
125 break;
126 case XsldbgCmdInfo::argString:
127 perc = strchr(cmds[i].fmt, '%');
128 assert(perc != 0 && perc[1] == 's');
129 assert(strchr(perc + 2, '%') == 0);
130 break;
131 case XsldbgCmdInfo::argNum:
132 perc = strchr(cmds[i].fmt, '%');
133 assert(perc != 0 && perc[1] == 'd');
134 assert(strchr(perc + 2, '%') == 0);
135 break;
136 case XsldbgCmdInfo::argStringNum:
137 perc = strchr(cmds[i].fmt, '%');
138 assert(perc != 0 && perc[1] == 's');
139 perc = strchr(perc + 2, '%');
140 assert(perc != 0 && perc[1] == 'd');
141 assert(strchr(perc + 2, '%') == 0);
142 break;
143 case XsldbgCmdInfo::argNumString:
144 perc = strchr(cmds[i].fmt, '%');
145 assert(perc != 0 && perc[1] == 'd');
146 perc = strchr(perc + 2, '%');
147 assert(perc != 0 && perc[1] == 's');
148 assert(strchr(perc + 2, '%') == 0);
149 break;
150 case XsldbgCmdInfo::argString2:
151 perc = strchr(cmds[i].fmt, '%');
152 assert(perc != 0 && perc[1] == 's');
153 perc = strchr(perc + 2, '%');
154 assert(perc != 0 && perc[1] == 's');
155 assert(strchr(perc + 2, '%') == 0);
156 break;
157 case XsldbgCmdInfo::argNum2:
158 perc = strchr(cmds[i].fmt, '%');
159 assert(perc != 0 && perc[1] == 'd');
160 perc = strchr(perc + 2, '%');
161 assert(perc != 0 && perc[1] == 'd');
162 assert(strchr(perc + 2, '%') == 0);
163 break;
166 #endif
169 XsldbgDriver::~XsldbgDriver()
174 QString
175 XsldbgDriver::driverName() const
177 return "XSLDBG";
180 QString
181 XsldbgDriver::defaultXsldbg()
183 return "xsldbg --lang en --shell --gdb";
186 QString
187 XsldbgDriver::defaultInvocation() const
189 return defaultXsldbg();
192 QStringList XsldbgDriver::boolOptionList() const
194 QStringList allOptions;
195 allOptions.append("verbose");
196 allOptions.append("repeat");
197 allOptions.append("debug");
198 allOptions.append("novalid");
199 allOptions.append("noout");
200 allOptions.append("html");
201 allOptions.append("docbook");
202 allOptions.append("nonet");
203 allOptions.append("catalogs");
204 allOptions.append("xinclude");
205 allOptions.append("profile");
206 return allOptions;
209 bool
210 XsldbgDriver::startup(QString cmdStr)
212 if (!DebuggerDriver::startup(cmdStr))
213 return false;
215 static const char xsldbgInitialize[] = "pwd\nsetoption gdb 2\n"; /* don't need to do anything else */
217 executeCmdString(DCinitialize, xsldbgInitialize, false);
219 return true;
222 void
223 XsldbgDriver::commandFinished(CmdQueueItem * cmd)
226 TRACE(__PRETTY_FUNCTION__);
227 // command string must be committed
228 if (!cmd->m_committed) {
229 // not commited!
230 TRACE("calling " +
231 (__PRETTY_FUNCTION__ +
232 (" with uncommited command:\n\t" + cmd->m_cmdString)));
233 return;
236 /* ok, the command is ready */
237 emit commandReceived(cmd, m_output.constData());
239 switch (cmd->m_cmd) {
240 case DCbt:
241 case DCinfolocals:
242 case DCrun:
243 case DCcont:
244 case DCstep:
245 case DCnext:
246 case DCfinish:{
247 if (!::isErrorExpr(m_output.constData()))
248 parseMarker();
249 else{
250 // This only shows an error for DCinfolocals
251 // need to update KDebugger::handleRunCommand ?
252 KMessageBox::sorry(0L, m_output);
255 break;
257 case DCinfolinemain:
258 if (!m_xslFile.isEmpty())
259 emit activateFileLine(m_xslFile, 0, DbgAddr());
260 break;
262 default:;
267 XsldbgDriver::findPrompt(const QByteArray& output) const
270 * If there's a prompt string in the collected output, it must be at
271 * the very end. We do a quick check whether the last characters of
272 * output are suitable and do the full search only if they are.
274 int len = output.length();
275 if (len < 11 || output[len-1] != ' ' || output[len-2] != '>')
276 return -1;
278 // There can be text between "(xsldbg) " and the "> " at the end
279 // since we do not know what that text is, we accept the former
280 // anywhere in the output.
281 return output.indexOf("(xsldbg) ");
284 void
285 XsldbgDriver::parseMarker()
287 char *p = m_output.data();
289 for (;;) {
290 if ((p == 0) || (*p == '\0')) {
291 m_output.clear();
292 return;
294 if (strncmp(p, "Breakpoint for file ", 20) == 0)
295 break;
296 // try to marker on next line !
297 p = strchr(p, '\n');
298 if ((p != 0) && (*p != '\0'))
299 p++;
303 // extract the marker
304 char *startMarker = p + 20;
306 //TRACE(QString("found marker:") + startMarker);
307 char *endMarker = strchr(startMarker, '\n');
309 if (endMarker == 0)
310 return;
312 *endMarker = '\0';
314 // extract filename and line number
315 static QRegExp MarkerRE(" at line (\\d+)");
317 int lineNoStart = MarkerRE.indexIn(startMarker);
319 if (lineNoStart >= 0) {
320 int lineNo = MarkerRE.cap(1).toInt();
322 DbgAddr address;
324 // now show the window
325 startMarker[lineNoStart-1] = '\0'; /* split off file name */
326 TRACE("Got file and line number");
327 startMarker++;
328 TRACE(QString(startMarker) + ": " + QString::number(lineNo));
329 emit activateFileLine(startMarker, lineNo - 1, address);
335 * Escapes characters that might lead to problems when they appear on gdb's
336 * command line.
338 static void
339 normalizeStringArg(QString & arg)
342 * Remove trailing backslashes. This approach is a little simplistic,
343 * but we know that there is at the moment no case where a trailing
344 * backslash would make sense.
346 while (!arg.isEmpty() && arg[arg.length() - 1] == '\\') {
347 arg = arg.left(arg.length() - 1);
352 QString
353 XsldbgDriver::makeCmdString(DbgCommand cmd, QString strArg)
355 assert(cmd >= 0 && cmd < NUM_CMDS);
356 assert(cmds[cmd].argsNeeded == XsldbgCmdInfo::argString);
358 normalizeStringArg(strArg);
360 if (cmd == DCcd) {
361 // need the working directory when parsing the output
362 m_programWD = strArg;
363 } else if (cmd == DCexecutable) {
364 // want to display the XSL file
365 m_xslFile = strArg;
368 QString cmdString;
369 cmdString.sprintf(cmds[cmd].fmt, strArg.toUtf8().constData());
370 return cmdString;
373 QString
374 XsldbgDriver::makeCmdString(DbgCommand cmd, int intArg)
376 assert(cmd >= 0 && cmd < NUM_CMDS);
377 assert(cmds[cmd].argsNeeded == XsldbgCmdInfo::argNum);
379 QString cmdString;
380 cmdString.sprintf(cmds[cmd].fmt, intArg);
381 return cmdString;
384 QString
385 XsldbgDriver::makeCmdString(DbgCommand cmd, QString strArg, int intArg)
387 assert(cmd >= 0 && cmd < NUM_CMDS);
388 assert(cmds[cmd].argsNeeded == XsldbgCmdInfo::argStringNum ||
389 cmds[cmd].argsNeeded == XsldbgCmdInfo::argNumString ||
390 cmd == DCexamine || cmd == DCtty);
392 normalizeStringArg(strArg);
394 QString cmdString;
396 if (cmd == DCtty) {
398 * intArg specifies which channels should be redirected to
399 * /dev/null. It is a value or'ed together from RDNstdin,
400 * RDNstdout, RDNstderr.
402 static const char *const runRedir[8] = {
404 " </dev/null",
405 " >/dev/null",
406 " </dev/null >/dev/null",
407 " 2>/dev/null",
408 " </dev/null 2>/dev/null",
409 " >/dev/null 2>&1",
410 " </dev/null >/dev/null 2>&1"
413 if (strArg.isEmpty())
414 intArg = 7; /* failsafe if no tty */
415 m_redirect = runRedir[intArg & 7];
417 return makeCmdString(DCtty, strArg); /* note: no problem if strArg empty */
420 if (cmd == DCexamine) {
421 // make a format specifier from the intArg
422 static const char size[16] = {
423 '\0', 'b', 'h', 'w', 'g'
425 static const char format[16] = {
426 '\0', 'x', 'd', 'u', 'o', 't',
427 'a', 'c', 'f', 's', 'i'
430 assert(MDTsizemask == 0xf); /* lowest 4 bits */
431 assert(MDTformatmask == 0xf0); /* next 4 bits */
432 int count = 16; /* number of entities to print */
433 char sizeSpec = size[intArg & MDTsizemask];
434 char formatSpec = format[(intArg & MDTformatmask) >> 4];
436 assert(sizeSpec != '\0');
437 assert(formatSpec != '\0');
438 // adjust count such that 16 lines are printed
439 switch (intArg & MDTformatmask) {
440 case MDTstring:
441 case MDTinsn:
442 break; /* no modification needed */
443 default:
444 // all cases drop through:
445 switch (intArg & MDTsizemask) {
446 case MDTbyte:
447 case MDThalfword:
448 count *= 2;
449 case MDTword:
450 count *= 2;
451 case MDTgiantword:
452 count *= 2;
454 break;
456 QString spec;
458 spec.sprintf("/%d%c%c", count, sizeSpec, formatSpec);
460 return makeCmdString(DCexamine, spec, strArg);
463 if (cmds[cmd].argsNeeded == XsldbgCmdInfo::argStringNum) {
464 // line numbers are zero-based
465 if (cmd == DCuntil || cmd == DCbreakline ||
466 cmd == DCtbreakline || cmd == DCinfoline) {
467 intArg++;
469 if (cmd == DCinfoline) {
470 // must split off file name part
471 strArg = QFileInfo(strArg).fileName();
473 cmdString.sprintf(cmds[cmd].fmt, strArg.toUtf8().constData(), intArg);
474 } else {
475 cmdString.sprintf(cmds[cmd].fmt, intArg, strArg.toUtf8().constData());
477 return cmdString;
480 QString
481 XsldbgDriver::makeCmdString(DbgCommand cmd, QString strArg1,
482 QString strArg2)
484 assert(cmd >= 0 && cmd < NUM_CMDS);
485 assert(cmds[cmd].argsNeeded == XsldbgCmdInfo::argString2);
487 normalizeStringArg(strArg1);
488 normalizeStringArg(strArg2);
490 QString cmdString;
491 cmdString.sprintf(cmds[cmd].fmt,
492 strArg1.toUtf8().constData(),
493 strArg2.toUtf8().constData());
494 return cmdString;
497 QString
498 XsldbgDriver::makeCmdString(DbgCommand cmd, int intArg1, int intArg2)
500 assert(cmd >= 0 && cmd < NUM_CMDS);
501 assert(cmds[cmd].argsNeeded == XsldbgCmdInfo::argNum2);
503 QString cmdString;
504 cmdString.sprintf(cmds[cmd].fmt, intArg1, intArg2);
505 return cmdString;
508 CmdQueueItem *
509 XsldbgDriver::executeCmd(DbgCommand cmd, bool clearLow)
511 assert(cmd >= 0 && cmd < NUM_CMDS);
512 assert(cmds[cmd].argsNeeded == XsldbgCmdInfo::argNone);
514 if (cmd == DCrun) {
515 m_haveCoreFile = false;
518 return executeCmdString(cmd, cmds[cmd].fmt, clearLow);
521 CmdQueueItem *
522 XsldbgDriver::executeCmd(DbgCommand cmd, QString strArg, bool clearLow)
524 return executeCmdString(cmd, makeCmdString(cmd, strArg), clearLow);
527 CmdQueueItem *
528 XsldbgDriver::executeCmd(DbgCommand cmd, int intArg, bool clearLow)
531 return executeCmdString(cmd, makeCmdString(cmd, intArg), clearLow);
534 CmdQueueItem *
535 XsldbgDriver::executeCmd(DbgCommand cmd, QString strArg, int intArg,
536 bool clearLow)
538 return executeCmdString(cmd, makeCmdString(cmd, strArg, intArg),
539 clearLow);
542 CmdQueueItem *
543 XsldbgDriver::executeCmd(DbgCommand cmd, QString strArg1, QString strArg2,
544 bool clearLow)
546 return executeCmdString(cmd, makeCmdString(cmd, strArg1, strArg2),
547 clearLow);
550 CmdQueueItem *
551 XsldbgDriver::executeCmd(DbgCommand cmd, int intArg1, int intArg2,
552 bool clearLow)
554 return executeCmdString(cmd, makeCmdString(cmd, intArg1, intArg2),
555 clearLow);
558 CmdQueueItem *
559 XsldbgDriver::queueCmd(DbgCommand cmd, QueueMode mode)
561 return queueCmdString(cmd, cmds[cmd].fmt, mode);
564 CmdQueueItem *
565 XsldbgDriver::queueCmd(DbgCommand cmd, QString strArg, QueueMode mode)
567 return queueCmdString(cmd, makeCmdString(cmd, strArg), mode);
570 CmdQueueItem *
571 XsldbgDriver::queueCmd(DbgCommand cmd, int intArg, QueueMode mode)
573 return queueCmdString(cmd, makeCmdString(cmd, intArg), mode);
576 CmdQueueItem *
577 XsldbgDriver::queueCmd(DbgCommand cmd, QString strArg, int intArg,
578 QueueMode mode)
580 return queueCmdString(cmd, makeCmdString(cmd, strArg, intArg), mode);
583 CmdQueueItem *
584 XsldbgDriver::queueCmd(DbgCommand cmd, QString strArg1, QString strArg2,
585 QueueMode mode)
587 return queueCmdString(cmd, makeCmdString(cmd, strArg1, strArg2), mode);
590 void
591 XsldbgDriver::terminate()
593 qDebug("XsldbgDriver::Terminate");
594 flushCommands();
595 executeCmdString(DCinitialize, "quit\n", true);
596 ::kill(pid(), SIGTERM);
597 m_state = DSidle;
600 void
601 XsldbgDriver::detachAndTerminate()
603 qDebug("XsldbgDriver::detachAndTerminate");
604 flushCommands();
605 executeCmdString(DCinitialize, "quit\n", true);
606 ::kill(pid(), SIGINT);
609 void
610 XsldbgDriver::interruptInferior()
612 // remove accidentally queued commands
613 qDebug("interruptInferior");
614 flushHiPriQueue();
615 ::kill(pid(), SIGINT);
618 static bool
619 isErrorExpr(const char *output)
621 int wordIndex;
622 bool result = false;
623 #define ERROR_WORD_COUNT 6
624 static const char *errorWords[ERROR_WORD_COUNT] = {
625 "Error:",
626 "error:", // libxslt error
627 "Unknown command",
628 "Warning:",
629 "warning:", // libxslt warning
630 "Information:" // xsldbg information
632 static int errorWordLength[ERROR_WORD_COUNT] = {
633 6, /* Error */
634 6, /* rror */
635 15, /* Unknown command*/
636 8, /* Warning */
637 8, /* warning */
638 12 /* Information */
641 for (wordIndex = 0; wordIndex < ERROR_WORD_COUNT; wordIndex++){
642 // ignore any warnings relating to local variables not being available
643 if (strncmp(output,
644 errorWords[wordIndex],
645 errorWordLength[wordIndex]) == 0 &&
646 (wordIndex == 0 && strstr(output, "try stepping past the xsl:param") == 0) ) {
647 result = true;
648 TRACE(QString("Error/Warning/Information from xsldbg ") + output);
649 break;
653 return result;
657 * Returns true if the output is an error message. If wantErrorValue is
658 * true, a new ExprValue object is created and filled with the error message.
660 static bool
661 parseErrorMessage(const char *output,
662 ExprValue * &variable, bool wantErrorValue)
664 if (isErrorExpr(output)) {
665 if (wantErrorValue) {
666 // put the error message as value in the variable
667 variable = new ExprValue(QString(), VarTree::NKplain);
668 const char *endMsg = strchr(output, '\n');
670 if (endMsg == 0)
671 endMsg = output + strlen(output);
672 variable->m_value = QString::fromLatin1(output, endMsg - output);
673 } else {
674 variable = 0;
676 return true;
678 return false;
682 void
683 XsldbgDriver::setPrintQStringDataCmd(const char* /*cmd*/)
687 ExprValue *
688 XsldbgDriver::parseQCharArray(const char */*output*/, bool /*wantErrorValue*/,
689 bool /*qt3like*/)
691 ExprValue *variable = 0;
693 TRACE("XsldbgDriver::parseQCharArray not implmented");
694 return variable;
697 static ExprValue *
698 parseVar(const char *&s)
700 const char *p = s;
701 ExprValue *variable = 0L;
702 QString name;
704 VarTree::NameKind kind;
706 TRACE(__PRETTY_FUNCTION__);
707 TRACE(p);
709 if (parseErrorMessage(p, variable, false) == true) {
710 TRACE("Found error message");
711 return variable;
714 if (strncmp(p, " Local", 6) == 0) {
715 /* skip " Local" */
716 p = p + 6;
717 TRACE("Found local variable");
718 } else if (strncmp(p, " Global", 7) == 0) {
719 /* skip " Global" */
720 p = p + 7;
721 TRACE("Found global variable");
722 } else if (strncmp(p, "= ", 2) == 0) {
723 /* we're processing the result of a "print command" */
724 /* find next line */
725 const char *nextLine = strchr(p, '\n');
727 TRACE("Found print expr");
728 if (nextLine) {
729 p = p + 2; /* skip the "= " */
730 name = QString::fromLatin1(p, nextLine - p);
731 kind = VarTree::NKplain;
732 p = nextLine + 1;
733 variable = new ExprValue(name, kind);
734 variable->m_varKind = VarTree::VKsimple;
735 parseValue(p, variable);
736 return variable;
738 } else
739 return variable; /* don't know what to do this this data abort!! */
741 // skip whitespace
742 while (isspace(*p))
743 p++;
745 if (*p != '='){
746 // No value provided just a name
747 TRACE(QString("Parse var: name") + p);
748 if (!parseName(p, name, kind)) {
749 return 0;
751 variable = new ExprValue(name, kind);
752 if (variable != 0L) {
753 variable->m_varKind = VarTree::VKsimple;
755 }else{
756 p++;
757 // skip whitespace
758 while (isspace(*p))
759 p++;
760 TRACE(QString("Parse var: name") + p);
761 if (!parseName(p, name, kind)) {
762 return 0;
764 variable = new ExprValue(name, kind);
765 if (variable != 0L) {
766 variable->m_varKind = VarTree::VKsimple;
768 if (*p == '\n')
769 p++;
770 if (!parseValue(p, variable)) {
771 delete variable;
772 return 0;
776 if (*p == '\n')
777 p++;
779 s = p;
780 return variable;
784 inline void
785 skipName(const char *&p)
787 // allow : (for enumeration values) and $ and . (for _vtbl.)
788 while (isalnum(*p) || *p == '_' || *p == ':' || *p == '$' || *p == '.')
789 p++;
792 static bool
793 parseName(const char *&s, QString & name, VarTree::NameKind & kind)
795 /* qDebug(__PRETTY_FUNCTION__); */
796 kind = VarTree::NKplain;
798 const char *p = s;
799 int len = 0;
801 // examples of names:
802 // help_cmd
804 while ((*p != '\n') && (*p != '\0')) {
805 len++;
806 p++;
810 name = QString::fromLatin1(s, len);
811 /* XSL variables will have a $ prefix to be evaluated
812 * properly */
813 //TRACE(QString("parseName got name" ) + name);
815 // return the new position
816 s = p;
817 return true;
820 static bool
821 parseValue(const char *&s, ExprValue * variable)
823 const char *start = s, *end = s;
824 ExprValue * childValue;
825 #define VALUE_END_MARKER_INDEX 0
827 /* This mark the end of a value */
828 static const char *marker[] = {
829 "\032\032", /* value end marker*/
830 "(xsldbg) ",
831 "Breakpoint at", /* stepped to next location */
832 "Breakpoint in", /* reached a set breakpoint */
833 "Reached ", /* reached template */
834 "Error:",
835 "Warning:",
836 "Information:",
837 "runtime error",
838 "xmlXPathEval:",
841 static char valueBuffer[2048];
842 int markerIndex = 0, foundEnd = 0;
843 size_t copySize;
845 if (variable == 0L)
846 return false; /* should never happen but .. */
848 while (start && (*start != '\0')) {
849 /* look for the next marker */
850 for (markerIndex = 0; marker[markerIndex] != 0; markerIndex++) {
851 foundEnd =
852 strncmp(start, marker[markerIndex],
853 strlen(marker[markerIndex])) == 0;
854 if (foundEnd)
855 break;
858 if (foundEnd)
859 break;
862 end = strchr(start, '\n');
863 if (end)
864 copySize = end - start;
865 else
866 copySize = strlen(start);
867 if (copySize >= sizeof(valueBuffer))
868 copySize = sizeof(valueBuffer)-1;
870 strncpy(valueBuffer, start, copySize);
871 valueBuffer[copySize] = '\0';
872 TRACE("Got value :");
873 TRACE(valueBuffer);
874 if ((variable->m_varKind == VarTree::VKsimple)) {
875 if (!variable->m_value.isEmpty()){
876 variable->m_varKind = VarTree::VKarray;
877 childValue = new ExprValue(variable->m_value, VarTree::NKplain);
878 variable->appendChild(childValue);
879 childValue = new ExprValue(valueBuffer, VarTree::NKplain);
880 variable->appendChild(childValue);
881 variable->m_value = "";
882 }else{
883 variable->m_value = valueBuffer;
885 }else{
886 childValue = new ExprValue(valueBuffer, VarTree::NKplain);
887 variable->appendChild(childValue);
890 if (*end =='\n'){
891 start = end + 1;
892 }else{
893 start = end + 1;
894 break;
898 if (foundEnd == 0)
899 TRACE(QString("Unable to find end on value near :") + start);
901 // If we've got something otherthan a end of value marker then
902 // advance to the end of this buffer
903 if (markerIndex != VALUE_END_MARKER_INDEX){
904 while (start && *start != '\0')
905 start++;
906 }else{
907 start = start + strlen(marker[0]);
910 s = start;
912 return true;
917 * Parses a stack frame.
919 static void
920 parseFrameInfo(const char *&s, QString & func,
921 QString & file, int &lineNo, DbgAddr & /*address*/)
923 const char *p = s, *endPos = s + strlen(s);
924 QString lineNoString;
926 TRACE("parseFrameInfo");
928 lineNo = -1;
930 /* skip 'template :\" */
931 p = p + 11;
932 // TRACE(p);
933 func = "";
934 while ((*p != '\"') && (*p != '\0')) {
935 func.append(*p);
936 p++;
938 while ((*p != '\0') && *p != '"')
939 p++;
940 if (*p != '\0')
941 p++;
942 ASSERT(p <= endPos);
943 if (p >= endPos) {
944 /* panic */
945 return;
948 /* skip mode :".*" */
949 while ((*p != '\0') && *p != '"')
950 p++;
951 if (*p != '\0')
952 p++;
953 while ((*p != '\0') && *p != '"')
954 p++;
956 /* skip '" in file ' */
957 p = p + 10;
958 if(*p == '"')
959 p++;
960 // TRACE(p);
961 file = "";
962 while (!isspace(*p) && (*p != '\"') && (*p != '\0')) {
963 file.append(*p);
964 p++;
966 if(*p == '"')
967 p++;
968 ASSERT(p <= endPos);
969 if (p >= endPos) {
970 /* panic */
971 return;
974 // TRACE(p);
975 /* skip ' : line '" */
976 p = p + 9;
977 // TRACE(p);
978 ASSERT(p <= endPos);
979 if (p >= endPos) {
980 /* panic */
981 return;
983 // TRACE(p);
984 if (isdigit(*p)) {
985 /* KDbg uses an offset of +1 for its line numbers */
986 lineNo = atoi(p) - 1;
987 lineNoString = QString::number(lineNo);
989 /* convert func into format needed */
990 func.append(" at ");
991 func.append(file);
992 func.append(':');
993 func.append(lineNoString);
995 /*advance to next line */
996 p = strchr(p, '\n');
997 if (p)
998 p++;
999 s = p;
1003 #undef ISSPACE
1006 * Parses a stack frame including its frame number
1008 static bool
1009 parseFrame(const char *&s, int &frameNo, QString & func,
1010 QString & file, int &lineNo, DbgAddr & address)
1013 // TRACE("XsldbgDriver ::parseFrame");
1014 /* skip leading 'where' or 'frame <frame_no>' */
1015 if ((strncmp(s, "where", 5) == 0) || (strncmp(s, "frame", 5) == 0)) {
1016 s = strchr(s, '\n');
1017 if ((*s != '\0') && (*s != '#'))
1018 s++;
1020 // TRACE(s);
1022 // Example:
1023 //#1 template :"/" in file /home/keith/anon_CVS/xsldbg/docs/en/xsldoc.xsl : line 21
1024 // must start with a hash mark followed by number
1025 if (s[0] != '#' || !isdigit(s[1]))
1026 return false;
1028 //TRACE("XsldbgDriver ::parseFrame got #");
1029 s++; /* skip the hash mark */
1030 // frame number
1031 frameNo = atoi(s);
1032 while (isdigit(*s))
1033 s++;
1035 //TRACE(QString("Got frame ").append(QString::number(frameNo)));
1036 // space
1037 while (isspace(*s))
1038 s++;
1039 parseFrameInfo(s, func, file, lineNo, address);
1040 // TRACE("Will next look at ");
1041 // TRACE(s);
1042 return true;
1045 void
1046 XsldbgDriver::parseBackTrace(const char *output,
1047 std::list < StackFrame > &stack)
1049 QString func, file;
1050 int lineNo, frameNo;
1051 DbgAddr address;
1053 while (::parseFrame(output, frameNo, func, file, lineNo, address)) {
1054 stack.push_back(StackFrame());
1055 StackFrame* frm = &stack.back();
1057 frm->frameNo = frameNo;
1058 frm->fileName = file;
1059 frm->lineNo = lineNo;
1060 frm->address = address;
1061 frm->var = new ExprValue(func, VarTree::NKplain);
1065 bool
1066 XsldbgDriver::parseFrameChange(const char *output, int &frameNo,
1067 QString & file, int &lineNo,
1068 DbgAddr & address)
1070 QString func;
1072 return::parseFrame(output, frameNo, func, file, lineNo, address);
1076 bool
1077 XsldbgDriver::parseBreakList(const char *output,
1078 std::list < Breakpoint > &brks)
1080 TRACE("parseBreakList");
1081 /* skip the first blank line */
1082 const char *p;
1084 // split up a line
1085 Breakpoint bp;
1086 char *dummy;
1087 p = strchr(output, '\n');/* skip the first blank line*/
1089 while ((p != 0) && (*p != '\0')) {
1090 if (*p == '\n')
1091 p++;
1092 QString templateName;
1093 //qDebug("Looking at :%s", p);
1094 if (strncmp(p, " Breakpoint", 11) != 0)
1095 break;
1096 p = p + 11;
1097 if (*p == '\0')
1098 break;
1100 //TRACE(p);
1101 // get Num
1102 bp.id = strtol(p, &dummy, 10); /* don't care about overflows */
1104 p = dummy;
1105 if ((p == 0) || (p[1] == '\0'))
1106 break;
1107 p++;
1109 //TRACE(p);
1110 // Get breakpoint state ie enabled/disabled
1111 if (strncmp(p, "enabled", 7) == 0) {
1112 bp.enabled = true;
1113 p = p + 7;
1114 } else {
1115 if (strncmp(p, "disabled", 8) == 0) {
1116 p = p + 8;
1117 bp.enabled = false;
1118 } else{
1119 TRACE("Parse error in breakpoint list");
1120 TRACE(p);
1121 return false;
1125 //TRACE("Looking for template");
1126 //TRACE(p);
1127 if (strncmp(p, " for template: \"", 16) == 0){
1128 p = p + 16;
1129 //TRACE("Looking for template name near");
1130 //TRACE(p);
1131 /* get the template name */
1132 while (p && (*p != '\0') && (*p != '\"')){
1133 templateName.append(*p);
1134 p++;
1136 if (*p == '\"'){
1137 p++;
1138 }else{
1139 TRACE("Error missed \" near");
1140 TRACE(p);
1144 //TRACE("Looking for mode near");
1145 //TRACE(p);
1146 if (strncmp(p, " mode: \"", 8) == 0){
1147 p = p + 8;
1148 while (p && *p != '\"')
1149 p++;
1150 if (p)
1151 p++;
1154 if (strncmp(p, " in file ", 9) != 0){
1155 TRACE("Parse error in breakpoint list");
1156 TRACE(p);
1157 return false;
1161 /* skip ' in file ' */
1162 p = p + 9;
1163 // TRACE(p);
1165 if (*p == '\"')
1166 p++;
1167 /* grab file name */
1168 QString file;
1169 while ((*p != '\"') && !isspace(*p)) {
1170 file.append(*p);
1171 p++;
1173 if (*p == '\"')
1174 p++;
1175 if (*p == '\0')
1176 break;
1178 /* skip ' : line ' */
1179 p = p + 8;
1180 while (isspace(*p)) {
1181 p++;
1183 //TRACE(p);
1184 QString lineNo;
1185 while (isdigit(*p)) {
1186 lineNo.append(*p);
1187 p++;
1190 // bp.lineNo is zero-based
1191 bp.lineNo = lineNo.toInt() - 1;
1192 bp.location = QString("in %1 at %2:%3").arg(templateName, file, lineNo);
1193 bp.fileName = file;
1194 brks.push_back(bp);
1196 if (p != 0) {
1197 p = strchr(p, '\n');
1198 if (p)
1199 p++;
1202 return true;
1205 std::list<ThreadInfo>
1206 XsldbgDriver::parseThreadList(const char */*output*/)
1208 return std::list<ThreadInfo>();
1211 bool
1212 XsldbgDriver::parseBreakpoint(const char *output, int &id,
1213 QString &file, int &lineNo, QString &address)
1215 // check for errors
1216 if ( strncmp(output, "Error:", 6) == 0) {
1217 return false;
1220 char *dummy;
1221 if (strncmp(output, "Breakpoint ", 11) != 0)
1222 return false;
1224 output += 11;
1225 if (!isdigit(*output))
1226 return false;
1228 // get Num
1229 id = strtol(output, &dummy, 10); /* don't care about overflows */
1230 if (output == dummy)
1231 return false;
1233 // the file name + lineNo will be filled in later from the breakpoint list
1234 file = address = QString();
1235 lineNo = 0;
1236 return true;
1239 void
1240 XsldbgDriver::parseLocals(const char *output, std::list < ExprValue* > &newVars)
1243 /* keep going until error or xsldbg prompt is found */
1244 while (*output != '\0') {
1245 ExprValue *variable = parseVar(output);
1247 if (variable == 0) {
1248 break;
1250 // do not add duplicates
1251 for (std::list<ExprValue*>::iterator o = newVars.begin(); o != newVars.end(); ++o) {
1252 if ((*o)->m_name == variable->m_name) {
1253 delete variable;
1255 goto skipDuplicate;
1258 newVars.push_back(variable);
1259 skipDuplicate:;
1264 ExprValue *
1265 XsldbgDriver::parsePrintExpr(const char *output, bool wantErrorValue)
1267 ExprValue* var = 0;
1268 // check for error conditions
1269 if (!parseErrorMessage(output, var, wantErrorValue)) {
1270 // parse the variable
1271 var = parseVar(output);
1273 return var;
1276 bool
1277 XsldbgDriver::parseChangeWD(const char *output, QString & message)
1279 bool isGood = false;
1281 if (strncmp(output, "Change to directory", 20) == 0) {
1282 output = output + 20; /* skip 'Change to directory' */
1283 message = QString(output).simplified();
1284 if (message.isEmpty()) {
1285 message = i18n("New working directory: ") + m_programWD;
1286 isGood = true;
1289 return isGood;
1292 bool
1293 XsldbgDriver::parseChangeExecutable(const char *output, QString & message)
1295 message = output;
1296 TRACE(QString("XsldbgDriver::parseChangeExecutable :") + output);
1297 m_haveCoreFile = false;
1299 if (strstr(output, "Load of source deferred. Use the run command") != 0) {
1300 TRACE("Parsed stylesheet executable");
1301 message = QString();
1303 return message.isEmpty();
1306 bool
1307 XsldbgDriver::parseCoreFile(const char *output)
1309 TRACE("XsldbgDriver::parseCoreFile");
1310 TRACE(output);
1312 if (strstr(output, "Load of data file deferred. Use the run command") != 0) {
1313 m_haveCoreFile = true;
1314 TRACE("Parsed data file name");
1317 return m_haveCoreFile;
1320 uint
1321 XsldbgDriver::parseProgramStopped(const char *output, QString & message)
1323 /* Not sure about this function leave it here for the moment */
1325 * return DebuggerDriver::SFrefreshBreak & DebuggerDriver::SFprogramActive;
1328 // go through the output, line by line, checking what we have
1329 const char *start = output - 1;
1330 uint flags = SFprogramActive;
1332 message = QString();
1333 do {
1334 start++; /* skip '\n' */
1336 if (strncmp(start, "Finished stylesheet\n\032\032\n", 21) == 0){
1337 // flags &= ~SFprogramActive;
1338 break;
1341 // next line, please
1342 start = strchr(start, '\n');
1343 } while (start != 0);
1345 return flags;
1348 QStringList
1349 XsldbgDriver::parseSharedLibs(const char */*output*/)
1351 return QStringList();
1354 bool
1355 XsldbgDriver::parseFindType(const char */*output*/, QString & /*type*/)
1357 return true;
1360 std::list<RegisterInfo>
1361 XsldbgDriver::parseRegisters(const char */*output*/)
1363 return std::list<RegisterInfo>();
1366 bool
1367 XsldbgDriver::parseInfoLine(const char */*output*/, QString & /*addrFrom*/,
1368 QString & /*addrTo*/)
1370 return false;
1373 std::list<DisassembledCode>
1374 XsldbgDriver::parseDisassemble(const char */*output*/)
1376 return std::list<DisassembledCode>();
1379 QString
1380 XsldbgDriver::parseMemoryDump(const char */*output*/,
1381 std::list < MemoryDump > &/*memdump*/)
1383 return i18n("No memory dump available");
1386 QString
1387 XsldbgDriver::parseSetVariable(const char */*output*/)
1389 QString msg;
1390 return msg;
1394 #include "xsldbgdriver.moc"