Switch KProcess to K3Process.
[kdbg.git] / kdbg / xsldbgdriver.cpp
blobcc8b807318ba0e23a5ec659fe9cfcbdd66949bf9
1 /*
2 * Copyright Johannes Sixt, Keith Isdale
3 * This file is licensed under the GNU General Public License Version 2.
4 * See the file COPYING in the toplevel directory of the source directory.
5 */
7 #include "xsldbgdriver.h"
8 #include "exprwnd.h"
9 #include <qstringlist.h>
10 #include <klocale.h> /* i18n */
11 #include <ctype.h>
12 #include <stdlib.h> /* strtol, atoi */
13 #include <string.h> /* strcpy */
14 #include <kmessagebox.h>
16 #include "assert.h"
17 #include "mydebug.h"
20 static ExprValue *parseVar(const char *&s);
21 static bool parseName(const char *&s, QString & name,
22 VarTree::NameKind & kind);
23 static bool parseValue(const char *&s, ExprValue * variable);
24 static bool isErrorExpr(const char *output);
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, "data %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, "step\n", XsldbgCmdInfo::argNone},
69 {DCnext, "next\n", XsldbgCmdInfo::argNone},
70 {DCnexti, "next\n", XsldbgCmdInfo::argNone},
71 {DCfinish, "stepup\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, "break -l %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 {DCprintDeref, "print 'print (*%s)'\n", XsldbgCmdInfo::argString},
85 {DCprintStruct, "print 'print %s'\n", XsldbgCmdInfo::argString},
86 {DCprintQStringStruct, "print 'print %s'\n", XsldbgCmdInfo::argString},
87 {DCframe, "frame %d\n", XsldbgCmdInfo::argNum},
88 {DCfindType, "print 'whatis %s'\n", XsldbgCmdInfo::argString},
89 {DCinfosharedlib, "stylesheets\n", XsldbgCmdInfo::argNone},
90 {DCthread, "print 'thread %d'\n", XsldbgCmdInfo::argNum},
91 {DCinfothreads, "print 'info threads'\n", XsldbgCmdInfo::argNone},
92 {DCinfobreak, "show\n", XsldbgCmdInfo::argNone},
93 {DCcondition, "print 'condition %d %s'\n", XsldbgCmdInfo::argNumString},
94 {DCsetpc, "print 'set variable $pc=%s'\n", XsldbgCmdInfo::argString},
95 {DCignore, "print 'ignore %d %d'\n", XsldbgCmdInfo::argNum2},
96 {DCprintWChar, "print 'ignore %s'\n", XsldbgCmdInfo::argString},
97 {DCsetvariable, "set %s %s\n", XsldbgCmdInfo::argString2},
100 #define NUM_CMDS (int(sizeof(cmds)/sizeof(cmds[0])))
101 #define MAX_FMTLEN 200
103 XsldbgDriver::XsldbgDriver():
104 DebuggerDriver()
106 m_haveDataFile = FALSE;
108 #ifndef NDEBUG
109 // check command info array
110 const 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 --lang en --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(K3Process * 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\nsetoption gdb 2\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 /* ok, the command is ready */
254 emit commandReceived(cmd, m_output);
256 switch (cmd->m_cmd) {
257 case DCbt:
258 case DCinfolocals:
259 case DCrun:
260 case DCcont:
261 case DCstep:
262 case DCnext:
263 case DCfinish:{
264 if (!::isErrorExpr(m_output))
265 parseMarker();
266 else{
267 // This only shows an error for DCinfolocals
268 // need to update KDebugger::handleRunCommand ?
269 KMessageBox::sorry(0L, m_output);
272 break;
274 case DCinfolinemain:
275 if (!m_xslFile.isEmpty())
276 emit activateFileLine(m_xslFile, 0, DbgAddr());
277 break;
279 default:;
284 XsldbgDriver::findPrompt(const char* output, size_t len) const
287 * If there's a prompt string in the collected output, it must be at
288 * the very end. We do a quick check whether the last characters of
289 * output are suitable and do the full search only if they are.
291 if (len < 11 || output[len-1] != ' ' || output[len-2] != '>')
292 return -1;
294 // There can be text between "(xsldbg) " and the "> " at the end
295 // since we do not know what that text is, we accept the former
296 // anywhere in the output.
297 const char* prompt = strstr(output, "(xsldbg) ");
298 return prompt ? prompt-output : 0;
301 void
302 XsldbgDriver::parseMarker()
304 char *p = m_output;
306 for (;;) {
307 if ((p == 0) || (*p == '\0')) {
308 m_output[0] = '\0';
309 return;
311 if (strncmp(p, "Breakpoint for file ", 20) == 0)
312 break;
313 // try to marker on next line !
314 p = strchr(p, '\n');
315 if ((p != 0) && (*p != '\0'))
316 p++;
320 // extract the marker
321 char *startMarker = p + 20;
323 //TRACE(QString("found marker:") + startMarker);
324 char *endMarker = strchr(startMarker, '\n');
326 if (endMarker == 0)
327 return;
329 *endMarker = '\0';
331 // extract filename and line number
332 static QRegExp MarkerRE(" at line (\\d+)");
334 int lineNoStart = MarkerRE.search(startMarker);
336 if (lineNoStart >= 0) {
337 int lineNo = MarkerRE.cap(1).toInt();
339 DbgAddr address;
341 // now show the window
342 startMarker[lineNoStart-1] = '\0'; /* split off file name */
343 TRACE("Got file and line number");
344 startMarker++;
345 TRACE(QString(startMarker) + ": " + QString::number(lineNo));
346 emit activateFileLine(startMarker, lineNo - 1, address);
352 * Escapes characters that might lead to problems when they appear on gdb's
353 * command line.
355 static void
356 normalizeStringArg(QString & arg)
359 * Remove trailing backslashes. This approach is a little simplistic,
360 * but we know that there is at the moment no case where a trailing
361 * backslash would make sense.
363 while (!arg.isEmpty() && arg[arg.length() - 1] == '\\') {
364 arg = arg.left(arg.length() - 1);
369 QString
370 XsldbgDriver::makeCmdString(DbgCommand cmd, QString strArg)
372 assert(cmd >= 0 && cmd < NUM_CMDS);
373 assert(cmds[cmd].argsNeeded == XsldbgCmdInfo::argString);
375 normalizeStringArg(strArg);
377 if (cmd == DCcd) {
378 // need the working directory when parsing the output
379 m_programWD = strArg;
380 } else if (cmd == DCexecutable) {
381 // want to display the XSL file
382 m_xslFile = strArg;
385 QString cmdString;
386 cmdString.sprintf(cmds[cmd].fmt, strArg.latin1());
387 return cmdString;
390 QString
391 XsldbgDriver::makeCmdString(DbgCommand cmd, int intArg)
393 assert(cmd >= 0 && cmd < NUM_CMDS);
394 assert(cmds[cmd].argsNeeded == XsldbgCmdInfo::argNum);
396 QString cmdString;
397 cmdString.sprintf(cmds[cmd].fmt, intArg);
398 return cmdString;
401 QString
402 XsldbgDriver::makeCmdString(DbgCommand cmd, QString strArg, int intArg)
404 assert(cmd >= 0 && cmd < NUM_CMDS);
405 assert(cmds[cmd].argsNeeded == XsldbgCmdInfo::argStringNum ||
406 cmds[cmd].argsNeeded == XsldbgCmdInfo::argNumString ||
407 cmd == DCexamine || cmd == DCtty);
409 normalizeStringArg(strArg);
411 QString cmdString;
413 if (cmd == DCtty) {
415 * intArg specifies which channels should be redirected to
416 * /dev/null. It is a value or'ed together from RDNstdin,
417 * RDNstdout, RDNstderr.
419 static const char *const runRedir[8] = {
421 " </dev/null",
422 " >/dev/null",
423 " </dev/null >/dev/null",
424 " 2>/dev/null",
425 " </dev/null 2>/dev/null",
426 " >/dev/null 2>&1",
427 " </dev/null >/dev/null 2>&1"
430 if (strArg.isEmpty())
431 intArg = 7; /* failsafe if no tty */
432 m_redirect = runRedir[intArg & 7];
434 return makeCmdString(DCtty, strArg); /* note: no problem if strArg empty */
437 if (cmd == DCexamine) {
438 // make a format specifier from the intArg
439 static const char size[16] = {
440 '\0', 'b', 'h', 'w', 'g'
442 static const char format[16] = {
443 '\0', 'x', 'd', 'u', 'o', 't',
444 'a', 'c', 'f', 's', 'i'
447 assert(MDTsizemask == 0xf); /* lowest 4 bits */
448 assert(MDTformatmask == 0xf0); /* next 4 bits */
449 int count = 16; /* number of entities to print */
450 char sizeSpec = size[intArg & MDTsizemask];
451 char formatSpec = format[(intArg & MDTformatmask) >> 4];
453 assert(sizeSpec != '\0');
454 assert(formatSpec != '\0');
455 // adjust count such that 16 lines are printed
456 switch (intArg & MDTformatmask) {
457 case MDTstring:
458 case MDTinsn:
459 break; /* no modification needed */
460 default:
461 // all cases drop through:
462 switch (intArg & MDTsizemask) {
463 case MDTbyte:
464 case MDThalfword:
465 count *= 2;
466 case MDTword:
467 count *= 2;
468 case MDTgiantword:
469 count *= 2;
471 break;
473 QString spec;
475 spec.sprintf("/%d%c%c", count, sizeSpec, formatSpec);
477 return makeCmdString(DCexamine, spec, strArg);
480 if (cmds[cmd].argsNeeded == XsldbgCmdInfo::argStringNum) {
481 // line numbers are zero-based
482 if (cmd == DCuntil || cmd == DCbreakline ||
483 cmd == DCtbreakline || cmd == DCinfoline) {
484 intArg++;
486 if (cmd == DCinfoline) {
487 // must split off file name part
488 int slash = strArg.findRev('/');
490 if (slash >= 0)
491 strArg = strArg.right(strArg.length() - slash - 1);
493 cmdString.sprintf(cmds[cmd].fmt, strArg.latin1(), intArg);
494 } else {
495 cmdString.sprintf(cmds[cmd].fmt, intArg, strArg.latin1());
497 return cmdString;
500 QString
501 XsldbgDriver::makeCmdString(DbgCommand cmd, QString strArg1,
502 QString strArg2)
504 assert(cmd >= 0 && cmd < NUM_CMDS);
505 assert(cmds[cmd].argsNeeded == XsldbgCmdInfo::argString2);
507 normalizeStringArg(strArg1);
508 normalizeStringArg(strArg2);
510 QString cmdString;
511 cmdString.sprintf(cmds[cmd].fmt, strArg1.latin1(), strArg2.latin1());
512 return cmdString;
515 QString
516 XsldbgDriver::makeCmdString(DbgCommand cmd, int intArg1, int intArg2)
518 assert(cmd >= 0 && cmd < NUM_CMDS);
519 assert(cmds[cmd].argsNeeded == XsldbgCmdInfo::argNum2);
521 QString cmdString;
522 cmdString.sprintf(cmds[cmd].fmt, intArg1, intArg2);
523 return cmdString;
526 CmdQueueItem *
527 XsldbgDriver::executeCmd(DbgCommand cmd, bool clearLow)
529 assert(cmd >= 0 && cmd < NUM_CMDS);
530 assert(cmds[cmd].argsNeeded == XsldbgCmdInfo::argNone);
532 if (cmd == DCrun) {
533 m_haveCoreFile = false;
536 return executeCmdString(cmd, cmds[cmd].fmt, clearLow);
539 CmdQueueItem *
540 XsldbgDriver::executeCmd(DbgCommand cmd, QString strArg, bool clearLow)
542 return executeCmdString(cmd, makeCmdString(cmd, strArg), clearLow);
545 CmdQueueItem *
546 XsldbgDriver::executeCmd(DbgCommand cmd, int intArg, bool clearLow)
549 return executeCmdString(cmd, makeCmdString(cmd, intArg), clearLow);
552 CmdQueueItem *
553 XsldbgDriver::executeCmd(DbgCommand cmd, QString strArg, int intArg,
554 bool clearLow)
556 return executeCmdString(cmd, makeCmdString(cmd, strArg, intArg),
557 clearLow);
560 CmdQueueItem *
561 XsldbgDriver::executeCmd(DbgCommand cmd, QString strArg1, QString strArg2,
562 bool clearLow)
564 return executeCmdString(cmd, makeCmdString(cmd, strArg1, strArg2),
565 clearLow);
568 CmdQueueItem *
569 XsldbgDriver::executeCmd(DbgCommand cmd, int intArg1, int intArg2,
570 bool clearLow)
572 return executeCmdString(cmd, makeCmdString(cmd, intArg1, intArg2),
573 clearLow);
576 CmdQueueItem *
577 XsldbgDriver::queueCmd(DbgCommand cmd, QueueMode mode)
579 return queueCmdString(cmd, cmds[cmd].fmt, mode);
582 CmdQueueItem *
583 XsldbgDriver::queueCmd(DbgCommand cmd, QString strArg, QueueMode mode)
585 return queueCmdString(cmd, makeCmdString(cmd, strArg), mode);
588 CmdQueueItem *
589 XsldbgDriver::queueCmd(DbgCommand cmd, int intArg, QueueMode mode)
591 return queueCmdString(cmd, makeCmdString(cmd, intArg), mode);
594 CmdQueueItem *
595 XsldbgDriver::queueCmd(DbgCommand cmd, QString strArg, int intArg,
596 QueueMode mode)
598 return queueCmdString(cmd, makeCmdString(cmd, strArg, intArg), mode);
601 CmdQueueItem *
602 XsldbgDriver::queueCmd(DbgCommand cmd, QString strArg1, QString strArg2,
603 QueueMode mode)
605 return queueCmdString(cmd, makeCmdString(cmd, strArg1, strArg2), mode);
608 void
609 XsldbgDriver::terminate()
611 qDebug("XsldbgDriver::Terminate");
612 flushCommands();
613 executeCmdString(DCinitialize, "quit\n", true);
614 kill(SIGTERM);
615 m_state = DSidle;
618 void
619 XsldbgDriver::detachAndTerminate()
621 qDebug("XsldbgDriver::detachAndTerminate");
622 flushCommands();
623 executeCmdString(DCinitialize, "quit\n", true);
624 kill(SIGINT);
627 void
628 XsldbgDriver::interruptInferior()
630 // remove accidentally queued commands
631 qDebug("interruptInferior");
632 flushHiPriQueue();
633 kill(SIGINT);
636 static bool
637 isErrorExpr(const char *output)
639 int wordIndex;
640 bool result = false;
641 #define ERROR_WORD_COUNT 6
642 static const char *errorWords[ERROR_WORD_COUNT] = {
643 "Error:",
644 "error:", // libxslt error
645 "Unknown command",
646 "Warning:",
647 "warning:", // libxslt warning
648 "Information:" // xsldbg information
650 static int errorWordLength[ERROR_WORD_COUNT] = {
651 6, /* Error */
652 6, /* rror */
653 15, /* Unknown command*/
654 8, /* Warning */
655 8, /* warning */
656 12 /* Information */
659 for (wordIndex = 0; wordIndex < ERROR_WORD_COUNT; wordIndex++){
660 // ignore any warnings relating to local variables not being available
661 if (strncmp(output,
662 errorWords[wordIndex],
663 errorWordLength[wordIndex]) == 0 &&
664 (wordIndex == 0 && strstr(output, "try stepping past the xsl:param") == 0) ) {
665 result = true;
666 TRACE(QString("Error/Warning/Information 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 ExprValue object is created and filled with the error message.
678 static bool
679 parseErrorMessage(const char *output,
680 ExprValue * &variable, bool wantErrorValue)
682 if (isErrorExpr(output)) {
683 if (wantErrorValue) {
684 // put the error message as value in the variable
685 variable = new ExprValue(QString(), VarTree::NKplain);
686 const char *endMsg = strchr(output, '\n');
688 if (endMsg == 0)
689 endMsg = output + strlen(output);
690 variable->m_value = QString::fromLatin1(output, endMsg - output);
691 } else {
692 variable = 0;
694 return true;
696 return false;
700 void
701 XsldbgDriver::setPrintQStringDataCmd(const char* /*cmd*/)
705 ExprValue *
706 XsldbgDriver::parseQCharArray(const char */*output*/, bool /*wantErrorValue*/,
707 bool /*qt3like*/)
709 ExprValue *variable = 0;
711 TRACE("XsldbgDriver::parseQCharArray not implmented");
712 return variable;
715 static ExprValue *
716 parseVar(const char *&s)
718 const char *p = s;
719 bool foundLocalVar = false;
720 ExprValue *variable = 0L;
721 QString name;
723 VarTree::NameKind kind;
725 TRACE(__PRETTY_FUNCTION__);
726 TRACE(p);
728 if (parseErrorMessage(p, variable, false) == true) {
729 TRACE("Found error message");
730 return variable;
733 if (strncmp(p, " Local", 6) == 0) {
734 foundLocalVar = true;
735 /* skip " Local" */
736 p = p + 6;
737 TRACE("Found local variable");
738 } else if (strncmp(p, " Global", 7) == 0) {
739 /* skip " Global" */
740 p = p + 7;
741 TRACE("Found global variable");
742 } else if (strncmp(p, "= ", 2) == 0) {
743 /* we're processing the result of a "print command" */
744 /* find next line */
745 const char *nextLine = strchr(p, '\n');
747 TRACE("Found print expr");
748 if (nextLine) {
749 p = p + 2; /* skip the "= " */
750 name = QString::fromLatin1(p, nextLine - p);
751 kind = VarTree::NKplain;
752 p = nextLine + 1;
753 variable = new ExprValue(name, kind);
754 variable->m_varKind = VarTree::VKsimple;
755 parseValue(p, variable);
756 return variable;
758 } else
759 return variable; /* don't know what to do this this data abort!! */
761 // skip whitespace
762 while (isspace(*p))
763 p++;
765 if (*p != '='){
766 // No value provided just a name
767 TRACE(QString("Parse var: name") + p);
768 if (!parseName(p, name, kind)) {
769 return 0;
771 variable = new ExprValue(name, kind);
772 if (variable != 0L) {
773 variable->m_varKind = VarTree::VKsimple;
775 }else{
776 p++;
777 // skip whitespace
778 while (isspace(*p))
779 p++;
780 TRACE(QString("Parse var: name") + p);
781 if (!parseName(p, name, kind)) {
782 return 0;
784 variable = new ExprValue(name, kind);
785 if (variable != 0L) {
786 variable->m_varKind = VarTree::VKsimple;
788 if (*p == '\n')
789 p++;
790 if (!parseValue(p, variable)) {
791 delete variable;
792 return 0;
796 if (*p == '\n')
797 p++;
799 s = p;
800 return variable;
804 inline void
805 skipName(const char *&p)
807 // allow : (for enumeration values) and $ and . (for _vtbl.)
808 while (isalnum(*p) || *p == '_' || *p == ':' || *p == '$' || *p == '.')
809 p++;
812 static bool
813 parseName(const char *&s, QString & name, VarTree::NameKind & kind)
815 /* qDebug(__PRETTY_FUNCTION__); */
816 kind = VarTree::NKplain;
818 const char *p = s;
819 int len = 0;
821 // examples of names:
822 // help_cmd
824 while ((*p != '\n') && (*p != '\0')) {
825 len++;
826 p++;
830 name = QString::fromLatin1(s, len);
831 /* XSL variables will have a $ prefix to be evaluated
832 * properly */
833 //TRACE(QString("parseName got name" ) + name);
835 // return the new position
836 s = p;
837 return true;
840 static bool
841 parseValue(const char *&s, ExprValue * variable)
843 const char *start = s, *end = s;
844 ExprValue * childValue;
845 #define VALUE_END_MARKER_INDEX 0
847 /* This mark the end of a value */
848 static const char *marker[] = {
849 "\032\032", /* value end marker*/
850 "(xsldbg) ",
851 "Breakpoint at", /* stepped to next location */
852 "Breakpoint in", /* reached a set breakpoint */
853 "Reached ", /* reached template */
854 "Error:",
855 "Warning:",
856 "Information:",
857 "runtime error",
858 "xmlXPathEval:",
861 static char valueBuffer[2048];
862 int markerIndex = 0, foundEnd = 0;
863 size_t copySize;
865 if (variable == 0L)
866 return false; /* should never happen but .. */
868 while (start && (*start != '\0')) {
869 /* look for the next marker */
870 for (markerIndex = 0; marker[markerIndex] != 0; markerIndex++) {
871 foundEnd =
872 strncmp(start, marker[markerIndex],
873 strlen(marker[markerIndex])) == 0;
874 if (foundEnd)
875 break;
878 if (foundEnd)
879 break;
882 end = strchr(start, '\n');
883 if (end)
884 copySize = end - start;
885 else
886 copySize = strlen(start);
887 if (copySize >= sizeof(valueBuffer))
888 copySize = sizeof(valueBuffer)-1;
890 strncpy(valueBuffer, start, copySize);
891 valueBuffer[copySize] = '\0';
892 TRACE("Got value :");
893 TRACE(valueBuffer);
894 if ((variable->m_varKind == VarTree::VKsimple)) {
895 if (!variable->m_value.isEmpty()){
896 variable->m_varKind = VarTree::VKarray;
897 childValue = new ExprValue(variable->m_value, VarTree::NKplain);
898 variable->appendChild(childValue);
899 childValue = new ExprValue(valueBuffer, VarTree::NKplain);
900 variable->appendChild(childValue);
901 variable->m_value = "";
902 }else{
903 variable->m_value = valueBuffer;
905 }else{
906 childValue = new ExprValue(valueBuffer, VarTree::NKplain);
907 variable->appendChild(childValue);
910 if (*end =='\n'){
911 start = end + 1;
912 }else{
913 start = end + 1;
914 break;
918 if (foundEnd == 0)
919 TRACE(QString("Unable to find end on value near :") + start);
921 // If we've got something otherthan a end of value marker then
922 // advance to the end of this buffer
923 if (markerIndex != VALUE_END_MARKER_INDEX){
924 while (start && *start != '\0')
925 start++;
926 }else{
927 start = start + strlen(marker[0]);
930 s = start;
932 return true;
937 * Parses a stack frame.
939 static void
940 parseFrameInfo(const char *&s, QString & func,
941 QString & file, int &lineNo, DbgAddr & /*address*/)
943 const char *p = s, *endPos = s + strlen(s);
944 QString lineNoString;
946 TRACE("parseFrameInfo");
948 lineNo = -1;
950 /* skip 'template :\" */
951 p = p + 11;
952 // TRACE(p);
953 func = "";
954 while ((*p != '\"') && (*p != '\0')) {
955 func.append(*p);
956 p++;
958 while ((*p != '\0') && *p != '"')
959 p++;
960 if (*p != '\0')
961 p++;
962 ASSERT(p <= endPos);
963 if (p >= endPos) {
964 /* panic */
965 return;
968 /* skip mode :".*" */
969 while ((*p != '\0') && *p != '"')
970 p++;
971 if (*p != '\0')
972 p++;
973 while ((*p != '\0') && *p != '"')
974 p++;
976 /* skip '" in file ' */
977 p = p + 10;
978 if(*p == '"')
979 p++;
980 // TRACE(p);
981 file = "";
982 while (!isspace(*p) && (*p != '\"') && (*p != '\0')) {
983 file.append(*p);
984 p++;
986 if(*p == '"')
987 p++;
988 ASSERT(p <= endPos);
989 if (p >= endPos) {
990 /* panic */
991 return;
994 // TRACE(p);
995 /* skip ' : line '" */
996 p = p + 9;
997 // TRACE(p);
998 ASSERT(p <= endPos);
999 if (p >= endPos) {
1000 /* panic */
1001 return;
1003 // TRACE(p);
1004 if (isdigit(*p)) {
1005 /* KDbg uses an offset of +1 for its line numbers */
1006 lineNo = atoi(p) - 1;
1007 lineNoString = QString::number(lineNo);
1009 /* convert func into format needed */
1010 func.append(" at ");
1011 func.append(file);
1012 func.append(':');
1013 func.append(lineNoString);
1015 /*advance to next line */
1016 p = strchr(p, '\n');
1017 if (p)
1018 p++;
1019 s = p;
1023 #undef ISSPACE
1026 * Parses a stack frame including its frame number
1028 static bool
1029 parseFrame(const char *&s, int &frameNo, QString & func,
1030 QString & file, int &lineNo, DbgAddr & address)
1033 // TRACE("XsldbgDriver ::parseFrame");
1034 /* skip leading 'where' or 'frame <frame_no>' */
1035 if ((strncmp(s, "where", 5) == 0) || (strncmp(s, "frame", 5) == 0)) {
1036 s = strchr(s, '\n');
1037 if ((*s != '\0') && (*s != '#'))
1038 s++;
1040 // TRACE(s);
1042 // Example:
1043 //#1 template :"/" in file /home/keith/anon_CVS/xsldbg/docs/en/xsldoc.xsl : line 21
1044 // must start with a hash mark followed by number
1045 if (s[0] != '#' || !isdigit(s[1]))
1046 return false;
1048 //TRACE("XsldbgDriver ::parseFrame got #");
1049 s++; /* skip the hash mark */
1050 // frame number
1051 frameNo = atoi(s);
1052 while (isdigit(*s))
1053 s++;
1055 //TRACE(QString("Got frame ").append(QString::number(frameNo)));
1056 // space
1057 while (isspace(*s))
1058 s++;
1059 parseFrameInfo(s, func, file, lineNo, address);
1060 // TRACE("Will next look at ");
1061 // TRACE(s);
1062 return true;
1065 void
1066 XsldbgDriver::parseBackTrace(const char *output,
1067 std::list < StackFrame > &stack)
1069 QString func, file;
1070 int lineNo, frameNo;
1071 DbgAddr address;
1073 while (::parseFrame(output, frameNo, func, file, lineNo, address)) {
1074 stack.push_back(StackFrame());
1075 StackFrame* frm = &stack.back();
1077 frm->frameNo = frameNo;
1078 frm->fileName = file;
1079 frm->lineNo = lineNo;
1080 frm->address = address;
1081 frm->var = new ExprValue(func, VarTree::NKplain);
1085 bool
1086 XsldbgDriver::parseFrameChange(const char *output, int &frameNo,
1087 QString & file, int &lineNo,
1088 DbgAddr & address)
1090 QString func;
1092 return::parseFrame(output, frameNo, func, file, lineNo, address);
1096 bool
1097 XsldbgDriver::parseBreakList(const char *output,
1098 std::list < Breakpoint > &brks)
1100 TRACE("parseBreakList");
1101 /* skip the first blank line */
1102 const char *p;
1104 // split up a line
1105 Breakpoint bp;
1106 char *dummy;
1107 p = strchr(output, '\n');/* skip the first blank line*/
1109 while ((p != 0) && (*p != '\0')) {
1110 if (*p == '\n')
1111 p++;
1112 QString templateName;
1113 //qDebug("Looking at :%s", p);
1114 if (strncmp(p, " Breakpoint", 11) != 0)
1115 break;
1116 p = p + 11;
1117 if (*p == '\0')
1118 break;
1120 //TRACE(p);
1121 // get Num
1122 bp.id = strtol(p, &dummy, 10); /* don't care about overflows */
1124 p = dummy;
1125 if ((p == 0) || (p[1] == '\0'))
1126 break;
1127 p++;
1129 //TRACE(p);
1130 // Get breakpoint state ie enabled/disabled
1131 if (strncmp(p, "enabled", 7) == 0) {
1132 bp.enabled = true;
1133 p = p + 7;
1134 } else {
1135 if (strncmp(p, "disabled", 8) == 0) {
1136 p = p + 8;
1137 bp.enabled = false;
1138 } else{
1139 TRACE("Parse error in breakpoint list");
1140 TRACE(p);
1141 return false;
1145 //TRACE("Looking for template");
1146 //TRACE(p);
1147 if (strncmp(p, " for template: \"", 16) == 0){
1148 p = p + 16;
1149 //TRACE("Looking for template name near");
1150 //TRACE(p);
1151 /* get the template name */
1152 while (p && (*p != '\0') && (*p != '\"')){
1153 templateName.append(*p);
1154 p++;
1156 if (*p == '\"'){
1157 p++;
1158 }else{
1159 TRACE("Error missed \" near");
1160 TRACE(p);
1164 //TRACE("Looking for mode near");
1165 //TRACE(p);
1166 if (strncmp(p, " mode: \"", 8) == 0){
1167 p = p + 8;
1168 while (p && *p != '\"')
1169 p++;
1170 if (p)
1171 p++;
1174 if (strncmp(p, " in file ", 9) != 0){
1175 TRACE("Parse error in breakpoint list");
1176 TRACE(p);
1177 return false;
1181 /* skip ' in file ' */
1182 p = p + 9;
1183 // TRACE(p);
1185 if (*p == '\"')
1186 p++;
1187 /* grab file name */
1188 QString file;
1189 while ((*p != '\"') && !isspace(*p)) {
1190 file.append(*p);
1191 p++;
1193 if (*p == '\"')
1194 p++;
1195 if (*p == '\0')
1196 break;
1198 /* skip ' : line ' */
1199 p = p + 8;
1200 while (isspace(*p)) {
1201 p++;
1203 //TRACE(p);
1204 QString lineNo;
1205 while (isdigit(*p)) {
1206 lineNo.append(*p);
1207 p++;
1210 // bp.lineNo is zero-based
1211 bp.lineNo = lineNo.toInt() - 1;
1212 bp.location = QString("in %1 at %2:%3").arg(templateName, file, lineNo);
1213 bp.fileName = file;
1214 brks.push_back(bp);
1216 if (p != 0) {
1217 p = strchr(p, '\n');
1218 if (p)
1219 p++;
1222 return true;
1225 std::list<ThreadInfo>
1226 XsldbgDriver::parseThreadList(const char */*output*/)
1228 return std::list<ThreadInfo>();
1231 bool
1232 XsldbgDriver::parseBreakpoint(const char *output, int &id,
1233 QString &file, int &lineNo, QString &address)
1235 // check for errors
1236 if ( strncmp(output, "Error:", 6) == 0) {
1237 return false;
1240 char *dummy;
1241 if (strncmp(output, "Breakpoint ", 11) != 0)
1242 return false;
1244 output += 11;
1245 if (!isdigit(*output))
1246 return false;
1248 // get Num
1249 id = strtol(output, &dummy, 10); /* don't care about overflows */
1250 if (output == dummy)
1251 return false;
1253 // the file name + lineNo will be filled in later from the breakpoint list
1254 file = address = QString();
1255 lineNo = 0;
1256 return true;
1259 void
1260 XsldbgDriver::parseLocals(const char *output, std::list < ExprValue* > &newVars)
1263 /* keep going until error or xsldbg prompt is found */
1264 while (*output != '\0') {
1265 ExprValue *variable = parseVar(output);
1267 if (variable == 0) {
1268 break;
1270 // do not add duplicates
1271 for (std::list<ExprValue*>::iterator o = newVars.begin(); o != newVars.end(); ++o) {
1272 if ((*o)->m_name == variable->m_name) {
1273 delete variable;
1275 goto skipDuplicate;
1278 newVars.push_back(variable);
1279 skipDuplicate:;
1284 ExprValue *
1285 XsldbgDriver::parsePrintExpr(const char *output, bool wantErrorValue)
1287 ExprValue* var = 0;
1288 // check for error conditions
1289 if (!parseErrorMessage(output, var, wantErrorValue)) {
1290 // parse the variable
1291 var = parseVar(output);
1293 return var;
1296 bool
1297 XsldbgDriver::parseChangeWD(const char *output, QString & message)
1299 bool isGood = false;
1301 if (strncmp(output, "Change to directory", 20) == 0) {
1302 output = output + 20; /* skip 'Change to directory' */
1303 message = QString(output).simplifyWhiteSpace();
1304 if (message.isEmpty()) {
1305 message = i18n("New working directory: ") + m_programWD;
1306 isGood = true;
1309 return isGood;
1312 bool
1313 XsldbgDriver::parseChangeExecutable(const char *output, QString & message)
1315 message = output;
1316 TRACE(QString("XsldbgDriver::parseChangeExecutable :") + output);
1317 m_haveCoreFile = false;
1319 if (strstr(output, "Load of source deferred. Use the run command") != 0) {
1320 TRACE("Parsed stylesheet executable");
1321 message = QString();
1323 return message.isEmpty();
1326 bool
1327 XsldbgDriver::parseCoreFile(const char *output)
1329 TRACE("XsldbgDriver::parseCoreFile");
1330 TRACE(output);
1332 if (strstr(output, "Load of data file deferred. Use the run command") != 0) {
1333 m_haveCoreFile = true;
1334 TRACE("Parsed data file name");
1337 return m_haveCoreFile;
1340 uint
1341 XsldbgDriver::parseProgramStopped(const char *output, QString & message)
1343 /* Not sure about this function leave it here for the moment */
1345 * return DebuggerDriver::SFrefreshBreak & DebuggerDriver::SFprogramActive;
1348 // go through the output, line by line, checking what we have
1349 const char *start = output - 1;
1350 uint flags = SFprogramActive;
1352 message = QString();
1353 do {
1354 start++; /* skip '\n' */
1356 if (strncmp(start, "Finished stylesheet\n\032\032\n", 21) == 0){
1357 // flags &= ~SFprogramActive;
1358 break;
1361 // next line, please
1362 start = strchr(start, '\n');
1363 } while (start != 0);
1365 return flags;
1368 QStringList
1369 XsldbgDriver::parseSharedLibs(const char */*output*/)
1371 return QStringList();
1374 bool
1375 XsldbgDriver::parseFindType(const char */*output*/, QString & /*type*/)
1377 return true;
1380 std::list<RegisterInfo>
1381 XsldbgDriver::parseRegisters(const char */*output*/)
1383 return std::list<RegisterInfo>();
1386 bool
1387 XsldbgDriver::parseInfoLine(const char */*output*/, QString & /*addrFrom*/,
1388 QString & /*addrTo*/)
1390 return false;
1393 std::list<DisassembledCode>
1394 XsldbgDriver::parseDisassemble(const char */*output*/)
1396 return std::list<DisassembledCode>();
1399 QString
1400 XsldbgDriver::parseMemoryDump(const char */*output*/,
1401 std::list < MemoryDump > &/*memdump*/)
1403 return i18n("No memory dump available");
1406 QString
1407 XsldbgDriver::parseSetVariable(const char */*output*/)
1409 QString msg;
1410 return msg;
1414 #include "xsldbgdriver.moc"