Add some tests that involve std::vector.
[kdbg.git] / kdbg / xsldbgdriver.cpp
blob47f2030b059a05b38e482d356fb4c406d9d13f15
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 <qstringlist.h>
10 #include <klocale.h> /* i18n */
11 #include <ctype.h>
12 #include <stdlib.h> /* strtol, atoi */
13 #include <string.h> /* strcpy */
14 #include <kmessagebox.h>
16 #include "assert.h"
17 #ifdef HAVE_CONFIG_H
18 #include "config.h"
19 #endif
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(), m_gdbMajor(2), m_gdbMinor(0)
109 m_promptRE.setPattern("\\(xsldbg\\) .*> ");
110 m_promptMinLen = 11;
111 m_promptLastChar = ' ';
113 m_markerRE.setPattern("^Breakpoint for file ");
114 m_haveDataFile = FALSE;
116 #ifndef NDEBUG
117 // check command info array
118 char *perc;
120 for (int i = 0; i < NUM_CMDS; i++) {
121 // must be indexable by DbgCommand values, i.e. sorted by DbgCommand values
122 assert(i == cmds[i].cmd);
123 // a format string must be associated
124 assert(cmds[i].fmt != 0);
125 assert(strlen(cmds[i].fmt) <= MAX_FMTLEN);
126 // format string must match arg specification
127 switch (cmds[i].argsNeeded) {
128 case XsldbgCmdInfo::argNone:
129 assert(strchr(cmds[i].fmt, '%') == 0);
130 break;
131 case XsldbgCmdInfo::argString:
132 perc = strchr(cmds[i].fmt, '%');
133 assert(perc != 0 && perc[1] == 's');
134 assert(strchr(perc + 2, '%') == 0);
135 break;
136 case XsldbgCmdInfo::argNum:
137 perc = strchr(cmds[i].fmt, '%');
138 assert(perc != 0 && perc[1] == 'd');
139 assert(strchr(perc + 2, '%') == 0);
140 break;
141 case XsldbgCmdInfo::argStringNum:
142 perc = strchr(cmds[i].fmt, '%');
143 assert(perc != 0 && perc[1] == 's');
144 perc = strchr(perc + 2, '%');
145 assert(perc != 0 && perc[1] == 'd');
146 assert(strchr(perc + 2, '%') == 0);
147 break;
148 case XsldbgCmdInfo::argNumString:
149 perc = strchr(cmds[i].fmt, '%');
150 assert(perc != 0 && perc[1] == 'd');
151 perc = strchr(perc + 2, '%');
152 assert(perc != 0 && perc[1] == 's');
153 assert(strchr(perc + 2, '%') == 0);
154 break;
155 case XsldbgCmdInfo::argString2:
156 perc = strchr(cmds[i].fmt, '%');
157 assert(perc != 0 && perc[1] == 's');
158 perc = strchr(perc + 2, '%');
159 assert(perc != 0 && perc[1] == 's');
160 assert(strchr(perc + 2, '%') == 0);
161 break;
162 case XsldbgCmdInfo::argNum2:
163 perc = strchr(cmds[i].fmt, '%');
164 assert(perc != 0 && perc[1] == 'd');
165 perc = strchr(perc + 2, '%');
166 assert(perc != 0 && perc[1] == 'd');
167 assert(strchr(perc + 2, '%') == 0);
168 break;
171 #endif
174 XsldbgDriver::~XsldbgDriver()
179 QString
180 XsldbgDriver::driverName() const
182 return "XSLDBG";
185 QString
186 XsldbgDriver::defaultXsldbg()
188 return "xsldbg --lang en --shell --gdb";
191 QString
192 XsldbgDriver::defaultInvocation() const
194 return defaultXsldbg();
197 QStringList XsldbgDriver::boolOptionList() const
199 QStringList allOptions;
200 allOptions.append("verbose");
201 allOptions.append("repeat");
202 allOptions.append("debug");
203 allOptions.append("novalid");
204 allOptions.append("noout");
205 allOptions.append("html");
206 allOptions.append("docbook");
207 allOptions.append("nonet");
208 allOptions.append("catalogs");
209 allOptions.append("xinclude");
210 allOptions.append("profile");
211 return allOptions;
215 void
216 XsldbgDriver::slotReceiveOutput(KProcess * process, char *buffer,
217 int buflen)
219 //TRACE(buffer);
220 if (m_state != DSidle) {
221 // TRACE(buffer);
222 DebuggerDriver::slotReceiveOutput(process, buffer, buflen);
223 } else {
224 if (strncmp(buffer, "quit", 4) == 0) {
225 TRACE("Ignoring text when xsldbg is quiting");
226 } else {
227 TRACE
228 ("Stray output received by XsldbgDriver::slotReceiveOutput");
229 TRACE(buffer);
234 bool
235 XsldbgDriver::startup(QString cmdStr)
237 if (!DebuggerDriver::startup(cmdStr))
238 return false;
240 static const char xsldbgInitialize[] = "pwd\nsetoption gdb 2\n"; /* don't need to do anything else */
242 executeCmdString(DCinitialize, xsldbgInitialize, false);
244 return true;
247 void
248 XsldbgDriver::commandFinished(CmdQueueItem * cmd)
251 TRACE(__PRETTY_FUNCTION__);
252 // command string must be committed
253 if (!cmd->m_committed) {
254 // not commited!
255 TRACE("calling " +
256 (__PRETTY_FUNCTION__ +
257 (" with uncommited command:\n\t" + cmd->m_cmdString)));
258 return;
261 switch (cmd->m_cmd) {
262 case DCinitialize:
263 // get version number from preamble
265 int len;
266 QRegExp xsldbgVersion("^XSLDBG [0-9]+\\.[0-9]+\\.[0-9]+");
267 int offset = xsldbgVersion.match(m_output, 0, &len);
269 if (offset >= 0) {
270 char *start = m_output + offset + 7; // skip "^XSLDBG "
271 char *end;
273 TRACE("Reading version");
274 TRACE(start);
275 m_gdbMajor = strtol(start, &end, 10);
276 m_gdbMinor = strtol(end + 1, 0, 10); // skip "."
277 if (start == end) {
278 // nothing was parsed
279 m_gdbMajor = 0;
280 m_gdbMinor = 7;
282 } else {
283 // assume some default version (what would make sense?)
284 m_gdbMajor = 0;
285 m_gdbMinor = 7;
287 TRACE(QString("Got version ") +
288 QString::number(m_gdbMajor) + "." +
289 QString::number(m_gdbMinor));
290 break;
292 default:;
294 /* ok, the command is ready */
295 emit commandReceived(cmd, m_output);
297 switch (cmd->m_cmd) {
298 case DCbt:
299 case DCinfolocals:
300 case DCrun:
301 case DCcont:
302 case DCstep:
303 case DCnext:
304 case DCfinish:{
305 if (!::isErrorExpr(m_output))
306 parseMarker();
307 else{
308 // This only shows an error for DCinfolocals
309 // need to update KDebugger::handleRunCommand ?
310 KMessageBox::sorry(0L, m_output);
313 break;
315 case DCinfolinemain:
316 if (!m_xslFile.isEmpty())
317 emit activateFileLine(m_xslFile, 0, DbgAddr());
318 break;
320 default:;
324 void
325 XsldbgDriver::parseMarker()
328 // TRACE("parseMarker : xsldbg");
329 // TRACE(m_output);
330 int len, markerStart = -1;
331 char *p = m_output;
333 while (markerStart == -1) {
334 if ((p == 0) || (*p == '\0')) {
335 m_output[0] = '\0';
336 return;
338 //TRACE(QString("parseMarker is looking at :") + p);
339 markerStart = m_markerRE.match(p, 0, &len);
340 if (markerStart == -1) {
341 // try to marker on next line !
342 p = strchr(p, '\n');
343 if ((p != 0) && (*p != '\0'))
344 p++;
349 // extract the marker
350 char *startMarker = p + markerStart + len;
352 //TRACE(QString("found marker:") + startMarker);
353 char *endMarker = strchr(startMarker, '\n');
355 if (endMarker == 0)
356 return;
358 *endMarker = '\0';
360 // extract filename and line number
361 static QRegExp MarkerRE(" at line [0-9]+");
363 int lineNoStart = MarkerRE.match(startMarker, 0, &len);
365 if (lineNoStart >= 0) {
366 int lineNo = atoi(startMarker + lineNoStart + 8);
368 DbgAddr address;
370 // now show the window
371 startMarker[lineNoStart-1] = '\0'; /* split off file name */
372 TRACE("Got file and line number");
373 startMarker++;
374 TRACE(QString(startMarker) + ": " + QString::number(lineNo));
375 emit activateFileLine(startMarker, lineNo - 1, address);
381 * Escapes characters that might lead to problems when they appear on gdb's
382 * command line.
384 static void
385 normalizeStringArg(QString & arg)
388 * Remove trailing backslashes. This approach is a little simplistic,
389 * but we know that there is at the moment no case where a trailing
390 * backslash would make sense.
392 while (!arg.isEmpty() && arg[arg.length() - 1] == '\\') {
393 arg = arg.left(arg.length() - 1);
398 QString
399 XsldbgDriver::makeCmdString(DbgCommand cmd, QString strArg)
401 assert(cmd >= 0 && cmd < NUM_CMDS);
402 assert(cmds[cmd].argsNeeded == XsldbgCmdInfo::argString);
404 normalizeStringArg(strArg);
406 if (cmd == DCcd) {
407 // need the working directory when parsing the output
408 m_programWD = strArg;
409 } else if (cmd == DCexecutable) {
410 // want to display the XSL file
411 m_xslFile = strArg;
414 QString cmdString;
415 cmdString.sprintf(cmds[cmd].fmt, strArg.latin1());
416 return cmdString;
419 QString
420 XsldbgDriver::makeCmdString(DbgCommand cmd, int intArg)
422 assert(cmd >= 0 && cmd < NUM_CMDS);
423 assert(cmds[cmd].argsNeeded == XsldbgCmdInfo::argNum);
425 QString cmdString;
426 cmdString.sprintf(cmds[cmd].fmt, intArg);
427 return cmdString;
430 QString
431 XsldbgDriver::makeCmdString(DbgCommand cmd, QString strArg, int intArg)
433 assert(cmd >= 0 && cmd < NUM_CMDS);
434 assert(cmds[cmd].argsNeeded == XsldbgCmdInfo::argStringNum ||
435 cmds[cmd].argsNeeded == XsldbgCmdInfo::argNumString ||
436 cmd == DCexamine || cmd == DCtty);
438 normalizeStringArg(strArg);
440 QString cmdString;
442 if (cmd == DCtty) {
444 * intArg specifies which channels should be redirected to
445 * /dev/null. It is a value or'ed together from RDNstdin,
446 * RDNstdout, RDNstderr.
448 static const char *const runRedir[8] = {
450 " </dev/null",
451 " >/dev/null",
452 " </dev/null >/dev/null",
453 " 2>/dev/null",
454 " </dev/null 2>/dev/null",
455 " >/dev/null 2>&1",
456 " </dev/null >/dev/null 2>&1"
459 if (strArg.isEmpty())
460 intArg = 7; /* failsafe if no tty */
461 m_redirect = runRedir[intArg & 7];
463 return makeCmdString(DCtty, strArg); /* note: no problem if strArg empty */
466 if (cmd == DCexamine) {
467 // make a format specifier from the intArg
468 static const char size[16] = {
469 '\0', 'b', 'h', 'w', 'g'
471 static const char format[16] = {
472 '\0', 'x', 'd', 'u', 'o', 't',
473 'a', 'c', 'f', 's', 'i'
476 assert(MDTsizemask == 0xf); /* lowest 4 bits */
477 assert(MDTformatmask == 0xf0); /* next 4 bits */
478 int count = 16; /* number of entities to print */
479 char sizeSpec = size[intArg & MDTsizemask];
480 char formatSpec = format[(intArg & MDTformatmask) >> 4];
482 assert(sizeSpec != '\0');
483 assert(formatSpec != '\0');
484 // adjust count such that 16 lines are printed
485 switch (intArg & MDTformatmask) {
486 case MDTstring:
487 case MDTinsn:
488 break; /* no modification needed */
489 default:
490 // all cases drop through:
491 switch (intArg & MDTsizemask) {
492 case MDTbyte:
493 case MDThalfword:
494 count *= 2;
495 case MDTword:
496 count *= 2;
497 case MDTgiantword:
498 count *= 2;
500 break;
502 QString spec;
504 spec.sprintf("/%d%c%c", count, sizeSpec, formatSpec);
506 return makeCmdString(DCexamine, spec, strArg);
509 if (cmds[cmd].argsNeeded == XsldbgCmdInfo::argStringNum) {
510 // line numbers are zero-based
511 if (cmd == DCuntil || cmd == DCbreakline ||
512 cmd == DCtbreakline || cmd == DCinfoline) {
513 intArg++;
515 if (cmd == DCinfoline) {
516 // must split off file name part
517 int slash = strArg.findRev('/');
519 if (slash >= 0)
520 strArg = strArg.right(strArg.length() - slash - 1);
522 cmdString.sprintf(cmds[cmd].fmt, strArg.latin1(), intArg);
523 } else {
524 cmdString.sprintf(cmds[cmd].fmt, intArg, strArg.latin1());
526 return cmdString;
529 QString
530 XsldbgDriver::makeCmdString(DbgCommand cmd, QString strArg1,
531 QString strArg2)
533 assert(cmd >= 0 && cmd < NUM_CMDS);
534 assert(cmds[cmd].argsNeeded == XsldbgCmdInfo::argString2);
536 normalizeStringArg(strArg1);
537 normalizeStringArg(strArg2);
539 QString cmdString;
540 cmdString.sprintf(cmds[cmd].fmt, strArg1.latin1(), strArg2.latin1());
541 return cmdString;
544 QString
545 XsldbgDriver::makeCmdString(DbgCommand cmd, int intArg1, int intArg2)
547 assert(cmd >= 0 && cmd < NUM_CMDS);
548 assert(cmds[cmd].argsNeeded == XsldbgCmdInfo::argNum2);
550 QString cmdString;
551 cmdString.sprintf(cmds[cmd].fmt, intArg1, intArg2);
552 return cmdString;
555 CmdQueueItem *
556 XsldbgDriver::executeCmd(DbgCommand cmd, bool clearLow)
558 assert(cmd >= 0 && cmd < NUM_CMDS);
559 assert(cmds[cmd].argsNeeded == XsldbgCmdInfo::argNone);
561 if (cmd == DCrun) {
562 m_haveCoreFile = false;
565 return executeCmdString(cmd, cmds[cmd].fmt, clearLow);
568 CmdQueueItem *
569 XsldbgDriver::executeCmd(DbgCommand cmd, QString strArg, bool clearLow)
571 return executeCmdString(cmd, makeCmdString(cmd, strArg), clearLow);
574 CmdQueueItem *
575 XsldbgDriver::executeCmd(DbgCommand cmd, int intArg, bool clearLow)
578 return executeCmdString(cmd, makeCmdString(cmd, intArg), clearLow);
581 CmdQueueItem *
582 XsldbgDriver::executeCmd(DbgCommand cmd, QString strArg, int intArg,
583 bool clearLow)
585 return executeCmdString(cmd, makeCmdString(cmd, strArg, intArg),
586 clearLow);
589 CmdQueueItem *
590 XsldbgDriver::executeCmd(DbgCommand cmd, QString strArg1, QString strArg2,
591 bool clearLow)
593 return executeCmdString(cmd, makeCmdString(cmd, strArg1, strArg2),
594 clearLow);
597 CmdQueueItem *
598 XsldbgDriver::executeCmd(DbgCommand cmd, int intArg1, int intArg2,
599 bool clearLow)
601 return executeCmdString(cmd, makeCmdString(cmd, intArg1, intArg2),
602 clearLow);
605 CmdQueueItem *
606 XsldbgDriver::queueCmd(DbgCommand cmd, QueueMode mode)
608 return queueCmdString(cmd, cmds[cmd].fmt, mode);
611 CmdQueueItem *
612 XsldbgDriver::queueCmd(DbgCommand cmd, QString strArg, QueueMode mode)
614 return queueCmdString(cmd, makeCmdString(cmd, strArg), mode);
617 CmdQueueItem *
618 XsldbgDriver::queueCmd(DbgCommand cmd, int intArg, QueueMode mode)
620 return queueCmdString(cmd, makeCmdString(cmd, intArg), mode);
623 CmdQueueItem *
624 XsldbgDriver::queueCmd(DbgCommand cmd, QString strArg, int intArg,
625 QueueMode mode)
627 return queueCmdString(cmd, makeCmdString(cmd, strArg, intArg), mode);
630 CmdQueueItem *
631 XsldbgDriver::queueCmd(DbgCommand cmd, QString strArg1, QString strArg2,
632 QueueMode mode)
634 return queueCmdString(cmd, makeCmdString(cmd, strArg1, strArg2), mode);
637 void
638 XsldbgDriver::terminate()
640 qDebug("XsldbgDriver::Terminate");
641 flushCommands();
642 executeCmdString(DCinitialize, "quit\n", true);
643 kill(SIGTERM);
644 m_state = DSidle;
647 void
648 XsldbgDriver::detachAndTerminate()
650 qDebug("XsldbgDriver::detachAndTerminate");
651 flushCommands();
652 executeCmdString(DCinitialize, "quit\n", true);
653 kill(SIGINT);
656 void
657 XsldbgDriver::interruptInferior()
659 // remove accidentally queued commands
660 qDebug("interruptInferior");
661 flushHiPriQueue();
662 kill(SIGINT);
665 static bool
666 isErrorExpr(const char *output)
668 int wordIndex;
669 bool result = false;
670 #define ERROR_WORD_COUNT 6
671 static const char *errorWords[ERROR_WORD_COUNT] = {
672 "Error:",
673 "error:", // libxslt error
674 "Unknown command",
675 "Warning:",
676 "warning:", // libxslt warning
677 "Information:" // xsldbg information
679 static int errorWordLength[ERROR_WORD_COUNT] = {
680 6, /* Error */
681 6, /* rror */
682 15, /* Unknown command*/
683 8, /* Warning */
684 8, /* warning */
685 12 /* Information */
688 for (wordIndex = 0; wordIndex < ERROR_WORD_COUNT; wordIndex++){
689 // ignore any warnings relating to local variables not being available
690 if (strncmp(output,
691 errorWords[wordIndex],
692 errorWordLength[wordIndex]) == 0 &&
693 (wordIndex == 0 && strstr(output, "try stepping past the xsl:param") == 0) ) {
694 result = true;
695 TRACE(QString("Error/Warning/Information from xsldbg ") + output);
696 break;
700 return result;
704 * Returns true if the output is an error message. If wantErrorValue is
705 * true, a new ExprValue object is created and filled with the error message.
707 static bool
708 parseErrorMessage(const char *output,
709 ExprValue * &variable, bool wantErrorValue)
711 if (isErrorExpr(output)) {
712 if (wantErrorValue) {
713 // put the error message as value in the variable
714 variable = new ExprValue(QString(), VarTree::NKplain);
715 const char *endMsg = strchr(output, '\n');
717 if (endMsg == 0)
718 endMsg = output + strlen(output);
719 variable->m_value = QString::fromLatin1(output, endMsg - output);
720 } else {
721 variable = 0;
723 return true;
725 return false;
729 void
730 XsldbgDriver::setPrintQStringDataCmd(const char* /*cmd*/)
734 ExprValue *
735 XsldbgDriver::parseQCharArray(const char */*output*/, bool /*wantErrorValue*/,
736 bool /*qt3like*/)
738 ExprValue *variable = 0;
740 TRACE("XsldbgDriver::parseQCharArray not implmented");
741 return variable;
744 static ExprValue *
745 parseVar(const char *&s)
747 const char *p = s;
748 bool foundLocalVar = false;
749 ExprValue *variable = 0L;
750 QString name;
752 VarTree::NameKind kind;
754 TRACE(__PRETTY_FUNCTION__);
755 TRACE(p);
757 if (parseErrorMessage(p, variable, false) == true) {
758 TRACE("Found error message");
759 return variable;
762 if (strncmp(p, " Local", 6) == 0) {
763 foundLocalVar = true;
764 /* skip " Local" */
765 p = p + 6;
766 TRACE("Found local variable");
767 } else if (strncmp(p, " Global", 7) == 0) {
768 /* skip " Global" */
769 p = p + 7;
770 TRACE("Found global variable");
771 } else if (strncmp(p, "= ", 2) == 0) {
772 /* we're processing the result of a "print command" */
773 /* find next line */
774 char *nextLine = strchr(p, '\n');
776 TRACE("Found print expr");
777 if (nextLine) {
778 p = p + 2; /* skip the "= " */
779 name = QString::fromLatin1(p, nextLine - p);
780 kind = VarTree::NKplain;
781 p = nextLine + 1;
782 variable = new ExprValue(name, kind);
783 variable->m_varKind = VarTree::VKsimple;
784 parseValue(p, variable);
785 return variable;
787 } else
788 return variable; /* don't know what to do this this data abort!! */
790 // skip whitespace
791 while (isspace(*p))
792 p++;
794 if (*p != '='){
795 // No value provided just a name
796 TRACE(QString("Parse var: name") + p);
797 if (!parseName(p, name, kind)) {
798 return 0;
800 variable = new ExprValue(name, kind);
801 if (variable != 0L) {
802 variable->m_varKind = VarTree::VKsimple;
804 }else{
805 p++;
806 // skip whitespace
807 while (isspace(*p))
808 p++;
809 TRACE(QString("Parse var: name") + p);
810 if (!parseName(p, name, kind)) {
811 return 0;
813 variable = new ExprValue(name, kind);
814 if (variable != 0L) {
815 variable->m_varKind = VarTree::VKsimple;
817 if (*p == '\n')
818 p++;
819 if (!parseValue(p, variable)) {
820 delete variable;
821 return 0;
825 if (*p == '\n')
826 p++;
828 s = p;
829 return variable;
833 inline void
834 skipName(const char *&p)
836 // allow : (for enumeration values) and $ and . (for _vtbl.)
837 while (isalnum(*p) || *p == '_' || *p == ':' || *p == '$' || *p == '.')
838 p++;
841 static bool
842 parseName(const char *&s, QString & name, VarTree::NameKind & kind)
844 /* qDebug(__PRETTY_FUNCTION__); */
845 kind = VarTree::NKplain;
847 const char *p = s;
848 int len = 0;
850 // examples of names:
851 // help_cmd
853 while ((*p != '\n') && (*p != '\0')) {
854 len++;
855 p++;
859 name = QString::fromLatin1(s, len);
860 /* XSL variables will have a $ prefix to be evaluated
861 * properly */
862 //TRACE(QString("parseName got name" ) + name);
864 // return the new position
865 s = p;
866 return true;
869 static bool
870 parseValue(const char *&s, ExprValue * variable)
872 const char *start = s, *end = s;
873 ExprValue * childValue;
874 #define VALUE_END_MARKER_INDEX 0
876 /* This mark the end of a value */
877 static const char *marker[] = {
878 "\032\032", /* value end marker*/
879 "(xsldbg) ",
880 "Breakpoint at", /* stepped to next location */
881 "Breakpoint in", /* reached a set breakpoint */
882 "Reached ", /* reached template */
883 "Error:",
884 "Warning:",
885 "Information:",
886 "runtime error",
887 "xmlXPathEval:",
890 static char valueBuffer[2048];
891 int markerIndex = 0, foundEnd = 0;
892 size_t copySize;
894 if (variable == 0L)
895 return false; /* should never happen but .. */
897 while (start && (*start != '\0')) {
898 /* look for the next marker */
899 for (markerIndex = 0; marker[markerIndex] != 0; markerIndex++) {
900 foundEnd =
901 strncmp(start, marker[markerIndex],
902 strlen(marker[markerIndex])) == 0;
903 if (foundEnd)
904 break;
907 if (foundEnd)
908 break;
911 end = strchr(start, '\n');
912 if (end)
913 copySize = end - start;
914 else
915 copySize = strlen(start);
916 if (copySize >= sizeof(valueBuffer))
917 copySize = sizeof(valueBuffer)-1;
919 strncpy(valueBuffer, start, copySize);
920 valueBuffer[copySize] = '\0';
921 TRACE("Got value :");
922 TRACE(valueBuffer);
923 if ((variable->m_varKind == VarTree::VKsimple)) {
924 if (!variable->m_value.isEmpty()){
925 variable->m_varKind = VarTree::VKarray;
926 childValue = new ExprValue(variable->m_value, VarTree::NKplain);
927 variable->appendChild(childValue);
928 childValue = new ExprValue(valueBuffer, VarTree::NKplain);
929 variable->appendChild(childValue);
930 variable->m_value = "";
931 }else{
932 variable->m_value = valueBuffer;
934 }else{
935 childValue = new ExprValue(valueBuffer, VarTree::NKplain);
936 variable->appendChild(childValue);
939 if (*end =='\n'){
940 start = end + 1;
941 }else{
942 start = end + 1;
943 break;
947 if (foundEnd == 0)
948 TRACE(QString("Unable to find end on value near :") + start);
950 // If we've got something otherthan a end of value marker then
951 // advance to the end of this buffer
952 if (markerIndex != VALUE_END_MARKER_INDEX){
953 while (start && *start != '\0')
954 start++;
955 }else{
956 start = start + strlen(marker[0]);
959 s = start;
961 return true;
966 * Parses a stack frame.
968 static void
969 parseFrameInfo(const char *&s, QString & func,
970 QString & file, int &lineNo, DbgAddr & /*address*/)
972 const char *p = s, *endPos = s + strlen(s);
973 QString lineNoString;
975 TRACE("parseFrameInfo");
977 lineNo = -1;
979 /* skip 'template :\" */
980 p = p + 11;
981 // TRACE(p);
982 func = "";
983 while ((*p != '\"') && (*p != '\0')) {
984 func.append(*p);
985 p++;
987 while ((*p != '\0') && *p != '"')
988 p++;
989 if (*p != '\0')
990 p++;
991 ASSERT(p <= endPos);
992 if (p >= endPos) {
993 /* panic */
994 return;
997 /* skip mode :".*" */
998 while ((*p != '\0') && *p != '"')
999 p++;
1000 if (*p != '\0')
1001 p++;
1002 while ((*p != '\0') && *p != '"')
1003 p++;
1005 /* skip '" in file ' */
1006 p = p + 10;
1007 if(*p == '"')
1008 p++;
1009 // TRACE(p);
1010 file = "";
1011 while (!isspace(*p) && (*p != '\"') && (*p != '\0')) {
1012 file.append(*p);
1013 p++;
1015 if(*p == '"')
1016 p++;
1017 ASSERT(p <= endPos);
1018 if (p >= endPos) {
1019 /* panic */
1020 return;
1023 // TRACE(p);
1024 /* skip ' : line '" */
1025 p = p + 9;
1026 // TRACE(p);
1027 ASSERT(p <= endPos);
1028 if (p >= endPos) {
1029 /* panic */
1030 return;
1032 // TRACE(p);
1033 if (isdigit(*p)) {
1034 /* KDbg uses an offset of +1 for its line numbers */
1035 lineNo = atoi(p) - 1;
1036 lineNoString = QString::number(lineNo);
1038 /* convert func into format needed */
1039 func.append(" at ");
1040 func.append(file);
1041 func.append(':');
1042 func.append(lineNoString);
1044 /*advance to next line */
1045 p = strchr(p, '\n');
1046 if (p)
1047 p++;
1048 s = p;
1052 #undef ISSPACE
1055 * Parses a stack frame including its frame number
1057 static bool
1058 parseFrame(const char *&s, int &frameNo, QString & func,
1059 QString & file, int &lineNo, DbgAddr & address)
1062 // TRACE("XsldbgDriver ::parseFrame");
1063 /* skip leading 'where' or 'frame <frame_no>' */
1064 if ((strncmp(s, "where", 5) == 0) || (strncmp(s, "frame", 5) == 0)) {
1065 s = strchr(s, '\n');
1066 if ((*s != '\0') && (*s != '#'))
1067 s++;
1069 // TRACE(s);
1071 // Example:
1072 //#1 template :"/" in file /home/keith/anon_CVS/xsldbg/docs/en/xsldoc.xsl : line 21
1073 // must start with a hash mark followed by number
1074 if (s[0] != '#' || !isdigit(s[1]))
1075 return false;
1077 //TRACE("XsldbgDriver ::parseFrame got #");
1078 s++; /* skip the hash mark */
1079 // frame number
1080 frameNo = atoi(s);
1081 while (isdigit(*s))
1082 s++;
1084 //TRACE(QString("Got frame ").append(QString::number(frameNo)));
1085 // space
1086 while (isspace(*s))
1087 s++;
1088 parseFrameInfo(s, func, file, lineNo, address);
1089 // TRACE("Will next look at ");
1090 // TRACE(s);
1091 return true;
1094 void
1095 XsldbgDriver::parseBackTrace(const char *output,
1096 QList < StackFrame > &stack)
1098 QString func, file;
1099 int lineNo, frameNo;
1100 DbgAddr address;
1102 while (::parseFrame(output, frameNo, func, file, lineNo, address)) {
1103 StackFrame *frm = new StackFrame;
1105 frm->frameNo = frameNo;
1106 frm->fileName = file;
1107 frm->lineNo = lineNo;
1108 frm->address = address;
1109 frm->var = new ExprValue(func, VarTree::NKplain);
1110 stack.append(frm);
1114 bool
1115 XsldbgDriver::parseFrameChange(const char *output, int &frameNo,
1116 QString & file, int &lineNo,
1117 DbgAddr & address)
1119 QString func;
1121 return::parseFrame(output, frameNo, func, file, lineNo, address);
1125 bool
1126 XsldbgDriver::parseBreakList(const char *output,
1127 QList < Breakpoint > &brks)
1129 TRACE("parseBreakList");
1130 /* skip the first blank line */
1131 const char *p;
1133 // split up a line
1134 QString location, file, lineNo;
1135 QString address;
1136 QString templateName;
1137 int hits = 0;
1138 int enabled = 0;
1139 uint ignoreCount = 0;
1140 QString condition;
1141 char *dummy;
1142 p = strchr(output, '\n');/* skip the first blank line*/
1144 while ((p != 0) && (*p != '\0')) {
1145 if (*p == '\n')
1146 p++;
1147 templateName = QString();
1148 Breakpoint::Type bpType = Breakpoint::breakpoint;
1149 //qDebug("Looking at :%s", p);
1150 if (strncmp(p, " Breakpoint", 11) != 0)
1151 break;
1152 p = p + 11;
1153 if (*p == '\0')
1154 break;
1156 //TRACE(p);
1157 // get Num
1158 long bpNum = strtol(p, &dummy, 10); /* don't care about overflows */
1160 p = dummy;
1161 if ((p == 0) || (p[1] == '\0'))
1162 break;
1163 p++;
1165 //TRACE(p);
1166 // Get breakpoint state ie enabled/disabled
1167 if (strncmp(p, "enabled", 7) == 0) {
1168 enabled = true;
1169 p = p + 7;
1170 } else {
1171 if (strncmp(p, "disabled", 8) == 0) {
1172 p = p + 8;
1173 enabled = false;
1174 } else{
1175 TRACE("Parse error in breakpoint list");
1176 TRACE(p);
1177 return false;
1181 //TRACE("Looking for template");
1182 //TRACE(p);
1183 if (strncmp(p, " for template: \"", 16) == 0){
1184 p = p + 16;
1185 //TRACE("Looking for template name near");
1186 //TRACE(p);
1187 /* get the template name */
1188 while (p && (*p != '\0') && (*p != '\"')){
1189 templateName.append(*p);
1190 p++;
1192 if (*p == '\"'){
1193 p++;
1194 }else{
1195 TRACE("Error missed \" near");
1196 TRACE(p);
1200 //TRACE("Looking for mode near");
1201 //TRACE(p);
1202 if (strncmp(p, " mode: \"", 8) == 0){
1203 p = p + 8;
1204 while (p && *p != '\"')
1205 p++;
1206 if (p)
1207 p++;
1210 if (strncmp(p, " in file ", 9) != 0){
1211 TRACE("Parse error in breakpoint list");
1212 TRACE(p);
1213 return false;
1217 /* skip ' in file ' */
1218 p = p + 9;
1219 // TRACE(p);
1221 if (*p == '\"')
1222 p++;
1223 /* grab file name */
1224 while ((*p != '\"') && !isspace(*p)) {
1225 file.append(*p);
1226 p++;
1228 if (*p == '\"')
1229 p++;
1230 if (*p == '\0')
1231 break;
1233 /* skip ' : line ' */
1234 p = p + 8;
1235 while (isspace(*p)) {
1236 p++;
1238 //TRACE(p);
1239 while (isdigit(*p)) {
1240 lineNo.append(*p);
1241 p++;
1246 Breakpoint *bp = new Breakpoint;
1247 if (bp != 0) {
1248 // take 1 of line number
1249 lineNo.setNum(lineNo.toInt() -1);
1250 bp->id = bpNum;
1251 bp->type = bpType;
1252 bp->temporary = false;
1253 bp->enabled = enabled;
1254 location.append("in ").append(templateName).append(" at ");
1255 location.append(file).append(":").append(lineNo);
1256 bp->location = location;
1257 bp->fileName = file;
1258 bp->lineNo = lineNo.toInt();
1259 bp->address = address;
1260 bp->hitCount = hits;
1261 bp->ignoreCount = ignoreCount;
1262 bp->condition = condition;
1263 brks.append(bp);
1264 location = "";
1265 lineNo = "";
1266 file = "";
1267 } else
1268 TRACE("Outof memory, breakpoint not created");
1270 if (p != 0) {
1271 p = strchr(p, '\n');
1272 if (p)
1273 p++;
1276 return true;
1279 bool
1280 XsldbgDriver::parseThreadList(const char */*output*/,
1281 QList < ThreadInfo > &/*threads*/)
1283 return true;
1286 bool
1287 XsldbgDriver::parseBreakpoint(const char *output, int &id,
1288 QString &file, int &lineNo, QString &address)
1290 // check for errors
1291 if ( strncmp(output, "Error:", 6) == 0) {
1292 return false;
1295 char *dummy;
1296 if (strncmp(output, "Breakpoint ", 11) != 0)
1297 return false;
1299 output += 11;
1300 if (!isdigit(*output))
1301 return false;
1303 // get Num
1304 id = strtol(output, &dummy, 10); /* don't care about overflows */
1305 if (output == dummy)
1306 return false;
1308 // the file name + lineNo will be filled in later from the breakpoint list
1309 file = address = QString();
1310 lineNo = 0;
1311 return true;
1314 void
1315 XsldbgDriver::parseLocals(const char *output, QList < ExprValue > &newVars)
1318 /* keep going until error or xsldbg prompt is found */
1319 while (*output != '\0') {
1320 ExprValue *variable = parseVar(output);
1322 if (variable == 0) {
1323 break;
1325 // do not add duplicates
1326 for (ExprValue * o = newVars.first(); o != 0; o = newVars.next()) {
1327 if (o->m_name == variable->m_name) {
1328 delete variable;
1330 goto skipDuplicate;
1333 newVars.append(variable);
1334 skipDuplicate:;
1339 ExprValue *
1340 XsldbgDriver::parsePrintExpr(const char *output, bool wantErrorValue)
1342 ExprValue* var = 0;
1343 // check for error conditions
1344 if (!parseErrorMessage(output, var, wantErrorValue)) {
1345 // parse the variable
1346 var = parseVar(output);
1348 return var;
1351 bool
1352 XsldbgDriver::parseChangeWD(const char *output, QString & message)
1354 bool isGood = false;
1356 if (strncmp(output, "Change to directory", 20) == 0) {
1357 output = output + 20; /* skip 'Change to directory' */
1358 message = QString(output).simplifyWhiteSpace();
1359 if (message.isEmpty()) {
1360 message = i18n("New working directory: ") + m_programWD;
1361 isGood = true;
1364 return isGood;
1367 bool
1368 XsldbgDriver::parseChangeExecutable(const char *output, QString & message)
1370 message = output;
1371 TRACE(QString("XsldbgDriver::parseChangeExecutable :") + output);
1372 m_haveCoreFile = false;
1375 * The command is successful if there is no output or the single
1376 * message (no debugging symbols found)...
1378 QRegExp exp(".*Load of source deferred. Use the run command.*");
1379 int len, index = exp.match(output, 0, &len);
1381 if (index != -1) {
1382 TRACE("Parsed stylesheet executable");
1383 message = "";
1385 return (output[0] == '\0') || (index != -1);
1388 bool
1389 XsldbgDriver::parseCoreFile(const char *output)
1391 TRACE("XsldbgDriver::parseCoreFile");
1392 TRACE(output);
1393 QRegExp exp(".*Load of data file deferred. Use the run command.*");
1394 int len, index = exp.match(output, 0, &len);
1396 if (index != -1) {
1397 m_haveCoreFile = true;
1398 TRACE("Parsed data file name");
1401 return m_haveCoreFile;
1404 uint
1405 XsldbgDriver::parseProgramStopped(const char *output, QString & message)
1407 /* Not sure about this function leave it here for the moment */
1409 * return DebuggerDriver::SFrefreshBreak & DebuggerDriver::SFprogramActive;
1412 // go through the output, line by line, checking what we have
1413 const char *start = output - 1;
1414 uint flags = SFprogramActive;
1416 message = QString();
1417 do {
1418 start++; /* skip '\n' */
1420 if (strncmp(start, "Finished stylesheet\n\032\032\n", 21) == 0){
1421 // flags &= ~SFprogramActive;
1422 break;
1425 // next line, please
1426 start = strchr(start, '\n');
1427 } while (start != 0);
1429 return flags;
1432 void
1433 XsldbgDriver::parseSharedLibs(const char */*output*/, QStrList & /*shlibs*/)
1435 /* empty */
1438 bool
1439 XsldbgDriver::parseFindType(const char */*output*/, QString & /*type*/)
1441 return true;
1444 void
1445 XsldbgDriver::parseRegisters(const char */*output*/,
1446 QList < RegisterInfo > &/*regs*/)
1451 bool
1452 XsldbgDriver::parseInfoLine(const char */*output*/, QString & /*addrFrom*/,
1453 QString & /*addrTo*/)
1455 return false;
1458 void
1459 XsldbgDriver::parseDisassemble(const char */*output*/,
1460 QList < DisassembledCode > &/*code*/)
1462 /* empty */
1465 QString
1466 XsldbgDriver::parseMemoryDump(const char */*output*/,
1467 QList < MemoryDump > &/*memdump*/)
1469 return i18n("No memory dump available");
1472 QString
1473 XsldbgDriver::parseSetVariable(const char */*output*/)
1475 QString msg;
1476 return msg;
1480 #include "xsldbgdriver.moc"