added Info.plist
[TestXSLT.git] / libsablot / src / engine / debugger.cpp
1 /* 
2  * The contents of this file are subject to the Mozilla Public
3  * License Version 1.1 (the "License"); you may not use this file
4  * except in compliance with the License. You may obtain a copy of
5  * the License at http://www.mozilla.org/MPL/
6  * 
7  * Software distributed under the License is distributed on an "AS
8  * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
9  * implied. See the License for the specific language governing
10  * rights and limitations under the License.
11  * 
12  * The Original Code is the Sablotron XSLT Processor.
13  * 
14  * The Initial Developer of the Original Code is Ginger Alliance Ltd.
15  * Portions created by Ginger Alliance are Copyright (C) 2000-2002
16  * Ginger Alliance Ltd. All Rights Reserved.
17  * 
18  * Contributor(s):
19  * 
20  * Alternatively, the contents of this file may be used under the
21  * terms of the GNU General Public License Version 2 or later (the
22  * "GPL"), in which case the provisions of the GPL are applicable 
23  * instead of those above.  If you wish to allow use of your 
24  * version of this file only under the terms of the GPL and not to
25  * allow others to use your version of this file under the MPL,
26  * indicate your decision by deleting the provisions above and
27  * replace them with the notice and other provisions required by
28  * the GPL.  If you do not delete the provisions above, a recipient
29  * may use your version of this file under either the MPL or the
30  * GPL.
31  */
32
33 #include "debugger.h"
34
35 #ifdef SABLOT_DEBUGGER
36
37 #include "sabdbg.h"
38 #include "datastr.h"
39 #include "situa.h"
40 #include "context.h"
41 #include "verts.h"
42 #include "tree.h"
43 #include "uri.h"
44 #include "guard.h"
45 #include "error.h"
46 #include <stdlib.h>
47 #include <string.h>
48 #include <stdio.h>
49
50 #ifdef SABLOT_GPL
51 #ifdef HAVE_READLINE_READLINE_H
52 #include <readline/readline.h>
53 #endif
54 #ifdef HAVE_READLINE_HISTORY_H
55 #include <readline/history.h>
56 #endif
57 #endif
58
59 ////////////////////////////////////////////////////////////
60 //globals
61 ////////////////////////////////////////////////////////////
62
63 Debugger* debugger = NULL;
64
65 void debuggerInit()
66 {
67   assert(!debugger);
68   debugger = new Debugger();
69 }
70
71 void debuggerDone()
72 {
73   delete debugger;
74 }
75
76 void debuggerEnterIdle()
77 {
78   debugger -> enterIdle();
79 }
80
81 char* dbgGetLine()
82 {
83   char* aux;
84 #ifdef HAVE_READLINE_READLINE_H
85   aux = readline("sablot> ");
86 #ifdef HAVE_READLINE_HISTORY_H
87   if (aux)
88     add_history(aux);
89 #endif
90 #else
91   aux = (char*)malloc(256);
92   printf("sablot> ");
93   fgets(aux, 256, stdin);
94 #endif
95   return aux;
96 }
97
98 ////////////////////////////////////////////////////////////
99 // help string
100 ////////////////////////////////////////////////////////////
101
102 const char* theUsageStr =
103 "Breakpoints:\n" \
104 "  break filename:line  - set the breakpoint\n" \
105 "  bstat                - show breakpoint stats (total/enabled/break)\n" \
106 "  B                    - list all breakpoints\n" \
107 "  condition num cond   - for the breakpoint NUM set the condition COND\n" \
108 "  del num              - delete the breakpoint NUM\n" \
109 "  disable num          - (toggle) the breakpoint number NUM\n" \
110 "  D                    - delete all breakpoints\n" \
111 "  ignore num count     - ignore the breakpoint NUM for COUNT times\n" \
112 "Execution control:\n" \
113 "  continue             - continue the execution\n" \
114 "  finish               - finish the current node parent\n" \
115 "  kill                 - stop the processing immediately\n" \
116 "  next                 - go to the next sibling\n" \
117 "  run                  - run the processor\n" \
118 "  step                 - continue until the next element\n" \
119 "  templ                - continue until the next template executed\n" \
120 "Processed data:\n" \
121 "  data                 - set the data file\n" \
122 "  param name value     - set the external parameter\n" \
123 "  P                    - list all params\n" \
124 "  PP                   - clear all params\n" \
125 "  sheet                - set the stylesheet\n" \
126 "Evaluation:\n" \
127 "  eval                 - evaluate the XPath expression\n" \
128 "  x [list | num]       - examine the current context\n" \
129 "Miscellaneous:\n" \
130 "  batch filename       - load the command set for file\n" \
131 "  help                 - print this help\n" \
132 "  output               - toggles output on/off\n" \
133 "  point                - show where you are\n" \
134 "  quit                 - quite the debgger\n";
135
136
137
138
139 ////////////////////////////////////////////////////////////
140 // helpers
141 ////////////////////////////////////////////////////////////
142
143 DbgCommandDescription theNullCommand =  
144   {NULL,          0,  0, false, false, SDBG_NOOP};
145
146 /*!!!!! MUST be sorted by command !!!! */
147 DbgCommandDescription dbgCommands[] = {
148   { "break",      1,  1, false, false, SDBG_BREAK },
149   { "batch",      1,  1, false, false, SDBG_BATCH },
150   { "bstat",      0,  0, false, false, SDBG_BREAK_STAT },
151   { "B",          0,  0, false, false, SDBG_BREAK_LIST },
152   { "continue",   0,  0, false, false, SDBG_CONTINUE },
153   { "condition",  1,  2, true,  false, SDBG_BREAK_COND },
154   { "del",        1,  1, false, false, SDBG_BREAK_CLEAR },
155   { "data",       0,  1, false, false, SDBG_DATA },
156   { "disable",    1,  1, false, false, SDBG_BREAK_DISABLE },
157   { "D",          0,  0, false, false, SDBG_BREAK_CLEARALL },
158   { "eval",       1,  1, true,  false, SDBG_EVAL },
159   { "finish",     0,  0, false, false, SDBG_FINISH },
160   { "help",       0,  0, false, false, SDBG_HELP },
161   { "ignore",     2,  2, false, false, SDBG_BREAK_IGNORE },
162   { "kill",       0,  0, false, false, SDBG_KILL },
163   { "next",       0,  0, false, false, SDBG_NEXT },
164   { "output",     0,  0, false, false, SDBG_OUTPUT },
165   { "param",      2,  2, true,  false, SDBG_PARAM },
166   { "point",      0,  0, false, false, SDBG_POINT },
167   { "P",          0,  0, false, false, SDBG_PARAM_LIST },
168   { "PP",         0,  0, false, false, SDBG_PARAM_CLEAR },
169   { "run",        0,  0, false, false, SDBG_RUN },
170   { "step",       0,  0, false, false, SDBG_STEP },
171   { "sheet",      0,  1, false, false, SDBG_SHEET },
172   { "templ",      0,  0, false, false, SDBG_TEMPLATE },
173   { "quit",       0,  0, false, false, SDBG_QUIT },
174   { "x",          0,  1, false, false, SDBG_CONTEXT },
175   {NULL,          0,  0, false, false, SDBG_NOOP}
176 };
177
178 char *ctxArgs[] = {
179   "list", "current", NULL
180 };
181
182 ////////////////////////////////////////////////////////////
183 // globals
184 ////////////////////////////////////////////////////////////
185
186 Bool strToInt(Str *str, long int& out)
187 {
188   char *val = (char*)*str;
189   char *end;
190   out = strtol(val, &end, 10);
191   return !*end;
192 }
193
194 ////////////////////////////////////////////////////////////
195 // Breakpoint
196 ////////////////////////////////////////////////////////////
197
198 Breakpoint::~Breakpoint()
199 {
200   if (expr) delete expr;
201 }
202
203 Bool Breakpoint::buildExpression(Sit S)
204 {
205   if (valid && element && !condition.isEmpty())
206     {
207       GP(Expression) e = new Expression(*element);
208       Bool failed = (*e).parse(S, condition);
209       if (!failed)
210         {
211           expr = e.keep();
212         }
213       else
214         {
215           enabled = false;
216           return false;      
217         }
218     }
219   return true;
220 }
221
222 Bool Breakpoint::matches(Sit S, Context* c, Bool& wasError)
223 {
224   wasError = false;
225   if (valid && expr)
226     {
227       assert(element);
228       Expression ret(*element);
229       Bool failed = expr -> eval(S, ret, c);
230       if (! failed)
231         {
232           return ret.tobool();
233         }
234       else
235         {
236           wasError = true;
237           return true;
238         }
239     }
240   else
241     return true;
242 }
243
244 Bool Breakpoint::countMatches(Sit S, Context* c, Bool& wasError)
245 {
246   Bool ret = matches(S, c, wasError);
247   if (ret) hitMatched++;
248   ret = ret && (wasError || hitMatched > ignoreNum);  
249   if (ret) hitBreak++;
250   return ret;
251 }
252
253 void Breakpoint::reset()
254 {
255   valid = false;
256   if (expr) delete expr;
257   expr = NULL;
258   hitTotal = hitEnabled = hitBreak = hitMatched =  0;
259 }
260
261 Bool Breakpoint::countEnabled()
262 {
263   hitTotal++;
264   if (enabled) hitEnabled++;
265   return enabled;
266 }
267
268 ////////////////////////////////////////////////////////////
269 // Breakpoint List
270 ////////////////////////////////////////////////////////////
271
272 Breakpoint* BPList::getBP(Str& uri, int line)
273 {
274   for (int i = 0; i < number(); i++)
275     {
276       if ((*this)[i] -> uri == uri && (*this)[i] -> line == line)
277         return (*this)[i];
278     }
279   return NULL;
280 }
281
282 Breakpoint* BPList::setBP(Str& uri, int line)
283 {
284   Breakpoint *ret = NULL;
285   Breakpoint *item = getBP(uri, line);
286   if (! item)
287     {
288       item = new Breakpoint();
289       append(item);
290       item -> uri = uri;
291       item -> line = line;
292       ret = item;
293     }
294
295   return ret;
296 }
297
298 Bool BPList::clearBP(int idx, Bool running)
299 {
300   if (idx >= 0 && idx < number())
301     {
302       if (running && (*this)[idx] -> element)
303         (*this)[idx] -> element -> breakpoint = NULL;
304       freerm(idx, FALSE);
305       printf("breakpoint deleted\n");
306     }
307   else
308     printf("invalid breakpoint number\n");
309   return true;
310 }
311
312 Bool BPList::disableBP(int idx)
313 {
314   if (idx >= 0 && idx < number())
315     {
316       (*this)[idx] -> enabled = !(*this)[idx] -> enabled;
317       printf("breakpoint #%d is now %s\n", idx,
318              (*this)[idx] -> enabled ? "enabled" : "disabled");
319       return true;
320     }
321   else
322     {
323       printf("invalid breakpoint number\n");
324       return false;
325     }
326 }
327
328 void BPList::clearAll(Bool running)
329 {
330   for (int i = number() - 1; i >= 0; i--)
331     {
332       clearBP(i, running);
333     }
334 }
335
336 void BPList::listAll(Bool running)
337 {
338   if ( number() )
339     {
340       for (int i = 0; i < number(); i++)
341         {
342           Breakpoint *item = (*this)[i];
343           printf("#%d: ", i);
344           if (running && ! item -> valid)
345             printf("(invalid) ");
346           else if (!item -> enabled)
347             printf("(disabled) ");
348           printf("%s:%d", (char*)item -> uri, item -> line);
349           if (! item -> condition.isEmpty())
350             printf(" condition:'%s'", (char*)item -> condition);
351           if ( item -> ignoreNum )
352             printf(" ignore:%d", item -> ignoreNum);
353           printf("\n");
354         }
355     }
356   else
357     {
358       printf("no breakpoints\n");
359     }
360 }
361
362 #define _STAT_LEN 10
363 void BPList::statAll(Bool running)
364 {
365   char buff[32];
366   if ( number() )
367     {
368       for (int i = 0; i < number(); i++)
369         {
370           Breakpoint *item = (*this)[i];
371           printf("#%d: ", i);
372           sprintf(buff, "(%d/%d/%d)", item -> hitTotal, 
373                   item -> hitEnabled, item -> hitBreak);
374           int len = strlen(buff);
375           printf("%s", buff);
376           if (len < _STAT_LEN)
377             {
378               memset(buff, ' ', _STAT_LEN - len);
379               buff[_STAT_LEN - len] = 0;
380             }
381           else
382             strcpy(buff, " ");
383           printf("%s", buff);
384           printf("%s at %d", (char*)item -> uri, item -> line);
385           printf("\n");
386         }
387     }
388   else
389     {
390       printf("no breakpoints\n");
391     }
392 }
393
394 void BPList::getLinesForURI(Str& uri, List<int>& lines)
395 {
396   int i;
397   for (i = 0; i < number(); i++)
398     {
399       if ((*this)[i] -> uri == uri)
400         lines.append((*this)[i] -> line);
401     }
402   //bubble sort
403   Bool cont = true;
404   while (cont)
405     {
406       cont = false;
407       for (i = 1; i < lines.number(); i++)
408         {
409           if (lines[i - 1] > lines[i]) 
410             {
411               cont = true;
412               lines.swap(i - 1, i);
413             }
414         }
415     }
416 }
417
418 void BPList::reset()
419 {
420   for (int i = 0; i < number(); i++)
421     {
422       Breakpoint *bp = (*this)[i];
423       bp -> reset();
424     }
425 }
426
427 Bool BPList::ignore(long int idx, long int num)
428 {
429   if (idx >= 0 && idx < number())
430     {
431       (*this)[idx] -> ignoreNum = num;
432       return true;
433     }
434   else
435     {
436       printf("invalid breakpoint number\n");  
437       return false;
438     }
439 }
440
441 Bool BPList::setExpression(Sit S, int idx, ArgList& args, Bool running)
442 {
443   if (idx >= 0 && idx < number())
444     {
445       Breakpoint *bp = (*this)[idx];
446       //delete old expression
447       if (bp -> expr) delete bp -> expr;
448       bp -> expr = NULL;
449       bp -> condition.empty();
450       if (args.number() == 3)
451         {
452           //set new one
453           bp -> condition = *(args[2]);
454           if (running && bp -> valid)
455             {
456               assert(bp -> element);
457               if (! bp -> buildExpression(S)) return false;
458             }
459         }
460     }
461   else
462     printf("invalid breakpoint number\n");
463   return true;
464 }
465
466 ////////////////////////////////////////////////////////////
467 /* debugger functions */
468 ////////////////////////////////////////////////////////////
469
470 Debugger::Debugger() 
471 {
472   running = false;
473   SablotCreateSituation(&situa);
474
475   msgHandler.makeCode = mhMakeCode;
476   msgHandler.log = mhLog;
477   msgHandler.error = mhError;
478
479   instep = false;
480   intempl = false;
481   nextele = NULL;
482   output = true;
483
484   sheet = NULL;
485 }
486
487 Debugger::~Debugger()
488 {
489   bpoints.freeall(FALSE);
490 }
491
492 // message handler
493 MH_ERROR Debugger::mhMakeCode(void *userData, SablotHandle processor_,
494                            int severity, unsigned short facility, 
495                            unsigned short code)
496 {
497   return code;
498 }
499
500 MH_ERROR Debugger::mhLog(void *userData, SablotHandle processor_,
501                       MH_ERROR code, MH_LEVEL level, char **fields)
502 {
503   return code;
504 }
505
506 MH_ERROR Debugger::mhError(void *userData, SablotHandle processor_,
507                         MH_ERROR code, MH_LEVEL level, 
508                         char **fields)
509 {
510   char **foo = fields;
511   Debugger *this_ = (Debugger*)userData;
512   while (*foo)
513     {
514       char *aux = strchr(*foo, ':');
515       Str key, val;
516       if (aux) 
517         {
518           *aux = 0;
519           key = *foo;
520           val = aux + 1;
521           *aux = ':';
522         }
523       else
524         {
525           val = *foo;
526         }
527       this_ -> messages.appendConstruct(key, val);
528       this_ -> errorCode = code;
529       foo++;
530     }
531   return code;
532 }
533
534 //the salt
535
536 void Debugger::setBreakpointsOnElement(Sit S, Element *e)
537 {
538   SubtreeInfo *info = e -> subtree;
539   if (info != currentInfo)
540     {
541       currentInfo = info;
542       if (info)
543         {
544           currentLines.deppendall();
545           bpoints.getLinesForURI(info -> getBaseURI(), currentLines);
546           //first met? set the next breakpoint line index to be set
547           if (info -> nextBPIndex == -2) 
548             info -> nextBPIndex = currentLines.number() ? 0 : -1;
549         }
550     }
551   if (info && info -> nextBPIndex >= 0)
552     {
553       int line = currentLines[info -> nextBPIndex];
554       if (e -> lineno == line)
555         {
556           Breakpoint *bp = bpoints.getBP(info -> getBaseURI(), line);
557           assert(bp);
558           e -> breakpoint = bp;
559           bp -> element = e;
560           bp -> valid = true;
561           //build expression (if any)
562           if (! bp -> buildExpression(S) )
563             {
564               reportError("wrong breakpoit expression:", false);
565             }
566           if (info -> nextBPIndex < (currentLines.number() - 1))
567             info -> nextBPIndex ++;
568           else
569             info -> nextBPIndex = -1;
570         }
571     }
572   //process recursively
573   for (int i = 0; i < e -> contents.number(); i++)
574     if (isElement(e -> contents[i]))
575         setBreakpointsOnElement(S, toE(e -> contents[i]));
576 }
577
578 void Debugger::setBreakpoints(Sit S, Tree* sheet_)
579 {
580   //info should be NULL
581   sheet = sheet_;
582   assert(sheet);
583   currentInfo = NULL;
584   currentLines.deppendall();
585   bpoints.reset();
586   setBreakpointsOnElement(S, &(sheet -> getRoot()));
587 }
588
589 void Debugger::enterIdle()
590 {
591   Bool quited;
592   prompt(*(Situation*)situa, NULL, NULL, quited);
593   while (!quited)
594     prompt(*(Situation*)situa, NULL, NULL, quited);
595 }
596
597
598 void Debugger::reportTrace(const char* msg, Str& uri, int line, Element *e)
599 {
600   Str name;
601   e -> getOwner().expandQStr(e -> getName(), name);
602   printf("\n%s in %s:%d (<%s>)\n",
603          msg, (char*)uri, line, (char*)name);
604 }
605
606 Bool Debugger::elementBreakable(Sit S, Element *e, Context *c, Bool report)
607 {
608   Bool ret = false;
609   Bool wasError = false;
610   if (e)
611     {
612       Str &uri = e -> getSubtreeInfo() -> getBaseURI();
613       int line = e -> lineno;
614
615       if ((ret = instep)) 
616         {
617           if (report) reportTrace("Step to", uri, line, e);
618         }
619       else if ((ret = (intempl && isXSL(e) && toX(e) -> op == XSL_TEMPLATE)))
620         {
621           if (report) reportTrace("Template met", uri, line, e);
622         }
623       else if ((ret = (e == nextele)))
624         {
625           if (report) reportTrace("Step (next) to", uri, line, e);
626         }
627       else if ((ret = (e -> breakpoint && e -> breakpoint -> countEnabled()
628                        && e -> breakpoint -> countMatches(S, c, wasError))))
629         {
630           if (wasError)
631             reportError("error evaluating bp condition:", false);
632           if (report) reportTrace("Breakpoint met", uri, line, e);
633         }
634     }
635   //clear helpers
636   instep = false;
637   if (ret) nextele = NULL;
638   if (ret) intempl = false;
639
640   return ret;
641 }
642
643 Bool Debugger::tokenize(char* what_, ArgList &where,
644                         DbgCommandDescription& desc)
645 {
646   Bool ret;
647   desc = theNullCommand;
648   char token[256];
649   char *what = what_;
650   what += strspn(what, " \n\t");
651   char *aux = strpbrk(what, " \n\t");
652   if (! aux) aux = what + strlen(what);
653
654   strncpy(token, what, aux - what);
655   token[aux - what] = 0;
656
657   //perhaps we didnt find anything
658   if (!token[0]) return true;
659
660   //now we lookup the command in the table
661   Str str(token);
662   //return the result of the command lookup
663   ret = lookupCommand(str, desc);
664   //
665   where.append(new Str(token));
666
667   int numargs = 100;
668   if (desc.joinRest) numargs = desc.argsMax;
669
670   //read the rest
671   int currnum = 1;
672   while (*aux && currnum < numargs)
673     {
674       aux += strspn(aux, " \n\t");
675       if (*aux)
676         {
677           what = aux;
678           aux = strpbrk(what, " \n\t");
679           if (! aux) aux = what + strlen(what);
680           strncpy(token, what, aux - what);
681           token[aux - what] = 0;
682           where.append(new Str(token));
683         }
684       currnum++;
685     }
686   //read the last
687   if (*aux)
688     {
689       aux += strspn(aux, " \n\t");
690       if (*aux)
691           where.append(new Str(aux));
692     }
693   return ret;
694 }
695
696 Bool Debugger::lookupArg(Str &str, char** lst, int &op)
697 {
698   int idx = 0;
699   op = -1;
700   char *item = lst[idx++];
701   while (op == -1 && item)
702     {
703       char* aux = (char*) str;
704       int len = strlen(aux);
705       if ( ! strncmp(aux, item, len) )
706         {
707           //lookahead
708           char* auxi = lst[idx];
709           if ( auxi )
710             {
711               if ( strncmp(aux, auxi, len) )
712                 op = idx - 1;
713               else 
714                 return false;
715             }
716           else
717             op = idx - 1;
718         }
719       item = lst[idx++];
720     }
721   return true;
722 }
723
724 Bool Debugger::lookupCommand(Str& str, DbgCommandDescription& out)
725 {
726   int idx = 0;
727   out = theNullCommand;
728   DbgCommandDescription desc = dbgCommands[idx++];
729   while (out.op == SDBG_NOOP && desc.command)
730     {
731       char* aux = (char*) str;
732       int len = strlen(aux);
733       if ( ! strncmp(aux, desc.command, len) )
734         {
735           //lookahead
736           DbgCommandDescription auxd = dbgCommands[idx];
737           if ( desc.strict && auxd.command )
738             {
739               if ( strncmp(aux, auxd.command, len) )
740                 out = desc;
741               else 
742                 return false;
743             }
744           else
745             out = desc;
746         }
747       desc = dbgCommands[idx++];
748     }
749
750   return true;
751 }
752
753 const char* Debugger::nodeTypeName(Vertex *v)
754 {
755   if ( isRoot(v) )
756     return "Root";
757   else if ( isXSL(v) )
758     return "XSLElement";
759   else if ( isExt(v) )
760     return "ExtensionElement";
761   else if ( isElement(v) )
762     return "Element";
763   else if ( isAttr(v) )
764     return "Attribute";
765   else if ( isPI(v) )
766     return "ProcessingInstruction";
767   else if ( isNS(v) )
768     return "NmSpace";
769   else if ( isComment(v) )
770     return "Comment";
771   else if ( isText(v) )
772     return "Text";
773   else 
774     return "unknown";
775 }
776
777 void Debugger::listNode(Sit S, NodeHandle node, Bool detailed)
778 {
779   Vertex *v = toV(node);
780
781   //get name
782   Str name, ns, cont;
783   if (isElement(v) || isAttr(v) || isPI(v))
784     {
785       v -> getOwner().expandQStr(v -> getName(), name);
786     }
787   //namespace
788   if (isElement(v) || isAttr(v))
789     {
790       ns = v -> getOwner().expand(v -> getName().getUri());
791     }
792
793   switch (baseType(v)) {
794   case VT_ATTRIBUTE:
795     cont = toA(v) -> cont; break;
796   case VT_TEXT:
797     cont = toText(v) -> cont; break;
798   case VT_PI:
799     cont = toPI(v) -> cont; break;
800   case VT_COMMENT:
801     cont = toComment(v) -> cont; break;
802   }
803
804   if (detailed)
805     {
806       //type
807       printf("type: %s\n", nodeTypeName(v));
808       //name
809       if (! name.isEmpty() )
810         {
811           printf("name: %s\n", (char*)name);
812         }
813       if (! ns.isEmpty() )
814         {
815           printf("ns: %s\n", (char*)ns);
816         }
817       if (! cont.isEmpty() )
818         {
819           printf("cont: %s\n", (char*)cont);
820         }
821     }
822   else
823     {
824       //type
825       printf("%s:", nodeTypeName(v));
826       //name
827       if (! name.isEmpty() )
828           printf(" name='%s'", (const char*) name);
829       //namespace
830       if (! ns.isEmpty() )
831           printf(" ns='%s'", (const char*) ns);
832       //content
833       if (! cont.isEmpty() )
834         {
835           char foo[11];
836           strncpy(foo, (char*)cont, 10);
837           foo[10] = 0;
838           printf(" cont='%s';", foo);
839         }
840
841       //terminal
842       printf("\n");
843     }
844 }
845
846 void Debugger::listContext(Sit S, Element* e, Context* c, ArgList &args)
847 {
848   if (args.number() == 1)
849     {
850       printf("Context:\n  size=%d\n  position=%d\n", 
851              c -> getSize(), c -> getPosition());
852       //list namespaces
853       printf("Namespaces:\n");
854       PList<Str*> curr;
855       Str prefix, uri;
856       for (int i = e -> namespaces.number() - 1; i >= 0; i--)
857         {
858           NmSpace *nm = toNS(e-> namespaces[i]);
859           prefix = e -> getOwner().expand(nm -> prefix);
860           uri = e -> getOwner().expand(nm -> uri);
861
862           Bool found = false;
863           for (int j = 0; j < curr.number() && !found; j++)
864             {
865               found = *(curr[j]) == prefix;
866             }
867         if (!found)
868           {
869             curr.append(new Str(prefix));
870             printf("  %s => %s\n", (char*)prefix, (char*)uri);
871           }
872         }
873       curr.freeall(false);
874     }
875   else
876     {
877       int op;
878       if (! lookupArg(*(args[1]), ctxArgs, op) )
879         printf("ambiguous command argument");
880       else
881         {
882           if (op >= 0) 
883             {
884               switch (op) {
885               case 0:
886                 {
887                   for (int i = 0; i < c -> getSize(); i++)
888                     {
889                       if (i == c -> getPosition())
890                         printf(">");
891                       else
892                         printf("#");
893                       printf("%d: ", i);
894                       listNode(S, (*c)[i], false);
895                     }
896                 }; break;
897               case 1:
898                 {
899                   printf("#current: ");
900                   listNode(S, c -> getCurrentNode(), true);
901                 }; break;
902               default:
903                 {
904                 }; break;
905               }
906             }
907           else
908             {
909               //try integer
910               long int idx;
911               if (strToInt(args[1], idx))
912                 {
913                   if (idx >= 0 && idx < c -> getSize())
914                     {
915                       listNode(S, (*c)[idx], true);
916                     }
917                   else
918                     {
919                       printf("index out of bounds\n");
920                     }
921                 }
922               else
923                 {
924                   printf("unknown command argument\n");
925                 }
926             }
927         }
928     }
929 }
930
931 Bool Debugger::findElementForBreakpoint(Element *e, Breakpoint* bp)
932 {
933   SubtreeInfo* info = e -> subtree;
934   if (info != currentInfo)
935     {
936       currentInfo = info;
937       inRightTree = info && (info -> getBaseURI() == bp -> uri);
938     }
939   //set breakpoint if we're in place
940   if (inRightTree && e -> lineno == bp -> line)
941     {
942       e -> breakpoint = bp;
943       bp -> element = e;
944       bp -> valid = true;
945       return true;
946     }
947   //process babies
948   for (int i = 0; i < e -> contents.number(); i++)
949     {
950       if (isElement(e -> contents[i]))
951         if (findElementForBreakpoint(toE(e -> contents[i]), bp))
952           return true;
953     }
954   return false;
955 }
956
957 Bool Debugger::setBreakpointRunning(Breakpoint *bp)
958 {
959   assert(sheet);
960   inRightTree = false;
961   currentInfo = NULL;
962
963   return findElementForBreakpoint(&(sheet -> getRoot()), bp);
964 }
965
966 void Debugger::doSetBreakpoint(Str& uri, int line)
967 {
968   Breakpoint *bp;
969   if ((bp = bpoints.setBP(uri, line)))
970     {
971       if (!running || setBreakpointRunning(bp))
972         printf("breakpoint set in %s at %d\n", (char*)uri, line);
973       else
974         printf("breakpoint set (invalid) in %s at %d\n", (char*)uri, line);
975     }
976   else
977     printf("breakpoint already exists\n");
978 }
979
980 void Debugger::setBreakpoint(Sit S, Element *e, ArgList& args)
981 {
982   if (args.number() == 1)
983     {
984       Str &uri = e -> getSubtreeInfo() -> getBaseURI();
985       int line = e -> lineno;
986       doSetBreakpoint(uri, line);
987     }
988   else
989     {
990       char *aux = (char*) *(args[1]);
991       char *sep = strchr(aux, ':');
992       if (sep)
993         {
994           *sep = '\0';
995           
996           char *aline = sep + 1;
997           Str file = aux;
998           char *aux;
999           int line = strtol(aline, &aux, 10);
1000           if (*aux != '\0' )
1001             {
1002               printf("bad location\n");
1003             }
1004           else
1005             {
1006               DStr theBase;
1007               my_getcwd(theBase);
1008               theBase = Str("file://") + theBase;
1009               
1010               Str abs;
1011               makeAbsoluteURI(S, file, theBase, abs);
1012               
1013               doSetBreakpoint(abs, line);
1014             }
1015
1016           *sep = ':';
1017         }
1018       else
1019         {
1020           printf("bad location\n");
1021         }
1022     }
1023 }
1024
1025 Bool Debugger::checkIdle()
1026 {
1027   if (running)
1028     printf("processor is running\n");
1029   return (! running);
1030 }
1031
1032 Bool Debugger::checkRunning()
1033 {
1034   if (! running)
1035     printf("processor is NOT running\n");
1036   return (running);
1037 }
1038
1039 void Debugger::run()
1040 {
1041
1042   errorCode = 0;
1043   outputLine = 1;
1044   outputLastLine = 0;
1045   messages.freeall(FALSE);
1046
1047   if (dataURI.isEmpty())
1048     printf("no datafile given\n");
1049
1050   if (sheetURI.isEmpty())
1051     printf("no stylesheet given\n");
1052
1053   if (!dataURI.isEmpty() && !sheetURI.isEmpty())
1054     {
1055       SablotHandle proc;
1056       SablotCreateProcessorForSituation(situa, &proc);
1057
1058       //register the message handler
1059       SablotRegHandler(proc, HLR_MESSAGE, &msgHandler, this);
1060
1061       for (int i = 0; i < params.number(); i++)
1062         {
1063           SablotAddParam(situa, proc, 
1064                          (char*)(params[i] -> key),
1065                          (char*)(params[i] -> value));
1066         }
1067
1068       running = true;
1069       instep = false;
1070       printf("debugger started\n");
1071       int code = SablotRunProcessorGen(situa, proc, 
1072                                        (const char*)sheetURI, 
1073                                        (const char*)dataURI, 
1074                                        "arg:/res");
1075       running = false;
1076       if (code)
1077         {
1078           if (this -> errorCode != DBG_BREAK_PROCESSOR)
1079             {
1080               reportError("\ndebugger finished with error: ", true);
1081             }
1082           else 
1083             {
1084               printf("debugger finished (killed)\n");
1085             }
1086           SablotClearSituation(situa);
1087         }
1088       else
1089         {
1090           printf("\ndebugger finished successfully\n");
1091         }
1092       SablotDestroyProcessor(proc);
1093     }
1094 }
1095
1096 void Debugger::reportError(const char* msg, Bool details)
1097 {
1098   printf("%s", msg);
1099   for (int i = 0; i < messages.number(); i++)
1100     {
1101       Str &key = messages[i] -> key;
1102       if (! (key == (const char*)"msgtype") &&
1103           ! (key == (const char*)"module") &&
1104           (details ||
1105            (! (key == (const char*)"URI") &&
1106             ! (key == (const char*)"line") &&
1107             ! (key == (const char*)"node")))
1108           )
1109         {
1110           printf("[%s: %s]", (char*)key, (char*)(messages[i] -> value));
1111         }
1112     }
1113   printf("\n");
1114   SablotClearSituation(situa);
1115 }
1116
1117 void Debugger::eval(Sit S, Context *c, Element *e, ArgList& args)
1118 {
1119   Str estr;
1120   if (args.number() == 1)
1121     {
1122       printf("no expression given\n");
1123       return;
1124     }
1125   else
1126     estr = *(args[1]);
1127
1128   Expression expr(*e);
1129   Bool failed = false;
1130   failed = expr.parse(S, estr);
1131
1132   if (! failed )
1133     {
1134       Expression ret(*e);
1135       failed = expr.eval(S, ret, c);
1136       if (!failed)
1137         {
1138           switch (ret.type) {
1139           case EX_NUMBER:
1140             {
1141               Str str;
1142               ret.tostring(S, str);
1143               printf("number: %s\n", (char*)str);
1144             }; break;
1145           case EX_STRING:
1146             {
1147               Str str;
1148               ret.tostring(S, str);
1149               printf("string: '%s'\n", (char*)str);
1150             }; break;
1151           case EX_BOOLEAN:
1152             {
1153               Str str;
1154               ret.tostring(S, str);
1155               printf("boolean: %s\n", (char*)str);
1156             }; break;
1157           case EX_NODESET:
1158             {
1159               const Context &nset = ret.tonodesetRef();
1160               printf("nodeset (size: %d):\n", nset.getSize());
1161               for (int i = 0; i < nset.getSize(); i++)
1162                 {
1163                   printf("#%d: ", i);
1164                   listNode(S, nset[i], false);
1165                 }
1166             }; break;
1167           case EX_FRAGMENT:
1168             {
1169               printf("RTF\n");
1170             }; break;
1171           case EX_EXTERNAL:
1172             {
1173               printf("ExternalValue\n");
1174             }; break;
1175           default:
1176             {
1177               printf("unknown return type (%d)\n", ret.type);
1178             }
1179           }
1180         }
1181     }
1182
1183   if (failed) reportError("error in evaluation: ", false);
1184 }
1185
1186 void Debugger::playBatch(Sit S, Element *e, Context *c, Str& file)
1187 {
1188   Bool done = false;
1189   char *res, line[256];
1190   FILE *f;
1191
1192   f = fopen((char*)file, "r");
1193   if (f)
1194     {
1195       Bool quited = false;
1196       res = fgets(line, 256, f);
1197       while (res && !done && !quited)
1198         {
1199           doCommand(S, e, c, line, done, quited);
1200           res = fgets(line, 256, f);
1201         }
1202     }
1203   else
1204     printf("can not read file %s\n", (char*)file);
1205 }
1206
1207 void Debugger::addParam(ArgList& args)
1208 {
1209   params.appendConstruct(*(args[1]), *(args[2]));
1210 }
1211
1212 Bool Debugger::checkArgs(DbgCommandDescription& desc, ArgList& args)
1213 {
1214   if (desc.op != SDBG_NOOP)
1215     {
1216       int i = args.number() - 1;
1217       return i >= desc.argsMin && i <= desc.argsMax;
1218     }
1219   else
1220     return true;
1221 }
1222
1223 Bool Debugger::doCommand(Sit S, Element *e, Context *c, 
1224                          char* line, Bool& done, Bool& quited)
1225 {
1226   ArgList args;
1227   quited = false;
1228   
1229   DbgCommandDescription cmd;
1230   if (! tokenize(line, args, cmd))
1231     {
1232       printf("ambiguous command\n");
1233     }
1234   else
1235     {
1236       if (!checkArgs(cmd, args))
1237         {
1238           printf("wrong number of arguments\n");
1239           return false;
1240         }
1241       //do the command
1242       switch (cmd.op) {
1243       case SDBG_NOOP:
1244         {
1245           if (args.number()) printf("unknown command\n");
1246         }; break;
1247       case SDBG_BREAK:
1248         {
1249           setBreakpoint(S, e, args);
1250         }; break;
1251       case SDBG_BREAK_LIST:
1252         {
1253           bpoints.listAll(running);
1254         }; break;
1255       case SDBG_BREAK_CLEAR:
1256         {
1257           long int idx;
1258           if (strToInt(args[1], idx))
1259               bpoints.clearBP(idx, running);
1260           else
1261             printf("invalid argument\n");
1262         }; break;
1263       case SDBG_BREAK_COND:
1264         {
1265           long int idx;
1266           if (strToInt(args[1], idx))
1267             {
1268               if (!bpoints.setExpression(S, idx, args, running))
1269                 {
1270                   reportError("wrong breakpoit expression:", false);
1271                 }
1272               else
1273                 printf("breakpoint modified\n");
1274             }
1275           else
1276             printf("invalid argument\n");                 
1277         }; break;
1278       case SDBG_BREAK_DISABLE:
1279         {
1280           long int idx;
1281           if (strToInt(args[1], idx))
1282             {
1283               if (bpoints.disableBP(idx))
1284                 printf("breakpoint modified\n");
1285             }
1286           else
1287             printf("invalid argument\n");
1288         }; break;
1289       case SDBG_BREAK_CLEARALL:
1290         {
1291           bpoints.clearAll(running);
1292           printf("all breakpoints deleted\n");
1293         }; break;
1294       case SDBG_BREAK_STAT:
1295         {
1296           bpoints.statAll(running);
1297         }; break;
1298       case SDBG_BREAK_IGNORE:
1299         {
1300           long int idx, num;
1301           if (strToInt(args[1], idx) && strToInt(args[2], num))
1302             {
1303               if (bpoints.ignore(idx, num))
1304                 printf("breakpoint modified\n");
1305             }
1306           else
1307             printf("invalid argument\n");
1308         }; break;
1309       case SDBG_CONTINUE:
1310         {
1311           if (checkRunning())
1312             {
1313               done = true;
1314             }
1315         }; break;
1316       case SDBG_CONTEXT:
1317         {
1318           if (checkRunning())
1319             {
1320               listContext(S, e, c, args);
1321             }
1322         }; break;
1323       case SDBG_EVAL:
1324         {
1325           if (checkRunning())
1326             {
1327               assert(e);
1328               eval(S, c, e, args);
1329             }
1330         }; break;
1331       case SDBG_BATCH:
1332         {
1333           playBatch(S, e, c, *(args[1]));
1334         }; break;
1335       case SDBG_PARAM:
1336         {
1337           if (checkIdle())
1338             {
1339               addParam(args);
1340             }
1341         }; break;
1342       case SDBG_PARAM_LIST:
1343         {
1344           if (params.number())
1345             for (int i = 0; i < params.number(); i++)
1346               {
1347                 printf("$%s = '%s'\n", 
1348                        (char*)(params[i] -> key), 
1349                        (char*)(params[i] -> value));
1350               }
1351           else
1352             printf("no params\n");
1353         }; break;
1354       case SDBG_PARAM_CLEAR:
1355         {
1356           if (checkIdle())
1357             {
1358               params.freeall(FALSE);
1359               printf("all params removed\n");
1360             }
1361         }; break;
1362       case SDBG_RUN:
1363         {
1364           if (checkIdle())
1365             {
1366               intempl = false;
1367               run();
1368             }
1369         }; break;
1370       case SDBG_DATA:
1371         {
1372           if (checkIdle())
1373             {
1374               if (args.number() < 2)
1375                 {
1376                   if (!dataURI.isEmpty())
1377                     printf("using datafile %s\n", (char*)dataURI);
1378                   else
1379                     printf("no datafile given\n");
1380                 }
1381               else
1382                 {
1383                   dataURI = *(args[1]);
1384                   printf("using datafile %s\n", (char*)dataURI);
1385                 }
1386             }
1387         }; break;
1388       case SDBG_SHEET:
1389         {
1390           if (checkIdle())
1391             {
1392               if (args.number() < 2)
1393                 {
1394                   if (!sheetURI.isEmpty())
1395                     printf("using stylesheet %s\n", (char*)sheetURI);
1396                   else
1397                     printf("no stylesheet given\n");
1398                 }
1399               else
1400                 {
1401                   sheetURI = *(args[1]);
1402                   printf("using stylesheet %s\n", (char*)sheetURI);
1403                 }
1404             }
1405         }; break;
1406       case SDBG_STEP:
1407         {
1408           if (checkRunning())
1409             {
1410               instep = true;
1411               done = true;
1412             }
1413         }; break;
1414       case SDBG_NEXT:
1415         {
1416           if (checkRunning())
1417             {
1418               Vertex *aux = e -> getNextSibling();
1419               if ( aux && isElement(aux) ) nextele = toE(aux);
1420               done = true;
1421             }
1422         }; break;
1423       case SDBG_FINISH:
1424         {
1425           if (checkRunning())
1426             {
1427               Vertex *aux = e -> parent -> getNextSibling();
1428               if ( aux && isElement(aux) ) nextele = toE(aux);
1429               done = true;
1430             }
1431         }; break;
1432       case SDBG_TEMPLATE:
1433         {
1434           intempl = true;
1435           if (!running) 
1436             run();
1437           else
1438             done = true; //continue
1439         }; break;
1440       case SDBG_OUTPUT:
1441         {
1442           output = !output;
1443           printf("output is now %s\n", output ? "on" : "off");
1444         }; break;
1445       case SDBG_POINT:
1446         {
1447           if (checkRunning())
1448             {
1449               Str &uri = e -> getSubtreeInfo() -> getBaseURI();
1450               int line = e -> lineno;
1451               printf("%s:%d\n", (char*)uri, line);
1452             }
1453         }; break;
1454       case SDBG_KILL:
1455         {
1456           if (checkRunning())
1457             {
1458               Err(S, DBG_BREAK_PROCESSOR);
1459             }
1460         }; break;
1461       case SDBG_HELP:
1462         {
1463           printUsage();
1464         } break;
1465       case SDBG_QUIT:
1466         {
1467           if (checkIdle())
1468             {
1469               quited = true;
1470               done = true;
1471               printf("debugger quited\n");
1472             }
1473         }; break;
1474       }
1475     }
1476
1477   args.freeall(FALSE);
1478   return OK;
1479 }
1480
1481 void Debugger::printUsage()
1482 {
1483   printf("%s", theUsageStr);
1484 }
1485
1486 eFlag Debugger::prompt(Sit S, Element *e, Context *c, Bool& quited)
1487 {
1488   char *line = NULL;
1489   Bool done = false;
1490   while (! done)
1491     {
1492       if (line) free(line);
1493       line = dbgGetLine();
1494       if (line) 
1495         {
1496           E( doCommand(S, e, c, line, done, quited) );
1497         }
1498     }
1499   if (line) free(line);
1500   return OK;
1501 }
1502
1503 eFlag Debugger::breakOnElement(Sit S, Element *e, Context *c)
1504 {
1505   Bool quited;
1506   if ( elementBreakable(S, e, c, true) )
1507     {
1508       E( prompt(S, e, c, quited) );
1509     }
1510   //quit may not happen while running
1511   return OK;
1512 }
1513
1514 void Debugger::report(Sit S, MsgType type, MsgCode code, 
1515                       const Str& arg1, const Str& arg2) const
1516 {
1517   S.message(type, code, arg1, arg2);
1518 }
1519
1520
1521 void Debugger::printOutput(const char* buff, int size)
1522 {
1523   char fmt[32];
1524   if (output)
1525     {
1526       if (outputLastLine < outputLine)
1527         {
1528           sprintf(fmt, "%d\t%%.%ds", outputLine, size);
1529           outputLastLine = outputLine;
1530         }
1531       else
1532         sprintf(fmt, "%%.%ds", size);
1533       
1534       printf(fmt, buff);
1535     }
1536   if (*buff == '\n') outputLine++;
1537 }
1538
1539 #endif //SABLOT_DEBUGGER