/* * Copyright 1999-2004 The Apache Software Foundation. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ /** * $Id: OS400PlatformUtils.cpp 232395 2005-08-12 21:14:44Z cargilld $ */ // --------------------------------------------------------------------------- // Includes // --------------------------------------------------------------------------- #define MY_XP_CPLUSPLUS #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "OS400PlatformUtils.hpp" #include #if defined (XML_USE_ICONV400_TRANSCODER) #include XERCES_CPP_NAMESPACE_BEGIN void cleanupDefaultConverter(); XERCES_CPP_NAMESPACE_END #elif defined (XML_USE_ICU_TRANSCODER) #include #else Transcoder not Specified - For OS/400 must be either ICU or Iconv400 #endif #if defined(XML_USE_MSGFILE_MESSAGELOADER) #include #elif defined(XML_USE_INMEM_MESSAGELOADER) #include #else #include #endif #if defined (XML_USE_NETACCESSOR_SOCKET) #include #endif XERCES_CPP_NAMESPACE_BEGIN char* PackingRepText(const char * const repText1, const char * const repText2, const char * const repText3, const char * const repText4); // --------------------------------------------------------------------------- // XMLPlatformUtils: Platform init method // --------------------------------------------------------------------------- XMLNetAccessor* XMLPlatformUtils::makeNetAccessor() { #if defined (XML_USE_NETACCESSOR_SOCKET) return new (fgMemoryManager) SocketNetAccessor(); #else return 0; #endif } // // This method is called very early in the bootstrapping process. This guy // must create a transcoding service and return it. It cannot use any string // methods, any transcoding services, throw any exceptions, etc... It just // makes a transcoding service and returns it, or returns zero on failure. // XMLTransService* XMLPlatformUtils::makeTransService() #if defined (XML_USE_ICU_TRANSCODER) { return new (fgMemoryManager) ICUTransService; } #elif defined (XML_USE_ICONV400_TRANSCODER) { return new (fgMemoryManager) Iconv400TransService; } #else { return new (fgMemoryManager) IconvTransService; } #endif // // This method is called by the platform independent part of this class // when client code asks to have one of the supported message sets loaded. // In our case, we use the ICU based message loader mechanism. // XMLMsgLoader* XMLPlatformUtils::loadAMsgSet(const XMLCh* const msgDomain) { XMLMsgLoader* retVal; try { #if defined(XML_USE_MSGFILE_MESSAGELOADER) retVal = new (fgMemoryManager) MsgCatalogLoader(msgDomain); #elif defined (XML_USE_ICU_MESSAGELOADER) retVal = new (fgMemoryManager) ICUMsgLoader(msgDomain); #elif defined (XML_USE_ICONV_MESSAGELOADER) retVal = new (fgMemoryManager) MsgCatalogLoader(msgDomain); #else retVal = new (fgMemoryManager) InMemMsgLoader(msgDomain); #endif } catch(const OutOfMemoryException&) { throw; } catch(...) { panic( PanicHandler::Panic_CantLoadMsgDomain ); } return retVal; } // --------------------------------------------------------------------------- // XMLPlatformUtils: The panic method // --------------------------------------------------------------------------- void XMLPlatformUtils::panic(const PanicHandler::PanicReasons reason) { if (fgUserPanicHandler) { fgUserPanicHandler->panic(reason); } // // We just print a message and exit, Note we are currently dependent on // the number of reasons being under 10 for this teo work // else { struct reason_code { char reason_char; char endofstring; }reason_code; reason_code.reason_char = '0'; reason_code.endofstring = '\0'; reason_code.reason_char = reason_code.reason_char + reason; send_message((char*)&reason_code,GENERAL_PANIC_MESSAGE,'e'); } } // --------------------------------------------------------------------------- // XMLPlatformUtils: File Methods // --------------------------------------------------------------------------- unsigned int XMLPlatformUtils::curFilePos(FileHandle theFile , MemoryManager* const manager) { // Get the current position int curPos = ftell( (FILE*)theFile); if (curPos == -1) ThrowXMLwithMemMgr(XMLPlatformUtilsException, XMLExcepts::File_CouldNotGetSize, manager); return (unsigned int)curPos; } void XMLPlatformUtils::closeFile(FileHandle theFile , MemoryManager* const manager) { if (fclose((FILE*)theFile)) ThrowXMLwithMemMgr(XMLPlatformUtilsException, XMLExcepts::File_CouldNotCloseFile, manager); } unsigned int XMLPlatformUtils::fileSize(FileHandle theFile , MemoryManager* const manager) { // Get the current position long int curPos = ftell((FILE*)theFile); if (curPos == -1) ThrowXMLwithMemMgr(XMLPlatformUtilsException, XMLExcepts::File_CouldNotGetCurPos, manager); // Seek to the end and save that value for return if (fseek( (FILE*)theFile, 0, SEEK_END) ) ThrowXMLwithMemMgr(XMLPlatformUtilsException, XMLExcepts::File_CouldNotSeekToEnd, manager); long int retVal = ftell( (FILE*)theFile); if (retVal == -1) ThrowXMLwithMemMgr(XMLPlatformUtilsException, XMLExcepts::File_CouldNotSeekToEnd, manager); // And put the pointer back if (fseek( (FILE*)theFile, curPos, SEEK_SET) ) ThrowXMLwithMemMgr(XMLPlatformUtilsException, XMLExcepts::File_CouldNotSeekToPos, manager); return (unsigned int)retVal; } FileHandle XMLPlatformUtils::openFile(const XMLCh* const fileName , MemoryManager* const manager) { char errno_id[7]; const char* tmpFileName = XMLString::transcode(fileName, manager); ArrayJanitor janText((char*)tmpFileName, manager); errno = 0; FileHandle retVal = (FILE*)fopen( tmpFileName , "rb" ); if (retVal == NULL) { send_message((char*)tmpFileName,FILE_OPEN_PROBLEMS,'d'); convert_errno(errno_id,errno); send_message(NULL,errno_id,'d'); return 0; } return retVal; } FileHandle XMLPlatformUtils::openFile(const char* const fileName , MemoryManager* const manager) { char errno_id[7]; errno = 0; FileHandle retVal = (FILE*)fopen( fileName , "rb" ); if (retVal == NULL) { send_message((char*)fileName,FILE_OPEN_PROBLEMS,'d'); convert_errno(errno_id,errno); send_message(NULL,errno_id,'d'); return 0; } return retVal; } FileHandle XMLPlatformUtils::openFileToWrite(const XMLCh* const fileName , MemoryManager* const manager) { const char* tmpFileName = XMLString::transcode(fileName, manager); ArrayJanitor janText((char*)tmpFileName, manager); return openFileToWrite(tmpFileName); } FileHandle XMLPlatformUtils::openFileToWrite(const char* const fileName , MemoryManager* const manager) { char errno_id[7]; errno = 0; FileHandle retVal = (FILE*)fopen( fileName , "wb" ); if (retVal == NULL) { send_message((char*)fileName,FILE_OPEN_PROBLEMS,'d'); convert_errno(errno_id,errno); send_message(NULL,errno_id,'d'); return 0; } return retVal; } unsigned int XMLPlatformUtils::readFileBuffer( FileHandle theFile , const unsigned int toRead , XMLByte* const toFill , MemoryManager* const manager) { size_t noOfItemsRead = fread( (void*) toFill, 1, toRead, (FILE*)theFile); if(ferror((FILE*)theFile)) { ThrowXMLwithMemMgr(XMLPlatformUtilsException, XMLExcepts::File_CouldNotReadFromFile, manager); } return (unsigned int)noOfItemsRead; } void XMLPlatformUtils::writeBufferToFile( FileHandle const theFile , long toWrite , const XMLByte* const toFlush , MemoryManager* const manager) { if (!theFile || (toWrite <= 0 ) || !toFlush ) return; const XMLByte* tmpFlush = (const XMLByte*) toFlush; size_t bytesWritten = 0; while (true) { bytesWritten=fwrite(tmpFlush, sizeof(XMLByte), toWrite, (FILE*)theFile); if(ferror((FILE*)theFile)) { ThrowXMLwithMemMgr(XMLPlatformUtilsException, XMLExcepts::File_CouldNotWriteToFile, manager); } if (bytesWritten < toWrite) //incomplete write { tmpFlush+=bytesWritten; toWrite-=bytesWritten; bytesWritten=0; } else return; } return; } void XMLPlatformUtils::resetFile(FileHandle theFile , MemoryManager* const manager) { // Seek to the start of the file if (fseek((FILE*)theFile, 0, SEEK_SET) ) ThrowXMLwithMemMgr(XMLPlatformUtilsException, XMLExcepts::File_CouldNotResetFile, manager); } // --------------------------------------------------------------------------- // XMLPlatformUtils: File system methods // --------------------------------------------------------------------------- //------------------------- //-- BEGIN realpath code -- //------------------------- #include #include #include // // realpath // // Resolve a file name into its absolute name // This function doesn't exist in the iSeries C runtime library so this partial replacement was // written to handle most of its function. This implementation doesn't resolve symbolic links // to their "real" paths because for the purposes of reading files the symlinks will work just // fine. // char *realpath(const char *file_name, char *resolved_name) { // Input: file_name - the name of a file or directory // Output: resolved_name - file_name with a fully qualified path and all "extraneous" path stuff removed // Returned value: Same as resolved_name unless there is an error in which case NULL is returned // // Possible errors: (no)==we don't check that // EACCES Read or search permission was denied for a component of the path name. // EINVAL File_name or resolved_name is a null pointer. // (no) ELOOP Too many symbolic links are encountered in translating file_name. // ENAMETOOLONG The length of file_name or resolved_name exceeds PATH_MAX or a path name component is longer than NAME_MAX. // ENOENT The file_name parameter does not exist or points to an empty string. // (no) ENOTDIR A component of the file_name prefix is not a directory. // If there is an error in resolving the name the return value will be NULL (i.e., 0) and resolved_name // will be changed to an empty string (a 0 will be written into its first character position) // Note for future expansion: "readlink" for links // Check for null name errors if (resolved_name == NULL) { errno = EINVAL; return(NULL); } // Assumption: At this point resolved_name points to a character array large enough to hold at least PATH_MAX characters if (file_name == NULL) { errno = EINVAL; *resolved_name = '\0'; return(NULL); } // Assumption: At this point file_name is a valid null terminated string // Check for empty name error if (*file_name == '\0') { errno = ENOENT; *resolved_name = '\0'; return(NULL); } char *from = (char*)file_name; char *to = resolved_name; int fromIdx=0, toIdx=0; if (*file_name == '/') { // If file_name starts with a '/', it's an absolute path // Everything's already set up properly to[toIdx++] = '/'; } // if else { // file_name doesn't start with a '/' so it is relative to the current directory // Prepend the current working directory before the file name getcwd(to, PATH_MAX); size_t cwd_len = strlen(resolved_name); // Assumption: getcwd returns a non-empty, valid, null terminated string // Add '/' on the cwd if needed // Note: I think the only time a '/' will end the cwd is if it is just "/" // but this covers otherwise just in case toIdx = cwd_len; if ((toIdx < PATH_MAX-1) && (to[toIdx] != '/')) { to[toIdx++] = '/'; } // if } // else // The target ends in a '/' at this point // Skip any leading '/'s in the source while (from[fromIdx] == '/') { fromIdx++; } // while // Copy from the source to the target removing extraneous "."s, "/"s, and ".."s as we go // Assumption: looking ahead for characters is OK because all the string end with '/0' while (from[fromIdx] != '\0' ) { // Assumption - at top of loop we are either at '..', '.', or 'xxxxxxxx' (where x is any non-'/' character), each either followed by a '/' or a closing '\0' // "../" => "/", also strips trailing ".." if ((from[fromIdx] == '.') && (from[fromIdx+1] == '.') && ((from[fromIdx+2] == '/') || (from[fromIdx+2] == '\0'))) { fromIdx += (from[fromIdx+2] == '\0') ? 2 : 3; // Back up to the previous '/' in the target except if we are at the first '/' already if (toIdx >= 2) { // Back up past the current '/' --toIdx; // Look back until we find the previous '/' while(to[toIdx-1] != '/') { --toIdx; } // while } // if } // if // "./" => "/", also strips trailing "." else if ((from[fromIdx] == '.') && ((from[fromIdx+1] == '/') || (from[fromIdx+1] == '\0'))) { fromIdx += (from[fromIdx+1] == '\0') ? 1 : 2; } // else if else { // Copy the characters up to the next '/' or to the closing '\0' while((from[fromIdx] != '/') && (from[fromIdx] != '\0')) { // Check that the file name won't be too long (allow for the '\0' too) if (toIdx >= PATH_MAX-1) { errno = ENAMETOOLONG; *resolved_name = '\0'; return(NULL); } // if to[toIdx++] = from[fromIdx++]; } // while } // else if (from[fromIdx] == '/') { // Skip any remaining '/'s in the source while (from[fromIdx] == '/') { fromIdx++; } // while // insert a '/' if there's more path to come (and room for it) if (from[fromIdx] != '\0') { // Check that the file name won't be too long (allow for the '\0' too) if (toIdx >= PATH_MAX-1) { errno = ENAMETOOLONG; *resolved_name = '\0'; return(NULL); } // if to[toIdx++] = '/'; } // if } // if } // while // Remove a trailing '/' (except from "/") if ((toIdx > 1) && (to[toIdx-1] == '/')) --toIdx; // End the string properly to[toIdx] = '\0'; // Check if the file exists if (access(resolved_name,F_OK)) { errno = EACCES; *resolved_name = '\0'; return(NULL); } // if // The file exists, return its name. return(resolved_name); } //----------------------- //-- END realpath code -- //----------------------- XMLCh* XMLPlatformUtils::getFullPath(const XMLCh* const srcPath, MemoryManager* const manager) { // // NOTE: THe path provided has always already been opened successfully, // so we know that its not some pathological freaky path. It comes in // in native format, and goes out as Unicode always // char* newSrc = XMLString::transcode(srcPath, manager); ArrayJanitor janText(newSrc, manager); // Use a local buffer that is big enough for the largest legal path char absPath[PATH_MAX + 1]; //get the absolute path char* retPath = realpath(newSrc, &absPath[0]); if (!retPath) { ThrowXMLwithMemMgr(XMLPlatformUtilsException, XMLExcepts::File_CouldNotGetBasePathName, manager); } return XMLString::transcode(absPath, manager); } bool XMLPlatformUtils::isRelative(const XMLCh* const toCheck , MemoryManager* const manager) { // Check for pathological case of empty path if (!toCheck[0]) return false; // // If it starts with a slash, then it cannot be relative. This covers // both something like "\Test\File.xml" and an NT Lan type remote path // that starts with a node like "\\MyNode\Test\File.xml". // if (*toCheck == chForwardSlash) return false; // Else assume its a relative path return true; } XMLCh* XMLPlatformUtils::getCurrentDirectory(MemoryManager* const manager) { char dirBuf[PATH_MAX + 2]; char *curDir = getcwd(&dirBuf[0], PATH_MAX + 1); if (!curDir) { ThrowXMLwithMemMgr(XMLPlatformUtilsException, XMLExcepts::File_CouldNotGetBasePathName, manager); } return XMLString::transcode(curDir, manager); } inline bool XMLPlatformUtils::isAnySlash(XMLCh c) { return ( chBackSlash == c || chForwardSlash == c); } void send_message (char * text, char * messageid, char type) { short textsize; char* buffer; char* anchor; char* id; char message_id[8] = "CPF9897";/* id for raw txt message */ char message_file_name[21]; char message_type[11] ="*DIAG ";/* send diagnostic message */ char call_stack[11] ="* " ;/* current callstack*/ int call_stack_counter= 0;/* sent to current call stack */ char message_key[4]; /* return value - not used */ struct { int bytes_available; int bytes_used; char exception_id[7]; char reserved; char exception_data[1]; } error_code; int msg_size; char* msg_type; error_code.bytes_available = sizeof(error_code); /* check input parameters and set up the message information */ if (messageid != 0) /* was a message id passed */ { if (strncmp(messageid,"CPF",3) && strncmp(messageid,"CPE",3)) strcpy(message_file_name,"QXMLMSG *LIBL "); else strcpy(message_file_name,"QCPFMSG QSYS "); id = messageid; /* yes - use the id, will be in QCPFMSG */ } else /* just use what we have for immediate text */ { id = &message_id[0]; strcpy(message_file_name,"QCPFMSG QSYS "); } if (type == 'e') /* is this the terminating exception */ msg_type = "*COMP ";/* set it as completion */ else /* currently all other messages are diagnostics */ msg_type = "*DIAG "; if (text != 0) /* was a text field passed */ { textsize = strlen(text); msg_size = textsize + sizeof(short); buffer = (char*)malloc(msg_size); anchor = buffer; memcpy(buffer, (void*)&textsize, sizeof(short)); buffer +=sizeof(short); memcpy(buffer, text, textsize); } else msg_size = 0; #pragma exception_handler(jsendprob, 0, _C1_ALL, _C2_ALL,_CTLA_HANDLE) QMHSNDPM((char *)id,&message_file_name,anchor, msg_size,(char*)msg_type,(char*)&call_stack, call_stack_counter,&message_key,&error_code); jsendprob: #pragma disable_handler return ; } void abnormal_termination(int termcode) { send_message(NULL,"CPF9899",'e'); /* send final exception that we have terminated*/ } // --------------------------------------------------------------------------- // XMLPlatformUtils: Timing Methods // --------------------------------------------------------------------------- unsigned long XMLPlatformUtils::getCurrentMillis() { _MI_Time mt; struct timeval tv; int rc; mattod(mt); rc = Qp0zCvtToTimeval(&tv, mt, QP0Z_CVTTIME_TO_TIMESTAMP); return((tv.tv_sec*1000 )+ (tv.tv_usec/1000)); } // ----------------------------------------------------------------------- // Mutex methods // ----------------------------------------------------------------------- #if !defined (APP_NO_THREADS) typedef XMLHolder MutexHolderType; static MutexHolderType* gAtomicOpMutex = 0; void XMLPlatformUtils::platformInit() { // // The gAtomicOpMutex mutex needs to be created // because compareAndSwap and incrementlocation and decrementlocation // does not have the atomic system calls for usage // Normally, mutexes are created on first use, but there is a // circular dependency between compareAndExchange() and // mutex creation that must be broken. gAtomicOpMutex = new (fgMemoryManager) MutexHolderType; if (pthread_mutex_init(&gAtomicOpMutex->fInstance, NULL)) { delete gAtomicOpMutex; gAtomicOpMutex = 0; panic( PanicHandler::Panic_SystemInit ); } } class RecursiveMutex : public XMemory { public: pthread_mutex_t mutex; int recursionCount; pthread_t tid; MemoryManager* fManager; RecursiveMutex(MemoryManager* manager) : fManager(manager) { if (pthread_mutex_init(&mutex, NULL)) XMLPlatformUtils::panic(PanicHandler::Panic_MutexErr); recursionCount = 0; tid.reservedHiId = 0; tid.reservedLoId = 0; tid.reservedHandle = 0; } ~RecursiveMutex() { if (pthread_mutex_destroy(&mutex)) ThrowXMLwithMemMgr(XMLPlatformUtilsException, XMLExcepts::Mutex_CouldNotDestroy, fManager); } void lock() { if (pthread_equal(tid, pthread_self())) { recursionCount++; return; } if (pthread_mutex_lock(&mutex) != 0) XMLPlatformUtils::panic(PanicHandler::Panic_MutexErr); tid = pthread_self(); recursionCount = 1; } void unlock() { if (--recursionCount > 0) return; if (pthread_mutex_unlock(&mutex) != 0) XMLPlatformUtils::panic(PanicHandler::Panic_MutexErr); tid.reservedHandle= 0; tid.reservedHiId = 0; tid.reservedLoId = 0; } }; void* XMLPlatformUtils::makeMutex(MemoryManager* manager) { return new (manager) RecursiveMutex(manager); } void XMLPlatformUtils::closeMutex(void* const mtxHandle) { if (mtxHandle == NULL) return; RecursiveMutex *rm = (RecursiveMutex *)mtxHandle; delete rm; } void XMLPlatformUtils::lockMutex(void* const mtxHandle) { if (mtxHandle == NULL) return; RecursiveMutex *rm = (RecursiveMutex *)mtxHandle; rm->lock(); } void XMLPlatformUtils::unlockMutex(void* const mtxHandle) { if (mtxHandle == NULL) return; RecursiveMutex *rm = (RecursiveMutex *)mtxHandle; rm->unlock(); } // ----------------------------------------------------------------------- // Miscellaneous synchronization methods // ----------------------------------------------------------------------- //atomic system calls in Solaris is only restricted to kernel libraries //So, to make operations thread safe we implement static mutex and lock //the atomic operations. It makes the process slow but what's the alternative! void* XMLPlatformUtils::compareAndSwap ( void** toFill , const void* const newValue , const void* const toCompare) { //return ((void*)cas32( (uint32_t*)toFill, (uint32_t)toCompare, (uint32_t)newValue) ); // the below calls are temporarily made till the above functions are part of user library // Currently its supported only in the kernel mode if (pthread_mutex_lock( &gAtomicOpMutex->fInstance)) panic(PanicHandler::Panic_SynchronizationErr); void *retVal = *toFill; if (*toFill == toCompare) *toFill = (void *)newValue; if (pthread_mutex_unlock( &gAtomicOpMutex->fInstance)) panic(PanicHandler::Panic_SynchronizationErr); return retVal; } int XMLPlatformUtils::atomicIncrement(int &location) { int current = location; int new_loc = current+1; while (_CMPSWP(¤t, &location, new_loc) == 0) new_loc = current+1; int tmp = new_loc; return tmp; } int XMLPlatformUtils::atomicDecrement(int &location) { int current = location; int new_loc = current-1; while (_CMPSWP(¤t, &location, new_loc) == 0) new_loc = current-1; int tmp = new_loc; return tmp; } #else // #if !defined (APP_NO_THREADS) void XMLPlatformUtils::platformInit() { // do nothing } void XMLPlatformUtils::closeMutex(void* const) { } void XMLPlatformUtils::lockMutex(void* constmanager) { } void* XMLPlatformUtils::makeMutex(MemoryManager*) { return 0; } void XMLPlatformUtils::unlockMutex(void* const) { } void* XMLPlatformUtils::compareAndSwap ( void** toFill, const void* const newValue, const void* const toCompare) { void *retVal = *toFill; if (*toFill == toCompare) *toFill = (void *)newValue; return retVal; } int XMLPlatformUtils::atomicIncrement(int &location) { return ++location; } int XMLPlatformUtils::atomicDecrement(int &location) { return --location; } #endif // APP_NO_THREADS /* * convert the errno value to a cpf message identifier by converting the * error to its decimal equivalent and appending "CPE" to the front * note that the caller passes the storage for the message id as a parm */ void convert_errno(char* errno_id,int errnum) { sprintf(errno_id,"CPE%d04" ,errnum ); return; } FileHandle XMLPlatformUtils::openStdInHandle(MemoryManager* const manager) { return (FileHandle)fdopen(dup(0), "r"); } void XMLPlatformUtils::platformTerm() { #if !defined (APP_NO_THREADS) pthread_mutex_destroy(&gAtomicOpMutex->fInstance); delete gAtomicOpMutex; gAtomicOpMutex = 0; #endif #if defined (XML_USE_ICONV400_TRANSCODER) cleanupDefaultConverter(); #endif } #include XERCES_CPP_NAMESPACE_END