//
// Copyright 2008 Perforce Software. All rights reserved.
//
// This file is part of Perforce - the FAST SCM System.
//
// StrXml:
// XML output methods class
//
// Instructions for using:
//
// You must call XMLHeader() to generate the header for your XML;
// Call XMLOutput*() when you get an p4api callback of type Output*()
// e.g. call XMLOutputStat() when you get an OutputStat() callback;
// You must call XMLEnd() when you are done.
//
// StrXml is a child of StrBuf; the XML itself is generated in the
// implicit StrBuf. You can use <<, .Clear(), .Set(), etc. to
// obtain the generated XML or add you own data to the stream.
//
// See //depot/main/p4-web/Panes/p4wMimeContentPane.cpp for a useage example
// -------------------------------------
// Includes
//
# include <stdhdrs.h>
# include <strbuf.h>
# include <strdict.h>
# include <strops.h>
# include <time.h>
# include <ctype.h>
# include <strxml.h>
#define NBRPREFIX "_"
void
StrXml::XMLHeader( const StrPtr *cmd, const StrPtr *args, const StrPtr *port,
const StrPtr *user, const StrPtr *client, int bUnicode )
{
*this << "<?xml version='1.0' encoding='";
fUnicode = bUnicode;
if ( bUnicode )
*this << "UTF-8";
else
*this << "ISO-8859-1";
fP4Cmd.Set(cmd);
*this << "'?>\n<perforce command=\"" << cmd;
if ( args->Length() )
*this << "\" args=\"" << EscapeHTML(StrRef(args->Text()));
*this << "\" server=\"" << EscapeHTML(StrRef(port->Text()));
*this << "\" user=\"" << EscapeHTML(StrRef(user->Text()));
*this << "\" client=\"" << EscapeHTML(StrRef(client->Text()));
char time_buffer[255 + 1];
time_buffer[0] = '\0';
StrBuf time_format;
time_format.Append( "%a, %d %b %Y %H:%M:%S %Z" );
time_t iTime;
time( &iTime );
strftime( time_buffer, sizeof( time_buffer ), time_format.Text(),
localtime( &iTime ) );
*this << "\" time=\"" << time_buffer << "\">\n";
}
void
StrXml::XMLOutputStat( StrDict * varList )
{
int i;
int seenXML = 0;
StrBuf msg;
StrRef var, val;
// Dump out the variables, using the GetVar( x ) interface.
// Don't display the function (duh), which is only relevant to rpc.
for( i = 0; varList->GetVar( i, var, val ); i++ )
{
if( var == "func" )
continue;
if( var == "specdef" )
{
*this << "<specdef>" << val << "</specdef>\n";
continue;
}
if (!seenXML)
{
seenXML++;
*this << "<" << fP4Cmd << ">\n";
}
char *p = var.Text();
if (*(p + var.Length() - 1) == '0')
{
i = XMLlist( varList, i );
continue;
}
StrBuf varbuf( var );
StrOps::Lower( varbuf );
var = varbuf;
*this << "<";
if (isdigit(*(var.Text())))
*this << NBRPREFIX;
*this << var;
if (val.Length())
{
if (strchr(val.Text(), '\n') || strchr(val.Text(), '\t') || strstr(val.Text(), " "))
*this << ">\n<![CDATA[" << val << "]]>\n";
else
*this << ">" << EscapeHTML(val);
*this << "</";
if (isdigit(*(var.Text())))
*this << NBRPREFIX;
*this << var << ">\n";
}
else
*this << "/>\n";
}
if (seenXML)
{
if (!strcmp(fP4Cmd.Text(), "annotate") && !fExtraTag.Length())
{
fExtraTag << "fileLines";
*this << "<" << fExtraTag << ">\n";
}
else
*this << "</" << fP4Cmd << ">\n";
}
}
int
StrXml::XMLlist( StrDict * varList, int i, char * remove, char *nextup )
{
StrRef vary, val;
StrBuf var;
char *p;
varList->GetVar( i, vary, val );
StrBuf listtag;
if (isdigit(*(vary.Text())))
listtag << NBRPREFIX;
listtag << vary.Text();
StrOps::Lower( listtag );
if (remove)
{
p = strstr(listtag.Text(), remove);
if (p)
{
strcpy(p, p+strlen(remove));
listtag.SetLength();
}
}
p = listtag.Text();
*(p + listtag.Length() - 1) = '\0';
listtag.SetLength();
*this << "<" << listtag << "s>\n";
StrBuf sameas;
StrBuf next;
int nxt = 0;
do
{
var.Clear();
if (isdigit(*(vary.Text())))
var << NBRPREFIX;
var << vary;
StrOps::Lower( var );
if (remove)
{
p = strstr(var.Text(), remove);
if (p)
{
strcpy(p, p+strlen(remove));
var.SetLength();
}
}
sameas.Set(var.Text() + listtag.Length());
next.Clear();
next << listtag << ++nxt;
*this << "<" << listtag;
if (val.Length())
*this << " value=\"" << EscapeHTML(val) << "\"";
*this << " id1=\"";
if (remove)
{
StrBuf temp;
temp << remove;
if ((p = strchr(temp.Text(), ',')) != NULL)
{
*p = '\0';
temp.SetLength();
}
*this << temp << "\" id2=\"";
}
*this << sameas << "\">\n";
while ( varList->GetVar( ++i, vary, val ) )
{
var.Clear();
if (isdigit(*(vary.Text())))
var << NBRPREFIX;
var << vary;
StrOps::Lower( var );
if (remove)
{
p = strstr(var.Text(), remove);
if (p)
{
strcpy(p, p+strlen(remove));
var.SetLength();
}
}
if (!strcmp(next.Text(), var.Text()))
break;
if (nextup && !strcmp(nextup, var.Text()))
break;
p = strchr(var.Text(), ',');
if (p)
{
StrBuf rmv;
rmv << p - sameas.Length();
*(rmv.Text() + sameas.Length() + 1) = '\0';
rmv.SetLength();
i = XMLlist( varList, i, rmv.Text(), next.Text() );
continue;
}
if (strcmp(var.Text() + var.Length() - sameas.Length(), sameas.Text()))
break;
*(var.Text() + var.Length() - sameas.Length()) = '\0';
var.SetLength();
*this << "<" << var;
if (val.Length())
{
*this << ">" << EscapeHTML(val);
*this << "</" << var << ">\n";
}
else
*this << "/>\n";
}
*this << "</" << listtag << ">\n";
} while (varList->GetVar( i, vary, val ) && !strcmp(next.Text(), var.Text()));
*this << "</" << listtag << "s>\n";
return --i;
}
StrBuf& StrXml::EscapeHTML(const StrPtr &s, int isUnicode)
{
fEscapeBuf.Clear(); // holds output
//
// Search for, and escape, <>&'s to > < and &
int i=0;
for( const char * p = s.Text(); *p != '\0'; p++, i++ ) {
unsigned char first = *p;
//
// See if we need to escape this character.
if( first == '<' ) {
fEscapeBuf.Append("<");
} else if( first == '>' ) {
fEscapeBuf.Append(">");
} else if( first == '&' ) {
fEscapeBuf.Append("&");
} else if( first == '"' ) {
fEscapeBuf.Append(""");
} else if ( first > 0x7F && !isUnicode ) {
//
// If it is outside of ascii, just use the numeric
// value
//
fEscapeBuf.Append( "&#" );
fEscapeBuf << (unsigned int)first;
fEscapeBuf.Append( ";" );
} else {
fEscapeBuf.Append(p, 1);
}
}
//
// We're done.
return fEscapeBuf;
}
void
StrXml::XMLOutputError( char *data )
{
*this << "<error>\n<![CDATA[\n" << EscapeHTML(StrRef(data)) << "]]>\n</error>\n";
}
void
StrXml::XMLOutputText( char *data )
{
*this << "<text>\n<![CDATA[\n" << EscapeHTML(StrRef(data)) << "]]>\n</text>\n";
}
void
StrXml::XMLOutputInfo(char *data, char level)
{
*this << "<info>\n<![CDATA[\n" << EscapeHTML(StrRef(data)) << "]]>\n</info>\n";
}
void
StrXml::XMLEnd()
{
if (fExtraTag.Length())
{
*this << "</" << fExtraTag << ">\n";
*this << "</" << fP4Cmd << ">\n";
}
*this << "</perforce>\n";
}