Extend copyright to 2018.
[kdbg.git] / kdbg / xsldbgdriver.cpp
blobdd2d6d29cda7d933488c67c8e423c404befb1d14
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 <QFileInfo>
10 #include <QRegExp>
11 #include <QStringList>
12 #include <klocalizedstring.h> /* i18n */
13 #include <ctype.h>
14 #include <signal.h>
15 #include <stdlib.h> /* strtol, atoi */
16 #include <string.h> /* strcpy */
17 #include <kmessagebox.h>
19 #include "assert.h"
20 #include "mydebug.h"
23 static ExprValue *parseVar(const char *&s);
24 static bool parseName(const char *&s, QString & name,
25 VarTree::NameKind & kind);
26 static bool parseValue(const char *&s, ExprValue * variable);
27 static bool isErrorExpr(const char *output);
29 #define TERM_IO_ALLOWED 1
31 // TODO: make this cmd info stuff non-static to allow multiple
32 // simultaneous gdbs to run!
34 struct XsldbgCmdInfo {
35 DbgCommand cmd;
36 const char *fmt; /* format string */
37 enum Args {
38 argNone, argString, argNum,
39 argStringNum, argNumString,
40 argString2, argNum2
41 } argsNeeded;
45 * The following array of commands must be sorted by the DC* values,
46 * because they are used as indices.
48 static XsldbgCmdInfo cmds[] = {
49 {DCinitialize, "init\n", XsldbgCmdInfo::argNone},
50 {DCtty, "tty %s\n", XsldbgCmdInfo::argString},
51 {DCexecutable, "source %s\n", XsldbgCmdInfo::argString}, /* force a restart */
52 {DCtargetremote, "print 'target remote %s'\n", XsldbgCmdInfo::argString},
53 {DCcorefile, "data %s\n", XsldbgCmdInfo::argString}, /* force a restart */
54 {DCattach, "print 'attach %s'\n", XsldbgCmdInfo::argString},
55 {DCinfolinemain, "print 'info main line'\n", XsldbgCmdInfo::argNone},
56 {DCinfolocals, "locals -f\n", XsldbgCmdInfo::argNone},
57 {DCinforegisters, "print 'info reg'\n", XsldbgCmdInfo::argNone},
58 {DCexamine, "print 'x %s %s'\n", XsldbgCmdInfo::argString2},
59 {DCinfoline, "print 'templates %s:%d'\n", XsldbgCmdInfo::argStringNum},
60 {DCdisassemble, "print 'disassemble %s %s'\n", XsldbgCmdInfo::argString2},
61 {DCsetargs, "data %s\n", XsldbgCmdInfo::argString},
62 {DCsetenv, "addparam %s %s\n", XsldbgCmdInfo::argString2},
63 {DCunsetenv, "unset env %s\n", XsldbgCmdInfo::argString},
64 {DCsetoption, "setoption %s %d\n", XsldbgCmdInfo::argStringNum},
65 {DCcd, "chdir %s\n", XsldbgCmdInfo::argString},
66 {DCbt, "where\n", XsldbgCmdInfo::argNone},
67 {DCrun, "run\nsource\n", XsldbgCmdInfo::argNone}, /* Ensure that at the start
68 of executing XSLT we show the XSLT file */
69 {DCcont, "continue\n", XsldbgCmdInfo::argNone},
70 {DCstep, "step\n", XsldbgCmdInfo::argNone},
71 {DCstepi, "step\n", XsldbgCmdInfo::argNone},
72 {DCnext, "next\n", XsldbgCmdInfo::argNone},
73 {DCnexti, "next\n", XsldbgCmdInfo::argNone},
74 {DCfinish, "stepup\n", XsldbgCmdInfo::argNone},
75 {DCuntil, "continue %s:%d\n", XsldbgCmdInfo::argStringNum},
76 {DCkill, "quit\n", XsldbgCmdInfo::argNone},
77 {DCdetach, "quit\n", XsldbgCmdInfo::argNone},
78 {DCbreaktext, "break %s\n", XsldbgCmdInfo::argString},
79 {DCbreakline, "break -l %s %d\n", XsldbgCmdInfo::argStringNum},
80 {DCtbreakline, "break -l %s %d\n", XsldbgCmdInfo::argStringNum },
81 {DCbreakaddr, "print `break *%s`\n", XsldbgCmdInfo::argString },
82 {DCtbreakaddr, "print `tbreak *%s`\n", XsldbgCmdInfo::argString },
83 {DCwatchpoint, "print 'watch %s'\n", XsldbgCmdInfo::argString},
84 {DCdelete, "delete %d\n", XsldbgCmdInfo::argNum},
85 {DCenable, "enable %d\n", XsldbgCmdInfo::argNum},
86 {DCdisable, "disable %d\n", XsldbgCmdInfo::argNum},
87 {DCprint, "print %s\n", XsldbgCmdInfo::argString},
88 {DCprintDeref, "print 'print (*%s)'\n", XsldbgCmdInfo::argString},
89 {DCprintStruct, "print 'print %s'\n", XsldbgCmdInfo::argString},
90 {DCprintQStringStruct, "print 'print %s'\n", XsldbgCmdInfo::argString},
91 {DCprintPopup, "print %s\n", XsldbgCmdInfo::argString},
92 {DCframe, "frame %d\n", XsldbgCmdInfo::argNum},
93 {DCfindType, "print 'whatis %s'\n", XsldbgCmdInfo::argString},
94 {DCinfosharedlib, "stylesheets\n", XsldbgCmdInfo::argNone},
95 {DCthread, "print 'thread %d'\n", XsldbgCmdInfo::argNum},
96 {DCinfothreads, "print 'info threads'\n", XsldbgCmdInfo::argNone},
97 {DCinfobreak, "show\n", XsldbgCmdInfo::argNone},
98 {DCcondition, "print 'condition %d %s'\n", XsldbgCmdInfo::argNumString},
99 {DCsetpc, "print 'set variable $pc=%s'\n", XsldbgCmdInfo::argString},
100 {DCignore, "print 'ignore %d %d'\n", XsldbgCmdInfo::argNum2},
101 {DCprintWChar, "print 'ignore %s'\n", XsldbgCmdInfo::argString},
102 {DCsetvariable, "set %s %s\n", XsldbgCmdInfo::argString2},
105 #define NUM_CMDS (int(sizeof(cmds)/sizeof(cmds[0])))
106 #define MAX_FMTLEN 200
108 XsldbgDriver::XsldbgDriver():
109 DebuggerDriver(),
110 m_haveDataFile(false)
112 #ifndef NDEBUG
113 // check command info array
114 const char *perc;
116 for (int i = 0; i < NUM_CMDS; i++) {
117 // must be indexable by DbgCommand values, i.e. sorted by DbgCommand values
118 assert(i == cmds[i].cmd);
119 // a format string must be associated
120 assert(cmds[i].fmt != 0);
121 assert(strlen(cmds[i].fmt) <= MAX_FMTLEN);
122 // format string must match arg specification
123 switch (cmds[i].argsNeeded) {
124 case XsldbgCmdInfo::argNone:
125 assert(strchr(cmds[i].fmt, '%') == 0);
126 break;
127 case XsldbgCmdInfo::argString:
128 perc = strchr(cmds[i].fmt, '%');
129 assert(perc != 0 && perc[1] == 's');
130 assert(strchr(perc + 2, '%') == 0);
131 break;
132 case XsldbgCmdInfo::argNum:
133 perc = strchr(cmds[i].fmt, '%');
134 assert(perc != 0 && perc[1] == 'd');
135 assert(strchr(perc + 2, '%') == 0);
136 break;
137 case XsldbgCmdInfo::argStringNum:
138 perc = strchr(cmds[i].fmt, '%');
139 assert(perc != 0 && perc[1] == 's');
140 perc = strchr(perc + 2, '%');
141 assert(perc != 0 && perc[1] == 'd');
142 assert(strchr(perc + 2, '%') == 0);
143 break;
144 case XsldbgCmdInfo::argNumString:
145 perc = strchr(cmds[i].fmt, '%');
146 assert(perc != 0 && perc[1] == 'd');
147 perc = strchr(perc + 2, '%');
148 assert(perc != 0 && perc[1] == 's');
149 assert(strchr(perc + 2, '%') == 0);
150 break;
151 case XsldbgCmdInfo::argString2:
152 perc = strchr(cmds[i].fmt, '%');
153 assert(perc != 0 && perc[1] == 's');
154 perc = strchr(perc + 2, '%');
155 assert(perc != 0 && perc[1] == 's');
156 assert(strchr(perc + 2, '%') == 0);
157 break;
158 case XsldbgCmdInfo::argNum2:
159 perc = strchr(cmds[i].fmt, '%');
160 assert(perc != 0 && perc[1] == 'd');
161 perc = strchr(perc + 2, '%');
162 assert(perc != 0 && perc[1] == 'd');
163 assert(strchr(perc + 2, '%') == 0);
164 break;
167 #endif
170 XsldbgDriver::~XsldbgDriver()
175 QString
176 XsldbgDriver::driverName() const
178 return "XSLDBG";
181 QString
182 XsldbgDriver::defaultXsldbg()
184 return "xsldbg --lang en --shell --gdb";
187 QString
188 XsldbgDriver::defaultInvocation() const
190 return defaultXsldbg();
193 QStringList XsldbgDriver::boolOptionList() const
195 QStringList allOptions;
196 allOptions.append("verbose");
197 allOptions.append("repeat");
198 allOptions.append("debug");
199 allOptions.append("novalid");
200 allOptions.append("noout");
201 allOptions.append("html");
202 allOptions.append("docbook");
203 allOptions.append("nonet");
204 allOptions.append("catalogs");
205 allOptions.append("xinclude");
206 allOptions.append("profile");
207 return allOptions;
210 bool
211 XsldbgDriver::startup(QString cmdStr)
213 if (!DebuggerDriver::startup(cmdStr))
214 return false;
216 static const char xsldbgInitialize[] = "pwd\nsetoption gdb 2\n"; /* don't need to do anything else */
218 executeCmdString(DCinitialize, xsldbgInitialize, false);
220 return true;
223 void
224 XsldbgDriver::commandFinished(CmdQueueItem * cmd)
227 TRACE(__PRETTY_FUNCTION__);
228 // command string must be committed
229 if (!cmd->m_committed) {
230 // not commited!
231 TRACE("calling " +
232 (__PRETTY_FUNCTION__ +
233 (" with uncommited command:\n\t" + cmd->m_cmdString)));
234 return;
237 /* ok, the command is ready */
238 emit commandReceived(cmd, m_output.constData());
240 switch (cmd->m_cmd) {
241 case DCbt:
242 case DCinfolocals:
243 case DCrun:
244 case DCcont:
245 case DCstep:
246 case DCnext:
247 case DCfinish:{
248 if (!::isErrorExpr(m_output.constData()))
249 parseMarker();
250 else{
251 // This only shows an error for DCinfolocals
252 // need to update KDebugger::handleRunCommand ?
253 KMessageBox::sorry(0L, m_output);
256 break;
258 case DCinfolinemain:
259 if (!m_xslFile.isEmpty())
260 emit activateFileLine(m_xslFile, 0, DbgAddr());
261 break;
263 default:;
268 XsldbgDriver::findPrompt(const QByteArray& output) const
271 * If there's a prompt string in the collected output, it must be at
272 * the very end. We do a quick check whether the last characters of
273 * output are suitable and do the full search only if they are.
275 int len = output.length();
276 if (len < 11 || output[len-1] != ' ' || output[len-2] != '>')
277 return -1;
279 // There can be text between "(xsldbg) " and the "> " at the end
280 // since we do not know what that text is, we accept the former
281 // anywhere in the output.
282 return output.indexOf("(xsldbg) ");
285 void
286 XsldbgDriver::parseMarker()
288 char *p = m_output.data();
290 for (;;) {
291 if ((p == 0) || (*p == '\0')) {
292 m_output.clear();
293 return;
295 if (strncmp(p, "Breakpoint for file ", 20) == 0)
296 break;
297 // try to marker on next line !
298 p = strchr(p, '\n');
299 if ((p != 0) && (*p != '\0'))
300 p++;
304 // extract the marker
305 char *startMarker = p + 20;
307 //TRACE(QString("found marker:") + startMarker);
308 char *endMarker = strchr(startMarker, '\n');
310 if (endMarker == 0)
311 return;
313 *endMarker = '\0';
315 // extract filename and line number
316 static QRegExp MarkerRE(" at line (\\d+)");
318 int lineNoStart = MarkerRE.indexIn(startMarker);
320 if (lineNoStart >= 0) {
321 int lineNo = MarkerRE.cap(1).toInt();
323 DbgAddr address;
325 // now show the window
326 startMarker[lineNoStart-1] = '\0'; /* split off file name */
327 TRACE("Got file and line number");
328 startMarker++;
329 TRACE(QString(startMarker) + ": " + QString::number(lineNo));
330 emit activateFileLine(startMarker, lineNo - 1, address);
336 * Escapes characters that might lead to problems when they appear on gdb's
337 * command line.
339 static void
340 normalizeStringArg(QString & arg)
343 * Remove trailing backslashes. This approach is a little simplistic,
344 * but we know that there is at the moment no case where a trailing
345 * backslash would make sense.
347 while (!arg.isEmpty() && arg[arg.length() - 1] == '\\') {
348 arg = arg.left(arg.length() - 1);
353 QString
354 XsldbgDriver::makeCmdString(DbgCommand cmd, QString strArg)
356 assert(cmd >= 0 && cmd < NUM_CMDS);
357 assert(cmds[cmd].argsNeeded == XsldbgCmdInfo::argString);
359 normalizeStringArg(strArg);
361 if (cmd == DCcd) {
362 // need the working directory when parsing the output
363 m_programWD = strArg;
364 } else if (cmd == DCexecutable) {
365 // want to display the XSL file
366 m_xslFile = strArg;
369 QString cmdString;
370 cmdString.sprintf(cmds[cmd].fmt, strArg.toUtf8().constData());
371 return cmdString;
374 QString
375 XsldbgDriver::makeCmdString(DbgCommand cmd, int intArg)
377 assert(cmd >= 0 && cmd < NUM_CMDS);
378 assert(cmds[cmd].argsNeeded == XsldbgCmdInfo::argNum);
380 QString cmdString;
381 cmdString.sprintf(cmds[cmd].fmt, intArg);
382 return cmdString;
385 QString
386 XsldbgDriver::makeCmdString(DbgCommand cmd, QString strArg, int intArg)
388 assert(cmd >= 0 && cmd < NUM_CMDS);
389 assert(cmds[cmd].argsNeeded == XsldbgCmdInfo::argStringNum ||
390 cmds[cmd].argsNeeded == XsldbgCmdInfo::argNumString ||
391 cmd == DCexamine || cmd == DCtty);
393 normalizeStringArg(strArg);
395 QString cmdString;
397 if (cmd == DCtty) {
399 * intArg specifies which channels should be redirected to
400 * /dev/null. It is a value or'ed together from RDNstdin,
401 * RDNstdout, RDNstderr.
403 static const char *const runRedir[8] = {
405 " </dev/null",
406 " >/dev/null",
407 " </dev/null >/dev/null",
408 " 2>/dev/null",
409 " </dev/null 2>/dev/null",
410 " >/dev/null 2>&1",
411 " </dev/null >/dev/null 2>&1"
414 if (strArg.isEmpty())
415 intArg = 7; /* failsafe if no tty */
416 m_redirect = runRedir[intArg & 7];
418 return makeCmdString(DCtty, strArg); /* note: no problem if strArg empty */
421 if (cmd == DCexamine) {
422 // make a format specifier from the intArg
423 static const char size[16] = {
424 '\0', 'b', 'h', 'w', 'g'
426 static const char format[16] = {
427 '\0', 'x', 'd', 'u', 'o', 't',
428 'a', 'c', 'f', 's', 'i'
431 assert(MDTsizemask == 0xf); /* lowest 4 bits */
432 assert(MDTformatmask == 0xf0); /* next 4 bits */
433 int count = 16; /* number of entities to print */
434 char sizeSpec = size[intArg & MDTsizemask];
435 char formatSpec = format[(intArg & MDTformatmask) >> 4];
437 assert(sizeSpec != '\0');
438 assert(formatSpec != '\0');
439 // adjust count such that 16 lines are printed
440 switch (intArg & MDTformatmask) {
441 case MDTstring:
442 case MDTinsn:
443 break; /* no modification needed */
444 default:
445 // all cases drop through:
446 switch (intArg & MDTsizemask) {
447 case MDTbyte:
448 case MDThalfword:
449 count *= 2;
450 case MDTword:
451 count *= 2;
452 case MDTgiantword:
453 count *= 2;
455 break;
457 QString spec;
459 spec.sprintf("/%d%c%c", count, sizeSpec, formatSpec);
461 return makeCmdString(DCexamine, spec, strArg);
464 if (cmds[cmd].argsNeeded == XsldbgCmdInfo::argStringNum) {
465 // line numbers are zero-based
466 if (cmd == DCuntil || cmd == DCbreakline ||
467 cmd == DCtbreakline || cmd == DCinfoline) {
468 intArg++;
470 if (cmd == DCinfoline) {
471 // must split off file name part
472 strArg = QFileInfo(strArg).fileName();
474 cmdString.sprintf(cmds[cmd].fmt, strArg.toUtf8().constData(), intArg);
475 } else {
476 cmdString.sprintf(cmds[cmd].fmt, intArg, strArg.toUtf8().constData());
478 return cmdString;
481 QString
482 XsldbgDriver::makeCmdString(DbgCommand cmd, QString strArg1,
483 QString strArg2)
485 assert(cmd >= 0 && cmd < NUM_CMDS);
486 assert(cmds[cmd].argsNeeded == XsldbgCmdInfo::argString2);
488 normalizeStringArg(strArg1);
489 normalizeStringArg(strArg2);
491 QString cmdString;
492 cmdString.sprintf(cmds[cmd].fmt,
493 strArg1.toUtf8().constData(),
494 strArg2.toUtf8().constData());
495 return cmdString;
498 QString
499 XsldbgDriver::makeCmdString(DbgCommand cmd, int intArg1, int intArg2)
501 assert(cmd >= 0 && cmd < NUM_CMDS);
502 assert(cmds[cmd].argsNeeded == XsldbgCmdInfo::argNum2);
504 QString cmdString;
505 cmdString.sprintf(cmds[cmd].fmt, intArg1, intArg2);
506 return cmdString;
509 QString
510 XsldbgDriver::makeCmdString(DbgCommand cmd)
512 assert(cmd >= 0 && cmd < NUM_CMDS);
513 assert(cmds[cmd].argsNeeded == XsldbgCmdInfo::argNone);
515 return cmds[cmd].fmt;
518 QString
519 XsldbgDriver::makeCmdString(DbgCommand cmd, QString strArg, int intArg1, int)
521 assert(cmd == DCexamine);
523 return makeCmdString(cmd, strArg, intArg1);
526 void
527 XsldbgDriver::terminate()
529 qDebug("XsldbgDriver::Terminate");
530 flushCommands();
531 executeCmdString(DCinitialize, "quit\n", true);
532 ::kill(pid(), SIGTERM);
533 m_state = DSidle;
536 void
537 XsldbgDriver::detachAndTerminate()
539 qDebug("XsldbgDriver::detachAndTerminate");
540 flushCommands();
541 executeCmdString(DCinitialize, "quit\n", true);
542 ::kill(pid(), SIGINT);
545 void
546 XsldbgDriver::interruptInferior()
548 // remove accidentally queued commands
549 qDebug("interruptInferior");
550 flushHiPriQueue();
551 ::kill(pid(), SIGINT);
554 static bool
555 isErrorExpr(const char *output)
557 int wordIndex;
558 bool result = false;
559 #define ERROR_WORD_COUNT 6
560 static const char *errorWords[ERROR_WORD_COUNT] = {
561 "Error:",
562 "error:", // libxslt error
563 "Unknown command",
564 "Warning:",
565 "warning:", // libxslt warning
566 "Information:" // xsldbg information
568 static int errorWordLength[ERROR_WORD_COUNT] = {
569 6, /* Error */
570 6, /* rror */
571 15, /* Unknown command*/
572 8, /* Warning */
573 8, /* warning */
574 12 /* Information */
577 for (wordIndex = 0; wordIndex < ERROR_WORD_COUNT; wordIndex++){
578 // ignore any warnings relating to local variables not being available
579 if (strncmp(output,
580 errorWords[wordIndex],
581 errorWordLength[wordIndex]) == 0 &&
582 (wordIndex == 0 && strstr(output, "try stepping past the xsl:param") == 0) ) {
583 result = true;
584 TRACE(QString("Error/Warning/Information from xsldbg ") + output);
585 break;
589 return result;
593 * Returns true if the output is an error message. If wantErrorValue is
594 * true, a new ExprValue object is created and filled with the error message.
596 static bool
597 parseErrorMessage(const char *output,
598 ExprValue * &variable, bool wantErrorValue)
600 if (isErrorExpr(output)) {
601 if (wantErrorValue) {
602 // put the error message as value in the variable
603 variable = new ExprValue(QString(), VarTree::NKplain);
604 const char *endMsg = strchr(output, '\n');
606 if (endMsg == 0)
607 endMsg = output + strlen(output);
608 variable->m_value = QString::fromLatin1(output, endMsg - output);
609 } else {
610 variable = 0;
612 return true;
614 return false;
618 void
619 XsldbgDriver::setPrintQStringDataCmd(const char* /*cmd*/)
623 ExprValue *
624 XsldbgDriver::parseQCharArray(const char */*output*/, bool /*wantErrorValue*/,
625 bool /*qt3like*/)
627 ExprValue *variable = 0;
629 TRACE("XsldbgDriver::parseQCharArray not implmented");
630 return variable;
633 static ExprValue *
634 parseVar(const char *&s)
636 const char *p = s;
637 ExprValue *variable = 0L;
638 QString name;
640 VarTree::NameKind kind;
642 TRACE(__PRETTY_FUNCTION__);
643 TRACE(p);
645 if (parseErrorMessage(p, variable, false) == true) {
646 TRACE("Found error message");
647 return variable;
650 if (strncmp(p, " Local", 6) == 0) {
651 /* skip " Local" */
652 p = p + 6;
653 TRACE("Found local variable");
654 } else if (strncmp(p, " Global", 7) == 0) {
655 /* skip " Global" */
656 p = p + 7;
657 TRACE("Found global variable");
658 } else if (strncmp(p, "= ", 2) == 0) {
659 /* we're processing the result of a "print command" */
660 /* find next line */
661 const char *nextLine = strchr(p, '\n');
663 TRACE("Found print expr");
664 if (nextLine) {
665 p = p + 2; /* skip the "= " */
666 name = QString::fromLatin1(p, nextLine - p);
667 kind = VarTree::NKplain;
668 p = nextLine + 1;
669 variable = new ExprValue(name, kind);
670 variable->m_varKind = VarTree::VKsimple;
671 parseValue(p, variable);
672 return variable;
674 } else
675 return variable; /* don't know what to do this this data abort!! */
677 // skip whitespace
678 while (isspace(*p))
679 p++;
681 if (*p != '='){
682 // No value provided just a name
683 TRACE(QString("Parse var: name") + p);
684 if (!parseName(p, name, kind)) {
685 return 0;
687 variable = new ExprValue(name, kind);
688 if (variable != 0L) {
689 variable->m_varKind = VarTree::VKsimple;
691 }else{
692 p++;
693 // skip whitespace
694 while (isspace(*p))
695 p++;
696 TRACE(QString("Parse var: name") + p);
697 if (!parseName(p, name, kind)) {
698 return 0;
700 variable = new ExprValue(name, kind);
701 if (variable != 0L) {
702 variable->m_varKind = VarTree::VKsimple;
704 if (*p == '\n')
705 p++;
706 if (!parseValue(p, variable)) {
707 delete variable;
708 return 0;
712 if (*p == '\n')
713 p++;
715 s = p;
716 return variable;
720 inline void
721 skipName(const char *&p)
723 // allow : (for enumeration values) and $ and . (for _vtbl.)
724 while (isalnum(*p) || *p == '_' || *p == ':' || *p == '$' || *p == '.')
725 p++;
728 static bool
729 parseName(const char *&s, QString & name, VarTree::NameKind & kind)
731 /* qDebug(__PRETTY_FUNCTION__); */
732 kind = VarTree::NKplain;
734 const char *p = s;
735 int len = 0;
737 // examples of names:
738 // help_cmd
740 while ((*p != '\n') && (*p != '\0')) {
741 len++;
742 p++;
746 name = QString::fromLatin1(s, len);
747 /* XSL variables will have a $ prefix to be evaluated
748 * properly */
749 //TRACE(QString("parseName got name" ) + name);
751 // return the new position
752 s = p;
753 return true;
756 static bool
757 parseValue(const char *&s, ExprValue * variable)
759 const char *start = s, *end = s;
760 ExprValue * childValue;
761 #define VALUE_END_MARKER_INDEX 0
763 /* This mark the end of a value */
764 static const char *marker[] = {
765 "\032\032", /* value end marker*/
766 "(xsldbg) ",
767 "Breakpoint at", /* stepped to next location */
768 "Breakpoint in", /* reached a set breakpoint */
769 "Reached ", /* reached template */
770 "Error:",
771 "Warning:",
772 "Information:",
773 "runtime error",
774 "xmlXPathEval:",
777 static char valueBuffer[2048];
778 int markerIndex = 0, foundEnd = 0;
779 size_t copySize;
781 if (variable == 0L)
782 return false; /* should never happen but .. */
784 while (start && (*start != '\0')) {
785 /* look for the next marker */
786 for (markerIndex = 0; marker[markerIndex] != 0; markerIndex++) {
787 foundEnd =
788 strncmp(start, marker[markerIndex],
789 strlen(marker[markerIndex])) == 0;
790 if (foundEnd)
791 break;
794 if (foundEnd)
795 break;
798 end = strchr(start, '\n');
799 if (end)
800 copySize = end - start;
801 else
802 copySize = strlen(start);
803 if (copySize >= sizeof(valueBuffer))
804 copySize = sizeof(valueBuffer)-1;
806 strncpy(valueBuffer, start, copySize);
807 valueBuffer[copySize] = '\0';
808 TRACE("Got value :");
809 TRACE(valueBuffer);
810 if ((variable->m_varKind == VarTree::VKsimple)) {
811 if (!variable->m_value.isEmpty()){
812 variable->m_varKind = VarTree::VKarray;
813 childValue = new ExprValue(variable->m_value, VarTree::NKplain);
814 variable->appendChild(childValue);
815 childValue = new ExprValue(valueBuffer, VarTree::NKplain);
816 variable->appendChild(childValue);
817 variable->m_value = "";
818 }else{
819 variable->m_value = valueBuffer;
821 }else{
822 childValue = new ExprValue(valueBuffer, VarTree::NKplain);
823 variable->appendChild(childValue);
826 if (*end =='\n'){
827 start = end + 1;
828 }else{
829 start = end + 1;
830 break;
834 if (foundEnd == 0)
835 TRACE(QString("Unable to find end on value near :") + start);
837 // If we've got something otherthan a end of value marker then
838 // advance to the end of this buffer
839 if (markerIndex != VALUE_END_MARKER_INDEX){
840 while (start && *start != '\0')
841 start++;
842 }else{
843 start = start + strlen(marker[0]);
846 s = start;
848 return true;
853 * Parses a stack frame.
855 static void
856 parseFrameInfo(const char *&s, QString & func,
857 QString & file, int &lineNo, DbgAddr & /*address*/)
859 const char *p = s, *endPos = s + strlen(s);
860 QString lineNoString;
862 TRACE("parseFrameInfo");
864 lineNo = -1;
866 /* skip 'template :\" */
867 p = p + 11;
868 // TRACE(p);
869 func = "";
870 while ((*p != '\"') && (*p != '\0')) {
871 func.append(*p);
872 p++;
874 while ((*p != '\0') && *p != '"')
875 p++;
876 if (*p != '\0')
877 p++;
878 ASSERT(p <= endPos);
879 if (p >= endPos) {
880 /* panic */
881 return;
884 /* skip mode :".*" */
885 while ((*p != '\0') && *p != '"')
886 p++;
887 if (*p != '\0')
888 p++;
889 while ((*p != '\0') && *p != '"')
890 p++;
892 /* skip '" in file ' */
893 p = p + 10;
894 if(*p == '"')
895 p++;
896 // TRACE(p);
897 file = "";
898 while (!isspace(*p) && (*p != '\"') && (*p != '\0')) {
899 file.append(*p);
900 p++;
902 if(*p == '"')
903 p++;
904 ASSERT(p <= endPos);
905 if (p >= endPos) {
906 /* panic */
907 return;
910 // TRACE(p);
911 /* skip ' : line '" */
912 p = p + 9;
913 // TRACE(p);
914 ASSERT(p <= endPos);
915 if (p >= endPos) {
916 /* panic */
917 return;
919 // TRACE(p);
920 if (isdigit(*p)) {
921 /* KDbg uses an offset of +1 for its line numbers */
922 lineNo = atoi(p) - 1;
923 lineNoString = QString::number(lineNo);
925 /* convert func into format needed */
926 func.append(" at ");
927 func.append(file);
928 func.append(':');
929 func.append(lineNoString);
931 /*advance to next line */
932 p = strchr(p, '\n');
933 if (p)
934 p++;
935 s = p;
939 #undef ISSPACE
942 * Parses a stack frame including its frame number
944 static bool
945 parseFrame(const char *&s, int &frameNo, QString & func,
946 QString & file, int &lineNo, DbgAddr & address)
949 // TRACE("XsldbgDriver ::parseFrame");
950 /* skip leading 'where' or 'frame <frame_no>' */
951 if ((strncmp(s, "where", 5) == 0) || (strncmp(s, "frame", 5) == 0)) {
952 s = strchr(s, '\n');
953 if ((*s != '\0') && (*s != '#'))
954 s++;
956 // TRACE(s);
958 // Example:
959 //#1 template :"/" in file /home/keith/anon_CVS/xsldbg/docs/en/xsldoc.xsl : line 21
960 // must start with a hash mark followed by number
961 if (s[0] != '#' || !isdigit(s[1]))
962 return false;
964 //TRACE("XsldbgDriver ::parseFrame got #");
965 s++; /* skip the hash mark */
966 // frame number
967 frameNo = atoi(s);
968 while (isdigit(*s))
969 s++;
971 //TRACE(QString("Got frame ").append(QString::number(frameNo)));
972 // space
973 while (isspace(*s))
974 s++;
975 parseFrameInfo(s, func, file, lineNo, address);
976 // TRACE("Will next look at ");
977 // TRACE(s);
978 return true;
981 void
982 XsldbgDriver::parseBackTrace(const char *output,
983 std::list < StackFrame > &stack)
985 QString func, file;
986 int lineNo, frameNo;
987 DbgAddr address;
989 while (::parseFrame(output, frameNo, func, file, lineNo, address)) {
990 stack.push_back(StackFrame());
991 StackFrame* frm = &stack.back();
993 frm->frameNo = frameNo;
994 frm->fileName = file;
995 frm->lineNo = lineNo;
996 frm->address = address;
997 frm->var = new ExprValue(func, VarTree::NKplain);
1001 bool
1002 XsldbgDriver::parseFrameChange(const char *output, int &frameNo,
1003 QString & file, int &lineNo,
1004 DbgAddr & address)
1006 QString func;
1008 return::parseFrame(output, frameNo, func, file, lineNo, address);
1012 bool
1013 XsldbgDriver::parseBreakList(const char *output,
1014 std::list < Breakpoint > &brks)
1016 TRACE("parseBreakList");
1017 /* skip the first blank line */
1018 const char *p;
1020 // split up a line
1021 Breakpoint bp;
1022 char *dummy;
1023 p = strchr(output, '\n');/* skip the first blank line*/
1025 while ((p != 0) && (*p != '\0')) {
1026 if (*p == '\n')
1027 p++;
1028 QString templateName;
1029 //qDebug("Looking at :%s", p);
1030 if (strncmp(p, " Breakpoint", 11) != 0)
1031 break;
1032 p = p + 11;
1033 if (*p == '\0')
1034 break;
1036 //TRACE(p);
1037 // get Num
1038 bp.id = strtol(p, &dummy, 10); /* don't care about overflows */
1040 p = dummy;
1041 if ((p == 0) || (p[1] == '\0'))
1042 break;
1043 p++;
1045 //TRACE(p);
1046 // Get breakpoint state ie enabled/disabled
1047 if (strncmp(p, "enabled", 7) == 0) {
1048 bp.enabled = true;
1049 p = p + 7;
1050 } else {
1051 if (strncmp(p, "disabled", 8) == 0) {
1052 p = p + 8;
1053 bp.enabled = false;
1054 } else{
1055 TRACE("Parse error in breakpoint list");
1056 TRACE(p);
1057 return false;
1061 //TRACE("Looking for template");
1062 //TRACE(p);
1063 if (strncmp(p, " for template: \"", 16) == 0){
1064 p = p + 16;
1065 //TRACE("Looking for template name near");
1066 //TRACE(p);
1067 /* get the template name */
1068 while (p && (*p != '\0') && (*p != '\"')){
1069 templateName.append(*p);
1070 p++;
1072 if (*p == '\"'){
1073 p++;
1074 }else{
1075 TRACE("Error missed \" near");
1076 TRACE(p);
1080 //TRACE("Looking for mode near");
1081 //TRACE(p);
1082 if (strncmp(p, " mode: \"", 8) == 0){
1083 p = p + 8;
1084 while (p && *p != '\"')
1085 p++;
1086 if (p)
1087 p++;
1090 if (strncmp(p, " in file ", 9) != 0){
1091 TRACE("Parse error in breakpoint list");
1092 TRACE(p);
1093 return false;
1097 /* skip ' in file ' */
1098 p = p + 9;
1099 // TRACE(p);
1101 if (*p == '\"')
1102 p++;
1103 /* grab file name */
1104 QString file;
1105 while ((*p != '\"') && !isspace(*p)) {
1106 file.append(*p);
1107 p++;
1109 if (*p == '\"')
1110 p++;
1111 if (*p == '\0')
1112 break;
1114 /* skip ' : line ' */
1115 p = p + 8;
1116 while (isspace(*p)) {
1117 p++;
1119 //TRACE(p);
1120 QString lineNo;
1121 while (isdigit(*p)) {
1122 lineNo.append(*p);
1123 p++;
1126 // bp.lineNo is zero-based
1127 bp.lineNo = lineNo.toInt() - 1;
1128 bp.location = QString("in %1 at %2:%3").arg(templateName, file, lineNo);
1129 bp.fileName = file;
1130 brks.push_back(bp);
1132 if (p != 0) {
1133 p = strchr(p, '\n');
1134 if (p)
1135 p++;
1138 return true;
1141 std::list<ThreadInfo>
1142 XsldbgDriver::parseThreadList(const char */*output*/)
1144 return std::list<ThreadInfo>();
1147 bool
1148 XsldbgDriver::parseBreakpoint(const char *output, int &id,
1149 QString &file, int &lineNo, QString &address)
1151 // check for errors
1152 if ( strncmp(output, "Error:", 6) == 0) {
1153 return false;
1156 char *dummy;
1157 if (strncmp(output, "Breakpoint ", 11) != 0)
1158 return false;
1160 output += 11;
1161 if (!isdigit(*output))
1162 return false;
1164 // get Num
1165 id = strtol(output, &dummy, 10); /* don't care about overflows */
1166 if (output == dummy)
1167 return false;
1169 // the file name + lineNo will be filled in later from the breakpoint list
1170 file = address = QString();
1171 lineNo = 0;
1172 return true;
1175 void
1176 XsldbgDriver::parseLocals(const char *output, std::list < ExprValue* > &newVars)
1179 /* keep going until error or xsldbg prompt is found */
1180 while (*output != '\0') {
1181 ExprValue *variable = parseVar(output);
1183 if (variable == 0) {
1184 break;
1186 // do not add duplicates
1187 for (std::list<ExprValue*>::iterator o = newVars.begin(); o != newVars.end(); ++o) {
1188 if ((*o)->m_name == variable->m_name) {
1189 delete variable;
1191 goto skipDuplicate;
1194 newVars.push_back(variable);
1195 skipDuplicate:;
1200 ExprValue *
1201 XsldbgDriver::parsePrintExpr(const char *output, bool wantErrorValue)
1203 ExprValue* var = 0;
1204 // check for error conditions
1205 if (!parseErrorMessage(output, var, wantErrorValue)) {
1206 // parse the variable
1207 var = parseVar(output);
1209 return var;
1212 bool
1213 XsldbgDriver::parseChangeWD(const char *output, QString & message)
1215 bool isGood = false;
1217 if (strncmp(output, "Change to directory", 20) == 0) {
1218 output = output + 20; /* skip 'Change to directory' */
1219 message = QString(output).simplified();
1220 if (message.isEmpty()) {
1221 message = i18n("New working directory: ") + m_programWD;
1222 isGood = true;
1225 return isGood;
1228 bool
1229 XsldbgDriver::parseChangeExecutable(const char *output, QString & message)
1231 message = output;
1232 TRACE(QString("XsldbgDriver::parseChangeExecutable :") + output);
1234 if (strstr(output, "Load of source deferred. Use the run command") != 0) {
1235 TRACE("Parsed stylesheet executable");
1236 message = QString();
1238 return message.isEmpty();
1241 bool
1242 XsldbgDriver::parseCoreFile(const char *output)
1244 TRACE("XsldbgDriver::parseCoreFile");
1245 TRACE(output);
1247 if (strstr(output, "Load of data file deferred. Use the run command") != 0) {
1248 TRACE("Parsed data file name");
1249 return true;
1252 return false;
1255 uint
1256 XsldbgDriver::parseProgramStopped(const char *output, bool,
1257 QString & message)
1259 /* Not sure about this function leave it here for the moment */
1261 * return DebuggerDriver::SFrefreshBreak & DebuggerDriver::SFprogramActive;
1264 // go through the output, line by line, checking what we have
1265 const char *start = output - 1;
1266 uint flags = SFprogramActive;
1268 message = QString();
1269 do {
1270 start++; /* skip '\n' */
1272 if (strncmp(start, "Finished stylesheet\n\032\032\n", 21) == 0){
1273 // flags &= ~SFprogramActive;
1274 break;
1277 // next line, please
1278 start = strchr(start, '\n');
1279 } while (start != 0);
1281 return flags;
1284 QStringList
1285 XsldbgDriver::parseSharedLibs(const char */*output*/)
1287 return QStringList();
1290 bool
1291 XsldbgDriver::parseFindType(const char */*output*/, QString & /*type*/)
1293 return true;
1296 std::list<RegisterInfo>
1297 XsldbgDriver::parseRegisters(const char */*output*/)
1299 return std::list<RegisterInfo>();
1302 bool
1303 XsldbgDriver::parseInfoLine(const char */*output*/, QString & /*addrFrom*/,
1304 QString & /*addrTo*/)
1306 return false;
1309 std::list<DisassembledCode>
1310 XsldbgDriver::parseDisassemble(const char */*output*/)
1312 return std::list<DisassembledCode>();
1315 QString
1316 XsldbgDriver::parseMemoryDump(const char */*output*/,
1317 std::list < MemoryDump > &/*memdump*/)
1319 return i18n("No memory dump available");
1322 QString
1323 XsldbgDriver::parseSetVariable(const char */*output*/)
1325 QString msg;
1326 return msg;
1330 #include "xsldbgdriver.moc"