Detect when the style sheet has finished its processing.
[kdbg.git] / kdbg / xsldbgdriver.cpp
blob8ef02c197308ac2b81cc35c34502d9b709d3453d
1 // $Id$
3 // Copyright by Johannes Sixt, Keith Isdale
4 // This file is under GPL, the GNU General Public Licence
6 #include "xsldbgdriver.h"
7 #include "exprwnd.h"
8 #include <qstringlist.h>
9 #include <klocale.h> /* i18n */
10 #include <ctype.h>
11 #include <stdlib.h> /* strtol, atoi */
12 #include <string.h> /* strcpy */
13 #include <kmessagebox.h>
15 #include "assert.h"
16 #ifdef HAVE_CONFIG_H
17 #include "config.h"
18 #endif
19 #include "mydebug.h"
22 static VarTree *parseVar(const char *&s);
23 static bool parseName(const char *&s, QString & name,
24 VarTree::NameKind & kind);
25 static bool parseValue(const char *&s, VarTree * variable);
26 static bool isErrorExpr(const char *output);
28 #define TERM_IO_ALLOWED 1
30 // TODO: make this cmd info stuff non-static to allow multiple
31 // simultaneous gdbs to run!
33 struct XsldbgCmdInfo {
34 DbgCommand cmd;
35 const char *fmt; /* format string */
36 enum Args {
37 argNone, argString, argNum,
38 argStringNum, argNumString,
39 argString2, argNum2
40 } argsNeeded;
44 * The following array of commands must be sorted by the DC* values,
45 * because they are used as indices.
47 static XsldbgCmdInfo cmds[] = {
48 {DCinitialize, "init\n", XsldbgCmdInfo::argNone},
49 {DCtty, "tty %s\n", XsldbgCmdInfo::argString},
50 {DCexecutable, "source %s\n", XsldbgCmdInfo::argString}, /* force a restart */
51 {DCtargetremote, "print 'target remote %s'\n", XsldbgCmdInfo::argString},
52 {DCcorefile, "data %s\n", XsldbgCmdInfo::argString}, /* force a restart */
53 {DCattach, "print 'attach %s'\n", XsldbgCmdInfo::argString},
54 {DCinfolinemain, "print 'info main line'\n", XsldbgCmdInfo::argNone},
55 {DCinfolocals, "locals -f\n", XsldbgCmdInfo::argNone},
56 {DCinforegisters, "print 'info reg'\n", XsldbgCmdInfo::argNone},
57 {DCexamine, "print 'x %s %s'\n", XsldbgCmdInfo::argString2},
58 {DCinfoline, "print 'templates %s:%d'\n", XsldbgCmdInfo::argStringNum},
59 {DCdisassemble, "print 'disassemble %s %s'\n", XsldbgCmdInfo::argString2},
60 {DCsetargs, "data %s\n", XsldbgCmdInfo::argString},
61 {DCsetenv, "addparam %s %s\n", XsldbgCmdInfo::argString2},
62 {DCunsetenv, "unset env %s\n", XsldbgCmdInfo::argString},
63 {DCsetoption, "setoption %s %d\n", XsldbgCmdInfo::argStringNum},
64 {DCcd, "chdir %s\n", XsldbgCmdInfo::argString},
65 {DCbt, "where\n", XsldbgCmdInfo::argNone},
66 {DCrun, "run\nsource\n", XsldbgCmdInfo::argNone}, /* Ensure that at the start
67 of executing XSLT we show the XSLT file */
68 {DCcont, "continue\n", XsldbgCmdInfo::argNone},
69 {DCstep, "step\n", XsldbgCmdInfo::argNone},
70 {DCstepi, "step\n", XsldbgCmdInfo::argNone},
71 {DCnext, "next\n", XsldbgCmdInfo::argNone},
72 {DCnexti, "next\n", XsldbgCmdInfo::argNone},
73 {DCfinish, "stepup\n", XsldbgCmdInfo::argNone},
74 {DCuntil, "continue %s:%d\n", XsldbgCmdInfo::argStringNum},
75 {DCkill, "quit\n", XsldbgCmdInfo::argNone},
76 {DCbreaktext, "break %s\n", XsldbgCmdInfo::argString},
77 {DCbreakline, "break -l %s %d\n", XsldbgCmdInfo::argStringNum},
78 {DCtbreakline, "break -l %s %d\n", XsldbgCmdInfo::argStringNum },
79 {DCbreakaddr, "print `break *%s`\n", XsldbgCmdInfo::argString },
80 {DCtbreakaddr, "print `tbreak *%s`\n", XsldbgCmdInfo::argString },
81 {DCwatchpoint, "print 'watch %s'\n", XsldbgCmdInfo::argString},
82 {DCdelete, "delete %d\n", XsldbgCmdInfo::argNum},
83 {DCenable, "enable %d\n", XsldbgCmdInfo::argNum},
84 {DCdisable, "disable %d\n", XsldbgCmdInfo::argNum},
85 {DCprint, "print %s\n", XsldbgCmdInfo::argString},
86 {DCprintStruct, "print 'print %s'\n", XsldbgCmdInfo::argString},
87 {DCprintQStringStruct, "print 'print %s'\n", XsldbgCmdInfo::argString},
88 {DCframe, "frame %d\n", XsldbgCmdInfo::argNum},
89 {DCfindType, "print 'whatis %s'\n", XsldbgCmdInfo::argString},
90 {DCinfosharedlib, "stylesheets\n", XsldbgCmdInfo::argNone},
91 {DCthread, "print 'thread %d'\n", XsldbgCmdInfo::argNum},
92 {DCinfothreads, "print 'info threads'\n", XsldbgCmdInfo::argNone},
93 {DCinfobreak, "show\n", XsldbgCmdInfo::argNone},
94 {DCcondition, "print 'condition %d %s'\n", XsldbgCmdInfo::argNumString},
95 {DCsetpc, "print 'set variable $pc=%s'\n", XsldbgCmdInfo::argString},
96 {DCignore, "print 'ignore %d %d'\n", XsldbgCmdInfo::argNum2},
99 #define NUM_CMDS (int(sizeof(cmds)/sizeof(cmds[0])))
100 #define MAX_FMTLEN 200
102 XsldbgDriver::XsldbgDriver():
103 DebuggerDriver(), m_gdbMajor(2), m_gdbMinor(0)
105 m_promptRE.setPattern("\\(xsldbg\\) .*> $");
106 m_promptMinLen = 11;
107 m_promptLastChar = ' ';
109 m_markerRE.setPattern("^Breakpoint at file ");
110 m_haveDataFile = FALSE;
112 #ifndef NDEBUG
113 // check command info array
114 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 --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;
211 void
212 XsldbgDriver::slotReceiveOutput(KProcess * process, char *buffer,
213 int buflen)
215 //TRACE(buffer);
216 if (m_state != DSidle) {
217 // TRACE(buffer);
218 DebuggerDriver::slotReceiveOutput(process, buffer, buflen);
219 } else {
220 if (strncmp(buffer, "quit", 4) == 0) {
221 TRACE("Ignoring text when xsldbg is quiting");
222 } else {
223 TRACE
224 ("Stray output received by XsldbgDriver::slotReceiveOutput");
225 TRACE(buffer);
230 bool
231 XsldbgDriver::startup(QString cmdStr)
233 if (!DebuggerDriver::startup(cmdStr))
234 return false;
236 static const char xsldbgInitialize[] = "pwd\n"; /* don't need to do anything else */
238 executeCmdString(DCinitialize, xsldbgInitialize, false);
240 return true;
243 void
244 XsldbgDriver::commandFinished(CmdQueueItem * cmd)
247 TRACE(__PRETTY_FUNCTION__);
248 // command string must be committed
249 if (!cmd->m_committed) {
250 // not commited!
251 TRACE("calling " +
252 (__PRETTY_FUNCTION__ +
253 (" with uncommited command:\n\t" + cmd->m_cmdString)));
254 return;
257 switch (cmd->m_cmd) {
258 case DCinitialize:
259 // get version number from preamble
261 int len;
262 QRegExp xsldbgVersion("^XSLDBG [0-9]+\\.[0-9]+\\.[0-9]+");
263 int offset = xsldbgVersion.match(m_output, 0, &len);
265 if (offset >= 0) {
266 char *start = m_output + offset + 7; // skip "^XSLDBG "
267 char *end;
269 TRACE("Reading version");
270 TRACE(start);
271 m_gdbMajor = strtol(start, &end, 10);
272 m_gdbMinor = strtol(end + 1, 0, 10); // skip "."
273 if (start == end) {
274 // nothing was parsed
275 m_gdbMajor = 0;
276 m_gdbMinor = 7;
278 } else {
279 // assume some default version (what would make sense?)
280 m_gdbMajor = 0;
281 m_gdbMinor = 7;
283 TRACE(QString("Got version ") +
284 QString::number(m_gdbMajor) + "." +
285 QString::number(m_gdbMinor));
286 break;
288 default:;
290 /* ok, the command is ready */
291 emit commandReceived(cmd, m_output);
293 switch (cmd->m_cmd) {
294 case DCbt:
295 case DCinfolocals:
296 case DCrun:
297 case DCcont:
298 case DCstep:
299 case DCnext:
300 case DCfinish:{
301 if (!::isErrorExpr(m_output))
302 parseMarker();
303 else{
304 // This only shows an error for DCinfolocals
305 // need to update KDebugger::handleRunCommand ?
306 KMessageBox::sorry(0L, m_output);
309 break;
311 case DCinfolinemain:
312 if (!m_xslFile.isEmpty())
313 emit activateFileLine(m_xslFile, 0, DbgAddr());
314 break;
316 default:;
320 void
321 XsldbgDriver::parseMarker()
324 // TRACE("parseMarker : xsldbg");
325 // TRACE(m_output);
326 int len, markerStart = -1;
327 char *p = m_output;
329 while (markerStart == -1) {
330 if ((p == 0) || (*p == '\0')) {
331 m_output[0] = '\0';
332 return;
334 //TRACE(QString("parseMarker is looking at :") + p);
335 markerStart = m_markerRE.match(p, 0, &len);
336 if (markerStart == -1) {
337 // try to marker on next line !
338 p = strchr(p, '\n');
339 if ((p != 0) && (*p != '\0'))
340 p++;
345 // extract the marker
346 char *startMarker = p + markerStart + len;
348 //TRACE(QString("found marker:") + startMarker);
349 char *endMarker = strchr(startMarker, '\n');
351 if (endMarker == 0)
352 return;
354 *endMarker = '\0';
356 // extract filename and line number
357 static QRegExp MarkerRE(" : line [0-9]+");
359 int lineNoStart = MarkerRE.match(startMarker, 0, &len);
361 if (lineNoStart >= 0) {
362 int lineNo = atoi(startMarker + lineNoStart + 8);
364 DbgAddr address;
366 // now show the window
367 startMarker[lineNoStart] = '\0'; /* split off file name */
368 TRACE("Got file and line number");
369 TRACE(QString(startMarker) + ": " + QString::number(lineNo));
370 emit activateFileLine(startMarker, lineNo - 1, address);
376 * Escapes characters that might lead to problems when they appear on gdb's
377 * command line.
379 static void
380 normalizeStringArg(QString & arg)
383 * Remove trailing backslashes. This approach is a little simplistic,
384 * but we know that there is at the moment no case where a trailing
385 * backslash would make sense.
387 while (!arg.isEmpty() && arg[arg.length() - 1] == '\\') {
388 arg = arg.left(arg.length() - 1);
393 QString
394 XsldbgDriver::makeCmdString(DbgCommand cmd, QString strArg)
396 assert(cmd >= 0 && cmd < NUM_CMDS);
397 assert(cmds[cmd].argsNeeded == XsldbgCmdInfo::argString);
399 normalizeStringArg(strArg);
401 if (cmd == DCcd) {
402 // need the working directory when parsing the output
403 m_programWD = strArg;
404 } else if (cmd == DCexecutable) {
405 // want to display the XSL file
406 m_xslFile = strArg;
409 SIZED_QString(cmdString, MAX_FMTLEN + strArg.length());
410 cmdString.sprintf(cmds[cmd].fmt, strArg.latin1());
411 return cmdString;
414 QString
415 XsldbgDriver::makeCmdString(DbgCommand cmd, int intArg)
417 assert(cmd >= 0 && cmd < NUM_CMDS);
418 assert(cmds[cmd].argsNeeded == XsldbgCmdInfo::argNum);
420 SIZED_QString(cmdString, MAX_FMTLEN + 30);
422 cmdString.sprintf(cmds[cmd].fmt, intArg);
423 return cmdString;
426 QString
427 XsldbgDriver::makeCmdString(DbgCommand cmd, QString strArg, int intArg)
429 assert(cmd >= 0 && cmd < NUM_CMDS);
430 assert(cmds[cmd].argsNeeded == XsldbgCmdInfo::argStringNum ||
431 cmds[cmd].argsNeeded == XsldbgCmdInfo::argNumString ||
432 cmd == DCexamine || cmd == DCtty);
434 normalizeStringArg(strArg);
436 SIZED_QString(cmdString, MAX_FMTLEN + 30 + strArg.length());
438 if (cmd == DCtty) {
440 * intArg specifies which channels should be redirected to
441 * /dev/null. It is a value or'ed together from RDNstdin,
442 * RDNstdout, RDNstderr.
444 static const char *const runRedir[8] = {
446 " </dev/null",
447 " >/dev/null",
448 " </dev/null >/dev/null",
449 " 2>/dev/null",
450 " </dev/null 2>/dev/null",
451 " >/dev/null 2>&1",
452 " </dev/null >/dev/null 2>&1"
455 if (strArg.isEmpty())
456 intArg = 7; /* failsafe if no tty */
457 m_redirect = runRedir[intArg & 7];
459 return makeCmdString(DCtty, strArg); /* note: no problem if strArg empty */
462 if (cmd == DCexamine) {
463 // make a format specifier from the intArg
464 static const char size[16] = {
465 '\0', 'b', 'h', 'w', 'g'
467 static const char format[16] = {
468 '\0', 'x', 'd', 'u', 'o', 't',
469 'a', 'c', 'f', 's', 'i'
472 assert(MDTsizemask == 0xf); /* lowest 4 bits */
473 assert(MDTformatmask == 0xf0); /* next 4 bits */
474 int count = 16; /* number of entities to print */
475 char sizeSpec = size[intArg & MDTsizemask];
476 char formatSpec = format[(intArg & MDTformatmask) >> 4];
478 assert(sizeSpec != '\0');
479 assert(formatSpec != '\0');
480 // adjust count such that 16 lines are printed
481 switch (intArg & MDTformatmask) {
482 case MDTstring:
483 case MDTinsn:
484 break; /* no modification needed */
485 default:
486 // all cases drop through:
487 switch (intArg & MDTsizemask) {
488 case MDTbyte:
489 case MDThalfword:
490 count *= 2;
491 case MDTword:
492 count *= 2;
493 case MDTgiantword:
494 count *= 2;
496 break;
498 QString spec;
500 spec.sprintf("/%d%c%c", count, sizeSpec, formatSpec);
502 return makeCmdString(DCexamine, spec, strArg);
505 if (cmds[cmd].argsNeeded == XsldbgCmdInfo::argStringNum) {
506 // line numbers are zero-based
507 if (cmd == DCuntil || cmd == DCbreakline ||
508 cmd == DCtbreakline || cmd == DCinfoline) {
509 intArg++;
511 if (cmd == DCinfoline) {
512 // must split off file name part
513 int slash = strArg.findRev('/');
515 if (slash >= 0)
516 strArg = strArg.right(strArg.length() - slash - 1);
518 cmdString.sprintf(cmds[cmd].fmt, strArg.latin1(), intArg);
519 } else {
520 cmdString.sprintf(cmds[cmd].fmt, intArg, strArg.latin1());
522 return cmdString;
525 QString
526 XsldbgDriver::makeCmdString(DbgCommand cmd, QString strArg1,
527 QString strArg2)
529 assert(cmd >= 0 && cmd < NUM_CMDS);
530 assert(cmds[cmd].argsNeeded == XsldbgCmdInfo::argString2);
532 normalizeStringArg(strArg1);
533 normalizeStringArg(strArg2);
535 SIZED_QString(cmdString,
536 MAX_FMTLEN + strArg1.length() + strArg2.length());
537 cmdString.sprintf(cmds[cmd].fmt, strArg1.latin1(), strArg2.latin1());
538 return cmdString;
541 QString
542 XsldbgDriver::makeCmdString(DbgCommand cmd, int intArg1, int intArg2)
544 assert(cmd >= 0 && cmd < NUM_CMDS);
545 assert(cmds[cmd].argsNeeded == XsldbgCmdInfo::argNum2);
547 SIZED_QString(cmdString, MAX_FMTLEN + 60);
548 cmdString.sprintf(cmds[cmd].fmt, intArg1, intArg2);
549 return cmdString;
552 CmdQueueItem *
553 XsldbgDriver::executeCmd(DbgCommand cmd, bool clearLow)
555 assert(cmd >= 0 && cmd < NUM_CMDS);
556 assert(cmds[cmd].argsNeeded == XsldbgCmdInfo::argNone);
558 if (cmd == DCrun) {
559 m_haveCoreFile = false;
562 return executeCmdString(cmd, cmds[cmd].fmt, clearLow);
565 CmdQueueItem *
566 XsldbgDriver::executeCmd(DbgCommand cmd, QString strArg, bool clearLow)
568 return executeCmdString(cmd, makeCmdString(cmd, strArg), clearLow);
571 CmdQueueItem *
572 XsldbgDriver::executeCmd(DbgCommand cmd, int intArg, bool clearLow)
575 return executeCmdString(cmd, makeCmdString(cmd, intArg), clearLow);
578 CmdQueueItem *
579 XsldbgDriver::executeCmd(DbgCommand cmd, QString strArg, int intArg,
580 bool clearLow)
582 return executeCmdString(cmd, makeCmdString(cmd, strArg, intArg),
583 clearLow);
586 CmdQueueItem *
587 XsldbgDriver::executeCmd(DbgCommand cmd, QString strArg1, QString strArg2,
588 bool clearLow)
590 return executeCmdString(cmd, makeCmdString(cmd, strArg1, strArg2),
591 clearLow);
594 CmdQueueItem *
595 XsldbgDriver::executeCmd(DbgCommand cmd, int intArg1, int intArg2,
596 bool clearLow)
598 return executeCmdString(cmd, makeCmdString(cmd, intArg1, intArg2),
599 clearLow);
602 CmdQueueItem *
603 XsldbgDriver::queueCmd(DbgCommand cmd, QueueMode mode)
605 return queueCmdString(cmd, cmds[cmd].fmt, mode);
608 CmdQueueItem *
609 XsldbgDriver::queueCmd(DbgCommand cmd, QString strArg, QueueMode mode)
611 return queueCmdString(cmd, makeCmdString(cmd, strArg), mode);
614 CmdQueueItem *
615 XsldbgDriver::queueCmd(DbgCommand cmd, int intArg, QueueMode mode)
617 return queueCmdString(cmd, makeCmdString(cmd, intArg), mode);
620 CmdQueueItem *
621 XsldbgDriver::queueCmd(DbgCommand cmd, QString strArg, int intArg,
622 QueueMode mode)
624 return queueCmdString(cmd, makeCmdString(cmd, strArg, intArg), mode);
627 CmdQueueItem *
628 XsldbgDriver::queueCmd(DbgCommand cmd, QString strArg1, QString strArg2,
629 QueueMode mode)
631 return queueCmdString(cmd, makeCmdString(cmd, strArg1, strArg2), mode);
634 void
635 XsldbgDriver::terminate()
637 qDebug("XsldbgDriver::Terminate");
638 flushCommands();
639 executeCmdString(DCinitialize, "quit\n", true);
640 kill(SIGTERM);
641 m_state = DSidle;
644 void
645 XsldbgDriver::detachAndTerminate()
647 qDebug("XsldbgDriver::detachAndTerminate");
648 flushCommands();
649 executeCmdString(DCinitialize, "quit\n", true);
650 kill(SIGINT);
653 void
654 XsldbgDriver::interruptInferior()
656 // remove accidentally queued commands
657 qDebug("interruptInferior");
658 flushHiPriQueue();
659 kill(SIGINT);
662 static bool
663 isErrorExpr(const char *output)
665 int wordIndex;
666 bool result = false;
667 #define ERROR_WORD_COUNT 5
668 static const char *errorWords[ERROR_WORD_COUNT] = {
669 "Error:",
670 "error:", // libxslt error
671 "Unknown command",
672 "Warning:",
673 "warning:" // libxslt warning
675 static int errorWordLength[ERROR_WORD_COUNT] = {
676 6, /* Error */
677 6, /* rror */
678 15, /* Unknown command*/
679 8, /* Warning */
680 8 /* warning */
683 for (wordIndex = 0; wordIndex < ERROR_WORD_COUNT; wordIndex++){
684 if (strncmp(output,
685 errorWords[wordIndex],
686 errorWordLength[wordIndex]) == 0){
687 result = true;
688 TRACE(QString("Error/Warning from xsldbg ") + output);
689 break;
693 return result;
697 * Returns true if the output is an error message. If wantErrorValue is
698 * true, a new VarTree object is created and filled with the error message.
700 static bool
701 parseErrorMessage(const char *output,
702 VarTree * &variable, bool wantErrorValue)
704 if (isErrorExpr(output)) {
705 if (wantErrorValue) {
706 // put the error message as value in the variable
707 variable = new VarTree(QString(), VarTree::NKplain);
708 const char *endMsg = strchr(output, '\n');
710 if (endMsg == 0)
711 endMsg = output + strlen(output);
712 variable->m_value = FROM_LATIN1(output, endMsg - output);
713 } else {
714 variable = 0;
716 return true;
718 return false;
722 VarTree *
723 XsldbgDriver::parseQCharArray(const char */*output*/, bool /*wantErrorValue*/,
724 bool /*qt3like*/)
726 VarTree *variable = 0;
728 TRACE("XsldbgDriver::parseQCharArray not implmented");
729 return variable;
732 static VarTree *
733 parseVar(const char *&s)
735 const char *p = s;
736 bool foundLocalVar = false;
737 VarTree *variable = 0L;
738 QString name;
740 VarTree::NameKind kind;
742 TRACE(__PRETTY_FUNCTION__);
743 TRACE(p);
745 if (parseErrorMessage(p, variable, false) == true) {
746 TRACE("Found error message");
747 return variable;
750 if (strncmp(p, " Local", 6) == 0) {
751 foundLocalVar = true;
752 /* skip " Local" */
753 p = p + 6;
754 TRACE("Found local variable");
755 } else if (strncmp(p, " Global", 7) == 0) {
756 /* skip " Global" */
757 p = p + 7;
758 TRACE("Found global variable");
759 } else if (strncmp(p, "= ", 2) == 0) {
760 /* we're processing the result of a "print command" */
761 /* find next line */
762 char *nextLine = strchr(p, '\n');
764 TRACE("Found print expr");
765 if (nextLine) {
766 char nameBuffer[100];
768 p = p + 2; /* skip the "= " */
769 strncpy(nameBuffer, p, nextLine - p);
770 p = nextLine + 1;
771 variable = new VarTree(nameBuffer, kind);
772 if (variable != 0L) {
773 variable->setDeleteChildren(true);
774 parseValue(p, variable);
776 return variable;
778 } else
779 return variable; /* don't know what to do this this data abort!! */
781 // skip whitespace
782 while (isspace(*p))
783 p++;
785 if (*p != '='){
786 // No value provided just a name
787 TRACE(QString("Parse var: name") + p);
788 if (!parseName(p, name, kind)) {
789 return 0;
791 variable = new VarTree(name, kind);
792 if (variable != 0L) {
793 variable->setDeleteChildren(true);
795 }else{
796 p++;
797 // skip whitespace
798 while (isspace(*p))
799 p++;
800 TRACE(QString("Parse var: name") + p);
801 if (!parseName(p, name, kind)) {
802 return 0;
804 variable = new VarTree(name, kind);
805 if (variable != 0L) {
806 variable->setDeleteChildren(true);
808 if (*p == '\n')
809 p++;
810 if (!parseValue(p, variable)) {
811 delete variable;
812 return 0;
816 if (*p == '\n')
817 p++;
819 s = p;
820 return variable;
824 inline void
825 skipName(const char *&p)
827 // allow : (for enumeration values) and $ and . (for _vtbl.)
828 while (isalnum(*p) || *p == '_' || *p == ':' || *p == '$' || *p == '.')
829 p++;
832 static bool
833 parseName(const char *&s, QString & name, VarTree::NameKind & kind)
835 /* qDebug(__PRETTY_FUNCTION__); */
836 kind = VarTree::NKplain;
838 const char *p = s;
839 int len = 0;
841 // examples of names:
842 // help_cmd
844 while ((*p != '\n') && (*p != '\0')) {
845 len++;
846 p++;
850 name = FROM_LATIN1(s, len);
851 /* XSL variables will have a $ prefix to be evaluated
852 * properly */
853 //TRACE(QString("parseName got name" ) + name);
855 // return the new position
856 s = p;
857 return true;
860 static bool
861 parseValue(const char *&s, VarTree * variable)
863 const char *start = s, *end = s;
864 VarTree * childValue;
865 #define VALUE_END_MARKER_INDEX 0
867 /* This mark the end of a value */
868 static const char *marker[] = {
869 "\032\032", /* value end marker*/
870 "(xsldbg) ",
871 "Breakpoint at", /* stepped to next location */
872 "Breakpoint in", /* reached a set breakpoint */
873 "Reached ", /* reached template */
874 "Error:",
875 "Warning:",
876 "runtime error",
877 "xmlXPathEval:",
880 static char valueBuffer[255];
881 int markerIndex = 0, foundEnd = 0;
882 size_t copySize;
884 if (variable == 0L)
885 return false; /* should never happen but .. */
887 variable->m_value = "";
888 while (start && (*start != '\0')) {
889 /* look for the next marker */
890 for (markerIndex = 0; marker[markerIndex] != 0; markerIndex++) {
891 foundEnd =
892 strncmp(start, marker[markerIndex],
893 strlen(marker[markerIndex])) == 0;
894 if (foundEnd)
895 break;
898 if (foundEnd)
899 break;
902 end = strchr(start, '\n');
903 if (end) {
904 end++;
906 copySize = end - start - 1;
907 if (copySize > sizeof(valueBuffer))
908 copySize = sizeof(valueBuffer);
910 strncpy(valueBuffer, start, copySize);
911 valueBuffer[copySize] = '\0';
912 TRACE("Got value :");
913 TRACE(valueBuffer);
914 childValue = new VarTree(valueBuffer, VarTree::NKplain);
915 variable->appendChild(childValue);
917 start = end;
918 } else {
919 childValue = new VarTree(start, VarTree::NKplain);
920 variable->appendChild(childValue);
921 break;
925 if (foundEnd == 0)
926 TRACE(QString("Unable to find end on value near :") + start);
928 // If we've got something otherthan a end of value marker then
929 // advance to the end of this buffer
930 if (markerIndex != VALUE_END_MARKER_INDEX){
931 while (start && *start != '\0')
932 start++;
933 }else{
934 start = start + strlen(marker[0]);
937 s = start;
939 return true;
944 * Parses a stack frame.
946 static void
947 parseFrameInfo(const char *&s, QString & func,
948 QString & file, int &lineNo, DbgAddr & /*address*/)
950 const char *p = s, *endPos = s + strlen(s);
951 QString lineNoString;
953 lineNo = -1;
955 /* skip 'template :\" */
956 p = p + 11;
957 //TRACE("parseFrameInfo");
958 // TRACE(p);
959 func = "";
960 while ((*p != '\"') && (*p != '\0')) {
961 func.append(*p);
962 p++;
964 ASSERT(p <= endPos);
965 if (p >= endPos) {
966 /* panic */
967 return;
970 /* skip mode :".*" */
971 while (p && *p != '"')
972 p++;
973 if (p)
974 p++;
975 while (p && *p != '"')
976 p++;
977 if (p)
978 p++;
979 while (p && *p != '"')
980 p++;
981 /* skip '" in file ' */
982 p = p + 10;
983 // TRACE(p);
984 file = "";
985 while (!isspace(*p) && (*p != '\0')) {
986 file.append(*p);
987 p++;
989 ASSERT(p <= endPos);
990 if (p >= endPos) {
991 /* panic */
992 return;
994 // TRACE(p);
995 /* skip ' : line '" */
996 p = p + 8;
997 // TRACE(p);
998 ASSERT(p <= endPos);
999 if (p >= endPos) {
1000 /* panic */
1001 return;
1003 if (isdigit(*p)) {
1004 /* KDbg uses an offset of +1 for its line numbers */
1005 lineNo = atoi(p) - 1;
1006 lineNoString = QString::number(lineNo);
1008 /* convert func into format needed */
1009 func.append(" at ");
1010 func.append(file);
1011 func.append(':');
1012 func.append(lineNoString);
1014 //TRACE(QString("Got frame : template :\"") + func + "\"file:" +
1015 // file + " line : " + lineNoString);
1017 /*advance to next line */
1018 p = strchr(p, '\n');
1019 if (p)
1020 p++;
1021 s = p;
1025 #undef ISSPACE
1028 * Parses a stack frame including its frame number
1030 static bool
1031 parseFrame(const char *&s, int &frameNo, QString & func,
1032 QString & file, int &lineNo, DbgAddr & address)
1035 // TRACE("XsldbgDriver ::parseFrame");
1036 /* skip leading 'where' or 'frame <frame_no>' */
1037 if ((strncmp(s, "where", 5) == 0) || (strncmp(s, "frame", 5) == 0)) {
1038 s = strchr(s, '\n');
1039 if ((*s != '\0') && (*s != '#'))
1040 s++;
1042 // TRACE(s);
1044 // Example:
1045 //#1 template :"/" in file /home/keith/anon_CVS/xsldbg/docs/en/xsldoc.xsl : line 21
1046 // must start with a hash mark followed by number
1047 if (s[0] != '#' || !isdigit(s[1]))
1048 return false;
1050 //TRACE("XsldbgDriver ::parseFrame got #");
1051 s++; /* skip the hash mark */
1052 // frame number
1053 frameNo = atoi(s);
1054 while (isdigit(*s))
1055 s++;
1057 //TRACE(QString("Got frame ").append(QString::number(frameNo)));
1058 // space
1059 while (isspace(*s))
1060 s++;
1061 parseFrameInfo(s, func, file, lineNo, address);
1062 // TRACE("Will next look at ");
1063 // TRACE(s);
1064 return true;
1067 void
1068 XsldbgDriver::parseBackTrace(const char *output,
1069 QList < StackFrame > &stack)
1071 QString func, file;
1072 int lineNo, frameNo;
1073 DbgAddr address;
1075 while (::parseFrame(output, frameNo, func, file, lineNo, address)) {
1076 StackFrame *frm = new StackFrame;
1078 frm->frameNo = frameNo;
1079 frm->fileName = file;
1080 frm->lineNo = lineNo;
1081 frm->address = address;
1082 frm->var = new VarTree(func, VarTree::NKplain);
1083 stack.append(frm);
1087 bool
1088 XsldbgDriver::parseFrameChange(const char *output, int &frameNo,
1089 QString & file, int &lineNo,
1090 DbgAddr & address)
1092 QString func;
1094 return::parseFrame(output, frameNo, func, file, lineNo, address);
1098 bool
1099 XsldbgDriver::parseBreakList(const char *output,
1100 QList < Breakpoint > &brks)
1102 TRACE("parseBreakList");
1103 /* skip the first blank line */
1104 const char *p;
1106 // split up a line
1107 QString location, file, lineNo;
1108 QString address;
1109 QString templateName;
1110 int hits = 0;
1111 int enabled = 0;
1112 uint ignoreCount = 0;
1113 QString condition;
1114 char *dummy;
1115 p = strchr(output, '\n');/* skip the first blank line*/
1117 while ((p != 0) && (*p != '\0')) {
1118 templateName = QString();
1119 p++;
1120 Breakpoint::Type bpType = Breakpoint::breakpoint;
1121 //qDebug("Looking at :%s", p);
1122 if (strncmp(p, " Breakpoint", 11) != 0)
1123 break;
1124 p = p + 11;
1125 if (*p == '\0')
1126 break;
1128 //TRACE(p);
1129 // get Num
1130 long bpNum = strtol(p, &dummy, 10); /* don't care about overflows */
1132 p = dummy;
1133 if ((p == 0) || (p[1] == '\0'))
1134 break;
1135 p++;
1137 //TRACE(p);
1138 // Get breakpoint state ie enabled/disabled
1139 if (strncmp(p, "enabled", 7) == 0) {
1140 enabled = true;
1141 p = p + 7;
1142 } else {
1143 if (strncmp(p, "disabled", 8) == 0) {
1144 p = p + 8;
1145 enabled = false;
1146 } else{
1147 TRACE("Parse error in breakpoint list");
1148 TRACE(p);
1149 return false;
1153 //TRACE("Looking for template");
1154 //TRACE(p);
1156 /* check for ' for template :"'*/
1157 if (strncmp(p, " for template :\"", 16) == 0){
1158 p = p + 16;
1159 //TRACE("Looking for template name near");
1160 //TRACE(p);
1161 /* get the template name */
1162 while (p && (*p != '\0') && (*p != '\"')){
1163 templateName.append(*p);
1164 p++;
1166 if (*p == '\"'){
1167 p++;
1168 }else{
1169 TRACE("Error missed \" near");
1170 TRACE(p);
1174 //TRACE("Looking for mode near");
1175 //TRACE(p);
1176 if (strncmp(p, " mode :\"", 8) == 0) {
1177 p = p + 8;
1178 while (p && *p != '\"')
1179 p++;
1180 if (p)
1181 p++;
1184 if (strncmp(p, " in file ", 9) != 0){
1185 TRACE("Parse error in breakpoint list");
1186 TRACE(p);
1187 return false;
1191 /* skip ' in file ' */
1192 p = p + 9;
1193 // TRACE(p);
1195 /* grab file name */
1196 while (!isspace(*p)) {
1197 file.append(*p);
1198 p++;
1200 if (*p == '\0')
1201 break;
1203 /* skip ' : line ' */
1204 p = p + 8;
1205 //TRACE(p);
1206 while (isdigit(*p)) {
1207 lineNo.append(*p);
1208 p++;
1212 //TRACE("Got breakpoint");
1214 Breakpoint *bp = new Breakpoint;
1216 if (bp != 0) {
1217 // take 1 of line number
1218 lineNo.setNum(lineNo.toInt() - 1);
1219 bp->id = bpNum;
1220 bp->type = bpType;
1221 bp->temporary = false;
1222 bp->enabled = enabled;
1223 location.append("in ").append(templateName).append(" at ");
1224 location.append(file).append(":").append(lineNo);
1225 bp->location = location;
1226 bp->fileName = file;
1227 bp->lineNo = lineNo.toInt();
1228 bp->address = address;
1229 bp->hitCount = hits;
1230 bp->ignoreCount = ignoreCount;
1231 bp->condition = condition;
1232 brks.append(bp);
1233 location = "";
1234 lineNo = "";
1235 file = "";
1236 } else
1237 TRACE("Outof memory, breakpoint not created");
1239 if (p != 0) {
1240 p = strchr(p, '\n');
1243 return true;
1246 bool
1247 XsldbgDriver::parseThreadList(const char */*output*/,
1248 QList < ThreadInfo > &/*threads*/)
1250 return true;
1253 bool
1254 XsldbgDriver::parseBreakpoint(const char *output, int &id,
1255 QString &file, int &lineNo, QString &address)
1257 // check for errors
1258 if ( strncmp(output, "Error:", 6) == 0) {
1259 return false;
1262 char *dummy;
1263 if (strncmp(output, "Breakpoint ", 11) != 0)
1264 return false;
1266 output += 11;
1267 if (!isdigit(*output))
1268 return false;
1270 // get Num
1271 id = strtol(output, &dummy, 10); /* don't care about overflows */
1272 if (output == dummy)
1273 return false;
1275 // the file name + lineNo will be filled in later from the breakpoint list
1276 file = address = QString();
1277 lineNo = 0;
1278 return true;
1281 void
1282 XsldbgDriver::parseLocals(const char *output, QList < VarTree > &newVars)
1285 /* keep going until error or xsldbg prompt is found */
1286 while (*output != '\0') {
1287 VarTree *variable = parseVar(output);
1289 if (variable == 0) {
1290 break;
1292 // do not add duplicates
1293 for (VarTree * o = newVars.first(); o != 0; o = newVars.next()) {
1294 if (o->getText() == variable->getText()) {
1295 delete variable;
1297 goto skipDuplicate;
1300 newVars.append(variable);
1301 skipDuplicate:;
1306 bool
1307 XsldbgDriver::parsePrintExpr(const char *output, bool wantErrorValue,
1308 VarTree * &var)
1310 // check for error conditions
1311 if (parseErrorMessage(output, var, wantErrorValue)) {
1312 return false;
1313 } else {
1314 // parse the variable
1315 var = parseVar(output);
1316 return true;
1320 bool
1321 XsldbgDriver::parseChangeWD(const char *output, QString & message)
1323 bool isGood = false;
1325 if (strncmp(output, "Change to directory", 20) == 0) {
1326 output = output + 20; /* skip 'Change to directory' */
1327 message = QString(output).simplifyWhiteSpace();
1328 if (message.isEmpty()) {
1329 message = i18n("New working directory: ") + m_programWD;
1330 isGood = true;
1333 return isGood;
1336 bool
1337 XsldbgDriver::parseChangeExecutable(const char *output, QString & message)
1339 message = output;
1340 TRACE(QString("XsldbgDriver::parseChangeExecutable :") + output);
1341 m_haveCoreFile = false;
1344 * The command is successful if there is no output or the single
1345 * message (no debugging symbols found)...
1347 QRegExp exp(".*Load of source deferred use run command.*");
1348 int len, index = exp.match(output, 0, &len);
1350 if (index != -1) {
1351 TRACE("Parsed stylesheet executable");
1352 message = "";
1354 return (output[0] == '\0') || (index != -1);
1357 bool
1358 XsldbgDriver::parseCoreFile(const char *output)
1360 TRACE("XsldbgDriver::parseCoreFile");
1361 TRACE(output);
1362 QRegExp exp(".*Load of xml data deferred use run command.*");
1363 int len, index = exp.match(output, 0, &len);
1365 if (index != -1) {
1366 m_haveCoreFile = true;
1367 TRACE("Parsed xml data file");
1370 return m_haveCoreFile;
1373 uint
1374 XsldbgDriver::parseProgramStopped(const char *output, QString & message)
1376 /* Not sure about this function leave it here for the moment */
1378 * return DebuggerDriver::SFrefreshBreak & DebuggerDriver::SFprogramActive;
1381 // go through the output, line by line, checking what we have
1382 const char *start = output - 1;
1383 uint flags = SFprogramActive;
1385 message = QString();
1386 do {
1387 start++; /* skip '\n' */
1389 if (strncmp(start, "Finished stylesheet\n\032\032\n", 21) == 0){
1390 flags &= ~SFprogramActive;
1391 break;
1394 // next line, please
1395 start = strchr(start, '\n');
1396 } while (start != 0);
1398 return flags;
1401 void
1402 XsldbgDriver::parseSharedLibs(const char */*output*/, QStrList & /*shlibs*/)
1404 /* empty */
1407 bool
1408 XsldbgDriver::parseFindType(const char */*output*/, QString & /*type*/)
1410 return true;
1413 void
1414 XsldbgDriver::parseRegisters(const char */*output*/,
1415 QList < RegisterInfo > &/*regs*/)
1420 bool
1421 XsldbgDriver::parseInfoLine(const char */*output*/, QString & /*addrFrom*/,
1422 QString & /*addrTo*/)
1424 return false;
1427 void
1428 XsldbgDriver::parseDisassemble(const char */*output*/,
1429 QList < DisassembledCode > &/*code*/)
1431 /* empty */
1434 QString
1435 XsldbgDriver::parseMemoryDump(const char */*output*/,
1436 QList < MemoryDump > &/*memdump*/)
1438 return i18n("No memory dump available");
1442 #include "xsldbgdriver.moc"