diff --git a/src/filesystem.cpp b/src/filesystem.cpp index 9d52a3c8..a03a1461 100644 --- a/src/filesystem.cpp +++ b/src/filesystem.cpp @@ -37,11 +37,16 @@ // File // public: -File::File(QFile *openfile, QObject *parent) : +File::File(QFile *openfile, QTextCodec *codec, QObject *parent) : QObject(parent), m_file(openfile) { m_fileStream.setDevice(m_file); + if ((QTextCodec *)NULL == codec) { + m_fileStream.setCodec(QTextCodec::codecForName("UTF-8")); + } else { + m_fileStream.setCodec(codec); + } } File::~File() @@ -273,12 +278,40 @@ QString FileSystem::absolute(const QString &relativePath) const return QFileInfo(relativePath).absoluteFilePath(); } +static inline QString getCharset(const QVariant &val) { + QVariant::Type type = val.type(); + + // val must be either a string or null/undefined. + if (QVariant::String != type && QVariant::Invalid != type) { + qDebug() << "FileSystem::open - " << "Charset must be a string!"; + return QString(); + } + + QString charset = val.toString(); + + // Default to UTF-8 + if (charset.isEmpty()) { + charset = "UTF-8"; + } + + return charset; +} + // Files -QObject *FileSystem::_open(const QString &path, const QString &mode) const +QObject *FileSystem::_open(const QString &path, const QVariantMap &opts) const { File *f = NULL; QFile *_f = new QFile(path); QFile::OpenMode modeCode = QFile::NotOpen; + QVariant modeVar = opts["mode"]; + + // Ensure only strings + if (modeVar.type() != QVariant::String) { + qDebug() << "FileSystem::open - " << "Mode must be a string!" << modeVar; + return NULL; + } + + QString mode = modeVar.toString(); // Ensure only one "mode character" has been selected if ( mode.length() != 1) { @@ -316,9 +349,16 @@ QObject *FileSystem::_open(const QString &path, const QString &mode) const } } + QString charset = getCharset(opts["charset"]); + QTextCodec *codec = QTextCodec::codecForName(charset.toAscii()); + if ((QTextCodec *)NULL == codec) { + qDebug() << "FileSystem::open - " << "Unknown charset:" << charset; + return NULL; + } + // Try to Open if ( _f->open(modeCode) ) { - f = new File(_f); + f = new File(_f, codec); if ( f ) { return f; } diff --git a/src/filesystem.h b/src/filesystem.h index 19303e53..9fc14e06 100644 --- a/src/filesystem.h +++ b/src/filesystem.h @@ -33,6 +33,7 @@ #include #include #include +#include #include #include @@ -41,7 +42,7 @@ class File : public QObject Q_OBJECT public: - File(QFile *openfile, QObject *parent = 0); + File(QFile *openfile, QTextCodec *codec, QObject *parent = 0); virtual ~File(); public slots: @@ -87,10 +88,10 @@ public slots: bool _removeTree(const QString &path) const; // Files - // 'open(path, mode)' implemented in "filesystem-shim.js" using '_open(path, mode)' - QObject *_open(const QString &path, const QString &mode) const; - // 'read(path)' implemented in "filesystem-shim.js" - // 'write(path, mode)' implemented in the "filesystem-shim.js" + // 'open(path, mode|options)' implemented in "filesystem-shim.js" using '_open(path, opts)' + QObject *_open(const QString &path, const QVariantMap &opts) const; + // 'read(path, options)' implemented in "filesystem-shim.js" + // 'write(path, mode|options)' implemented in the "filesystem-shim.js" // 'remove(path)' implemented in "filesystem-shim.js" using '_remove(path)' bool _remove(const QString &path) const; // 'copy(source, destination)' implemented in "filesystem-shim.js" using '_copy(source, destination)' diff --git a/src/modules/fs.js b/src/modules/fs.js index 8aa0c496..d7095bb4 100644 --- a/src/modules/fs.js +++ b/src/modules/fs.js @@ -36,11 +36,30 @@ * It will throw exception if it fails. * * @param path Path of the file to open - * @param mode Open Mode. A string made of 'r', 'w', 'a/+' characters. + * @param modeOrOpts + * mode: Open Mode. A string made of 'r', 'w', 'a/+' characters. + * opts: Options. + * - mode (see Open Mode above) + * - charset An IANA, case insensitive, charset name. * @return "file" object */ -exports.open = function (path, mode) { - var file = exports._open(path, mode); +exports.open = function (path, modeOrOpts) { + var file, opts; + + // Extract charset from opts + if (modeOrOpts == null) { + // Empty options + opts = {}; + } else if (typeof modeOrOpts !== 'object') { + opts = { + mode: modeOrOpts + }; + } else { + opts = modeOrOpts; + } + + // Open file + file = exports._open(path, opts); if (file) { return file; } @@ -51,10 +70,16 @@ exports.open = function (path, mode) { * It will throw an exception if it fails. * * @param path Path of the file to read from + * @param opts Options. + * - charset An IANA, case insensitive, charset name. * @return file content */ -exports.read = function (path) { - var f = exports.open(path, 'r'), +exports.read = function (path, opts) { + if (opts == null || typeof opts !== 'object') { + opts = {}; + } + opts.mode = 'r'; + var f = exports.open(path, opts), content = f.read(); f.close(); @@ -66,10 +91,17 @@ exports.read = function (path) { * * @param path Path of the file to read from * @param content Content to write to the file - * @param mode Open Mode. A string made of 'w' or 'a / +' characters. + * @param modeOrOpts + * mode: Open Mode. A string made of 'r', 'w', 'a/+' characters. + * opts: Options. + * - mode (see Open Mode above) + * - charset An IANA, case insensitive, charset name. */ -exports.write = function (path, content, mode) { - var f = exports.open(path, mode); +exports.write = function (path, content, modeOrOpts) { + if (modeOrOpts == null) { + modeOrOpts = {}; + } + var f = exports.open(path, modeOrOpts); f.write(content); f.close(); diff --git a/test/fs-spec-01.js b/test/fs-spec-01.js index 91af7bc2..c47d649b 100644 --- a/test/fs-spec-01.js +++ b/test/fs-spec-01.js @@ -3,6 +3,7 @@ describe("Basic Files API (read, write, remove, ...)", function() { FILENAME_COPY = FILENAME + ".copy", FILENAME_MOVED = FILENAME + ".moved", FILENAME_EMPTY = FILENAME + ".empty", + FILENAME_ENC = FILENAME + ".enc", ABSENT = "absent-01.test"; it("should be able to create and write a file", function() { @@ -80,4 +81,20 @@ describe("Basic Files API (read, write, remove, ...)", function() { fs.copy(ABSENT, FILENAME_COPY); }).toThrow("Unable to copy file '" + ABSENT + "' at '" + FILENAME_COPY + "'"); }); + + it("should be read/write utf8 text by default", function() { + var content, output = "ÄABCÖ"; + try { + var f = fs.open(FILENAME_ENC, "w"); + f.write(output); + f.close(); + + f = fs.open(FILENAME_ENC, "r"); + content = f.read(); + f.close(); + + fs.remove(FILENAME_ENC); + } catch (e) { } + expect(content).toEqual(output); + }); }); \ No newline at end of file