Don't start with a tiny window when started for the first time.
[kdbg.git] / kdbg / xsldbgdriver.cpp
blob7f10bff8ced500311afe771f2385a584e495a0bf
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 {DCprintDeref, "print 'print (*%s)'\n", XsldbgCmdInfo::argString},
87 {DCprintStruct, "print 'print %s'\n", XsldbgCmdInfo::argString},
88 {DCprintQStringStruct, "print 'print %s'\n", XsldbgCmdInfo::argString},
89 {DCframe, "frame %d\n", XsldbgCmdInfo::argNum},
90 {DCfindType, "print 'whatis %s'\n", XsldbgCmdInfo::argString},
91 {DCinfosharedlib, "stylesheets\n", XsldbgCmdInfo::argNone},
92 {DCthread, "print 'thread %d'\n", XsldbgCmdInfo::argNum},
93 {DCinfothreads, "print 'info threads'\n", XsldbgCmdInfo::argNone},
94 {DCinfobreak, "show\n", XsldbgCmdInfo::argNone},
95 {DCcondition, "print 'condition %d %s'\n", XsldbgCmdInfo::argNumString},
96 {DCsetpc, "print 'set variable $pc=%s'\n", XsldbgCmdInfo::argString},
97 {DCignore, "print 'ignore %d %d'\n", XsldbgCmdInfo::argNum2},
98 {DCsetvariable, "set %s %s\n", XsldbgCmdInfo::argString2},
101 #define NUM_CMDS (int(sizeof(cmds)/sizeof(cmds[0])))
102 #define MAX_FMTLEN 200
104 XsldbgDriver::XsldbgDriver():
105 DebuggerDriver(), m_gdbMajor(2), m_gdbMinor(0)
107 m_promptRE.setPattern("\\(xsldbg\\) .*> ");
108 m_promptMinLen = 11;
109 m_promptLastChar = ' ';
111 m_markerRE.setPattern("^Breakpoint for file ");
112 m_haveDataFile = FALSE;
114 #ifndef NDEBUG
115 // check command info array
116 char *perc;
118 for (int i = 0; i < NUM_CMDS; i++) {
119 // must be indexable by DbgCommand values, i.e. sorted by DbgCommand values
120 assert(i == cmds[i].cmd);
121 // a format string must be associated
122 assert(cmds[i].fmt != 0);
123 assert(strlen(cmds[i].fmt) <= MAX_FMTLEN);
124 // format string must match arg specification
125 switch (cmds[i].argsNeeded) {
126 case XsldbgCmdInfo::argNone:
127 assert(strchr(cmds[i].fmt, '%') == 0);
128 break;
129 case XsldbgCmdInfo::argString:
130 perc = strchr(cmds[i].fmt, '%');
131 assert(perc != 0 && perc[1] == 's');
132 assert(strchr(perc + 2, '%') == 0);
133 break;
134 case XsldbgCmdInfo::argNum:
135 perc = strchr(cmds[i].fmt, '%');
136 assert(perc != 0 && perc[1] == 'd');
137 assert(strchr(perc + 2, '%') == 0);
138 break;
139 case XsldbgCmdInfo::argStringNum:
140 perc = strchr(cmds[i].fmt, '%');
141 assert(perc != 0 && perc[1] == 's');
142 perc = strchr(perc + 2, '%');
143 assert(perc != 0 && perc[1] == 'd');
144 assert(strchr(perc + 2, '%') == 0);
145 break;
146 case XsldbgCmdInfo::argNumString:
147 perc = strchr(cmds[i].fmt, '%');
148 assert(perc != 0 && perc[1] == 'd');
149 perc = strchr(perc + 2, '%');
150 assert(perc != 0 && perc[1] == 's');
151 assert(strchr(perc + 2, '%') == 0);
152 break;
153 case XsldbgCmdInfo::argString2:
154 perc = strchr(cmds[i].fmt, '%');
155 assert(perc != 0 && perc[1] == 's');
156 perc = strchr(perc + 2, '%');
157 assert(perc != 0 && perc[1] == 's');
158 assert(strchr(perc + 2, '%') == 0);
159 break;
160 case XsldbgCmdInfo::argNum2:
161 perc = strchr(cmds[i].fmt, '%');
162 assert(perc != 0 && perc[1] == 'd');
163 perc = strchr(perc + 2, '%');
164 assert(perc != 0 && perc[1] == 'd');
165 assert(strchr(perc + 2, '%') == 0);
166 break;
169 #endif
172 XsldbgDriver::~XsldbgDriver()
177 QString
178 XsldbgDriver::driverName() const
180 return "XSLDBG";
183 QString
184 XsldbgDriver::defaultXsldbg()
186 return "xsldbg --lang en --shell --gdb";
189 QString
190 XsldbgDriver::defaultInvocation() const
192 return defaultXsldbg();
195 QStringList XsldbgDriver::boolOptionList() const
197 QStringList allOptions;
198 allOptions.append("verbose");
199 allOptions.append("repeat");
200 allOptions.append("debug");
201 allOptions.append("novalid");
202 allOptions.append("noout");
203 allOptions.append("html");
204 allOptions.append("docbook");
205 allOptions.append("nonet");
206 allOptions.append("catalogs");
207 allOptions.append("xinclude");
208 allOptions.append("profile");
209 return allOptions;
213 void
214 XsldbgDriver::slotReceiveOutput(KProcess * process, char *buffer,
215 int buflen)
217 //TRACE(buffer);
218 if (m_state != DSidle) {
219 // TRACE(buffer);
220 DebuggerDriver::slotReceiveOutput(process, buffer, buflen);
221 } else {
222 if (strncmp(buffer, "quit", 4) == 0) {
223 TRACE("Ignoring text when xsldbg is quiting");
224 } else {
225 TRACE
226 ("Stray output received by XsldbgDriver::slotReceiveOutput");
227 TRACE(buffer);
232 bool
233 XsldbgDriver::startup(QString cmdStr)
235 if (!DebuggerDriver::startup(cmdStr))
236 return false;
238 static const char xsldbgInitialize[] = "pwd\nsetoption gdb 2\n"; /* don't need to do anything else */
240 executeCmdString(DCinitialize, xsldbgInitialize, false);
242 return true;
245 void
246 XsldbgDriver::commandFinished(CmdQueueItem * cmd)
249 TRACE(__PRETTY_FUNCTION__);
250 // command string must be committed
251 if (!cmd->m_committed) {
252 // not commited!
253 TRACE("calling " +
254 (__PRETTY_FUNCTION__ +
255 (" with uncommited command:\n\t" + cmd->m_cmdString)));
256 return;
259 switch (cmd->m_cmd) {
260 case DCinitialize:
261 // get version number from preamble
263 int len;
264 QRegExp xsldbgVersion("^XSLDBG [0-9]+\\.[0-9]+\\.[0-9]+");
265 int offset = xsldbgVersion.match(m_output, 0, &len);
267 if (offset >= 0) {
268 char *start = m_output + offset + 7; // skip "^XSLDBG "
269 char *end;
271 TRACE("Reading version");
272 TRACE(start);
273 m_gdbMajor = strtol(start, &end, 10);
274 m_gdbMinor = strtol(end + 1, 0, 10); // skip "."
275 if (start == end) {
276 // nothing was parsed
277 m_gdbMajor = 0;
278 m_gdbMinor = 7;
280 } else {
281 // assume some default version (what would make sense?)
282 m_gdbMajor = 0;
283 m_gdbMinor = 7;
285 TRACE(QString("Got version ") +
286 QString::number(m_gdbMajor) + "." +
287 QString::number(m_gdbMinor));
288 break;
290 default:;
292 /* ok, the command is ready */
293 emit commandReceived(cmd, m_output);
295 switch (cmd->m_cmd) {
296 case DCbt:
297 case DCinfolocals:
298 case DCrun:
299 case DCcont:
300 case DCstep:
301 case DCnext:
302 case DCfinish:{
303 if (!::isErrorExpr(m_output))
304 parseMarker();
305 else{
306 // This only shows an error for DCinfolocals
307 // need to update KDebugger::handleRunCommand ?
308 KMessageBox::sorry(0L, m_output);
311 break;
313 case DCinfolinemain:
314 if (!m_xslFile.isEmpty())
315 emit activateFileLine(m_xslFile, 0, DbgAddr());
316 break;
318 default:;
322 void
323 XsldbgDriver::parseMarker()
326 // TRACE("parseMarker : xsldbg");
327 // TRACE(m_output);
328 int len, markerStart = -1;
329 char *p = m_output;
331 while (markerStart == -1) {
332 if ((p == 0) || (*p == '\0')) {
333 m_output[0] = '\0';
334 return;
336 //TRACE(QString("parseMarker is looking at :") + p);
337 markerStart = m_markerRE.match(p, 0, &len);
338 if (markerStart == -1) {
339 // try to marker on next line !
340 p = strchr(p, '\n');
341 if ((p != 0) && (*p != '\0'))
342 p++;
347 // extract the marker
348 char *startMarker = p + markerStart + len;
350 //TRACE(QString("found marker:") + startMarker);
351 char *endMarker = strchr(startMarker, '\n');
353 if (endMarker == 0)
354 return;
356 *endMarker = '\0';
358 // extract filename and line number
359 static QRegExp MarkerRE(" at line [0-9]+");
361 int lineNoStart = MarkerRE.match(startMarker, 0, &len);
363 if (lineNoStart >= 0) {
364 int lineNo = atoi(startMarker + lineNoStart + 8);
366 DbgAddr address;
368 // now show the window
369 startMarker[lineNoStart-1] = '\0'; /* split off file name */
370 TRACE("Got file and line number");
371 startMarker++;
372 TRACE(QString(startMarker) + ": " + QString::number(lineNo));
373 emit activateFileLine(startMarker, lineNo - 1, address);
379 * Escapes characters that might lead to problems when they appear on gdb's
380 * command line.
382 static void
383 normalizeStringArg(QString & arg)
386 * Remove trailing backslashes. This approach is a little simplistic,
387 * but we know that there is at the moment no case where a trailing
388 * backslash would make sense.
390 while (!arg.isEmpty() && arg[arg.length() - 1] == '\\') {
391 arg = arg.left(arg.length() - 1);
396 QString
397 XsldbgDriver::makeCmdString(DbgCommand cmd, QString strArg)
399 assert(cmd >= 0 && cmd < NUM_CMDS);
400 assert(cmds[cmd].argsNeeded == XsldbgCmdInfo::argString);
402 normalizeStringArg(strArg);
404 if (cmd == DCcd) {
405 // need the working directory when parsing the output
406 m_programWD = strArg;
407 } else if (cmd == DCexecutable) {
408 // want to display the XSL file
409 m_xslFile = strArg;
412 SIZED_QString(cmdString, MAX_FMTLEN + strArg.length());
413 cmdString.sprintf(cmds[cmd].fmt, strArg.latin1());
414 return cmdString;
417 QString
418 XsldbgDriver::makeCmdString(DbgCommand cmd, int intArg)
420 assert(cmd >= 0 && cmd < NUM_CMDS);
421 assert(cmds[cmd].argsNeeded == XsldbgCmdInfo::argNum);
423 SIZED_QString(cmdString, MAX_FMTLEN + 30);
425 cmdString.sprintf(cmds[cmd].fmt, intArg);
426 return cmdString;
429 QString
430 XsldbgDriver::makeCmdString(DbgCommand cmd, QString strArg, int intArg)
432 assert(cmd >= 0 && cmd < NUM_CMDS);
433 assert(cmds[cmd].argsNeeded == XsldbgCmdInfo::argStringNum ||
434 cmds[cmd].argsNeeded == XsldbgCmdInfo::argNumString ||
435 cmd == DCexamine || cmd == DCtty);
437 normalizeStringArg(strArg);
439 SIZED_QString(cmdString, MAX_FMTLEN + 30 + strArg.length());
441 if (cmd == DCtty) {
443 * intArg specifies which channels should be redirected to
444 * /dev/null. It is a value or'ed together from RDNstdin,
445 * RDNstdout, RDNstderr.
447 static const char *const runRedir[8] = {
449 " </dev/null",
450 " >/dev/null",
451 " </dev/null >/dev/null",
452 " 2>/dev/null",
453 " </dev/null 2>/dev/null",
454 " >/dev/null 2>&1",
455 " </dev/null >/dev/null 2>&1"
458 if (strArg.isEmpty())
459 intArg = 7; /* failsafe if no tty */
460 m_redirect = runRedir[intArg & 7];
462 return makeCmdString(DCtty, strArg); /* note: no problem if strArg empty */
465 if (cmd == DCexamine) {
466 // make a format specifier from the intArg
467 static const char size[16] = {
468 '\0', 'b', 'h', 'w', 'g'
470 static const char format[16] = {
471 '\0', 'x', 'd', 'u', 'o', 't',
472 'a', 'c', 'f', 's', 'i'
475 assert(MDTsizemask == 0xf); /* lowest 4 bits */
476 assert(MDTformatmask == 0xf0); /* next 4 bits */
477 int count = 16; /* number of entities to print */
478 char sizeSpec = size[intArg & MDTsizemask];
479 char formatSpec = format[(intArg & MDTformatmask) >> 4];
481 assert(sizeSpec != '\0');
482 assert(formatSpec != '\0');
483 // adjust count such that 16 lines are printed
484 switch (intArg & MDTformatmask) {
485 case MDTstring:
486 case MDTinsn:
487 break; /* no modification needed */
488 default:
489 // all cases drop through:
490 switch (intArg & MDTsizemask) {
491 case MDTbyte:
492 case MDThalfword:
493 count *= 2;
494 case MDTword:
495 count *= 2;
496 case MDTgiantword:
497 count *= 2;
499 break;
501 QString spec;
503 spec.sprintf("/%d%c%c", count, sizeSpec, formatSpec);
505 return makeCmdString(DCexamine, spec, strArg);
508 if (cmds[cmd].argsNeeded == XsldbgCmdInfo::argStringNum) {
509 // line numbers are zero-based
510 if (cmd == DCuntil || cmd == DCbreakline ||
511 cmd == DCtbreakline || cmd == DCinfoline) {
512 intArg++;
514 if (cmd == DCinfoline) {
515 // must split off file name part
516 int slash = strArg.findRev('/');
518 if (slash >= 0)
519 strArg = strArg.right(strArg.length() - slash - 1);
521 cmdString.sprintf(cmds[cmd].fmt, strArg.latin1(), intArg);
522 } else {
523 cmdString.sprintf(cmds[cmd].fmt, intArg, strArg.latin1());
525 return cmdString;
528 QString
529 XsldbgDriver::makeCmdString(DbgCommand cmd, QString strArg1,
530 QString strArg2)
532 assert(cmd >= 0 && cmd < NUM_CMDS);
533 assert(cmds[cmd].argsNeeded == XsldbgCmdInfo::argString2);
535 normalizeStringArg(strArg1);
536 normalizeStringArg(strArg2);
538 SIZED_QString(cmdString,
539 MAX_FMTLEN + strArg1.length() + strArg2.length());
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 SIZED_QString(cmdString, MAX_FMTLEN + 60);
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 if (strncmp(output,
690 errorWords[wordIndex],
691 errorWordLength[wordIndex]) == 0){
692 result = true;
693 TRACE(QString("Error/Warning/Information from xsldbg ") + output);
694 break;
698 return result;
702 * Returns true if the output is an error message. If wantErrorValue is
703 * true, a new VarTree object is created and filled with the error message.
705 static bool
706 parseErrorMessage(const char *output,
707 VarTree * &variable, bool wantErrorValue)
709 if (isErrorExpr(output)) {
710 if (wantErrorValue) {
711 // put the error message as value in the variable
712 variable = new VarTree(QString(), VarTree::NKplain);
713 const char *endMsg = strchr(output, '\n');
715 if (endMsg == 0)
716 endMsg = output + strlen(output);
717 variable->m_value = FROM_LATIN1(output, endMsg - output);
718 } else {
719 variable = 0;
721 return true;
723 return false;
727 void
728 XsldbgDriver::setPrintQStringDataCmd(const char* /*cmd*/)
732 VarTree *
733 XsldbgDriver::parseQCharArray(const char */*output*/, bool /*wantErrorValue*/,
734 bool /*qt3like*/)
736 VarTree *variable = 0;
738 TRACE("XsldbgDriver::parseQCharArray not implmented");
739 return variable;
742 static VarTree *
743 parseVar(const char *&s)
745 const char *p = s;
746 bool foundLocalVar = false;
747 VarTree *variable = 0L;
748 QString name;
750 VarTree::NameKind kind;
752 TRACE(__PRETTY_FUNCTION__);
753 TRACE(p);
755 if (parseErrorMessage(p, variable, false) == true) {
756 TRACE("Found error message");
757 return variable;
760 if (strncmp(p, " Local", 6) == 0) {
761 foundLocalVar = true;
762 /* skip " Local" */
763 p = p + 6;
764 TRACE("Found local variable");
765 } else if (strncmp(p, " Global", 7) == 0) {
766 /* skip " Global" */
767 p = p + 7;
768 TRACE("Found global variable");
769 } else if (strncmp(p, "= ", 2) == 0) {
770 /* we're processing the result of a "print command" */
771 /* find next line */
772 char *nextLine = strchr(p, '\n');
774 TRACE("Found print expr");
775 if (nextLine) {
776 p = p + 2; /* skip the "= " */
777 name = FROM_LATIN1(p, nextLine - p);
778 kind = VarTree::NKplain;
779 p = nextLine + 1;
780 variable = new VarTree(name, kind);
781 variable->m_varKind = VarTree::VKsimple;
782 variable->setDeleteChildren(true);
783 parseValue(p, variable);
784 return variable;
786 } else
787 return variable; /* don't know what to do this this data abort!! */
789 // skip whitespace
790 while (isspace(*p))
791 p++;
793 if (*p != '='){
794 // No value provided just a name
795 TRACE(QString("Parse var: name") + p);
796 if (!parseName(p, name, kind)) {
797 return 0;
799 variable = new VarTree(name, kind);
800 if (variable != 0L) {
801 variable->m_varKind = VarTree::VKsimple;
802 variable->setDeleteChildren(true);
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 VarTree(name, kind);
814 if (variable != 0L) {
815 variable->m_varKind = VarTree::VKsimple;
816 variable->setDeleteChildren(true);
818 if (*p == '\n')
819 p++;
820 if (!parseValue(p, variable)) {
821 delete variable;
822 return 0;
826 if (*p == '\n')
827 p++;
829 s = p;
830 return variable;
834 inline void
835 skipName(const char *&p)
837 // allow : (for enumeration values) and $ and . (for _vtbl.)
838 while (isalnum(*p) || *p == '_' || *p == ':' || *p == '$' || *p == '.')
839 p++;
842 static bool
843 parseName(const char *&s, QString & name, VarTree::NameKind & kind)
845 /* qDebug(__PRETTY_FUNCTION__); */
846 kind = VarTree::NKplain;
848 const char *p = s;
849 int len = 0;
851 // examples of names:
852 // help_cmd
854 while ((*p != '\n') && (*p != '\0')) {
855 len++;
856 p++;
860 name = FROM_LATIN1(s, len);
861 /* XSL variables will have a $ prefix to be evaluated
862 * properly */
863 //TRACE(QString("parseName got name" ) + name);
865 // return the new position
866 s = p;
867 return true;
870 static bool
871 parseValue(const char *&s, VarTree * variable)
873 const char *start = s, *end = s;
874 VarTree * childValue;
875 #define VALUE_END_MARKER_INDEX 0
877 /* This mark the end of a value */
878 static const char *marker[] = {
879 "\032\032", /* value end marker*/
880 "(xsldbg) ",
881 "Breakpoint at", /* stepped to next location */
882 "Breakpoint in", /* reached a set breakpoint */
883 "Reached ", /* reached template */
884 "Error:",
885 "Warning:",
886 "Information:",
887 "runtime error",
888 "xmlXPathEval:",
891 static char valueBuffer[2048];
892 int markerIndex = 0, foundEnd = 0;
893 size_t copySize;
895 if (variable == 0L)
896 return false; /* should never happen but .. */
898 while (start && (*start != '\0')) {
899 /* look for the next marker */
900 for (markerIndex = 0; marker[markerIndex] != 0; markerIndex++) {
901 foundEnd =
902 strncmp(start, marker[markerIndex],
903 strlen(marker[markerIndex])) == 0;
904 if (foundEnd)
905 break;
908 if (foundEnd)
909 break;
912 end = strchr(start, '\n');
913 if (end)
914 copySize = end - start;
915 else
916 copySize = strlen(start);
917 if (copySize >= sizeof(valueBuffer))
918 copySize = sizeof(valueBuffer)-1;
920 strncpy(valueBuffer, start, copySize);
921 valueBuffer[copySize] = '\0';
922 TRACE("Got value :");
923 TRACE(valueBuffer);
924 if ((variable->m_varKind == VarTree::VKsimple)) {
925 if (!variable->m_value.isEmpty()){
926 variable->m_varKind = VarTree::VKarray;
927 childValue = new VarTree(variable->m_value, VarTree::NKplain);
928 variable->appendChild(childValue);
929 childValue = new VarTree(valueBuffer, VarTree::NKplain);
930 variable->appendChild(childValue);
931 variable->m_value = "";
932 }else{
933 variable->m_value = valueBuffer;
935 }else{
936 childValue = new VarTree(valueBuffer, VarTree::NKplain);
937 variable->appendChild(childValue);
940 if (*end =='\n'){
941 start = end + 1;
942 }else{
943 start = end + 1;
944 break;
948 if (foundEnd == 0)
949 TRACE(QString("Unable to find end on value near :") + start);
951 // If we've got something otherthan a end of value marker then
952 // advance to the end of this buffer
953 if (markerIndex != VALUE_END_MARKER_INDEX){
954 while (start && *start != '\0')
955 start++;
956 }else{
957 start = start + strlen(marker[0]);
960 s = start;
962 return true;
967 * Parses a stack frame.
969 static void
970 parseFrameInfo(const char *&s, QString & func,
971 QString & file, int &lineNo, DbgAddr & /*address*/)
973 const char *p = s, *endPos = s + strlen(s);
974 QString lineNoString;
976 TRACE("parseFrameInfo");
978 lineNo = -1;
980 /* skip 'template :\" */
981 p = p + 11;
982 // TRACE(p);
983 func = "";
984 while ((*p != '\"') && (*p != '\0')) {
985 func.append(*p);
986 p++;
988 while ((*p != '\0') && *p != '"')
989 p++;
990 if (*p != '\0')
991 p++;
992 ASSERT(p <= endPos);
993 if (p >= endPos) {
994 /* panic */
995 return;
998 /* skip mode :".*" */
999 while ((*p != '\0') && *p != '"')
1000 p++;
1001 if (*p != '\0')
1002 p++;
1003 while ((*p != '\0') && *p != '"')
1004 p++;
1006 /* skip '" in file ' */
1007 p = p + 10;
1008 if(*p == '"')
1009 p++;
1010 // TRACE(p);
1011 file = "";
1012 while (!isspace(*p) && (*p != '\"') && (*p != '\0')) {
1013 file.append(*p);
1014 p++;
1016 if(*p == '"')
1017 p++;
1018 ASSERT(p <= endPos);
1019 if (p >= endPos) {
1020 /* panic */
1021 return;
1024 // TRACE(p);
1025 /* skip ' : line '" */
1026 p = p + 9;
1027 // TRACE(p);
1028 ASSERT(p <= endPos);
1029 if (p >= endPos) {
1030 /* panic */
1031 return;
1033 // TRACE(p);
1034 if (isdigit(*p)) {
1035 /* KDbg uses an offset of +1 for its line numbers */
1036 lineNo = atoi(p) - 1;
1037 lineNoString = QString::number(lineNo);
1039 /* convert func into format needed */
1040 func.append(" at ");
1041 func.append(file);
1042 func.append(':');
1043 func.append(lineNoString);
1045 /*advance to next line */
1046 p = strchr(p, '\n');
1047 if (p)
1048 p++;
1049 s = p;
1053 #undef ISSPACE
1056 * Parses a stack frame including its frame number
1058 static bool
1059 parseFrame(const char *&s, int &frameNo, QString & func,
1060 QString & file, int &lineNo, DbgAddr & address)
1063 // TRACE("XsldbgDriver ::parseFrame");
1064 /* skip leading 'where' or 'frame <frame_no>' */
1065 if ((strncmp(s, "where", 5) == 0) || (strncmp(s, "frame", 5) == 0)) {
1066 s = strchr(s, '\n');
1067 if ((*s != '\0') && (*s != '#'))
1068 s++;
1070 // TRACE(s);
1072 // Example:
1073 //#1 template :"/" in file /home/keith/anon_CVS/xsldbg/docs/en/xsldoc.xsl : line 21
1074 // must start with a hash mark followed by number
1075 if (s[0] != '#' || !isdigit(s[1]))
1076 return false;
1078 //TRACE("XsldbgDriver ::parseFrame got #");
1079 s++; /* skip the hash mark */
1080 // frame number
1081 frameNo = atoi(s);
1082 while (isdigit(*s))
1083 s++;
1085 //TRACE(QString("Got frame ").append(QString::number(frameNo)));
1086 // space
1087 while (isspace(*s))
1088 s++;
1089 parseFrameInfo(s, func, file, lineNo, address);
1090 // TRACE("Will next look at ");
1091 // TRACE(s);
1092 return true;
1095 void
1096 XsldbgDriver::parseBackTrace(const char *output,
1097 QList < StackFrame > &stack)
1099 QString func, file;
1100 int lineNo, frameNo;
1101 DbgAddr address;
1103 while (::parseFrame(output, frameNo, func, file, lineNo, address)) {
1104 StackFrame *frm = new StackFrame;
1106 frm->frameNo = frameNo;
1107 frm->fileName = file;
1108 frm->lineNo = lineNo;
1109 frm->address = address;
1110 frm->var = new VarTree(func, VarTree::NKplain);
1111 stack.append(frm);
1115 bool
1116 XsldbgDriver::parseFrameChange(const char *output, int &frameNo,
1117 QString & file, int &lineNo,
1118 DbgAddr & address)
1120 QString func;
1122 return::parseFrame(output, frameNo, func, file, lineNo, address);
1126 bool
1127 XsldbgDriver::parseBreakList(const char *output,
1128 QList < Breakpoint > &brks)
1130 TRACE("parseBreakList");
1131 /* skip the first blank line */
1132 const char *p;
1134 // split up a line
1135 QString location, file, lineNo;
1136 QString address;
1137 QString templateName;
1138 int hits = 0;
1139 int enabled = 0;
1140 uint ignoreCount = 0;
1141 QString condition;
1142 char *dummy;
1143 p = strchr(output, '\n');/* skip the first blank line*/
1145 while ((p != 0) && (*p != '\0')) {
1146 if (*p == '\n')
1147 p++;
1148 templateName = QString();
1149 Breakpoint::Type bpType = Breakpoint::breakpoint;
1150 //qDebug("Looking at :%s", p);
1151 if (strncmp(p, " Breakpoint", 11) != 0)
1152 break;
1153 p = p + 11;
1154 if (*p == '\0')
1155 break;
1157 //TRACE(p);
1158 // get Num
1159 long bpNum = strtol(p, &dummy, 10); /* don't care about overflows */
1161 p = dummy;
1162 if ((p == 0) || (p[1] == '\0'))
1163 break;
1164 p++;
1166 //TRACE(p);
1167 // Get breakpoint state ie enabled/disabled
1168 if (strncmp(p, "enabled", 7) == 0) {
1169 enabled = true;
1170 p = p + 7;
1171 } else {
1172 if (strncmp(p, "disabled", 8) == 0) {
1173 p = p + 8;
1174 enabled = false;
1175 } else{
1176 TRACE("Parse error in breakpoint list");
1177 TRACE(p);
1178 return false;
1182 //TRACE("Looking for template");
1183 //TRACE(p);
1184 if (strncmp(p, " for template: \"", 16) == 0){
1185 p = p + 16;
1186 //TRACE("Looking for template name near");
1187 //TRACE(p);
1188 /* get the template name */
1189 while (p && (*p != '\0') && (*p != '\"')){
1190 templateName.append(*p);
1191 p++;
1193 if (*p == '\"'){
1194 p++;
1195 }else{
1196 TRACE("Error missed \" near");
1197 TRACE(p);
1201 //TRACE("Looking for mode near");
1202 //TRACE(p);
1203 if (strncmp(p, " mode: \"", 8) == 0){
1204 p = p + 8;
1205 while (p && *p != '\"')
1206 p++;
1207 if (p)
1208 p++;
1211 if (strncmp(p, " in file ", 9) != 0){
1212 TRACE("Parse error in breakpoint list");
1213 TRACE(p);
1214 return false;
1218 /* skip ' in file ' */
1219 p = p + 9;
1220 // TRACE(p);
1222 if (*p == '\"')
1223 p++;
1224 /* grab file name */
1225 while ((*p != '\"') && !isspace(*p)) {
1226 file.append(*p);
1227 p++;
1229 if (*p == '\"')
1230 p++;
1231 if (*p == '\0')
1232 break;
1234 /* skip ' : line ' */
1235 p = p + 8;
1236 while (isspace(*p)) {
1237 p++;
1239 //TRACE(p);
1240 while (isdigit(*p)) {
1241 lineNo.append(*p);
1242 p++;
1247 Breakpoint *bp = new Breakpoint;
1248 if (bp != 0) {
1249 // take 1 of line number
1250 lineNo.setNum(lineNo.toInt() -1);
1251 bp->id = bpNum;
1252 bp->type = bpType;
1253 bp->temporary = false;
1254 bp->enabled = enabled;
1255 location.append("in ").append(templateName).append(" at ");
1256 location.append(file).append(":").append(lineNo);
1257 bp->location = location;
1258 bp->fileName = file;
1259 bp->lineNo = lineNo.toInt();
1260 bp->address = address;
1261 bp->hitCount = hits;
1262 bp->ignoreCount = ignoreCount;
1263 bp->condition = condition;
1264 brks.append(bp);
1265 location = "";
1266 lineNo = "";
1267 file = "";
1268 } else
1269 TRACE("Outof memory, breakpoint not created");
1271 if (p != 0) {
1272 p = strchr(p, '\n');
1273 if (p)
1274 p++;
1277 return true;
1280 bool
1281 XsldbgDriver::parseThreadList(const char */*output*/,
1282 QList < ThreadInfo > &/*threads*/)
1284 return true;
1287 bool
1288 XsldbgDriver::parseBreakpoint(const char *output, int &id,
1289 QString &file, int &lineNo, QString &address)
1291 // check for errors
1292 if ( strncmp(output, "Error:", 6) == 0) {
1293 return false;
1296 char *dummy;
1297 if (strncmp(output, "Breakpoint ", 11) != 0)
1298 return false;
1300 output += 11;
1301 if (!isdigit(*output))
1302 return false;
1304 // get Num
1305 id = strtol(output, &dummy, 10); /* don't care about overflows */
1306 if (output == dummy)
1307 return false;
1309 // the file name + lineNo will be filled in later from the breakpoint list
1310 file = address = QString();
1311 lineNo = 0;
1312 return true;
1315 void
1316 XsldbgDriver::parseLocals(const char *output, QList < VarTree > &newVars)
1319 /* keep going until error or xsldbg prompt is found */
1320 while (*output != '\0') {
1321 VarTree *variable = parseVar(output);
1323 if (variable == 0) {
1324 break;
1326 // do not add duplicates
1327 for (VarTree * o = newVars.first(); o != 0; o = newVars.next()) {
1328 if (o->getText() == variable->getText()) {
1329 delete variable;
1331 goto skipDuplicate;
1334 newVars.append(variable);
1335 skipDuplicate:;
1340 bool
1341 XsldbgDriver::parsePrintExpr(const char *output, bool wantErrorValue,
1342 VarTree * &var)
1344 // check for error conditions
1345 if (parseErrorMessage(output, var, wantErrorValue)) {
1346 return false;
1347 } else {
1348 // parse the variable
1349 var = parseVar(output);
1350 return true;
1354 bool
1355 XsldbgDriver::parseChangeWD(const char *output, QString & message)
1357 bool isGood = false;
1359 if (strncmp(output, "Change to directory", 20) == 0) {
1360 output = output + 20; /* skip 'Change to directory' */
1361 message = QString(output).simplifyWhiteSpace();
1362 if (message.isEmpty()) {
1363 message = i18n("New working directory: ") + m_programWD;
1364 isGood = true;
1367 return isGood;
1370 bool
1371 XsldbgDriver::parseChangeExecutable(const char *output, QString & message)
1373 message = output;
1374 TRACE(QString("XsldbgDriver::parseChangeExecutable :") + output);
1375 m_haveCoreFile = false;
1378 * The command is successful if there is no output or the single
1379 * message (no debugging symbols found)...
1381 QRegExp exp(".*Load of source deferred. Use the run command.*");
1382 int len, index = exp.match(output, 0, &len);
1384 if (index != -1) {
1385 TRACE("Parsed stylesheet executable");
1386 message = "";
1388 return (output[0] == '\0') || (index != -1);
1391 bool
1392 XsldbgDriver::parseCoreFile(const char *output)
1394 TRACE("XsldbgDriver::parseCoreFile");
1395 TRACE(output);
1396 QRegExp exp(".*Load of data file deferred. Use the run command.*");
1397 int len, index = exp.match(output, 0, &len);
1399 if (index != -1) {
1400 m_haveCoreFile = true;
1401 TRACE("Parsed data file name");
1404 return m_haveCoreFile;
1407 uint
1408 XsldbgDriver::parseProgramStopped(const char *output, QString & message)
1410 /* Not sure about this function leave it here for the moment */
1412 * return DebuggerDriver::SFrefreshBreak & DebuggerDriver::SFprogramActive;
1415 // go through the output, line by line, checking what we have
1416 const char *start = output - 1;
1417 uint flags = SFprogramActive;
1419 message = QString();
1420 do {
1421 start++; /* skip '\n' */
1423 if (strncmp(start, "Finished stylesheet\n\032\032\n", 21) == 0){
1424 // flags &= ~SFprogramActive;
1425 break;
1428 // next line, please
1429 start = strchr(start, '\n');
1430 } while (start != 0);
1432 return flags;
1435 void
1436 XsldbgDriver::parseSharedLibs(const char */*output*/, QStrList & /*shlibs*/)
1438 /* empty */
1441 bool
1442 XsldbgDriver::parseFindType(const char */*output*/, QString & /*type*/)
1444 return true;
1447 void
1448 XsldbgDriver::parseRegisters(const char */*output*/,
1449 QList < RegisterInfo > &/*regs*/)
1454 bool
1455 XsldbgDriver::parseInfoLine(const char */*output*/, QString & /*addrFrom*/,
1456 QString & /*addrTo*/)
1458 return false;
1461 void
1462 XsldbgDriver::parseDisassemble(const char */*output*/,
1463 QList < DisassembledCode > &/*code*/)
1465 /* empty */
1468 QString
1469 XsldbgDriver::parseMemoryDump(const char */*output*/,
1470 QList < MemoryDump > &/*memdump*/)
1472 return i18n("No memory dump available");
1475 QString
1476 XsldbgDriver::parseSetVariable(const char */*output*/)
1478 QString msg;
1479 return msg;
1483 #include "xsldbgdriver.moc"