/* * Copyright 1995, 2002 Perforce Software. All rights reserved. */ /* * Error.cc - accumulate and report layered errors * * This module containts the manipulation of Error, mostly wrappers * of methods to the private ErrorPrivate, which are are managed in * erroritms.cc. */ # include <stdhdrs.h> # include <strbuf.h> # include <strdict.h> # include <strtable.h> # include <strops.h> # include <error.h> # include <errorpvt.h> const char * Error::severityText[] = { "empty", "info", "warning", "error", "error" }; /* * Error::Error() - cheaply initialize error struct * * Note that setting severity = E_EMPTY is all that is needed * to indicate no error. Things get initialized when severity * gets upgraded. */ Error::~Error() { delete ep; } /* * Error::=() - copy an Error struct, piece by piece * Note that this makes a shallow copy. Use Snap() if you need a deep copy. */ void Error::operator =( const Error &s ) { // Only severity needed if empty severity = s.severity; if( severity == E_EMPTY ) return; // Copy underbelly. if( !ep ) ep = new ErrorPrivate; genericCode = s.genericCode; if( s.ep ) *ep = *s.ep; } /* * Error::Merge() - Merge one Error struct into another */ Error & Error::Merge( const Error &source ) { /* If source error more severe than mine, save severity & generic */ if( source.severity >= severity ) { severity = source.severity; genericCode = source.genericCode; } if( !ep ) { // just copy the error private from the source ep = new ErrorPrivate; *ep = *source.ep; } else { // need to merge the error privates ep->Merge( source.ep ); } return *this; } /* * Error::Set() - add an error message into an Error struct */ Error & Error::Set( const ErrorId &id ) { /* First access? Set private. */ if( !ep ) ep = new ErrorPrivate; /* First error? Clear things out */ if( severity == E_EMPTY ) ep->Clear(); /* If error more severe that previous, save severity & generic */ ErrorSeverity s = (ErrorSeverity)id.Severity(); if( s >= severity ) { severity = s; genericCode = id.Generic(); } /* Now prepare the error message for formatting. */ ep->Set( id ); return *this; } Error & Error::operator <<( const StrPtr &arg ) { ep->SetArg( arg ); return *this; } Error & Error::operator <<( const char *arg ) { ep->SetArg( StrRef( arg ) ); return *this; } Error & Error::operator <<( const StrPtr *arg ) { ep->SetArg( *arg ); return *this; } Error & Error::operator <<( int arg ) { ep->SetArg( StrNum( arg ) ); return *this; } /* * Error::Get() - get an individual Error item */ ErrorId * Error::GetId( int i ) const { return !ep || i < 0 || i >= ep->errorCount ? 0 : &ep->ids[i]; } int Error::GetErrorCount() const { return ep ? ep->errorCount : 0; } void Error::LimitErrorCount() { if( ep && ep->errorCount > OldErrorMax ) ep->errorCount = OldErrorMax; } /* * Error::GetDict() - get StrDict of error parameters */ StrDict * Error::GetDict() { return ep ? ep->whichDict : 0; } /* * Error::Fmt() - format an error message * * Error messages are already formatted by Set() and <<. */ void Error::Fmt( StrBuf &buf, int opts ) const { Fmt( -1, buf, opts ); } void Error::Fmt( int eno, StrBuf &buf, int opts ) const { if( !severity ) return; if( !IsInfo() ) buf.Clear(); StrBuf lfmt; StrPtr *l = 0; if( !( opts & EF_NOXLATE ) ) { lfmt.Set( "lfmt" ); l = &lfmt; } for( int i = ep->errorCount; i-- > 0; ) { if( eno != -1 && eno != (i+1) ) continue; if( opts & EF_CODE ) { buf << StrNum( ep->ids[ i ].UniqueCode() ); buf.Extend( ':' ); } if( opts & EF_INDENT ) buf.Append( "\t", 1 ); StrPtr *s; StrRef fmt; if( !l || !( s = ep->whichDict->GetVar( *l, i ) ) ) { fmt.Set( (char *)ep->ids[i].fmt ); s = &fmt; } StrOps::Expand2( buf, *s, *ep->whichDict ); // Always insert; sometimes append if( eno == -1 && ( i || opts & EF_NEWLINE ) ) buf.Append( "\n", 1 ); } } /* * Error::Snap() - after UnMarshall1, copy all data into Error * * Resulting data is local: * * whichDict is ErrorPrivate::errorDict * ids[].fmt in ErrorPrivate::marshall */ void Error::Snap() { // ErrorPrivate::operator = handles snapping. if (ep) *ep = *ep; } const ErrorId * Error::MapError( const struct ErrorIdMap map[] ) { // shortcut if there are no ErrorIds to lookup if( !ep ) { return NULL; } for(int i = 0; map[i].incomingError.UniqueCode() != 0; i++) { for( int j=0; j < ep->errorCount; j++) { if (map[i].incomingError.code == ep->ids[j].code ) { return &(map[i].outgoingError); } } } return NULL; } /* * Errorpvt.cc - private data for Error object */ void ErrorPrivate::operator =( const ErrorPrivate &s ) { // Handle self-assign as a way of snapping data. int isSelf = this == &s; // Simple parts walk = 0; errorCount = s.errorCount; fmtSource = s.fmtSource; // Dictionary. // It's a little faster to use BufferDict::operator = // than CopyVars(), so take that shortcut if we can. if( s.whichDict != &s.errorDict ) errorDict.CopyVars( *s.whichDict ); else if( !isSelf ) errorDict = s.errorDict; whichDict = &errorDict; // Copy ids over. if( !isSelf ) for( int i = 0; i < errorCount; i++ ) ids[i] = s.ids[i]; // Snap ids[].fmt. // If self-assign, we skip this if they are already in the fmtBuf. // Otherwise, we skip only if they are constants (via Error::Set()) // and thus global. if( isSelf ? ( fmtSource != isFmtBuf ) : ( fmtSource != isConst ) ) { int i; fmtbuf.Clear(); for( i = 0; i < errorCount; i++ ) { fmtbuf.Append( ids[i].fmt ); fmtbuf.Extend( 0 ); } char *p = fmtbuf.Text(); for( i = 0; i < errorCount; i++ ) { ids[i].fmt = p; p += strlen( p ) + 1; } fmtSource = isFmtBuf; } // Copy arg walk state as offset from last id's fmt. if ( s.walk ) walk = ids[errorCount-1].fmt + (s.walk - s.ids[errorCount-1].fmt); } void ErrorPrivate::Merge( const ErrorPrivate *ep ) { if( ep == this || ep->errorCount == 0 ) return; int i; int mergeCount = ep->errorCount; if( errorCount + mergeCount > ErrorMax ) mergeCount = ErrorMax - errorCount; for( i = 0; i < mergeCount; ++i ) ids[ errorCount + i ] = ep->ids[ i ]; // errorDict.CopyVars( *ep->whichDict ); // we can't use copy because we want to merge StrRef var, val; for( i = 0; ep->whichDict->GetVar( i, var, val ); i++ ) errorDict.SetVar( var, val ); whichDict = &errorDict; errorCount += mergeCount; if( ep->fmtSource != isConst ) { StrBuf newfmt; for( i = 0; i < errorCount; i++ ) { newfmt.Append( ids[i].fmt ); newfmt.Extend( 0 ); } fmtbuf.Set( newfmt ); char *p = fmtbuf.Text(); for( i = 0; i < errorCount; i++ ) { ids[i].fmt = p; p += strlen( p ) + 1; } fmtSource = isFmtBuf; } } /* * ErrorPrivate::SetArg() - provide formatting parameters */ void ErrorPrivate::SetArg( const StrPtr &arg ) { /* If we've already finished walking the fmt string */ /* we can't really format any more parameters. */ if( !walk ) return; /* Loop past %%'s and %'stuff'% looking for %param% */ while( ( walk = strchr( walk, '%' ) ) ) { if( *++walk == '\'' ) { walk = strchr( walk, '%' ); if( !walk ) return; } else if( *walk == '%' ) ; else break; ++walk; } // No more %?s if( !walk ) return; /* The %parameter% name is in the fmt string. */ /* Just bail if there is no ending %. */ const char *p; if( !( p = strchr( walk, '%' ) ) ) return; // %var% - set variable to arg // %!var% - ignore arg (leave variable unset) if( *walk != '!' ) whichDict->SetVar( StrRef( walk, p - walk ), arg ); walk = p + 1; } /* * Error::Dump() * ErrorPrivate::Dump() - debugging */ void Error::Dump( const char *trace ) { printf( "Error %s %p\n", trace, this ); printf( "\tSeverity %d (%s)\n", severity, FmtSeverity() ); if( severity == E_EMPTY ) return; printf( "\tGeneric %d\n", genericCode ); ep->Dump(); } void ErrorPrivate::Dump() { int i; StrRef r, l; printf( "\tCount %d\n", errorCount ); for( i = 0; i < errorCount; i++ ) { printf( "\t\t%d: %d (sub %d sys %d gen %d args %d sev %d code %d)\n", i, ids[i].code, ids[i].SubCode(), ids[i].Subsystem(), ids[i].Generic(), ids[i].ArgCount(), ids[i].Severity(), ids[i].UniqueCode() ); printf( "\t\t%d: %s\n", i, ids[i].fmt ); } for( i = 0; whichDict->GetVar( i, r, l ); i++ ) { // For null termination StrBuf r1, l1; r1 = r; l1 = l; printf( "\t\t%s = %s\n", r1.Text(), l1.Text() ); } }