Latest updates from Keith for xsldbg 3.0.5.
[kdbg.git] / kdbg / xsldbgdriver.cpp
blobdf383027b7436dbc94865af5903689819c589607
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 */
14 #include "assert.h"
15 #ifdef HAVE_CONFIG_H
16 #include "config.h"
17 #endif
18 #include "mydebug.h"
21 static VarTree *parseVar(const char *&s);
22 static bool parseName(const char *&s, QString & name,
23 VarTree::NameKind & kind);
24 static bool parseValue(const char *&s, VarTree * variable);
26 #define TERM_IO_ALLOWED 1
28 // TODO: make this cmd info stuff non-static to allow multiple
29 // simultaneous gdbs to run!
31 struct XsldbgCmdInfo {
32 DbgCommand cmd;
33 const char *fmt; /* format string */
34 enum Args {
35 argNone, argString, argNum,
36 argStringNum, argNumString,
37 argString2, argNum2
38 } argsNeeded;
42 * The following array of commands must be sorted by the DC* values,
43 * because they are used as indices.
45 static XsldbgCmdInfo cmds[] = {
46 {DCinitialize, "init\n", XsldbgCmdInfo::argNone},
47 {DCtty, "tty %s\n", XsldbgCmdInfo::argString},
48 {DCexecutable, "source %s\n", XsldbgCmdInfo::argString}, /* force a restart */
49 {DCtargetremote, "print 'target remote %s'\n", XsldbgCmdInfo::argString},
50 {DCcorefile, "data %s\n", XsldbgCmdInfo::argString}, /* force a restart */
51 {DCattach, "print 'attach %s'\n", XsldbgCmdInfo::argString},
52 {DCinfolinemain, "print 'info main line'\n", XsldbgCmdInfo::argNone},
53 {DCinfolocals, "locals -f\n", XsldbgCmdInfo::argNone},
54 {DCinforegisters, "print 'info reg'\n", XsldbgCmdInfo::argNone},
55 {DCexamine, "print 'x %s %s'\n", XsldbgCmdInfo::argString2},
56 {DCinfoline, "print 'templates %s:%d'\n", XsldbgCmdInfo::argStringNum},
57 {DCdisassemble, "print 'disassemble %s %s'\n", XsldbgCmdInfo::argString2},
58 {DCsetargs, "setargs %s\n", XsldbgCmdInfo::argString},
59 {DCsetenv, "addparam %s %s\n", XsldbgCmdInfo::argString2},
60 {DCunsetenv, "unset env %s\n", XsldbgCmdInfo::argString},
61 {DCsetoption, "setoption %s %d\n", XsldbgCmdInfo::argStringNum},
62 {DCcd, "chdir %s\n", XsldbgCmdInfo::argString},
63 {DCbt, "where\n", XsldbgCmdInfo::argNone},
64 {DCrun, "run\nsource\n", XsldbgCmdInfo::argNone}, /* Ensure that at the start
65 of executing XSLT we show the XSLT file */
66 {DCcont, "continue\n", XsldbgCmdInfo::argNone},
67 {DCstep, "step\n", XsldbgCmdInfo::argNone},
68 {DCstepi, "print 'DCstepi'\n", XsldbgCmdInfo::argNone},
69 {DCnext, "next\n", XsldbgCmdInfo::argNone},
70 {DCnexti, "print 'nexti'\n", XsldbgCmdInfo::argNone},
71 {DCfinish, "quit\n", XsldbgCmdInfo::argNone},
72 {DCuntil, "continue %s:%d\n", XsldbgCmdInfo::argStringNum},
73 {DCkill, "quit\n", XsldbgCmdInfo::argNone},
74 {DCbreaktext, "break %s\n", XsldbgCmdInfo::argString},
75 {DCbreakline, "break -l %s %d\n", XsldbgCmdInfo::argStringNum},
76 {DCtbreakline, "print `tbreak %s:%d`\n", XsldbgCmdInfo::argStringNum },
77 {DCbreakaddr, "print `break *%s`\n", XsldbgCmdInfo::argString },
78 {DCtbreakaddr, "print `tbreak *%s`\n", XsldbgCmdInfo::argString },
79 {DCwatchpoint, "print 'watch %s'\n", XsldbgCmdInfo::argString},
80 {DCdelete, "delete %d\n", XsldbgCmdInfo::argNum},
81 {DCenable, "enable %d\n", XsldbgCmdInfo::argNum},
82 {DCdisable, "disable %d\n", XsldbgCmdInfo::argNum},
83 {DCprint, "print %s\n", XsldbgCmdInfo::argString},
84 {DCprintStruct, "print 'print %s'\n", XsldbgCmdInfo::argString},
85 {DCprintQStringStruct, "print 'print %s'\n", XsldbgCmdInfo::argString},
86 {DCframe, "frame %d\n", XsldbgCmdInfo::argNum},
87 {DCfindType, "print 'whatis %s'\n", XsldbgCmdInfo::argString},
88 {DCinfosharedlib, "stylesheets\n", XsldbgCmdInfo::argNone},
89 {DCthread, "print 'thread %d'\n", XsldbgCmdInfo::argNum},
90 {DCinfothreads, "print 'info threads'\n", XsldbgCmdInfo::argNone},
91 {DCinfobreak, "show\n", XsldbgCmdInfo::argNone},
92 {DCcondition, "print 'condition %d %s'\n", XsldbgCmdInfo::argNumString},
93 {DCignore, "print 'ignore %d %d'\n", XsldbgCmdInfo::argNum2},
96 #define NUM_CMDS (int(sizeof(cmds)/sizeof(cmds[0])))
97 #define MAX_FMTLEN 200
99 XsldbgDriver::XsldbgDriver():
100 DebuggerDriver(), m_gdbMajor(2), m_gdbMinor(0)
102 m_promptRE.setPattern("\\(xsldbg\\) .*> $");
103 m_promptMinLen = 11;
104 m_promptLastChar = ' ';
106 m_markerRE.setPattern("^Breakpoint at file ");
108 #ifndef NDEBUG
109 // check command info array
110 char *perc;
112 for (int i = 0; i < NUM_CMDS; i++) {
113 // must be indexable by DbgCommand values, i.e. sorted by DbgCommand values
114 assert(i == cmds[i].cmd);
115 // a format string must be associated
116 assert(cmds[i].fmt != 0);
117 assert(strlen(cmds[i].fmt) <= MAX_FMTLEN);
118 // format string must match arg specification
119 switch (cmds[i].argsNeeded) {
120 case XsldbgCmdInfo::argNone:
121 assert(strchr(cmds[i].fmt, '%') == 0);
122 break;
123 case XsldbgCmdInfo::argString:
124 perc = strchr(cmds[i].fmt, '%');
125 assert(perc != 0 && perc[1] == 's');
126 assert(strchr(perc + 2, '%') == 0);
127 break;
128 case XsldbgCmdInfo::argNum:
129 perc = strchr(cmds[i].fmt, '%');
130 assert(perc != 0 && perc[1] == 'd');
131 assert(strchr(perc + 2, '%') == 0);
132 break;
133 case XsldbgCmdInfo::argStringNum:
134 perc = strchr(cmds[i].fmt, '%');
135 assert(perc != 0 && perc[1] == 's');
136 perc = strchr(perc + 2, '%');
137 assert(perc != 0 && perc[1] == 'd');
138 assert(strchr(perc + 2, '%') == 0);
139 break;
140 case XsldbgCmdInfo::argNumString:
141 perc = strchr(cmds[i].fmt, '%');
142 assert(perc != 0 && perc[1] == 'd');
143 perc = strchr(perc + 2, '%');
144 assert(perc != 0 && perc[1] == 's');
145 assert(strchr(perc + 2, '%') == 0);
146 break;
147 case XsldbgCmdInfo::argString2:
148 perc = strchr(cmds[i].fmt, '%');
149 assert(perc != 0 && perc[1] == 's');
150 perc = strchr(perc + 2, '%');
151 assert(perc != 0 && perc[1] == 's');
152 assert(strchr(perc + 2, '%') == 0);
153 break;
154 case XsldbgCmdInfo::argNum2:
155 perc = strchr(cmds[i].fmt, '%');
156 assert(perc != 0 && perc[1] == 'd');
157 perc = strchr(perc + 2, '%');
158 assert(perc != 0 && perc[1] == 'd');
159 assert(strchr(perc + 2, '%') == 0);
160 break;
163 #endif
166 XsldbgDriver::~XsldbgDriver()
171 QString
172 XsldbgDriver::driverName() const
174 return "XSLDBG";
177 QString
178 XsldbgDriver::defaultXsldbg()
180 return "xsldbg --shell --gdb";
183 QString
184 XsldbgDriver::defaultInvocation() const
186 return defaultXsldbg();
189 QStringList XsldbgDriver::boolOptionList() const
191 QStringList allOptions;
192 allOptions.append("verbose");
193 allOptions.append("repeat");
194 allOptions.append("debug");
195 allOptions.append("novalid");
196 allOptions.append("noout");
197 allOptions.append("html");
198 allOptions.append("docbook");
199 allOptions.append("nonet");
200 allOptions.append("catalogs");
201 allOptions.append("xinclude");
202 allOptions.append("profile");
203 return allOptions;
207 void
208 XsldbgDriver::slotReceiveOutput(KProcess * process, char *buffer,
209 int buflen)
211 //TRACE(buffer);
212 if (m_state != DSidle) {
213 // TRACE(buffer);
214 DebuggerDriver::slotReceiveOutput(process, buffer, buflen);
215 } else {
216 if (strncmp(buffer, "quit", 4) == 0) {
217 TRACE("Ignoring text when xsldbg is quiting");
218 } else {
219 TRACE
220 ("Stray output received by XsldbgDriver::slotReceiveOutput");
221 TRACE(buffer);
226 bool
227 XsldbgDriver::startup(QString cmdStr)
229 if (!DebuggerDriver::startup(cmdStr))
230 return false;
232 static const char xsldbgInitialize[] = "pwd\n"; /* don't need to do anything else */
234 executeCmdString(DCinitialize, xsldbgInitialize, false);
236 return true;
239 void
240 XsldbgDriver::commandFinished(CmdQueueItem * cmd)
243 TRACE(__PRETTY_FUNCTION__);
244 // command string must be committed
245 if (!cmd->m_committed) {
246 // not commited!
247 TRACE("calling " +
248 (__PRETTY_FUNCTION__ +
249 (" with uncommited command:\n\t" + cmd->m_cmdString)));
250 return;
253 switch (cmd->m_cmd) {
254 case DCinitialize:
255 // get version number from preamble
257 int len;
258 QRegExp xsldbgVersion("^XSLDBG [0-9]+\\.[0-9]+\\.[0-9]+");
259 int offset = xsldbgVersion.match(m_output, 0, &len);
261 if (offset >= 0) {
262 char *start = m_output + offset + 7; // skip "^XSLDBG "
263 char *end;
265 TRACE("Reading version");
266 TRACE(start);
267 m_gdbMajor = strtol(start, &end, 10);
268 m_gdbMinor = strtol(end + 1, 0, 10); // skip "."
269 if (start == end) {
270 // nothing was parsed
271 m_gdbMajor = 0;
272 m_gdbMinor = 7;
274 } else {
275 // assume some default version (what would make sense?)
276 m_gdbMajor = 0;
277 m_gdbMinor = 7;
279 TRACE(QString("Got version ") +
280 QString::number(m_gdbMajor) + "." +
281 QString::number(m_gdbMinor));
282 break;
284 default:;
286 /* ok, the command is ready */
287 emit commandReceived(cmd, m_output);
289 switch (cmd->m_cmd) {
290 case DCbt:
291 case DCinfolocals:
292 case DCrun:
293 case DCcont:
294 case DCstep:
295 case DCnext:
296 case DCfinish:{
297 parseMarker();
299 break;
301 default:;
305 void
306 XsldbgDriver::parseMarker()
309 // TRACE("parseMarker : xsldbg");
310 // TRACE(m_output);
311 int len, markerStart = -1;
312 char *p = m_output;
314 while (markerStart == -1) {
315 if ((p == 0) || (*p == '\0')) {
316 m_output[0] = '\0';
317 return;
319 //TRACE(QString("parseMarker is looking at :") + p);
320 markerStart = m_markerRE.match(p, 0, &len);
321 if (markerStart == -1) {
322 // try to marker on next line !
323 p = strchr(p, '\n');
324 if ((p != 0) && (*p != '\0'))
325 p++;
330 // extract the marker
331 char *startMarker = p + markerStart + len;
333 //TRACE(QString("found marker:") + startMarker);
334 char *endMarker = strchr(startMarker, '\n');
336 if (endMarker == 0)
337 return;
339 *endMarker = '\0';
341 // extract filename and line number
342 static QRegExp MarkerRE(" : line [0-9]+");
344 int lineNoStart = MarkerRE.match(startMarker, 0, &len);
346 if (lineNoStart >= 0) {
347 int lineNo = atoi(startMarker + lineNoStart + 8);
349 DbgAddr address;
351 // now show the window
352 startMarker[lineNoStart] = '\0'; /* split off file name */
353 TRACE("Got file and line number");
354 TRACE(QString(startMarker) + ": " + QString::number(lineNo));
355 emit activateFileLine(startMarker, lineNo - 1, address);
361 * Escapes characters that might lead to problems when they appear on gdb's
362 * command line.
364 static void
365 normalizeStringArg(QString & arg)
368 * Remove trailing backslashes. This approach is a little simplistic,
369 * but we know that there is at the moment no case where a trailing
370 * backslash would make sense.
372 while (!arg.isEmpty() && arg[arg.length() - 1] == '\\') {
373 arg = arg.left(arg.length() - 1);
378 QString
379 XsldbgDriver::makeCmdString(DbgCommand cmd, QString strArg)
381 assert(cmd >= 0 && cmd < NUM_CMDS);
382 assert(cmds[cmd].argsNeeded == XsldbgCmdInfo::argString);
384 normalizeStringArg(strArg);
386 if (cmd == DCcd) {
387 // need the working directory when parsing the output
388 m_programWD = strArg;
391 SIZED_QString(cmdString, MAX_FMTLEN + strArg.length());
392 cmdString.sprintf(cmds[cmd].fmt, strArg.latin1());
393 return cmdString;
396 QString
397 XsldbgDriver::makeCmdString(DbgCommand cmd, int intArg)
399 assert(cmd >= 0 && cmd < NUM_CMDS);
400 assert(cmds[cmd].argsNeeded == XsldbgCmdInfo::argNum);
402 SIZED_QString(cmdString, MAX_FMTLEN + 30);
404 cmdString.sprintf(cmds[cmd].fmt, intArg);
405 return cmdString;
408 QString
409 XsldbgDriver::makeCmdString(DbgCommand cmd, QString strArg, int intArg)
411 assert(cmd >= 0 && cmd < NUM_CMDS);
412 assert(cmds[cmd].argsNeeded == XsldbgCmdInfo::argStringNum ||
413 cmds[cmd].argsNeeded == XsldbgCmdInfo::argNumString ||
414 cmd == DCexamine || cmd == DCtty);
416 normalizeStringArg(strArg);
418 SIZED_QString(cmdString, MAX_FMTLEN + 30 + strArg.length());
420 if (cmd == DCtty) {
422 * intArg specifies which channels should be redirected to
423 * /dev/null. It is a value or'ed together from RDNstdin,
424 * RDNstdout, RDNstderr.
426 static const char *const runRedir[8] = {
428 " </dev/null",
429 " >/dev/null",
430 " </dev/null >/dev/null",
431 " 2>/dev/null",
432 " </dev/null 2>/dev/null",
433 " >/dev/null 2>&1",
434 " </dev/null >/dev/null 2>&1"
437 if (strArg.isEmpty())
438 intArg = 7; /* failsafe if no tty */
439 m_redirect = runRedir[intArg & 7];
441 return makeCmdString(DCtty, strArg); /* note: no problem if strArg empty */
444 if (cmd == DCexamine) {
445 // make a format specifier from the intArg
446 static const char size[16] = {
447 '\0', 'b', 'h', 'w', 'g'
449 static const char format[16] = {
450 '\0', 'x', 'd', 'u', 'o', 't',
451 'a', 'c', 'f', 's', 'i'
454 assert(MDTsizemask == 0xf); /* lowest 4 bits */
455 assert(MDTformatmask == 0xf0); /* next 4 bits */
456 int count = 16; /* number of entities to print */
457 char sizeSpec = size[intArg & MDTsizemask];
458 char formatSpec = format[(intArg & MDTformatmask) >> 4];
460 assert(sizeSpec != '\0');
461 assert(formatSpec != '\0');
462 // adjust count such that 16 lines are printed
463 switch (intArg & MDTformatmask) {
464 case MDTstring:
465 case MDTinsn:
466 break; /* no modification needed */
467 default:
468 // all cases drop through:
469 switch (intArg & MDTsizemask) {
470 case MDTbyte:
471 case MDThalfword:
472 count *= 2;
473 case MDTword:
474 count *= 2;
475 case MDTgiantword:
476 count *= 2;
478 break;
480 QString spec;
482 spec.sprintf("/%d%c%c", count, sizeSpec, formatSpec);
484 return makeCmdString(DCexamine, spec, strArg);
487 if (cmds[cmd].argsNeeded == XsldbgCmdInfo::argStringNum) {
488 // line numbers are zero-based
489 if (cmd == DCuntil || cmd == DCbreakline ||
490 cmd == DCtbreakline || cmd == DCinfoline) {
491 intArg++;
493 if (cmd == DCinfoline) {
494 // must split off file name part
495 int slash = strArg.findRev('/');
497 if (slash >= 0)
498 strArg = strArg.right(strArg.length() - slash - 1);
500 cmdString.sprintf(cmds[cmd].fmt, strArg.latin1(), intArg);
501 } else {
502 cmdString.sprintf(cmds[cmd].fmt, intArg, strArg.latin1());
504 return cmdString;
507 QString
508 XsldbgDriver::makeCmdString(DbgCommand cmd, QString strArg1,
509 QString strArg2)
511 assert(cmd >= 0 && cmd < NUM_CMDS);
512 assert(cmds[cmd].argsNeeded == XsldbgCmdInfo::argString2);
514 normalizeStringArg(strArg1);
515 normalizeStringArg(strArg2);
517 SIZED_QString(cmdString,
518 MAX_FMTLEN + strArg1.length() + strArg2.length());
519 cmdString.sprintf(cmds[cmd].fmt, strArg1.latin1(), strArg2.latin1());
520 return cmdString;
523 QString
524 XsldbgDriver::makeCmdString(DbgCommand cmd, int intArg1, int intArg2)
526 assert(cmd >= 0 && cmd < NUM_CMDS);
527 assert(cmds[cmd].argsNeeded == XsldbgCmdInfo::argNum2);
529 SIZED_QString(cmdString, MAX_FMTLEN + 60);
530 cmdString.sprintf(cmds[cmd].fmt, intArg1, intArg2);
531 return cmdString;
534 CmdQueueItem *
535 XsldbgDriver::executeCmd(DbgCommand cmd, bool clearLow)
537 assert(cmd >= 0 && cmd < NUM_CMDS);
538 assert(cmds[cmd].argsNeeded == XsldbgCmdInfo::argNone);
540 if (cmd == DCrun) {
541 m_haveCoreFile = false;
544 return executeCmdString(cmd, cmds[cmd].fmt, clearLow);
547 CmdQueueItem *
548 XsldbgDriver::executeCmd(DbgCommand cmd, QString strArg, bool clearLow)
550 return executeCmdString(cmd, makeCmdString(cmd, strArg), clearLow);
553 CmdQueueItem *
554 XsldbgDriver::executeCmd(DbgCommand cmd, int intArg, bool clearLow)
557 return executeCmdString(cmd, makeCmdString(cmd, intArg), clearLow);
560 CmdQueueItem *
561 XsldbgDriver::executeCmd(DbgCommand cmd, QString strArg, int intArg,
562 bool clearLow)
564 return executeCmdString(cmd, makeCmdString(cmd, strArg, intArg),
565 clearLow);
568 CmdQueueItem *
569 XsldbgDriver::executeCmd(DbgCommand cmd, QString strArg1, QString strArg2,
570 bool clearLow)
572 return executeCmdString(cmd, makeCmdString(cmd, strArg1, strArg2),
573 clearLow);
576 CmdQueueItem *
577 XsldbgDriver::executeCmd(DbgCommand cmd, int intArg1, int intArg2,
578 bool clearLow)
580 return executeCmdString(cmd, makeCmdString(cmd, intArg1, intArg2),
581 clearLow);
584 CmdQueueItem *
585 XsldbgDriver::queueCmd(DbgCommand cmd, QueueMode mode)
587 return queueCmdString(cmd, cmds[cmd].fmt, mode);
590 CmdQueueItem *
591 XsldbgDriver::queueCmd(DbgCommand cmd, QString strArg, QueueMode mode)
593 return queueCmdString(cmd, makeCmdString(cmd, strArg), mode);
596 CmdQueueItem *
597 XsldbgDriver::queueCmd(DbgCommand cmd, int intArg, QueueMode mode)
599 return queueCmdString(cmd, makeCmdString(cmd, intArg), mode);
602 CmdQueueItem *
603 XsldbgDriver::queueCmd(DbgCommand cmd, QString strArg, int intArg,
604 QueueMode mode)
606 return queueCmdString(cmd, makeCmdString(cmd, strArg, intArg), mode);
609 CmdQueueItem *
610 XsldbgDriver::queueCmd(DbgCommand cmd, QString strArg1, QString strArg2,
611 QueueMode mode)
613 return queueCmdString(cmd, makeCmdString(cmd, strArg1, strArg2), mode);
616 void
617 XsldbgDriver::terminate()
619 qDebug("XsldbgDriver::Terminate");
620 flushCommands();
621 executeCmdString(DCinitialize, "quit\n", true);
622 kill(SIGTERM);
623 m_state = DSidle;
626 void
627 XsldbgDriver::detachAndTerminate()
629 qDebug("XsldbgDriver::detachAndTerminate");
630 flushCommands();
631 executeCmdString(DCinitialize, "quit\n", true);
632 kill(SIGINT);
635 void
636 XsldbgDriver::interruptInferior()
638 // remove accidentally queued commands
639 qDebug("interruptInferior");
640 flushHiPriQueue();
641 kill(SIGINT);
644 static bool
645 isErrorExpr(const char *output)
647 int wordIndex;
648 bool result = false;
649 #define ERROR_WORD_COUNT 3
650 static const char *errorWords[ERROR_WORD_COUNT] = {
651 "Error:",
652 "Unknown command",
653 "Warning:"
655 static int errorWordLength[ERROR_WORD_COUNT] = {
656 5, /* Error */
657 15, /* Unknown command*/
658 7 /* Warning */
661 for (wordIndex = 0; wordIndex < ERROR_WORD_COUNT; wordIndex++){
662 if (strncmp(output,
663 errorWords[wordIndex],
664 errorWordLength[wordIndex]) == 0){
665 result = true;
666 TRACE(QString("Error/Warning from xsldbg ") + output);
667 break;
671 return result;
675 * Returns true if the output is an error message. If wantErrorValue is
676 * true, a new VarTree object is created and filled with the error message.
678 static bool
679 parseErrorMessage(const char *output,
680 VarTree * &variable, bool wantErrorValue)
682 if (isErrorExpr(output)) {
683 if (wantErrorValue) {
684 // put the error message as value in the variable
685 variable = new VarTree(QString(), VarTree::NKplain);
686 const char *endMsg = strchr(output, '\n');
688 if (endMsg == 0)
689 endMsg = output + strlen(output);
690 variable->m_value = FROM_LATIN1(output, endMsg - output);
691 } else {
692 variable = 0;
694 return true;
696 return false;
700 VarTree *
701 XsldbgDriver::parseQCharArray(const char */*output*/, bool /*wantErrorValue*/,
702 bool /*qt3like*/)
704 VarTree *variable = 0;
706 TRACE("XsldbgDriver::parseQCharArray not implmented");
707 return variable;
710 static VarTree *
711 parseVar(const char *&s)
713 const char *p = s;
714 bool foundLocalVar = false;
715 VarTree *variable = 0L;
716 QString name;
718 VarTree::NameKind kind;
720 TRACE(__PRETTY_FUNCTION__);
721 TRACE(p);
723 if (parseErrorMessage(p, variable, false) == true) {
724 TRACE("Found error message");
725 return variable;
728 if (strncmp(p, " Local", 6) == 0) {
729 foundLocalVar = true;
730 /* skip " Local" */
731 p = p + 6;
732 TRACE("Found local variable");
733 } else if (strncmp(p, " Global", 7) == 0) {
734 /* skip " Global" */
735 p = p + 7;
736 TRACE("Found global variable");
737 } else if (strncmp(p, "= ", 2) == 0) {
738 /* we're processing the result of a "print command" */
739 /* find next line */
740 char *nextLine = strchr(p, '\n');
742 TRACE("Found print expr");
743 if (nextLine) {
744 char nameBuffer[100];
746 p = p + 2; /* skip the "= " */
747 strncpy(nameBuffer, p, nextLine - p);
748 p = nextLine + 1;
749 variable = new VarTree(nameBuffer, kind);
750 if (variable != 0L) {
751 variable->setDeleteChildren(true);
752 parseValue(p, variable);
754 return variable;
756 } else
757 return variable; /* don't know what to do this this data abort!! */
759 // skip whitespace
760 while (isspace(*p))
761 p++;
763 if (*p != '='){
764 // No value provided just a name
765 TRACE(QString("Parse var: name") + p);
766 if (!parseName(p, name, kind)) {
767 return 0;
769 variable = new VarTree(name, kind);
770 if (variable != 0L) {
771 variable->setDeleteChildren(true);
773 }else{
774 p++;
775 // skip whitespace
776 while (isspace(*p))
777 p++;
778 TRACE(QString("Parse var: name") + p);
779 if (!parseName(p, name, kind)) {
780 return 0;
782 variable = new VarTree(name, kind);
783 if (variable != 0L) {
784 variable->setDeleteChildren(true);
786 if (*p == '\n')
787 p++;
788 if (!parseValue(p, variable)) {
789 delete variable;
790 return 0;
794 if (*p == '\n')
795 p++;
797 s = p;
798 return variable;
802 inline void
803 skipName(const char *&p)
805 // allow : (for enumeration values) and $ and . (for _vtbl.)
806 while (isalnum(*p) || *p == '_' || *p == ':' || *p == '$' || *p == '.')
807 p++;
810 static bool
811 parseName(const char *&s, QString & name, VarTree::NameKind & kind)
813 /* qDebug(__PRETTY_FUNCTION__); */
814 kind = VarTree::NKplain;
816 const char *p = s;
817 int len = 0;
819 // examples of names:
820 // help_cmd
822 while ((*p != '\n') && (*p != '\0')) {
823 len++;
824 p++;
828 name = FROM_LATIN1(s, len);
829 /* XSL variables will have a $ prefix to be evaluated
830 * properly */
831 //TRACE(QString("parseName got name" ) + name);
833 // return the new position
834 s = p;
835 return true;
838 static bool
839 parseValue(const char *&s, VarTree * variable)
841 const char *start = s, *end = s;
842 VarTree * childValue;
843 #define VALUE_END_MARKER_INDEX 0
845 /* This mark the end of a value */
846 static const char *marker[] = {
847 "\032\032", /* value end marker*/
848 "(xsldbg) ",
849 "Breakpoint at", /* stepped to next location */
850 "Breakpoint in", /* reached a set breakpoint */
851 "Reached ", /* reached template */
852 "Error:",
853 "Warning:",
854 "runtime error",
855 "xmlXPathEval:",
858 static char valueBuffer[255];
859 int markerIndex = 0, foundEnd = 0;
860 size_t copySize;
862 if (variable == 0L)
863 return false; /* should never happen but .. */
865 variable->m_value = "";
866 while (start && (*start != '\0')) {
867 /* look for the next marker */
868 for (markerIndex = 0; marker[markerIndex] != 0; markerIndex++) {
869 foundEnd =
870 strncmp(start, marker[markerIndex],
871 strlen(marker[markerIndex])) == 0;
872 if (foundEnd)
873 break;
876 if (foundEnd)
877 break;
880 end = strchr(start, '\n');
881 if (end) {
882 end++;
884 copySize = end - start - 1;
885 if (copySize > sizeof(valueBuffer))
886 copySize = sizeof(valueBuffer);
888 strncpy(valueBuffer, start, copySize);
889 valueBuffer[copySize] = '\0';
890 TRACE("Got value :");
891 TRACE(valueBuffer);
892 childValue = new VarTree(valueBuffer, VarTree::NKplain);
893 variable->appendChild(childValue);
895 start = end;
896 } else {
897 childValue = new VarTree(start, VarTree::NKplain);
898 variable->appendChild(childValue);
899 break;
903 if (foundEnd == 0)
904 TRACE(QString("Unable to find end on value near :") + start);
906 // If we've got something otherthan a end of value marker then
907 // advance to the end of this buffer
908 if (markerIndex != VALUE_END_MARKER_INDEX){
909 while (start && *start != '\0')
910 start++;
911 }else{
912 start = start + strlen(marker[0]);
915 s = start;
917 return true;
922 * Parses a stack frame.
924 static void
925 parseFrameInfo(const char *&s, QString & func,
926 QString & file, int &lineNo, DbgAddr & /*address*/)
928 const char *p = s, *endPos = s + strlen(s);
929 QString lineNoString;
931 lineNo = -1;
933 /* skip 'template :\" */
934 p = p + 11;
935 //TRACE("parseFrameInfo");
936 // TRACE(p);
937 func = "";
938 while ((*p != '\"') && (*p != '\0')) {
939 func.append(*p);
940 p++;
942 ASSERT(p <= endPos);
943 if (p >= endPos) {
944 /* panic */
945 return;
948 /* skip mode :".*" */
949 while (p && *p != '"')
950 p++;
951 if (p)
952 p++;
953 while (p && *p != '"')
954 p++;
955 if (p)
956 p++;
957 while (p && *p != '"')
958 p++;
959 /* skip '" in file ' */
960 p = p + 10;
961 // TRACE(p);
962 file = "";
963 while (!isspace(*p) && (*p != '\0')) {
964 file.append(*p);
965 p++;
967 ASSERT(p <= endPos);
968 if (p >= endPos) {
969 /* panic */
970 return;
972 // TRACE(p);
973 /* skip ' : line '" */
974 p = p + 8;
975 // TRACE(p);
976 ASSERT(p <= endPos);
977 if (p >= endPos) {
978 /* panic */
979 return;
981 if (isdigit(*p)) {
982 /* KDbg uses an offset of +1 for its line numbers */
983 lineNo = atoi(p) - 1;
984 lineNoString = QString::number(lineNo);
986 /* convert func into format needed */
987 func.append(" at ");
988 func.append(file);
989 func.append(':');
990 func.append(lineNoString);
992 //TRACE(QString("Got frame : template :\"") + func + "\"file:" +
993 // file + " line : " + 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 QList < StackFrame > &stack)
1049 QString func, file;
1050 int lineNo, frameNo;
1051 DbgAddr address;
1053 while (::parseFrame(output, frameNo, func, file, lineNo, address)) {
1054 StackFrame *frm = new StackFrame;
1056 frm->frameNo = frameNo;
1057 frm->fileName = file;
1058 frm->lineNo = lineNo;
1059 frm->address = address;
1060 frm->var = new VarTree(func, VarTree::NKplain);
1061 stack.append(frm);
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 QList < Breakpoint > &brks)
1080 TRACE("parseBreakList");
1081 /* skip the first blank line */
1082 const char *p;
1084 // split up a line
1085 QString location, file, lineNo;
1086 QString address;
1087 QString templateName;
1088 int hits = 0;
1089 int enabled = 0;
1090 uint ignoreCount = 0;
1091 QString condition;
1092 char *dummy;
1093 p = strchr(output, '\n');/* skip the first blank line*/
1095 while ((p != 0) && (*p != '\0')) {
1096 templateName = QString();
1097 p++;
1098 Breakpoint::Type bpType = Breakpoint::breakpoint;
1099 //qDebug("Looking at :%s", p);
1100 if (strncmp(p, " Breakpoint", 11) != 0)
1101 break;
1102 p = p + 11;
1103 if (*p == '\0')
1104 break;
1106 //TRACE(p);
1107 // get Num
1108 long bpNum = strtol(p, &dummy, 10); /* don't care about overflows */
1110 p = dummy;
1111 if ((p == 0) || (p[1] == '\0'))
1112 break;
1113 p++;
1115 //TRACE(p);
1116 // Get breakpoint state ie enabled/disabled
1117 if (strncmp(p, "enabled", 7) == 0) {
1118 enabled = true;
1119 p = p + 7;
1120 } else {
1121 if (strncmp(p, "disabled", 8) == 0) {
1122 p = p + 8;
1123 enabled = false;
1124 } else{
1125 TRACE("Parse error in breakpoint list");
1126 TRACE(p);
1127 return false;
1131 //TRACE("Looking for template");
1132 //TRACE(p);
1134 /* check for ' for template :"'*/
1135 if (strncmp(p, " for template :\"", 16) == 0){
1136 p = p + 16;
1137 //TRACE("Looking for template name near");
1138 //TRACE(p);
1139 /* get the template name */
1140 while (p && (*p != '\0') && (*p != '\"')){
1141 templateName.append(*p);
1142 p++;
1144 if (*p == '\"'){
1145 p++;
1146 }else{
1147 TRACE("Error missed \" near");
1148 TRACE(p);
1152 //TRACE("Looking for mode near");
1153 //TRACE(p);
1154 if (strncmp(p, " mode :\"", 8) == 0) {
1155 p = p + 8;
1156 while (p && *p != '\"')
1157 p++;
1158 if (p)
1159 p++;
1162 if (strncmp(p, " in file ", 9) != 0){
1163 TRACE("Parse error in breakpoint list");
1164 TRACE(p);
1165 return false;
1169 /* skip ' in file ' */
1170 p = p + 9;
1171 // TRACE(p);
1173 /* grab file name */
1174 while (!isspace(*p)) {
1175 file.append(*p);
1176 p++;
1178 if (*p == '\0')
1179 break;
1181 /* skip ' : line ' */
1182 p = p + 8;
1183 //TRACE(p);
1184 while (isdigit(*p)) {
1185 lineNo.append(*p);
1186 p++;
1190 //TRACE("Got breakpoint");
1192 Breakpoint *bp = new Breakpoint;
1194 if (bp != 0) {
1195 // take 1 of line number
1196 lineNo.setNum(lineNo.toInt() - 1);
1197 bp->id = bpNum;
1198 bp->type = bpType;
1199 bp->temporary = false;
1200 bp->enabled = enabled;
1201 location.append("in ").append(templateName).append(" at ");
1202 location.append(file).append(":").append(lineNo);
1203 bp->location = location;
1204 bp->fileName = file;
1205 bp->lineNo = lineNo.toInt();
1206 bp->address = address;
1207 bp->hitCount = hits;
1208 bp->ignoreCount = ignoreCount;
1209 bp->condition = condition;
1210 brks.append(bp);
1211 location = "";
1212 lineNo = "";
1213 file = "";
1214 } else
1215 TRACE("Outof memory, breakpoint not created");
1217 if (p != 0) {
1218 p = strchr(p, '\n');
1221 return true;
1224 bool
1225 XsldbgDriver::parseThreadList(const char */*output*/,
1226 QList < ThreadInfo > &/*threads*/)
1228 return true;
1231 bool
1232 XsldbgDriver::parseBreakpoint(const char */*output*/, int &/*id*/,
1233 QString & /*file*/, int &/*lineNo*/)
1235 TRACE("parseBreakpoint");
1236 return true;
1239 void
1240 XsldbgDriver::parseLocals(const char *output, QList < VarTree > &newVars)
1243 /* keep going until error or xsldbg prompt is found */
1244 while (*output != '\0') {
1245 VarTree *variable = parseVar(output);
1247 if (variable == 0) {
1248 break;
1250 // do not add duplicates
1251 for (VarTree * o = newVars.first(); o != 0; o = newVars.next()) {
1252 if (o->getText() == variable->getText()) {
1253 delete variable;
1255 goto skipDuplicate;
1258 newVars.append(variable);
1259 skipDuplicate:;
1264 bool
1265 XsldbgDriver::parsePrintExpr(const char *output, bool wantErrorValue,
1266 VarTree * &var)
1268 // check for error conditions
1269 if (parseErrorMessage(output, var, wantErrorValue)) {
1270 return false;
1271 } else {
1272 // parse the variable
1273 var = parseVar(output);
1274 return true;
1278 bool
1279 XsldbgDriver::parseChangeWD(const char *output, QString & message)
1281 bool isGood = false;
1283 if (strncmp(output, "Change to directory", 20) == 0) {
1284 output = output + 20; /* skip 'Change to directory' */
1285 message = QString(output).simplifyWhiteSpace();
1286 if (message.isEmpty()) {
1287 message = i18n("New working directory: ") + m_programWD;
1288 isGood = true;
1291 return isGood;
1294 bool
1295 XsldbgDriver::parseChangeExecutable(const char *output, QString & message)
1297 message = output;
1298 TRACE(QString("XsldbgDriver::parseChangeExecutable :") + output);
1299 m_haveCoreFile = false;
1302 * The command is successful if there is no output or the single
1303 * message (no debugging symbols found)...
1305 QRegExp exp(".*Load of source deferred use run command.*");
1306 int len, index = exp.match(output, 0, &len);
1308 if (index != -1) {
1309 TRACE("Parsed stylesheet executable");
1310 message = "";
1312 return (output[0] == '\0') || (index != -1);
1315 bool
1316 XsldbgDriver::parseCoreFile(const char *output)
1318 TRACE("XsldbgDriver::parseCoreFile");
1319 TRACE(output);
1320 QRegExp exp(".*Load of xml data deferred use run command.*");
1321 int len, index = exp.match(output, 0, &len);
1323 if (index != -1) {
1324 m_haveCoreFile = true;
1325 TRACE("Parsed xml data file");
1328 return m_haveCoreFile;
1331 uint
1332 XsldbgDriver::parseProgramStopped(const char *output, QString & message)
1334 /* Not sure about this function leave it here for the moment */
1336 * return DebuggerDriver::SFrefreshBreak & DebuggerDriver::SFprogramActive;
1339 // go through the output, line by line, checking what we have
1340 const char *start = output - 1;
1341 uint flags = SFprogramActive;
1343 message = QString();
1344 do {
1345 start++; /* skip '\n' */
1347 if (strncmp(start, "Program ", 8) == 0 ||
1348 strncmp(start, "ptrace: ", 8) == 0) {
1350 * When we receive a signal, the program remains active.
1352 * Special: If we "stopped" in a corefile, the string "Program
1353 * terminated with signal"... is displayed. (Normally, we see
1354 * "Program received signal"... when a signal happens.)
1356 if (strncmp(start, "Program exited", 14) == 0 ||
1357 (strncmp(start, "Program terminated", 18) == 0
1358 && !m_haveCoreFile)
1359 || strncmp(start, "ptrace: ", 8) == 0) {
1360 flags &= ~SFprogramActive;
1362 // set message
1363 const char *endOfMessage = strchr(start, '\n');
1365 if (endOfMessage == 0)
1366 endOfMessage = start + strlen(start);
1367 message = FROM_LATIN1(start, endOfMessage - start);
1368 } else if (strncmp(start, "Breakpoint ", 11) == 0) {
1370 * We stopped at a (permanent) breakpoint (gdb doesn't tell us
1371 * that it stopped at a temporary breakpoint).
1373 flags |= SFrefreshBreak;
1374 } else if (strstr(start, "re-reading symbols.") != 0) {
1375 flags |= SFrefreshSource;
1377 // next line, please
1378 start = strchr(start, '\n');
1379 } while (start != 0);
1382 * Gdb only notices when new threads have appeared, but not when a
1383 * thread finishes. So we always have to assume that the list of
1384 * threads has changed.
1386 flags |= SFrefreshThreads;
1388 return flags;
1393 void
1394 XsldbgDriver::parseSharedLibs(const char */*output*/, QStrList & /*shlibs*/)
1396 /* empty */
1399 bool
1400 XsldbgDriver::parseFindType(const char */*output*/, QString & /*type*/)
1402 return true;
1405 void
1406 XsldbgDriver::parseRegisters(const char */*output*/,
1407 QList < RegisterInfo > &/*regs*/)
1412 bool
1413 XsldbgDriver::parseInfoLine(const char */*output*/, QString & /*addrFrom*/,
1414 QString & /*addrTo*/)
1416 return true;
1419 void
1420 XsldbgDriver::parseDisassemble(const char */*output*/,
1421 QList < DisassembledCode > &/*code*/)
1423 /* empty */
1426 QString
1427 XsldbgDriver::parseMemoryDump(const char */*output*/,
1428 QList < MemoryDump > &/*memdump*/)
1430 return i18n("No memory dump available");
1434 #include "xsldbgdriver.moc"