gecko-dev/lib/libaddr/abprint.cpp
1998-06-22 21:46:46 +00:00

649 lines
19 KiB
C++

/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*-
*
* The contents of this file are subject to the Netscape Public License
* Version 1.0 (the "NPL"); you may not use this file except in
* compliance with the NPL. You may obtain a copy of the NPL at
* http://www.mozilla.org/NPL/
*
* Software distributed under the NPL is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL
* for the specific language governing rights and limitations under the
* NPL.
*
* The Initial Developer of this code under the NPL is Netscape
* Communications Corporation. Portions created by Netscape are
* Copyright (C) 1998 Netscape Communications Corporation. All Rights
* Reserved.
*/
#ifndef _ABTABLE_
#include "abtable.h"
#endif
#ifndef _ABMODEL_
#include "abmodel.h"
#endif
/*3456789_123456789_123456789_123456789_123456789_123456789_123456789_12345678*/
/* ----- ----- ----- ----- ----- ----- ----- ----- ----- ----- */
#if AB_CONFIG_TRACE_orDEBUG_orPRINT
static const char* ab_Printer_kClassName /*i*/ = "ab_Printer";
#endif /* end AB_CONFIG_TRACE_orDEBUG_orPRINT*/
#if AB_CONFIG_TRACE_orDEBUG_orPRINT
static const char* ab_FilePrinter_kClassName /*i*/ = "ab_FilePrinter";
#endif /* end AB_CONFIG_TRACE_orDEBUG_orPRINT*/
/* ===== ===== ===== ===== ab_Printer ===== ===== ===== ===== */
// ````` ````` ````` ````` ````` ````` ````` `````
// virtual ab_Object methods
ab_Printer::~ab_Printer() /*i*/
{
}
// ````` ````` ````` ````` ````` ````` ````` `````
// non-poly ab_Printer methods
void ab_Printer::Print(ab_Env* ev, const char* inFormat, ...) /*i*/
{
#ifdef AB_CONFIG_PRINT
if ( this->IsOpenObject() )
{
char buf[ AB_Env_kFormatBufferSize ];
va_list args;
va_start(args,inFormat);
PR_vsnprintf(buf, AB_Env_kFormatBufferSize, inFormat, args);
va_end(args);
this->PutString(ev, buf);
}
#ifdef AB_CONFIG_DEBUG
else
{
ab_Env_BeginMethod(ev, ab_Printer_kClassName, "Print")
#ifdef AB_CONFIG_TRACE
if ( ev->DoTrace() )
this->TraceObject(ev);
#endif /*AB_CONFIG_TRACE*/
#ifdef AB_CONFIG_DEBUG
ev->NewAbookFault(ab_Object_kFaultNotOpen);
#endif /*AB_CONFIG_DEBUG*/
ab_Env_EndMethod(ev)
}
#endif /*AB_CONFIG_DEBUG*/
#endif /*AB_CONFIG_PRINT*/
}
ab_Printer::ab_Printer(const ab_Usage& inUsage) /*i*/
: ab_Object(inUsage),
mPrinter_Depth( 0 )
{
}
// ~~~~~ ~~~~~ ~~~~~ ~~~~~ begin DefaultPrinterHex() ~~~~~ ~~~~~ ~~~~~ ~~~~~
// NOTE: DefaultPrinterHex() copies public domain IronDoc almost verbatim.
/*| kHexDigits: array of hexadecimal bytes useful for hex conversion.
|*/
#if AB_CONFIG_TRACE_orDEBUG_orPRINT
const char* ab_Printer_kHexDigits = "0123456789ABCDEF";
#endif /* end AB_CONFIG_TRACE_orDEBUG_orPRINT*/
/*| hex_digit_count: number of hexadecimal digits needed to write inPos.
|*/
#if AB_CONFIG_TRACE_orDEBUG_orPRINT
static ab_num // (near verbatim copy of IronDoc's FePos_HexDigitCount)
ab_Pos_hex_digit_count(ab_pos self)
{
ab_num count = 0;
if ( self ) /* are we considering a non-zero value? */
{
if ( self > 0x0FFFF ) /* more than four digits? */
{
count += 4;
self = (self >> 16) & 0x0FFFF; /* AND to avoid sign extension */
}
if ( self > 0x0FF ) /* at least two more digits? */
{
count += 2;
self >>= 8;
}
if ( self > 0x0F ) /* at least one more digit? */
{
count += 1;
self >>= 4;
}
if ( self )
count += 1;
}
else
count = 1; /* zero requires one digit to represent: "0" */
return count;
}
#endif /* end AB_CONFIG_TRACE_orDEBUG_orPRINT*/
/*| as_hex: write self in hex to outBuf$ assuming that self takes
**| hexDigitCount$ digits to write in hexadecimal. Presumably the
**| caller has called ab_Pos_hex_digit_count() for the maximum value of
**| self that will be used and knows how much space to use. Obviously
**| outBuf must point to at least hexDigitCount$ bytes of space.
**|
**|| The hexadecimal digits are written backwards, since this is an easy
**| way to do this without getting confused. as_hex is intended to be
**| a safely faster way to format in hexadecimal than by using sprintf().
|*/
#if AB_CONFIG_TRACE_orDEBUG_orPRINT
static char* // (near verbatim copy of IronDoc's FePos_AsHex)
ab_Pos_as_hex(ab_pos self, char* outBuf, ab_num hexDigitCount) /*i*/
{
if ( hexDigitCount > 16 ) /* does digit count exceed all reason? */
hexDigitCount = 16; /* clamp to a more reasonable value */
if ( hexDigitCount ) /* does the caller want to write anything? */
{
char* p = outBuf + hexDigitCount; /* one past spot for trailing digit */
while ( --p >= outBuf ) /* have not yet written leading digit? */
{
*p = ab_Printer_kHexDigits[ self & 0x0F ]; /* least sig digit */
self >>= 4; /* shift smaller by one hex digit */
}
}
return outBuf;
}
#endif /* end AB_CONFIG_TRACE_orDEBUG_orPRINT*/
/* ````` design for ab_Printer::DefaultPrinterHex() output format `````
** <ab:hex:seq size=42 pos="#00000400">
** <ab:x x="74776173 20627269 6C6C6967 20616E64" a="twas brillig and" p=400/>
** <ab:x x="20746865 20736C69 74687920 746F7665" a=" the slithy tove" p=410/>
** <ab:x x="73206469 64206779 7265" a="s did gyre" p=420/>
** </ab:hex:seq>
**
** 0123456789_123456789_123456789_123456789_123456789_123456789_123456789_
** ........|<-------------- 35 --------------->|...|<----- 16 ----->|
** <ab:x x="20746865 20736C69 74687920 746F7665" a=" the slithy tove" p=410/>
** .........^9......................................^49.................^69
**
** <ab:hex:seq len=36 pos="#400" key="x:hex,a:ascii,p:pos">
** </ab:hex:seq>
*/
#define ab_Printer_kAsciiRepIndex /*i*/ 49 // index of ascii representation
#define ab_Printer_kAsciiReplength /*i*/ 16 // length of ascii representation
#define ab_Printer_kHexRepIndex /*i*/ 9 // index of hex representation
#define ab_Printer_kHexRepLength /*i*/ 35 // length of hex representation
#define ab_Printer_kPosRepIndex /*i*/ 69 // index of pos representation
#define ab_Printer_kPosRepMaxLength /*i*/ 8 // max size of pos representation
#define ab_Printer_kHexBytesPerGroup /*i*/ 4 // bytes to group without spaces
#define ab_Printer_kHexPerLine /*i*/ 16 // bytes to format per line
#define ab_Printer_kHexLineBufferSize /*i*/ 128 // size of line buffer
#define ab_Printer_kMaxHexSize /*i*/ ( 256 * 1024 ) // max (to catch errors)
/*| kHexLineTemplate: formatted sample line to initialize non-variable parts.
|*/
#ifdef AB_CONFIG_PRINT
static const char* ab_Printer_kHexLineTemplate = /*i*/
"<ab:x x=\"74776173 20627269 6C6C6967 20616E64\" a=\"twas brillig and\" p=12345678/>";
#endif /*AB_CONFIG_PRINT*/
void ab_Printer::DefaultPrinterHex(ab_Env* ev, const void* inBuf, /*i*/
ab_num inSize, ab_pos inPos)
// DefaultPrinterHex() is a good default implementation for Hex()
{
#ifdef AB_CONFIG_PRINT
char posXmlBuf[ 32 ];
char lineBuf[ ab_Printer_kHexLineBufferSize + 2 ]; // line format buffer
ab_num maxPosDigits = ab_Pos_hex_digit_count(inPos + inSize);
XP_SPRINTF(posXmlBuf, "#%08lX", inPos);
if ( inSize <= ab_Printer_kMaxHexSize ) // reasonable requested size?
{
if ( inSize ) // is there any content to write in hex format?
{
char* hex = lineBuf + ab_Printer_kHexRepIndex; // hex format goes here
char* ascii = lineBuf + ab_Printer_kAsciiRepIndex; // ascii format here
char* pos = lineBuf + ab_Printer_kPosRepIndex; // position goes here
ab_num quantum = ab_Printer_kHexPerLine; // bytes to write per line
ab_num localPos = 0; // relative to beginning of inBuf
ab_bool needEndQuotes = AB_kFalse; // need to write end quotes?
this->Print(ev, "<ab:hex:seq size=\"%lu\" pos=\"%.32s\">",
(unsigned long) inSize, posXmlBuf); // begin enclosing tag
XP_STRCPY(lineBuf, ab_Printer_kHexLineTemplate); // init constant parts
if ( maxPosDigits < 8 ) // need to move up the "/>" end of tag?
{
char* p = pos + maxPosDigits; // first byte after pos
*p++ = '/';
*p++ = '>';
*p++ = '\0';
}
this->PushDepth(ev); // indent all the hex tags
for ( ; inSize && ev->Good(); localPos += ab_Printer_kHexPerLine )
{
ab_num count = quantum; // bytes to write for this line
int perGroup = ab_Printer_kHexBytesPerGroup; // chars before a blank
const char* source = ((const char*) inBuf) + localPos;
hex = lineBuf + ab_Printer_kHexRepIndex; // reset hex cursor
ascii = lineBuf + ab_Printer_kAsciiRepIndex; // reset ascii cursor
if ( inSize < ab_Printer_kHexPerLine ) // short partial last line?
{
quantum = count = inSize; // don't write more bytes than remain
needEndQuotes = AB_kTrue; // line is short, so move up end quote
// for short line, write all blanks and wipe the old end quotes:
XP_MEMSET(hex, ' ', ab_Printer_kHexRepLength + 1);
XP_MEMSET(ascii, ' ', ab_Printer_kAsciiReplength + 1);
}
inSize -= quantum;
// write the variable hex formatting for this line:
while ( count ) // more hex to write for this line?
{
register char c = *source++; // next char to format
*hex++ = (char) ab_Printer_kHexDigits[ (c>>4) & 0x0F ];
*hex++ = (char) ab_Printer_kHexDigits[ c & 0x0F ];
if ( --count && !--perGroup ) // more line but group is done?
{
hex++; // leave a blank between groups
perGroup = ab_Printer_kHexBytesPerGroup;
}
if ( c > 0x7F || !isprint(c) ) // nonprint char?
c = (char) '.'; // just use a period
*ascii++ = c;
}
if ( needEndQuotes ) // working on the last short line?
{
*hex++ = '"';
*ascii++ = '"';
}
// write the pos after "p=":
ab_Pos_as_hex(localPos + inPos, pos, maxPosDigits);
// now display the formatted line buffer:
this->NewlineIndent(ev, /*newlines*/ 1); // prep next line
this->PutString(ev, lineBuf); // write <ab:x .../> tag
}
this->PopDepth(ev);
this->NewlineIndent(ev, /*newlines*/ 1);
this->PutString(ev, "</ab:hex:seq>"); // now end enclosing tag
}
else
this->Print(ev, "<ab:hex:seq size=\"0\" pos=\"%.32s\"/>", posXmlBuf);
}
else ev->NewAbookFault(ab_Printer_kFaultHexSizeTooBig );
#endif /*AB_CONFIG_PRINT*/
}
// ~~~~~ ~~~~~ ~~~~~ ~~~~~ end DefaultPrinterHex() ~~~~~ ~~~~~ ~~~~~ ~~~~~
/* ===== ===== ===== ===== ab_FilePrinter ===== ===== ===== ===== */
// ````` ````` ````` ````` ````` ````` ````` `````
// virtual ab_Printer methods
void ab_FilePrinter::PutString(ab_Env* ev, const char* inMessage) /*i*/
{
#ifdef AB_CONFIG_PRINT
static const char* kMethodPutString = "PutString";
if ( this->IsOpenObject() )
{
ab_File* file = mFilePrinter_File;
if ( file && file->IsOpenObject() )
{
file->Write(ev, inMessage, XP_STRLEN(inMessage));
}
#ifdef AB_CONFIG_DEBUG
else this->file_printer_bad_file(ev, kMethodPutString);
#endif /*AB_CONFIG_DEBUG*/
}
#ifdef AB_CONFIG_DEBUG
else this->file_printer_not_open(ev, kMethodPutString);
#endif /*AB_CONFIG_DEBUG*/
#endif /*AB_CONFIG_PRINT*/
}
void ab_FilePrinter::Newline(ab_Env* ev, ab_num inNewlines) /*i*/
{
#ifdef AB_CONFIG_PRINT
static const char* kMethodNewline = "Newline";
if ( this->IsOpenObject() )
{
ab_File* file = mFilePrinter_File;
if ( file && file->IsOpenObject() )
{
file->WriteNewlines(ev, inNewlines);
}
#ifdef AB_CONFIG_DEBUG
else this->file_printer_bad_file(ev, kMethodNewline);
#endif /*AB_CONFIG_DEBUG*/
}
#ifdef AB_CONFIG_DEBUG
else this->file_printer_not_open(ev, kMethodNewline);
#endif /*AB_CONFIG_DEBUG*/
#endif /*AB_CONFIG_PRINT*/
}
#define ab_FilePrinter_kBlanksLength /*i*/ 32 /* static blank string length */
void ab_FilePrinter::file_printer_indent(ab_Env* ev) const /*i*/
{
static const char* kBlanks = " ";
/* comment to count blanks -- 123456789 123456789 123456789 12 */
ab_num remaining = mPrinter_Depth * 2; /* two blanks per indent */
if ( remaining > 1024 ) /* more than some reasonable upper bound? */
remaining = 1024;
while ( remaining && ev->Good() ) /* more blanks to write? */
{
ab_num quantum = remaining;
if ( quantum > ab_FilePrinter_kBlanksLength )
quantum = ab_FilePrinter_kBlanksLength;
mFilePrinter_File->Write(ev, kBlanks, quantum);
remaining -= quantum;
}
}
void ab_FilePrinter::NewlineIndent(ab_Env* ev, ab_num inNewlines) /*i*/
{
#ifdef AB_CONFIG_PRINT
static const char* kMethodNewlineIndent = "NewlineIndent";
if ( this->IsOpenObject() )
{
ab_File* file = mFilePrinter_File;
if ( file && file->IsOpenObject() )
{
file->WriteNewlines(ev, inNewlines);
if ( ev->Good() )
this->file_printer_indent(ev);
}
#ifdef AB_CONFIG_DEBUG
else this->file_printer_bad_file(ev, kMethodNewlineIndent);
#endif /*AB_CONFIG_DEBUG*/
}
#ifdef AB_CONFIG_DEBUG
else this->file_printer_not_open(ev, kMethodNewlineIndent);
#endif /*AB_CONFIG_DEBUG*/
#endif /*AB_CONFIG_PRINT*/
}
void ab_FilePrinter::Flush(ab_Env* ev) /*i*/
{
#ifdef AB_CONFIG_PRINT
static const char* kMethodFlush = "Flush";
if ( this->IsOpenObject() )
{
ab_File* file = mFilePrinter_File;
if ( file && file->IsOpenObject() )
{
file->Flush(ev);
}
#ifdef AB_CONFIG_DEBUG
else this->file_printer_bad_file(ev, kMethodFlush);
#endif /*AB_CONFIG_DEBUG*/
}
#ifdef AB_CONFIG_DEBUG
else this->file_printer_not_open(ev, kMethodFlush);
#endif /*AB_CONFIG_DEBUG*/
#endif /*AB_CONFIG_PRINT*/
}
void ab_FilePrinter::Hex(ab_Env* ev, const void* inBuf, ab_num inSize, /*i*/
ab_pos inPos)
{
#ifdef AB_CONFIG_PRINT
static const char* kMethodHex = "Hex";
if ( this->IsOpenObject() )
{
ab_File* file = mFilePrinter_File;
if ( file && file->IsOpenObject() )
{
this->DefaultPrinterHex(ev, inBuf, inSize, inPos);
}
#ifdef AB_CONFIG_DEBUG
else this->file_printer_bad_file(ev, kMethodHex);
#endif /*AB_CONFIG_DEBUG*/
}
#ifdef AB_CONFIG_DEBUG
else this->file_printer_not_open(ev, kMethodHex);
#endif /*AB_CONFIG_DEBUG*/
#endif /*AB_CONFIG_PRINT*/
}
void ab_FilePrinter::file_printer_not_open(ab_Env* ev, /*i*/
const char* inMethod) const
{
ab_Env_BeginMethod(ev, ab_FilePrinter_kClassName, inMethod)
#ifdef AB_CONFIG_TRACE
if ( ev->DoTrace() )
this->TraceObject(ev);
#endif /*AB_CONFIG_TRACE*/
ev->NewAbookFault(ab_Object_kFaultNotOpen);
ab_Env_EndMethod(ev)
}
void ab_FilePrinter::file_printer_bad_file(ab_Env* ev, /*i*/
const char* inMethod) const
{
ab_Env_BeginMethod(ev, ab_FilePrinter_kClassName, inMethod)
#ifdef AB_CONFIG_TRACE
if ( ev->DoTrace() )
this->TraceObject(ev);
#endif /*AB_CONFIG_TRACE*/
if ( mFilePrinter_File )
{
if ( mFilePrinter_File->IsOpenObject() )
{
ev->NewAbookFault(ab_Printer_kFaultConfused);
}
else ev->NewAbookFault(ab_Printer_kFaultFileNotOpen);
}
else ev->NewAbookFault(ab_Object_kFaultNotOpen);
this->CastAwayConstCloseObject(ev);
ab_Env_EndMethod(ev)
}
ab_num ab_FilePrinter::PushDepth(ab_Env* ev) /*i*/
{
if ( this->IsOpenObject() )
{
++mPrinter_Depth;
}
#ifdef AB_CONFIG_DEBUG
else this->file_printer_not_open(ev, "PushDepth");
#endif /*AB_CONFIG_DEBUG*/
return mPrinter_Depth;
}
ab_num ab_FilePrinter::PopDepth(ab_Env* ev) /*i*/
{
if ( this->IsOpenObject() && mPrinter_Depth )
{
--mPrinter_Depth;
}
#ifdef AB_CONFIG_DEBUG
else this->file_printer_not_open(ev, "PopDepth");
#endif /*AB_CONFIG_DEBUG*/
return mPrinter_Depth;
}
// ````` ````` ````` ````` ````` ````` ````` `````
// virtual ab_Object methods
char* ab_FilePrinter::ObjectAsString(ab_Env* ev, char* outXmlBuf) const /*i*/
{
AB_USED_PARAMS_1(ev);
#if AB_CONFIG_TRACE_orDEBUG_orPRINT
XP_SPRINTF(outXmlBuf,
"<ab:part:str me=\"^%lX\" pd=\"%lu\" pf=\"^%lX\" rc=\"%lu\" a=\"%.9s\" u=\"%.9s\"/>",
(long) this, // me=\"^%lX\"
(long) mPrinter_Depth, // pd=\"%lu\"
(long) mFilePrinter_File, // pf=\"^%lX\"
(unsigned long) mObject_RefCount, // rc=\"%lu\"
this->GetObjectAccessAsString(), // ac=\"%.9s\"
this->GetObjectUsageAsString() // us=\"%.9s\"
);
#else
*outXmlBuf = 0; /* empty string */
#endif /*AB_CONFIG_TRACE_orDEBUG_orPRINT*/
return outXmlBuf;
}
void ab_FilePrinter::CloseObject(ab_Env* ev) /*i*/
{
if ( this->IsOpenObject() )
{
this->MarkClosing();
this->CloseFilePrinter(ev);
this->MarkShut();
}
}
void ab_FilePrinter::PrintObject(ab_Env* ev, ab_Printer* ioPrinter) const /*i*/
{
#ifdef AB_CONFIG_PRINT
ioPrinter->PutString(ev, "<ab:file:printer>");
char xmlBuf[ ab_Object_kXmlBufSize + 2 ];
if ( this->IsOpenObject() )
{
ioPrinter->PushDepth(ev); // indent all objects in the list
ioPrinter->NewlineIndent(ev, /*count*/ 1);
ioPrinter->PutString(ev, this->ObjectAsString(ev, xmlBuf));
if ( mFilePrinter_File )
{
ioPrinter->NewlineIndent(ev, /*count*/ 1);
mFilePrinter_File->PrintObject(ev, ioPrinter);
}
ioPrinter->PopDepth(ev); // stop indentation
}
else // use ab_Object::ObjectAsString() for non-objects:
{
ioPrinter->PutString(ev, this->ab_Object::ObjectAsString(ev, xmlBuf));
}
ioPrinter->NewlineIndent(ev, /*count*/ 1);
ioPrinter->PutString(ev, "</ab:file:printer>");
#endif /*AB_CONFIG_PRINT*/
}
ab_FilePrinter::~ab_FilePrinter() /*i*/
{
AB_ASSERT(mFilePrinter_File==0);
#if AB_CONFIG_TRACE_orDEBUG_orPRINT
ab_Object* obj = mFilePrinter_File;
if ( obj )
obj->ObjectNotReleasedPanic(ab_FilePrinter_kClassName);
#endif /*AB_CONFIG_TRACE_orDEBUG_orPRINT*/
}
// ````` ````` ````` ````` ````` ````` ````` `````
// non-poly ab_FilePrinter methods
ab_FilePrinter::ab_FilePrinter(ab_Env* ev, const ab_Usage& inUsage, /*i*/
ab_File* ioFile)
: ab_Printer(inUsage),
mFilePrinter_File( 0 )
{
ab_Env_BeginMethod(ev, ab_FilePrinter_kClassName, ab_FilePrinter_kClassName)
if ( ev->Good() )
{
if ( ioFile )
{
if ( ioFile->AcquireObject(ev) )
mFilePrinter_File = ioFile;
}
else ev->NewAbookFault(ab_Object_kFaultNullObjectParameter);
}
ab_Env_EndMethod(ev)
}
void ab_FilePrinter::CloseFilePrinter(ab_Env* ev) /*i*/
{
ab_Env_BeginMethod(ev, ab_FilePrinter_kClassName, "CloseFilePrinter")
ab_Object* obj = mFilePrinter_File;
if ( obj )
{
mFilePrinter_File = 0;
obj->ReleaseObject(ev);
}
ab_Env_EndMethod(ev)
}
static ab_FilePrinter* ab_FilePrinter_g_log_printer = 0; // GetLogFilePrinter()
/*static*/
ab_FilePrinter* ab_FilePrinter::GetLogFilePrinter(ab_Env* ev) /*i*/
// standard session printer (uses ab_StdioFile::GetLogStdioFile()).
// This printer instance is used by ab_Env::GetLogFileEnv().
// This printer instance is used by ab_FileTracer::GetLogFileTracer().
// (Please, never close this printer instance.)
{
ab_FilePrinter* outPrinter = 0;
ab_Env_BeginMethod(ev, ab_FilePrinter_kClassName, "GetLogFilePrinter")
outPrinter = ab_FilePrinter_g_log_printer;
if ( !outPrinter )
{
ab_StdioFile* file = ab_StdioFile::GetLogStdioFile(ev);
if ( file )
{
outPrinter = new(*ev) ab_FilePrinter(ev, ab_Usage::kHeap, file);
if ( outPrinter )
{
ab_FilePrinter_g_log_printer = outPrinter;
outPrinter->AcquireObject(ev);
}
}
}
if ( outPrinter )
{
#ifdef AB_CONFIG_TRACE
if ( ev->DoTrace() )
outPrinter->TraceObject(ev);
#endif /*AB_CONFIG_TRACE*/
}
ab_Env_EndMethod(ev)
return outPrinter;
}