/*************************************************************************** nodeview_cmds.c - node viewing commands for xsldbg ------------------- begin : Wed Nov 21 2001 copyright : (C) 2001 by Keith Isdale email : k_isdale@tpg.com.au ***************************************************************************/ /*************************************************************************** * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation; either version 2 of the License, or * * (at your option) any later version. * * * ***************************************************************************/ #include "xsldbg.h" #include #include #include #include /* for isspace*/ #include "debugXSL.h" #include "arraylist.h" #include "breakpoint.h" #include "xsldbgmsg.h" #include "xsldbgthread.h" /* for getThreadStatus */ #include "files.h" #include "options.h" /* ----------------------------------------- Private function declarations for nodeview_cmds.c -------------------------------------------*/ static xmlChar nodeViewBuffer[500]; static int printVariableValue = 0; /* * xslDbgShellPrintNames: * Print a name of variable found by scanning variable table * It is used by print_variable function. * @payload : not used * @data : not used * @name : the variable name */ void *xslDbgShellPrintNames(void *payload, void *data, xmlChar * name); /** * xslDbgCatToFile: * @node : Is valid * @file : Is valid * * Send the results of cat command in @node to @file */ static void xslDbgCatToFile(xmlNodePtr node, FILE * file); /** * printXPathObject: * @item : XPath object to print * @xPath : The XPath used to find item * * Print an XPath object * * Returns 1 on success, * 0 otherwise */ static int printXPathObject(xmlXPathObjectPtr item, xmlChar* xPath); /* ------------------------------------- End private functions ---------------------------------------*/ /** * xslDbgShellPrintList: * @ctxt: The current shell context * @arg: What xpath to display and in UTF-8 * @dir: If 1 print in dir mode?, * otherwise ls mode * * Print list of nodes in either ls or dir format * * Returns 1 on success, * 0 otherwise */ int xslDbgShellPrintList(xmlShellCtxtPtr ctxt, xmlChar * arg, int dir) { xmlXPathObjectPtr list; int result = 0; if (!ctxt || !arg) { #ifdef WITH_XSLDBG_DEBUG_PROCESS xsltGenericError(xsltGenericErrorContext, "Error: NULL arguments provided\n"); #endif return result; } if (arg[0] == 0) { if (dir) xmlShellDir(ctxt, NULL, ctxt->node, NULL); else xmlShellList(ctxt, NULL, ctxt->node, NULL); result = 1; /*assume that this worked */ } else { ctxt->pctxt->node = ctxt->node; ctxt->pctxt->node = ctxt->node; if (!xmlXPathNsLookup(ctxt->pctxt, (xmlChar *) "xsl")) xmlXPathRegisterNs(ctxt->pctxt, (xmlChar *) "xsl", XSLT_NAMESPACE); list = xmlXPathEval(arg, ctxt->pctxt); if (list != NULL) { switch (list->type) { case XPATH_NODESET:{ int indx; for (indx = 0; indx < list->nodesetval->nodeNr; indx++) { if (dir) xmlShellList(ctxt, NULL, list->nodesetval-> nodeTab[indx], NULL); else xmlShellList(ctxt, NULL, list->nodesetval-> nodeTab[indx], NULL); } result = 1; break; } default: xmlShellPrintXPathError(list->type, (char *) arg); } xmlXPathFreeObject(list); } else { xsldbgGenericErrorFunc(i18n("Error: XPath %1 results in an empty Node Set.\n").arg(xsldbgText(arg))); } ctxt->pctxt->node = NULL; } return result; } /** * xslDbgCatToFile: * @node : Is valid * @file : Is valid * * Send the results of cat command in @node to @file */ void xslDbgCatToFile(xmlNodePtr node, FILE * file) { if (!node || !file) return; /* assume that HTML usage is enabled */ if (node->doc->type == XML_HTML_DOCUMENT_NODE) { if (node->type == XML_HTML_DOCUMENT_NODE) htmlDocDump(file, (htmlDocPtr) node); else htmlNodeDumpFile(file, node->doc, node); } else if (node->type == XML_DOCUMENT_NODE) { /* turn off encoding for the moment and just dump UTF-8 * which will be converted by xsldbgGeneralErrorFunc */ xmlDocPtr doc = (xmlDocPtr) node; const xmlChar *encoding = doc->encoding; if (encoding) { xsldbgGenericErrorFunc(i18n("Information: Temporarily setting document's encoding to UTF-8. Previously was %1.\n").arg(xsldbgText(encoding))); } doc->encoding = (xmlChar *) "UTF-8"; xmlDocDump(file, (xmlDocPtr) node); doc->encoding = encoding; } else { xmlElemDump(file, node->doc, node); } } /** * printXPathObject: * @item : XPath object to print * @xPath : The XPath used to find item * * Print an XPath object * * Returns 1 on success, * 0 otherwise */ static int printXPathObject(xmlXPathObjectPtr item, xmlChar* xPath){ int result = 0; if (item){ switch (item->type) { case XPATH_BOOLEAN: xsltGenericError(xsltGenericErrorContext, "= %s\n%s\n", xPath, xmlBoolToText(item->boolval)); result = 1; break; case XPATH_NUMBER: xsltGenericError(xsltGenericErrorContext, "= %s\n%0g\n", xPath, item->floatval); result = 1; break; /* case XPATH_NODESET:*/ default:{ /* We may need to convert this XPath to a string, plus ensure that we print required the number of lines of text */ int indx; const char *fileName = filesTempFileName(0); FILE *file = NULL; if (!fileName) break; file = fopen(fileName, "w+"); if (!file) { xsldbgGenericErrorFunc(i18n("Error: Unable to save temporary results to %1.\n").arg(xsldbgText(fileName))); break; } else { fprintf(file, "= %s\n", xPath); switch(item->type){ case XPATH_NODESET: if (item->nodesetval){ for (indx = 0; indx < item->nodesetval->nodeNr; indx++){ xslDbgCatToFile(item->nodesetval-> nodeTab[indx], file); } } else { xsldbgGenericErrorFunc(i18n("Error: XPath %1 results in an empty Node Set.\n").arg(xsldbgText(xPath))); } break; case XPATH_STRING: if (item->stringval) fprintf(file, "\'%s\'", item->stringval); else fprintf(file, "%s", i18n("NULL string value supplied.").utf8().data()); break; default:{ xmlXPathObjectPtr tempObj = xmlXPathObjectCopy(item); if (tempObj) tempObj = xmlXPathConvertString(tempObj); if (tempObj && tempObj->stringval){ fprintf(file, "%s", tempObj->stringval); }else{ fprintf(file, "%s", i18n("Unable to convert XPath to string.").utf8().data()); } if (tempObj) xmlXPathFreeObject(tempObj); } break; fprintf(file,"\n"); } /* inner switch statement */ if (getThreadStatus() == XSLDBG_MSG_THREAD_RUN) { fclose(file); file = NULL; /* send the data to application */ notifyXsldbgApp(XSLDBG_MSG_FILEOUT, fileName); } else { int lineCount = 0, gdbModeEnabled = 0; /* save the value of option to speed things up * a bit */ gdbModeEnabled = optionsGetIntOption(OPTIONS_GDB); rewind(file); /* when gdb mode is enable then only print the first * GDB_LINES_TO_PRINT lines */ while (!feof(file)) { if (fgets ((char *) nodeViewBuffer, sizeof(nodeViewBuffer), file)) xsltGenericError (xsltGenericErrorContext, "%s", nodeViewBuffer); if (gdbModeEnabled) { lineCount++; /* there is an overhead of two lines * when print expression values */ if (lineCount == GDB_LINES_TO_PRINT + 2) { xsltGenericError (xsltGenericErrorContext, "..."); break; } } } xsltGenericError (xsltGenericErrorContext, "\n"); } if (file) fclose(file); result = 1; break; } } } } return result; } /** * xslDbgShellCat: * @styleCtxt: the current stylesheet context * @ctxt: The current shell context * @arg: The xpath to print (in UTF-8) * * Print the result of an xpath expression. This can include variables * if styleCtxt is not NULL * * Returns 1 on success, * 0 otherwise */ int xslDbgShellCat(xsltTransformContextPtr styleCtxt, xmlShellCtxtPtr ctxt, xmlChar * arg) { xmlXPathObjectPtr list; int result = 0; static const char * QUIET_STR = "-q"; bool silenceCtxtErrors = false; if ((arg == NULL) || (xmlStrLen(arg) == 0)) arg = (xmlChar *) "."; /* Do we quietly ingore style context errors */ if (strncasecmp((char*)arg, QUIET_STR, strlen(QUIET_STR))== 0){ silenceCtxtErrors = true; arg = arg + strlen(QUIET_STR); while (isspace(*arg)){ arg++; } } if (!styleCtxt || !ctxt || !ctxt->node) { if (!(!xsldbgReachedFirstTemplate && silenceCtxtErrors)) xsldbgGenericErrorFunc(i18n("Warning: Unable to print expression. No stylesheet was properly loaded.\n")); return 0; } if ((arg == NULL) || (xmlStrLen(arg) == 0)) arg = (xmlChar *) "."; ctxt->pctxt->node = ctxt->node; if (!styleCtxt) { list = xmlXPathEval((xmlChar *) arg, ctxt->pctxt); } else { xmlNodePtr savenode = styleCtxt->xpathCtxt->node; ctxt->pctxt->node = ctxt->node; styleCtxt->xpathCtxt->node = ctxt->node; if (!xmlXPathNsLookup(styleCtxt->xpathCtxt, (xmlChar *) "xsl")) xmlXPathRegisterNs(styleCtxt->xpathCtxt, (xmlChar *) "xsl", XSLT_NAMESPACE); list = xmlXPathEval((xmlChar *) arg, styleCtxt->xpathCtxt); styleCtxt->xpathCtxt->node = savenode; } if (list != NULL) { result = printXPathObject(list, arg); xmlXPathFreeObject(list); } else { xsldbgGenericErrorFunc(i18n("Error: XPath %1 results in an empty Node Set.\n").arg(xsldbgText(arg))); } ctxt->pctxt->node = NULL; return result; } /* only used by xslDbgPrintNames and xslDbgPrintVariable cound number of variables */ static int varCount; /* * xslDbgShellPrintNames: * Print a name of variable found by scanning variable table * It is used by print_variable function. * @payload : Global variable of type xsltStackElemPtr * @data : not used * @name : the variable name */ void * xslDbgShellPrintNames(void *payload, void *data, xmlChar * name) { Q_UNUSED(payload); Q_UNUSED(data); if (getThreadStatus() == XSLDBG_MSG_THREAD_RUN) { notifyListQueue(payload); } else if (payload && name) { xmlChar * fullQualifiedName = nodeViewBuffer; xsltStackElemPtr item = (xsltStackElemPtr)payload; if (item->nameURI == NULL){ snprintf((char*)fullQualifiedName, sizeof(nodeViewBuffer), "$%s", item->name); }else{ snprintf((char*)fullQualifiedName, sizeof(nodeViewBuffer), "$%s:%s", item->nameURI, item->name); } if (printVariableValue == 0){ xsldbgGenericErrorFunc(i18n(" Global %1\n").arg(xsldbgText(fullQualifiedName))); }else{ if (item->computed == 1){ xsldbgGenericErrorFunc(i18n(" Global ")); printXPathObject(item->value, fullQualifiedName); }else if (item->tree){ xsldbgGenericErrorFunc(i18n(" Global = %1\n").arg(xsldbgText(fullQualifiedName))); xslDbgCatToFile(item->tree, stderr); }else if (item->select){ xsldbgGenericErrorFunc(i18n(" Global = %1\n%2").arg(xsldbgText(fullQualifiedName)).arg(xsldbgText(item->select))); }else{ /* can't find a value give only a variable name an error message */ xsldbgGenericErrorFunc(i18n(" Global = %1\n%2").arg(xsldbgText(fullQualifiedName)).arg(i18n("Warning: No value assigned to variable.\n"))); } xsltGenericError(xsltGenericErrorContext, "\n\032\032\n"); } varCount++; } return NULL; } /** * xslDbgShellPrintVariable: * @styleCtxt: The current stylesheet context * @arg: The name of variable to look for '$' prefix is optional and in UTF-8 * @type: A valid VariableTypeEnum * * Print the value variable specified by args. * * Returns 1 on success, * 0 otherwise */ int xslDbgShellPrintVariable(xsltTransformContextPtr styleCtxt, xmlChar * arg, VariableTypeEnum type) { int result = 0; /* command argument to include both name and its value */ static const char * FULLNAME_STR = "-f"; /* Quietly exit if an invalid stylesheet is provided */ static const char * QUIET_STR = "-q"; bool silenceCtxtErrors = false; if (!arg) { #ifdef WITH_XSLDBG_DEBUG_PROCESS xsltGenericError(xsltGenericErrorContext, "Error: NULL argument provided\n"); #endif return result; } varCount = 0; /* Do we quietly ingore style context errors */ if (strncasecmp((char*)arg, QUIET_STR, strlen(QUIET_STR))== 0){ silenceCtxtErrors = true; arg = arg + strlen(QUIET_STR); while (isspace(*arg)){ arg++; } } if (!styleCtxt) { if (!(!xsldbgReachedFirstTemplate && silenceCtxtErrors)) xsldbgGenericErrorFunc(i18n("Error: Debugger has no files loaded or libxslt has not reached a template.\nTry reloading files or taking more steps.\n")); return result; } /* Do we include the name as well as its value */ if (strncasecmp((char*)arg, FULLNAME_STR, strlen(FULLNAME_STR))== 0){ printVariableValue = 1; arg = arg + strlen(FULLNAME_STR); while (isspace(*arg)){ arg++; } } if (arg[0] == 0) { /* list variables of type requested */ if (type == DEBUG_GLOBAL_VAR) { if (styleCtxt->globalVars) { if (getThreadStatus() == XSLDBG_MSG_THREAD_RUN) { notifyListStart(XSLDBG_MSG_GLOBALVAR_CHANGED); /* list global variables */ xmlHashScan(styleCtxt->globalVars, (xmlHashScanner) xslDbgShellPrintNames, NULL); notifyListSend(); } else /* list global variables */ xmlHashScan(styleCtxt->globalVars, (xmlHashScanner) xslDbgShellPrintNames, NULL); result = 1; /* ensure that the locals follow imediately after the * globals when in gdb mode */ if (optionsGetIntOption(OPTIONS_GDB) == 0) xsltGenericError(xsltGenericErrorContext, "\n"); } else { if (getThreadStatus() != XSLDBG_MSG_THREAD_RUN) { /* Don't show this message when running as a thread as it * is annoying */ xsldbgGenericErrorFunc(i18n("Error: Libxslt has not initialized variables yet; try stepping to a template.\n")); } else { /* send an empty list */ notifyListStart(XSLDBG_MSG_GLOBALVAR_CHANGED); notifyListSend(); result = 1; } } } else { /* list local variables */ if (styleCtxt->varsNr && styleCtxt->varsTab) { if (getThreadStatus() == XSLDBG_MSG_THREAD_RUN) { notifyListStart(XSLDBG_MSG_LOCALVAR_CHANGED); for (int i = styleCtxt->varsNr; i > styleCtxt->varsBase; i--) { xsltStackElemPtr item = styleCtxt->varsTab[i-1]; while (item) { notifyListQueue(item); item = item->next; } } notifyListSend(); } else { xmlChar * fullQualifiedName = nodeViewBuffer; for (int i = styleCtxt->varsNr; i > styleCtxt->varsBase; i--) { xsltStackElemPtr item = styleCtxt->varsTab[i-1]; while (item) { if (item->name) { if (item->nameURI == NULL){ snprintf((char*)fullQualifiedName, sizeof(nodeViewBuffer), "$%s", item->name); }else{ snprintf((char*)fullQualifiedName, sizeof(nodeViewBuffer), "$%s:%s", item->nameURI, item->name); } if (printVariableValue == 0){ xsldbgGenericErrorFunc(i18n(" Local %1").arg(xsldbgText(fullQualifiedName))); }else{ if (item->computed == 1){ xsldbgGenericErrorFunc(i18n(" Local ")); printXPathObject(item->value, fullQualifiedName); }else if (item->tree){ xsldbgGenericErrorFunc(i18n(" Local = %1\n").arg(xsldbgText(fullQualifiedName))); xslDbgCatToFile(item->tree, stderr); }else if (item->select){ xsldbgGenericErrorFunc(i18n(" Local = %1\n%2").arg(xsldbgText(fullQualifiedName)).arg(xsldbgText(item->select))); }else{ /* can't find a value give only a variable name and an error */ xsldbgGenericErrorFunc(i18n(" Local = %1\n%2").arg(xsldbgText(fullQualifiedName)).arg(i18n("Warning: No value assigned to variable.\n"))); } } xsltGenericError(xsltGenericErrorContext, "\n\032\032\n"); } item = item->next; } } } result = 1; xsltGenericError(xsltGenericErrorContext, "\n"); } else { if (getThreadStatus() != XSLDBG_MSG_THREAD_RUN) { /* Don't show this message when running as a thread as it * is annoying */ xsldbgGenericErrorFunc(i18n("Error: Libxslt has not initialized variables yet; try stepping past the xsl:param elements in the template.\n")); } else { /* send an empty list */ notifyListStart(XSLDBG_MSG_LOCALVAR_CHANGED); notifyListSend(); result = 1; } } } } else { /* Display the value of variable */ if (arg[0] == '$') { printXPathObject(xmlXPathEval(arg, styleCtxt->xpathCtxt), arg); xsltGenericError(xsltGenericErrorContext, "\032\032\n"); } else { xmlStrCpy(nodeViewBuffer, "$"); xmlStrCat(nodeViewBuffer, arg); printXPathObject(xmlXPathEval((xmlChar*)nodeViewBuffer,styleCtxt->xpathCtxt), (xmlChar*)nodeViewBuffer); xsltGenericError(xsltGenericErrorContext, "\032\032\n"); } } printVariableValue = 0; return result; }