//============================================================================= // // File : KvsObject_file.cpp // Creation date : Fri Mar 18 21:30:48 CEST 2005 // by Tonino Imbesi(Grifisx) and Alessandro Carbone(Noldor) // // This file is part of the KVIrc IRC client distribution // Copyright (C) 2000-2010 Szymon Stefanek (pragma at kvirc dot net) // // This program 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 2 // of the License, or (at your option) any later version. // // This program 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 this program. If not, write to the Free Software Foundation, // Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. // //============================================================================= #include "KvsObject_file.h" #include "kvi_debug.h" #include "KviLocale.h" #include "KviError.h" #include "KviFile.h" #include "KviFileUtils.h" #include "KvsObject_memoryBuffer.h" #include //#include #include #define CHECK_FILE_IS_OPEN \ if(!m_pFile->isOpen()) \ { \ c->warning(__tr2qs_ctx("File is not open!", "objects")); \ return true; \ } // needed for $open() const char * const mod_tbl[] = { "Raw", "ReadOnly", "WriteOnly", "ReadWrite", "Append", "Truncate" }; const QIODevice::OpenMode mod_cod[] = { QIODevice::Unbuffered, QIODevice::ReadOnly, QIODevice::WriteOnly, QIODevice::ReadWrite, QIODevice::Append, QIODevice::Truncate }; #define mod_num (sizeof(mod_tbl) / sizeof(mod_tbl[0])) /* @doc: file @keyterms: file object class, creating files @title: file class @type: class @short: I/O device that operates on files @inherits: [class]object[/class] @description: This object class provides access to files - basic I/O operations, copying, removing etc. @functions: !fn: $setName() Sets the file name to . It does [b]not move[/b] the file, it just changes the file the object is [i]pointing to[/i]. You can not change names of already open files.[br] See also: [classfnc]$open[/classfnc](), [classfnc]$name[/classfnc](). !fn: $name() Returns name set by [classfnc]$setName[/classfnc]().[br] See also: [classfnc]$setName[/classfnc](). !fn: $open(, ) Attempts to open the file in specified mode or modes [i]sum[/i]. Valid modes are:[br] [pre] RAW - RAW, non-buffered access ReadOnly - opens the file read-only WriteOnly - opens the file write-only ReadWrite - opens the file in read-write mode Append - opens the file in append mode. The file index is set to the end of the file. Truncate - truncates the file [/pre] If you call this function without any parameters, the file is opened in read-only mode.[br] When working with buffered files, data is not written directly to the file at once. You must call [classfnc]$flush[/classfnc]() to force it.[br] See also: [classfnc]$close[/classfnc](), [classfnc]$flush[/classfnc](). !fn: $isOpen() Returns '1' if the file is open, '0' otherwise. !fn: $close() Closes the file, flushing the buffers first.[br] See also: [classfnc]$flush[/classfnc](). !fn: $flush() Flushes the file buffer to disk. Calling this after opening the file in RAW mode doesn't make much sense.[br] See also: [classfnc]$open[/classfnc](), [classfnc]$close[/classfnc](). !fn: $size() Returns current file size. !fn: $atEnd() Returns '1' if you have reached end of the file, [b]0[/b] otherwise. [br]See also: [classfnc]$seek[/classfnc](), [classfnc]$where[/classfnc](). !fn: $where() Returns current position in the file (file index).[br] See also: [classfnc]$seek[/classfnc](). !fn: $seek() Sets the file index to .[br] See also: [classfnc]$where[/classfnc](), [classfnc]$atEnd[/classfnc](). !fn: $putch() Writes character to the file and increments file index.[br] See also: [classfnc]$getch[/classfnc](), [classfnc]$ungetch[/classfnc](). !fn: $getch() Reads a character from the file and increments file index.[br] See also: [classfnc]$putch[/classfnc](), [classfnc]$ungetch[/classfnc](). !fn: $ungetch() Puts the character back to the file and decrements the file index. This is usually called to undo a $getch() call.[br] See also: [classfnc]$getch[/classfnc], [classfnc]$putch[/classfnc](). !fn: $readLine() Reads a line of text from the file and increments file index. !fn: $writeLine() Appends a line of text to the end of the file. $readBlock() $writeBlock() */ KVSO_BEGIN_REGISTERCLASS(KvsObject_file, "file", "object") KVSO_REGISTER_HANDLER(KvsObject_file, "setName", setName) KVSO_REGISTER_HANDLER(KvsObject_file, "name", name) KVSO_REGISTER_HANDLER(KvsObject_file, "open", open) KVSO_REGISTER_HANDLER(KvsObject_file, "isOpen", isOpen) KVSO_REGISTER_HANDLER(KvsObject_file, "close", close) KVSO_REGISTER_HANDLER(KvsObject_file, "flush", flush) KVSO_REGISTER_HANDLER(KvsObject_file, "size", size) KVSO_REGISTER_HANDLER(KvsObject_file, "resize", resize) KVSO_REGISTER_HANDLER(KvsObject_file, "atEnd", atEnd) KVSO_REGISTER_HANDLER(KvsObject_file, "where", where) KVSO_REGISTER_HANDLER(KvsObject_file, "seek", seek) KVSO_REGISTER_HANDLER(KvsObject_file, "putch", putch) KVSO_REGISTER_HANDLER(KvsObject_file, "getch", getch) KVSO_REGISTER_HANDLER(KvsObject_file, "ungetch", unGetch) KVSO_REGISTER_HANDLER(KvsObject_file, "readByte", readByte) KVSO_REGISTER_HANDLER(KvsObject_file, "readBlock", readBlock) KVSO_REGISTER_HANDLER(KvsObject_file, "writeBlock", writeBlock) KVSO_REGISTER_HANDLER(KvsObject_file, "readHexBlock", readHexBlock) KVSO_REGISTER_HANDLER(KvsObject_file, "writeHexBlock", writeHexBlock) KVSO_REGISTER_HANDLER(KvsObject_file, "readLine", readLine) KVSO_REGISTER_HANDLER(KvsObject_file, "writeLine", writeLine) KVSO_REGISTER_HANDLER(KvsObject_file, "write", write) KVSO_REGISTER_HANDLER(KvsObject_file, "read", read) KVSO_END_REGISTERCLASS(KvsObject_file) KVSO_BEGIN_CONSTRUCTOR(KvsObject_file, KvsObject_widget) m_pFile = new KviFile(); KVSO_END_CONSTRUCTOR(KvsObject_file) KVSO_BEGIN_DESTRUCTOR(KvsObject_file) if(m_pFile) delete m_pFile; KVSO_END_CONSTRUCTOR(KvsObject_file) KVSO_CLASS_FUNCTION(file, setName) { CHECK_INTERNAL_POINTER(m_pFile) QString szName; KVSO_PARAMETERS_BEGIN(c) KVSO_PARAMETER("file_name", KVS_PT_STRING, 0, szName) KVSO_PARAMETERS_END(c) m_pFile->setFileName(szName); return true; } KVSO_CLASS_FUNCTION(file, name) { CHECK_INTERNAL_POINTER(m_pFile) c->returnValue()->setString(m_pFile->fileName()); return true; } KVSO_CLASS_FUNCTION(file, open) { CHECK_INTERNAL_POINTER(m_pFile) QStringList modes; KVSO_PARAMETERS_BEGIN(c) KVSO_PARAMETER("file_mode", KVS_PT_STRINGLIST, KVS_PF_OPTIONAL, modes) KVSO_PARAMETERS_END(c) if(m_pFile->fileName().isEmpty()) { c->warning(__tr2qs_ctx("Empty filename string", "objects")); return true; } QIODevice::OpenMode mod, sum; if(modes.empty()) sum = QIODevice::ReadWrite | QIODevice::Append; // if no parameters given, default to ReadWrite | Append else { for(int idx = 0; idx < modes.count(); idx++) { int found = false; for(unsigned int j = 0; j < mod_num; j++) { if(KviQString::equalCI(modes.at(idx), mod_tbl[j])) { mod = mod_cod[j]; found = true; break; } } if(found) sum = sum | mod; else c->warning(__tr2qs_ctx("No such open mode '%Q'", "objects"), &modes.at(idx)); } } c->returnValue()->setBoolean(m_pFile->open(sum)); return true; } KVSO_CLASS_FUNCTION(file, isOpen) { CHECK_INTERNAL_POINTER(m_pFile) c->returnValue()->setBoolean(m_pFile->isOpen()); return true; } KVSO_CLASS_FUNCTION(file, close) { CHECK_INTERNAL_POINTER(m_pFile) CHECK_FILE_IS_OPEN m_pFile->close(); return true; } KVSO_CLASS_FUNCTION(file, flush) { CHECK_INTERNAL_POINTER(m_pFile) CHECK_FILE_IS_OPEN m_pFile->flush(); return true; } KVSO_CLASS_FUNCTION(file, size) { CHECK_INTERNAL_POINTER(m_pFile) c->returnValue()->setInteger((kvs_int_t)(m_pFile->size())); return true; } KVSO_CLASS_FUNCTION(file, atEnd) { CHECK_INTERNAL_POINTER(m_pFile) c->returnValue()->setInteger((kvs_int_t)(m_pFile->atEnd())); return true; } KVSO_CLASS_FUNCTION(file, where) { CHECK_INTERNAL_POINTER(m_pFile) CHECK_FILE_IS_OPEN c->returnValue()->setInteger((kvs_int_t)(m_pFile->pos())); return true; } KVSO_CLASS_FUNCTION(file, seek) { CHECK_INTERNAL_POINTER(m_pFile) kvs_uint_t uIndex; KVSO_PARAMETERS_BEGIN(c) KVSO_PARAMETER("index", KVS_PT_UNSIGNEDINTEGER, 0, uIndex) KVSO_PARAMETERS_END(c) CHECK_FILE_IS_OPEN m_pFile->seek(uIndex); return true; } KVSO_CLASS_FUNCTION(file, resize) { CHECK_INTERNAL_POINTER(m_pFile) kvs_int_t iSize; KVSO_PARAMETERS_BEGIN(c) KVSO_PARAMETER("size", KVS_PT_INTEGER, 0, iSize) KVSO_PARAMETERS_END(c) CHECK_FILE_IS_OPEN m_pFile->resize(iSize); return true; } KVSO_CLASS_FUNCTION(file, putch) { CHECK_INTERNAL_POINTER(m_pFile) QString szChar; KVSO_PARAMETERS_BEGIN(c) KVSO_PARAMETER("char", KVS_PT_STRING, 0, szChar) KVSO_PARAMETERS_END(c) if(szChar.length() > 1) c->warning(__tr2qs_ctx("Argument too long, using only first char", "objects")); const char * ch = szChar.toUtf8().data(); if(!m_pFile->putChar(ch[0])) c->warning(__tr2qs_ctx("Write error occurred!", "objects")); return true; } KVSO_CLASS_FUNCTION(file, getch) { CHECK_INTERNAL_POINTER(m_pFile) CHECK_FILE_IS_OPEN char ch; if(!m_pFile->getChar(&ch)) c->warning(__tr2qs_ctx("Read error occurred!", "objects")); // c->error ? QString szChar = QChar(ch); c->returnValue()->setString(szChar); return true; } KVSO_CLASS_FUNCTION(file, readByte) { CHECK_INTERNAL_POINTER(m_pFile) CHECK_FILE_IS_OPEN char ch; if(!m_pFile->getChar(&ch)) c->warning(__tr2qs_ctx("Read error occurred!", "objects")); // c->error ? c->returnValue()->setInteger((kvs_int_t)ch); return true; } KVSO_CLASS_FUNCTION(file, unGetch) { CHECK_INTERNAL_POINTER(m_pFile) CHECK_FILE_IS_OPEN QString szChar; KVSO_PARAMETERS_BEGIN(c) KVSO_PARAMETER("char", KVS_PT_STRING, 0, szChar) KVSO_PARAMETERS_END(c) if(szChar.length() > 1) c->warning(__tr2qs_ctx("Argument too long, using only the first char", "objects")); const char * ch = szChar.toUtf8().data(); m_pFile->ungetChar(ch[0]); return true; } KVSO_CLASS_FUNCTION(file, readBlock) { CHECK_INTERNAL_POINTER(m_pFile) CHECK_FILE_IS_OPEN kvs_uint_t uLen; KviKvsObject * pObject; kvs_hobject_t hObject; KVSO_PARAMETERS_BEGIN(c) KVSO_PARAMETER("length", KVS_PT_UNSIGNEDINTEGER, 0, uLen) KVSO_PARAMETER("hobject", KVS_PT_HOBJECT, KVS_PF_OPTIONAL, hObject) KVSO_PARAMETERS_END(c) if(uLen > (kvs_uint_t)m_pFile->size()) uLen = m_pFile->size(); if(hObject) { pObject = KviKvsKernel::instance()->objectController()->lookupObject(hObject); if(!pObject) { c->warning(__tr2qs_ctx("Buffer parameter is not an object", "objects")); return true; } if(!pObject->inheritsClass("memorybuffer")) { c->warning(__tr2qs_ctx("Buffer parameter is not a memorybuffer object", "objects")); return true; } ((KvsObject_memoryBuffer *)pObject)->pBuffer()->append(m_pFile->read(uLen)); return true; } else { char * buff = new char[uLen + 1]; int rlen = m_pFile->read(buff, uLen); buff[rlen] = '\0'; QString szBlock(buff); delete[] buff; c->returnValue()->setString(szBlock); } return true; } KVSO_CLASS_FUNCTION(file, read) { CHECK_INTERNAL_POINTER(m_pFile) CHECK_FILE_IS_OPEN QString szType; KVSO_PARAMETERS_BEGIN(c) KVSO_PARAMETER("type", KVS_PT_STRING, 0, szType) KVSO_PARAMETERS_END(c) if(KviQString::equalCI(szType, "String")) { QString szStr; m_pFile->load(szStr); c->returnValue()->setString(szStr); } else if(KviQString::equalCI(szType, "Integer")) { int iValue; m_pFile->load(iValue); c->returnValue()->setInteger(iValue); } else if(KviQString::equalCI(szType, "Array")) { QString szData; m_pFile->load(szData); KviKvsVariant * pVar = KviKvsVariant::unserialize(szData); if(pVar->isArray()) c->returnValue()->setArray(pVar->array()); else c->warning(__tr2qs_ctx("The incoming data is not an array", "objects")); } else if(KviQString::equalCI(szType, "Dict")) { QString szData; m_pFile->load(szData); KviKvsVariant * pVar = KviKvsVariant::unserialize(szData); if(pVar->isHash()) c->returnValue()->setHash(pVar->hash()); else c->warning(__tr2qs_ctx("The incoming data is not a dictionary", "objects")); } else if(KviQString::equalCI(szType, "String")) { QString szStr; m_pFile->load(szStr); c->returnValue()->setString(szStr); } else { c->warning(__tr2qs_ctx("Unsupported datatype '%Q'", "objects"), &szType); } return true; } KVSO_CLASS_FUNCTION(file, write) { CHECK_INTERNAL_POINTER(m_pFile) CHECK_FILE_IS_OPEN KviKvsVariant * pVariantData; KVSO_PARAMETERS_BEGIN(c) KVSO_PARAMETER("variant_data", KVS_PT_VARIANT, 0, pVariantData) KVSO_PARAMETERS_END(c) if(pVariantData->isInteger()) { kvs_int_t num; pVariantData->asInteger(num); m_pFile->save(num); } if(pVariantData->isHash() || pVariantData->isArray() || pVariantData->isString()) { QString szTmp; pVariantData->serialize(szTmp); m_pFile->save(szTmp); } return true; } KVSO_CLASS_FUNCTION(file, writeBlock) { CHECK_INTERNAL_POINTER(m_pFile) kvs_uint_t uLen; KviKvsObject * pObject; KviKvsVariant * pVariantData; kvs_hobject_t hObject; KVSO_PARAMETERS_BEGIN(c) KVSO_PARAMETER("string_or_hobject", KVS_PT_VARIANT, 0, pVariantData) KVSO_PARAMETER("length", KVS_PT_UNSIGNEDINTEGER, KVS_PF_OPTIONAL, uLen) KVSO_PARAMETERS_END(c) if(!m_pFile->isOpen()) c->warning(__tr2qs_ctx("File is not open!", "objects")); else { if(pVariantData->isHObject()) { pVariantData->asHObject(hObject); pObject = KviKvsKernel::instance()->objectController()->lookupObject(hObject); if(!pObject) { c->warning(__tr2qs_ctx("Buffer parameter is not an object", "objects")); return true; } if(pObject->inheritsClass("memorybuffer")) { if(!uLen) { if(((KvsObject_memoryBuffer *)pObject)->pBuffer()->size()) uLen = ((KvsObject_memoryBuffer *)pObject)->pBuffer()->size(); else { c->warning(__tr2qs_ctx("The memoryBuffer object is empty: nothing will be saved", "objects")); } } const char * pData = ((KvsObject_memoryBuffer *)pObject)->pBuffer()->data(); int rlen = m_pFile->write(pData, uLen); c->returnValue()->setInteger(rlen); } else { c->warning(__tr2qs_ctx("Buffer parameter is not a memorybuffer object", "objects")); return true; } } else { if(!pVariantData->isString()) { c->warning(__tr2qs_ctx("Block parameter must be a string or a memorybuffer object", "objects")); return true; } QString szBlock; pVariantData->asString(szBlock); const char * block = szBlock.toUtf8().data(); int rlen = m_pFile->write(block, uLen); c->returnValue()->setInteger(rlen); } } return true; } KVSO_CLASS_FUNCTION(file, readLine) { CHECK_INTERNAL_POINTER(m_pFile) CHECK_FILE_IS_OPEN QString buffer; buffer = m_pFile->readLine(); c->returnValue()->setString(buffer); return true; } KVSO_CLASS_FUNCTION(file, writeLine) { CHECK_INTERNAL_POINTER(m_pFile) CHECK_FILE_IS_OPEN QString szLine; KVSO_PARAMETERS_BEGIN(c) KVSO_PARAMETER("text_line", KVS_PT_STRING, 0, szLine) KVSO_PARAMETERS_END(c) QTextStream ts(m_pFile); ts << szLine; return true; } KVSO_CLASS_FUNCTION(file, readHexBlock) { CHECK_INTERNAL_POINTER(m_pFile) CHECK_FILE_IS_OPEN kvs_uint_t uLen; KVSO_PARAMETERS_BEGIN(c) KVSO_PARAMETER("length", KVS_PT_UNSIGNEDINTEGER, 0, uLen) KVSO_PARAMETERS_END(c) if(uLen > (kvs_uint_t)m_pFile->size()) uLen = m_pFile->size(); char * buff = new char[uLen]; char * str = new char[(uLen * 2) + 1]; m_pFile->flush(); // advice from QFile man page (to avoid trash) int rlen = m_pFile->read(buff, uLen); int index = 0; unsigned char byte, msb, lsb = 0; for(int i = 0; i < rlen; i++) { byte = (unsigned char)buff[i]; lsb = byte & 0x0f; msb = byte >> 4; msb > 9 ? msb += '7' : msb += '0'; lsb > 9 ? lsb += '7' : lsb += '0'; str[index++] = msb; str[index++] = lsb; } str[index] = '\0'; c->returnValue()->setString(str); delete[] str; delete[] buff; return true; } KVSO_CLASS_FUNCTION(file, writeHexBlock) { CHECK_INTERNAL_POINTER(m_pFile) CHECK_FILE_IS_OPEN QString szBlock; kvs_uint_t uLen; KVSO_PARAMETERS_BEGIN(c) KVSO_PARAMETER("text_block", KVS_PT_STRING, 0, szBlock) KVSO_PARAMETER("length", KVS_PT_UNSIGNEDINTEGER, KVS_PF_OPTIONAL, uLen) KVSO_PARAMETERS_END(c) if(szBlock.length() % 2) { c->warning(__tr2qs_ctx("Length of hex string is not multiple of 2", "objects")); return true; } if(uLen > ((uint)szBlock.length() / 2) || !uLen) uLen = szBlock.length(); else uLen *= 2; unsigned char byte, lsb, msb; for(unsigned int i = 0; i < uLen; i += 2) { msb = szBlock.at(i).toLatin1(); lsb = szBlock.at(i + 1).toLatin1(); if(((msb >= 'A' && msb <= 'F') || (msb >= '0' && msb <= '9')) && ((lsb >= 'A' && lsb <= 'F') || (lsb >= '0' && lsb <= '9'))) { msb >= 'A' ? msb -= '7' : msb -= '0'; lsb >= 'A' ? lsb -= '7' : lsb -= '0'; } else { c->warning(__tr2qs_ctx("The hex string is not correct!", "objects")); return true; } byte = (msb * 16) + lsb; m_pFile->putChar(byte); } c->returnValue()->setInteger(uLen / 2); return true; }