/* * This file is part of rasdaman community. * * Rasdaman community 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 3 of the License, or * (at your option) any later version. * * Rasdaman community is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with rasdaman community. If not, see . * * Copyright 2003, 2004, 2005, 2006, 2007, 2008, 2009 Peter Baumann / rasdaman GmbH. * * For more information please see * or contact Peter Baumann via . */ /*** * SOURCE: error.cc * * MODULE: raslib * CLASS: r_Error * * COMMENTS: * - in general, string should be used instead of dynamic char* mgmnt * - r_Error specializations must not use int literals * - freeTextTable() does not free strings -> mem leak */ static const char rcsid[] = "@(#)raslib, r_Error: $Id: error.cc,v 1.60 2005/09/03 20:35:12 rasdev Exp $"; using namespace std; using namespace std; #include "mymalloc/mymalloc.h" #include "raslib/error.hh" #include "raslib/rmdebug.hh" #include "debug.hh" #include #ifdef __VISUALC__ #include #else #include #endif #include #include #include #include #include #include using std::endl; using std::ends; using std::ios; using std::ostrstream; r_Error::errorInfo* r_Error::textList = NULL; r_Error::r_Error() : theKind(r_Error_General), errorText(0), errorNo(0) { resetErrorText(); } r_Error::r_Error(const r_Error& err) : errorText(0), errorNo(err.errorNo), theKind(err.theKind) { if (err.errorText) { errorText = new char[ strlen(err.errorText)+1 ]; strcpy(errorText, err.errorText); } } r_Error::r_Error(kind the_kind, unsigned int newErrorNo) : errorText(0), theKind(the_kind), errorNo(newErrorNo) { resetErrorText(); } r_Error::~r_Error() throw() { if (errorText) delete[] errorText; } const char* r_Error::what() const throw () { return (const char*)errorText; } const r_Error& r_Error::operator=(const r_Error& obj) { if (this != &obj) { if (errorText) { delete[] errorText; errorText = NULL; } theKind = obj.theKind; errorNo = obj.errorNo; if (obj.errorText) { errorText = new char[ strlen(obj.errorText)+1 ]; strcpy(errorText, obj.errorText); } } return *this; } char* r_Error::serialiseError() { // default implementation for errors not of kind r_Error_SerialisableException char* retVal = NULL; char buf[80]; // should be enough for two numbers sprintf(buf, "%d\t%d", theKind, errorNo); retVal = (char*)mymalloc(strlen(buf) + 1); strcpy(retVal, buf); return retVal; } r_Error* r_Error::getAnyError(char* serErr) { r_Error* retVal = NULL; char* currChar = NULL; kind newTheKind; unsigned int newErrNum = 0; // first read the attributes of r_Error to find out which subclass it is // (stored in errNum). newTheKind = (r_Error::kind)strtol(serErr, &currChar, 10); newErrNum = strtol(currChar+1, &currChar, 10); if (newTheKind != r_Error_SerialisableException) { retVal = new r_Error(newTheKind, newErrNum); } else { // add new serialisable errors to this case! switch (newErrNum) { case 206: retVal = new r_Ebase_dbms(newTheKind, newErrNum, currChar+1); break; } } return retVal; } // // --- error text file table maintenance --------------------------------- // /* * list of error codes, contining numerical error code, error flag char * (warning or error), and error text. * It is modelled as nested pairs to allow using standard classes. * Filled from file. */ list, char*> > errorTexts; /// has error text file been loaded, i.e., is table filled? bool errorTextsLoaded = false; void initTextTable() { char errorFileName[FILENAME_MAX]; // error file path/name std::ifstream errortexts; string line; // current input line read from file char errKind; int errNo; char errText[1000]; unsigned int numOfEntries = 0; int result; // sscanf() result // determine file path + name int filenameLength = snprintf( errorFileName, FILENAME_MAX, "%s/%s", SHARE_DATA_DIR, ERRORTEXT_FILE ); if (filenameLength < FILENAME_MAX ) { RMInit::logOut << "Using error text file: " << errorFileName << endl; } else { errorFileName[FILENAME_MAX-1] = '\0'; // force-terminate string before printing cerr << "Warning: error text file path longer than allowed by OS, likely file will not be found: " << errorFileName << endl; } // errortexts.open(errorFileName, ios::nocreate | ios::in); errortexts.open(errorFileName, ios::in); // In case the file 'errtxts' can't be found #ifdef __VISUALC__ if (!errortexts.is_open()) #else if (!errortexts) #endif { r_Error::textList = NULL; RMInit::logOut << "No error texts file found at: " << errorFileName << endl; } else // File errtxts found { // read entries from file numOfEntries = 0; // just for displaying, currently not used otherwise while ( ! errortexts.eof() ) { getline( errortexts, line ); char *errText = (char*) mymalloc( line.length() + 1 ); if (errText == NULL) { RMInit::logOut << "Fatal error: cannot allocate error text table line #" << numOfEntries << endl; throw r_Error( r_Error::r_Error_MemoryAllocation ); } // general line format is (aside of comments and empty lines): ddd^f^text... result = sscanf( line.c_str(), "%d^%c^%[^\n]s\n", &errNo, &errKind, errText ); if (result == 3) // consider only lines that match given structure { errorTexts.push_back( pair,char*> (pair( errNo, errKind ), errText) ); numOfEntries++; } } // RMInit::logOut << "number of error texts loaded: " << numOfEntries << endl; errorTextsLoaded = true; } errortexts.close(); } void freeTextTable() { list, char*> >::iterator iter = errorTexts.begin(); list, char*> >::iterator end = errorTexts.end(); if (errorTextsLoaded) // have we initialized the table previously? { // yes -> free each string #if 0 // doesn't work yet while ( iter != NULL && iter != end ) { cout << "freeing " << iter->second << endl << flush; free( iter->second ); iter->second = NULL; // delete[] iter->second; } #endif errorTexts.clear(); // now clear list itself } } r_Error::r_Error(unsigned int errorno) : errorText(NULL), theKind(r_Error_General), errorNo(errorno) { resetErrorText(); } void r_Error::setErrorTextOnKind() { char buffer[256]; switch (theKind) { case r_Error_General : strcpy(buffer, " ODMG General"); break; case r_Error_DatabaseClassMismatch : strcpy(buffer, "Database Class Mismatch"); break; case r_Error_DatabaseClassUndefined : strcpy(buffer, "Database Class Undefined"); break; case r_Error_DatabaseClosed : strcpy(buffer, "Database Closed"); break; case r_Error_DatabaseOpen : strcpy(buffer, "Database Open"); break; case r_Error_DateInvalid : strcpy(buffer, "Date Invalid"); break; case r_Error_IteratorExhausted : strcpy(buffer, "Iterator Exhausted"); break; case r_Error_NameNotUnique : strcpy(buffer, "Name Not Unique"); break; case r_Error_QueryParameterCountInvalid : strcpy(buffer, "Query Parameter Count Invalid"); break; case r_Error_QueryParameterTypeInvalid : strcpy(buffer, "Query Parameter Type Invalid"); break; case r_Error_RefInvalid : strcpy(buffer, "Ref Invalid"); break; case r_Error_RefNull : strcpy(buffer, "Ref Null"); break; case r_Error_TimeInvalid : strcpy(buffer, "Time Invalid"); break; case r_Error_TimestampInvalid : strcpy(buffer, "Timestamp Invalid"); break; case r_Error_TransactionOpen : strcpy(buffer, "Transaction Open"); break; case r_Error_TransactionNotOpen : strcpy(buffer, "Transaction Not Open"); break; case r_Error_TypeInvalid : strcpy(buffer, "Type Invalid"); break; case r_Error_DatabaseUnknown : strcpy(buffer, "Database Unknown"); break; case r_Error_TransferFailed : strcpy(buffer, "Transfer Failed"); break; case r_Error_HostInvalid : strcpy(buffer, "Host Invalid"); break; case r_Error_ServerInvalid : strcpy(buffer, "Server Invalid"); break; case r_Error_ClientUnknown : strcpy(buffer, "Client Unknown"); break; case r_Error_ObjectUnknown : strcpy(buffer, "Object Unknown"); break; case r_Error_ObjectInvalid : strcpy(buffer, "Object Invalid"); break; case r_Error_QueryExecutionFailed : strcpy(buffer, "Query Execution Failed"); break; case r_Error_BaseDBMSFailed : strcpy(buffer, "Base DBMS Failed"); break; case r_Error_CollectionElementTypeMismatch : strcpy(buffer, "Collection Element Type Mismatch"); break; case r_Error_CreatingOIdFailed : strcpy(buffer, "Creation of OID failed"); break; case r_Error_TransactionReadOnly : strcpy(buffer, "Transaction is read only"); break; case r_Error_LimitsMismatch : strcpy(buffer, "Limits reported to an object mismatch"); break; case r_Error_NameInvalid : strcpy(buffer, "Name Invalid"); break; case r_Error_FeatureNotSupported : strcpy(buffer, "Feature is not supported"); break; case r_Error_AccesDenied: strcpy(buffer, "Access denied"); break; case r_Error_MemoryAllocation: strcpy(buffer, "Memory allocation failed"); break; case r_Error_InvalidOptimizationLevel: strcpy(buffer, "Illegal value for optimization level"); break; default: strcpy(buffer, "not specified"); break; } if (errorText) delete[] errorText; char preText[] = "Exception: "; errorText = new char[strlen(preText) + strlen(buffer) + 1]; strcpy(errorText, preText); strcat(errorText, buffer); } void r_Error::setErrorTextOnNumber() { char *result = NULL; // ptr to error text constructed // delete old error text, if any if (errorText) { delete[] errorText; errorText = 0; } // has list been built earlier? if (errorTextsLoaded) { // yes, we have a list -> search // search through list to find entry list, char*> >::iterator iter = errorTexts.begin(); list, char*> >::iterator end = errorTexts.end(); bool found = false; while ( iter != end && ! found ) { if (iter->first.first == errorNo) found = true; else iter++; } if (found) result = iter->second; else result = "(no explanation text available for this error code.)"; } else // no, there is no spoon -err- list result = "(no explanation text available - cannot find/load file with standard error messages.)"; errorText = new char[strlen(result) + 1]; if (errorText == NULL) { RMInit::logOut << "Error: cannot allocate error text." << endl; throw r_Error( r_Error::r_Error_MemoryAllocation ); } else strcpy(errorText, result); return; } void r_Error::setTextParameter(const char* parameterName, int value) { // convert long value to string char valueString[256]; ostrstream valueStream(valueString, sizeof(valueString) ); valueStream << value << ends; setTextParameter(parameterName, valueString); } void r_Error::setTextParameter(const char* parameterName, const char* value) { if (errorText) { // locate the next matching parameter in the query string char* paramStart = strstr(errorText, parameterName); if (paramStart) { // allocate a new query string and fill it char* paramEnd = NULL; int paramLength = 0; char* tmpText = NULL; int newLength = 0; tmpText = errorText; paramLength = strlen(parameterName); paramEnd = paramStart + paramLength; newLength = strlen(tmpText) - paramLength + strlen(value) + 1; errorText = new char[newLength]; *paramStart = '\0'; ostrstream queryStream(errorText, newLength); queryStream << tmpText << value << paramEnd << ends; delete[] tmpText; } } } void r_Error::resetErrorText() { // If no error number is specified use the error kind for the text. if (errorNo) setErrorTextOnNumber(); else setErrorTextOnKind(); } r_Eno_interval::r_Eno_interval() : r_Error(201) { TALK( "r_Error::resetErrorText() - code 201" ); resetErrorText(); } r_Eindex_violation::r_Eindex_violation(r_Range dlow, r_Range dhigh, r_Range dindex) : r_Error(202), low(dlow), high(dhigh), index(dindex) { TALK( "r_Error::r_Eindex_violation() - code 202" ); resetErrorText(); } void r_Eindex_violation::resetErrorText() { setErrorTextOnNumber(); setTextParameter("$low", low ); setTextParameter("$high", high ); setTextParameter("$index", index); } r_Edim_mismatch::r_Edim_mismatch(r_Dimension pdim1, r_Dimension pdim2) : r_Error(203), dim1(pdim1), dim2(pdim2) { TALK( "r_Error::r_Edim_mismatch() - code 203; dim1=" << pdim1 << ", dim2=" << pdim2 ); resetErrorText(); } void r_Edim_mismatch::resetErrorText() { setErrorTextOnNumber(); setTextParameter("$dim1", dim1); setTextParameter("$dim2", dim2); } r_Einit_overflow::r_Einit_overflow() : r_Error(204) { TALK( "r_Error::r_Einit_overflow() - code 204" ); resetErrorText(); } r_Eno_cell::r_Eno_cell() : r_Error(205) { TALK( "r_Error::r_Eno_cell() - code 205" ); resetErrorText(); } r_Equery_execution_failed::r_Equery_execution_failed(unsigned int errorno, unsigned int lineno, unsigned int columnno, const char* initToken) : r_Error(errorno), lineNo(lineno), columnNo(columnno) { TALK( "r_Error::r_Equery_execution_failed() - errorno=" << errorno ); token = new char[strlen(initToken)+1]; strcpy(token, initToken); resetErrorText(); } r_Equery_execution_failed::r_Equery_execution_failed(const r_Equery_execution_failed &err) : r_Error(err), lineNo(0), columnNo(0) { TALK( "r_Error::r_Equery_execution_failed()" ); lineNo = err.lineNo; columnNo = err.columnNo; token = new char[strlen(err.token)+1]; strcpy(token, err.token); } r_Equery_execution_failed::~r_Equery_execution_failed() throw() { if (token) delete[] token; } void r_Equery_execution_failed::resetErrorText() { setErrorTextOnNumber(); setTextParameter("$errorNo", errorNo); setTextParameter("$lineNo", lineNo); setTextParameter("$columnNo", columnNo); setTextParameter("$token", token); } r_Elimits_mismatch::r_Elimits_mismatch(r_Range lim1, r_Range lim2) : r_Error(r_Error_LimitsMismatch), i1(lim1), i2(lim2) { TALK( "r_Error::r_Elimits_mismatch() - lim1=" << lim1 << ", lim2=" << lim2 ); resetErrorText(); } void r_Elimits_mismatch::resetErrorText() { setErrorTextOnNumber(); setTextParameter("$dim1", i1); setTextParameter("$dim2", i2); } /* ------------------------------------------------------------------ class r_Ebase_dbms ------------------------------------------------------------------ */ r_Ebase_dbms::r_Ebase_dbms(const long& newDbmsErrNum, const char* newDbmsErrTxt) : r_Error(r_Error_SerialisableException, 206), dbmsErrNum(newDbmsErrNum), whatTxt(0) { TALK( "r_Error::r_Ebase_dbms() code 206 - " << newDbmsErrTxt ); baseDBMS = strdup("Error in base DBMS, error number: "); dbmsErrTxt = strdup(newDbmsErrTxt); buildWhat(); } r_Ebase_dbms::r_Ebase_dbms(const r_Ebase_dbms& obj) : r_Error(obj), dbmsErrNum(0), whatTxt(0) { TALK( "r_Error::r_Ebase_dbms()" ); dbmsErrNum = obj.dbmsErrNum; if (obj.baseDBMS) { baseDBMS = (char*)mymalloc(strlen(obj.baseDBMS) + 1); strcpy(baseDBMS, obj.baseDBMS); } else { baseDBMS = 0; } if (obj.dbmsErrTxt) { dbmsErrTxt = (char*)mymalloc(strlen(obj.dbmsErrTxt) + 1); strcpy(dbmsErrTxt, obj.dbmsErrTxt); } else { dbmsErrTxt = 0; } buildWhat(); } // r_Ebase_dbms: constructor receiving kind, errno, and descriptive string // NB: the string must have the format "\t\t" // (currently the only location where this is used is getAnyError() above) r_Ebase_dbms::r_Ebase_dbms(kind newTheKind, unsigned long newErrNum, const char* myStr) : r_Error(newTheKind, newErrNum), whatTxt(0) { TALK( "r_Error::r_Ebase_dbms() - kind=" << newTheKind ); // as the const char* cannot be passed to strtol() - this was a bug anyway - // we copy the string. Efficiency is not a concern here. -- PB 2005-jan-14 // const char* currChar = myStr; char* tmpBuf = NULL; // temp copy of myStr char* currChar = NULL; // ptr iterating over tmpBuf tmpBuf = strdup( myStr ); currChar = tmpBuf; // initialize char ptr to begin of buffer // read the attributes of r_Ebase_dbms from the string already partially parsed by // r_Error::getAnyError(char* serErr) dbmsErrNum = strtol(currChar, &currChar, 10); // of course \t is part of the string, so we trick a little char* tmpPtr = strchr(((char*)currChar)+1, '\t'); if (tmpPtr==NULL) // no trailing information found? { baseDBMS = strdup( "unknown" ); dbmsErrTxt = strdup( "unknown" ); } else // yes -> analyse it { *tmpPtr = '\0'; // terminate item for strdup() baseDBMS = strdup(currChar+1); // extract item (db name) from string *tmpPtr = '\t'; // re-substitute EOS with tab currChar = strchr(currChar+1, '\t'); // search for tab as item delimiter dbmsErrTxt = strdup(currChar+1); // extract final item which is error text } buildWhat(); free( tmpBuf ); // allocated in strdup() -- PB 2005-jan-14 } r_Ebase_dbms::~r_Ebase_dbms() throw() { if (whatTxt) free(whatTxt); if (dbmsErrTxt) free(dbmsErrTxt); if (baseDBMS) free(baseDBMS); } const r_Ebase_dbms& r_Ebase_dbms::operator=(const r_Ebase_dbms& obj) { if (this != &obj) { dbmsErrNum = obj.dbmsErrNum; if (obj.baseDBMS) { if (baseDBMS) free(baseDBMS); baseDBMS = (char*)mymalloc(strlen(obj.baseDBMS) + 1); strcpy(baseDBMS, obj.baseDBMS); } else { baseDBMS = 0; } if (obj.dbmsErrTxt) { if (dbmsErrTxt) free(dbmsErrTxt); dbmsErrTxt = (char*)mymalloc(strlen(obj.dbmsErrTxt) + 1); strcpy(dbmsErrTxt, obj.dbmsErrTxt); } else { dbmsErrTxt = 0; } buildWhat(); } return *this; } void r_Ebase_dbms::buildWhat() { // Ok, we have to build the error message for this class. The problem is that ressource // allocation is involved here! if(whatTxt) free(whatTxt); // assumes 10 digits for errNum whatTxt = (char*)mymalloc(strlen(baseDBMS) + strlen(dbmsErrTxt) + 12); strcpy(whatTxt, baseDBMS); sprintf(whatTxt + strlen(whatTxt), "%d\n", dbmsErrNum); strcat(whatTxt, dbmsErrTxt); } const char* r_Ebase_dbms::what() const throw() { return whatTxt; } char* r_Ebase_dbms::serialiseError() { char* tmpRes = r_Error::serialiseError(); char buf[40]; char* retVal; sprintf(buf, "\t%d\t", dbmsErrNum); retVal = (char*)mymalloc(strlen(tmpRes) + strlen(buf) + strlen(baseDBMS) + strlen(dbmsErrTxt) + 2); strcpy(retVal, tmpRes); strcat(retVal, buf); strcat(retVal, baseDBMS); strcat(retVal, "\t"); strcat(retVal, dbmsErrTxt); free(tmpRes); return retVal; } r_Eno_permission::r_Eno_permission() :r_Error(r_Error_AccesDenied,NO_PERMISSION_FOR_OPERATION) { TALK( "r_Error::r_Eno_permission()" ); resetErrorText(); } r_Ememory_allocation::r_Ememory_allocation(): r_Error(r_Error_MemoryAllocation, 66) // 66 is: mem alloc failed { TALK( "r_Error::r_Ememory_allocation() - code 66" ); resetErrorText(); } r_Ecapability_refused::r_Ecapability_refused() :r_Error(r_Error_AccesDenied,CAPABILITY_REFUSED) { TALK( "r_Error::r_Ecapability_refused()" ); resetErrorText(); }