mgetl: fix trouble with long file and LF on Windows 58/20758/7
Antoine ELIAS [Tue, 29 Jan 2019 08:14:24 +0000 (09:14 +0100)]
tests = [...
    "bug_10056"
    "bug_13119"
    "bug_14156"
    "bug_15018"
    "bug_2059"
    "bug_2610"
    "bug_2636"
    "bug_3427"
    "bug_3700"
    "bug_6449"
    "bug_6451"
    "bug_6452"
    "bug_7840"
    "bug_8006"
    "bug_9570"
    "getURL"
    "mfprintf"
    "mgetl"
    "mopen.encoding"
    "mputl"
    "mputstr"];

test_run("fileio", tests)

Change-Id: Ifa48f3d8f37646edb0e2b32ed813a37389e2dadc

scilab/modules/fileio/sci_gateway/cpp/sci_mgetl.cpp
scilab/modules/fileio/src/cpp/mgetl.cpp
scilab/modules/fileio/tests/unit_tests/mgetl.dia.ref [deleted file]
scilab/modules/fileio/tests/unit_tests/mgetl.tst

index 49e991a..d8db7f7 100644 (file)
@@ -44,7 +44,7 @@ types::Function::ReturnValue sci_mgetl(types::typed_list &in, int _iRetCount, ty
 
     if (in.size() < 1 || in.size() > 2)
     {
-        Scierror(77, _("%s: Wrong number of input arguments: %d to %d expected.\n"), "mgetl" , 1, 2);
+        Scierror(77, _("%s: Wrong number of input arguments: %d to %d expected.\n"), "mgetl", 1, 2);
         return types::Function::OK;
     }
 
@@ -151,9 +151,13 @@ types::Function::ReturnValue sci_mgetl(types::typed_list &in, int _iRetCount, ty
     if (wcReadedStrings && iLinesRead > 0)
     {
         types::String *pS = new types::String(iLinesRead, 1);
-        pS->set(wcReadedStrings);
+        wchar_t** w = pS->get();
+        for (int i = 0; i < iLinesRead; ++i)
+        {
+            w[i] = wcReadedStrings[i];
+        }
         out.push_back(pS);
-        freeArrayOfWideString(wcReadedStrings, iLinesRead);
+        FREE(wcReadedStrings);
     }
     else
     {
index c1cf6e1..ca355c9 100644 (file)
@@ -2,6 +2,7 @@
 * Scilab ( http://www.scilab.org/ ) - This file is part of Scilab
 * Copyright (C) 2010 - DIGITEO - Allan CORNET
 * Copyright (C) 2010 - DIGITEO - Antoine ELIAS
+* Copyright (C) 2019 - ESI - Antoine ELIAS
 *
 * Copyright (C) 2012 - 2016 - Scilab Enterprises
 *
 * along with this program.
 *
 */
-/*--------------------------------------------------------------------------*/
 
-#include <iostream>
 #include <fstream>
+#include <iostream>
 
 extern "C"
 {
-#include "mgetl.h"
-#include "freeArrayOfString.h"
 #include "charEncoding.h"
+#include "freeArrayOfString.h"
+#include "mgetl.h"
 #include "sci_malloc.h"
 #include "sciprint.h"
 }
 #include "filemanager.hxx"
 
-#include <string.h>
 #include <stdio.h>
+#include <string.h>
 
 #ifdef _MSC_VER
 #include <Windows.h>
 #endif
 
-#ifdef BUFFER_SIZE
-#undef BUFFER_SIZE
-#endif
-#define BUFFER_SIZE 4096
-
 static const unsigned char UTF8_BOM[] = {0xEF, 0xBB, 0xBF, 0x00};
 
-int mgetl(int iFileID, int iLineCount, wchar_t ***pwstLines)
+//remove \r
+inline void rtrim(char* s)
+{
+    size_t n = strlen(s);
+    if (n && s[n - 1] == '\r')
+    {
+        s[n - 1] = 0;
+    }
+}
+
+int mgetl(int iFileID, int iLineCount, wchar_t*** pwstLines)
 {
     *pwstLines = NULL;
 
     // get file descriptor
     types::File* pFile = FileManager::getFile(iFileID);
-    FILE *fd;
+    FILE* fd;
     if (pFile != NULL)
     {
         fd = pFile->getFiledesc();
@@ -91,113 +96,14 @@ int mgetl(int iFileID, int iLineCount, wchar_t ***pwstLines)
     //seek to same position
     ifs.seekg(orig);
 
-    std::list<std::string> lst;
-
-    bool lineReach = false;
-    std::string previous;
-    size_t offset = 0;
-    while (lst.size() < iLineCount && ifs.eof() == false)
-    {
-        int delimiter_size = 1;
-        size_t sp = previous.size();
 #define MAX_READ_LEN 262144
-        char buf[MAX_READ_LEN + 1] = {0};
-        ifs.read(buf, MAX_READ_LEN);
-        size_t s = strlen(buf);
-        //extract lines
-        char* ptr = buf;
-        for (int i = 0; i < s; ++i)
-        {
-            if (buf[i] == '\n')
-            {
-                //delimit line
-                buf[i] = '\0';
-                if (i > 0 && buf[i - 1] == '\r')
-                {
-                    buf[i - 1] = '\0';
-                    delimiter_size = 2;
-                }
-
-                //add line to list
-                if (sp)
-                {
-                    previous += ptr;
-                    lst.push_back(previous);
-#ifdef _MSC_VER
-                    offset += previous.size() + 2;
-#else
-                    offset += previous.size() + delimiter_size;
-#endif
-                    previous.clear();
-                }
-                else
-                {
-                    lst.emplace_back(ptr);
-#ifdef _MSC_VER
-                    offset += strlen(ptr) + 2;
-#else
-                    offset += strlen(ptr) + delimiter_size;
-#endif
-                }
-
-                //move ptr to first next line char
-                ptr = buf + i + 1;
-
-                if (iLineCount != -1 && lst.size() >= iLineCount)
-                {
-                    //rewind
-#ifndef _MSC_VER
-                    auto t = ifs.tellg();
-#else
-                    std::fpos_t t = ifs.tellg().seekpos();
-#endif
-                    if (t <= 0)
-                    {
-                        //reset error flags
-                        ifs.clear();
-                    }
-
-                    ifs.seekg(orig + offset, std::ios::beg);
-                    lineReach = true;
-                    break;
-                }
-            }
-        }
-
-        if (ptr == buf)
-        {
-            //long line
-            previous += buf;
-        }
-        else if (lineReach == false)
-        {
-            int offset = (int)(buf + s - ptr);
-            if (offset)
-            {
-                if (!ifs.eof())
-                {
-                    //some data stay in buf, rewind file to begin of this data and read it again
-#ifndef _MSC_VER
-                    auto cur1 = ifs.tellg();
-#else
-                    std::fpos_t cur1 = ifs.tellg().seekpos();
-#endif
-                    ifs.seekg((std::streamoff)cur1 - offset, std::ios::beg);
-                }
-                else
-                {
-                    //some data stay in buf but oef is reached, add ptr data in list
-                    std::string str(ptr);
-                    lst.push_back(str);
-                }
-            }
-        }
-    }
+    char str[MAX_READ_LEN];
+    std::vector<wchar_t*> lst;
 
-    if (previous.size())
+    while ((iLineCount == -1 || lst.size() < iLineCount) && ifs.getline(str, MAX_READ_LEN))
     {
-        lst.push_back(previous);
-        previous.clear();
+        rtrim(str);
+        lst.push_back(to_wide_string(str));
     }
 
     int nbLinesOut = (int)lst.size();
@@ -206,7 +112,8 @@ int mgetl(int iFileID, int iLineCount, wchar_t ***pwstLines)
         return 0;
     }
 
-    *pwstLines = (wchar_t**)MALLOC(nbLinesOut * sizeof(wchar_t**));
+    *pwstLines = (wchar_t**)MALLOC(nbLinesOut * sizeof(wchar_t*));
+
     if (*pwstLines == NULL)
     {
         return -1;
@@ -214,9 +121,7 @@ int mgetl(int iFileID, int iLineCount, wchar_t ***pwstLines)
 
     for (int i = 0; i < nbLinesOut; ++i)
     {
-        std::string s = lst.front();
-        (*pwstLines)[i] = to_wide_string(s.data());
-        lst.pop_front();
+        (*pwstLines)[i] = lst[i];
     }
 
 #ifndef _MSC_VER
@@ -229,6 +134,7 @@ int mgetl(int iFileID, int iLineCount, wchar_t ***pwstLines)
     {
         fseek(fd, pos, SEEK_SET);
     }
+
     ifs.close();
 #endif
 
diff --git a/scilab/modules/fileio/tests/unit_tests/mgetl.dia.ref b/scilab/modules/fileio/tests/unit_tests/mgetl.dia.ref
deleted file mode 100644 (file)
index 6b430ea..0000000
+++ /dev/null
@@ -1,104 +0,0 @@
-// =============================================================================
-// Scilab ( http://www.scilab.org/ ) - This file is part of Scilab
-// Copyright (C) 2010 - DIGITEO - Allan CORNET
-//
-//  This file is distributed under the same license as the Scilab package.
-// =============================================================================
-// <-- CLI SHELL MODE -->
-// =============================================================================
-// Unitary tests for mgetl function
-//==============================================================================
-function [filename, txt] = create_file(arg)
-    if isempty(arg) then
-        txt = [];
-        filename = tempname();
-    elseif typeof(arg) == "string" then
-        txt = arg;
-        filename = tempname();
-        if arg <> [] then
-            mputl(arg, filename);
-        end
-    elseif typeof(arg) == "constant" then
-        txt = "";
-        for i=1:size(arg, '*')
-            txt = [txt; strcat(repmat(ascii(96+i), 1, arg(i)))];
-        end
-        filename = tempname();
-        mputl(txt, filename);
-    end
-end
-function fd = open_create_file(txt)
-    fd = mopen(create_file(txt), "rt");
-endfunction
-function check_mgetl(f, expected, varargin)
-    txt = mgetl(f, varargin(:));
-    assert_checkequal(txt, expected);
-endfunction
-ref = ["1 Scilab"; ..
-        "2 Puffin"; ..
-        "3 "; ..
-        "4 Puffin"; ..
-        "5 Scilab"];
-// test simple
-check_mgetl(create_file("a"), "a");
-check_mgetl(create_file(ref), ref);
-// test file argument: file descriptor
-fd = open_create_file(ref);
-check_mgetl(fd, [], 0)
-mclose(fd);
-// test line count argument: read all lines
-check_mgetl(create_file(ref), ref, -1);
-check_mgetl(create_file(ref), ref, -10);
-// test line count argument: read zero lines
-check_mgetl(create_file(ref), [], 0);
-// test line count argument: read some lines
-check_mgetl(create_file(ref), ref(1), 1);
-check_mgetl(create_file(ref), ref(1:2), 2);
-check_mgetl(create_file(ref), ref, 100);
-// read all lines in several calls
-fd = open_create_file(ref);
-check_mgetl(fd, ref(1), 1);
-check_mgetl(fd, ref(2:3), 2);
-check_mgetl(fd, ref(4:5), 3);
-mclose(fd);
-// test empty file
-check_mgetl(create_file([]), []);
-// test file empty lines
-filename = tempname();
-fd = mopen(filename, "wb");
-mfprintf(fd, ascii(10));
-mclose(fd);
-check_mgetl(filename, "");
-txt = mgetl("SCI/modules/fileio/tests/unit_tests/text.txt");
-assert_checkequal(size(txt), [6 1]);
-assert_checkequal(txt(6), "");
-// test file with different EOL (end of line)
-ref_EOL = ["line with CR LF"; ..
-    "line with LF"; ..
-    "line with no EOL"];
-filename = tempname();
-fd = mopen(filename, "wb");
-mfprintf(fd, "%s" + ascii(13) + ascii(10), ref_EOL(1));
-mfprintf(fd, "%s" + ascii(10), ref_EOL(2));
-mfprintf(fd, "%s", ref_EOL(3));
-mclose(fd);
-check_mgetl(filename, ref_EOL);
-// test file with BOM
-txt = mgetl("SCI/modules/fileio/tests/unit_tests/text-UTF8BOM.txt");
-assert_checkequal(size(txt), [13 1]);
-r = "Scilab is a numerical computational package developed since 1990 by researchers from the INRIA and the Ecole nationale des ponts et chaussees (ENPC). It is, since the creation of the Scilab consortium in May 2003, developed and maintained by the INRIA.";
-assert_checkequal(txt(1), r);
-// test when line size exceeeds reading buffer size (currently 4096 characters per line)
-[filename, ref2] = create_file([5000, 5000]);
-check_mgetl(filename, ref2);
-[filename, ref2] = create_file([100, 5000, 15000]);
-check_mgetl(filename, ref2);
-[filename, ref2] = create_file([4095, 8191, 16383]);
-check_mgetl(filename, ref2);
-[filename, ref2] = create_file([4096, 8192, 16384]);
-check_mgetl(filename, ref2);
-// test error management
-ierr = execstr("mgetl(1000, 1)", "errcatch");
-assert_checkequal(ierr, 999);
-ierr = execstr("mgetl(TMPDIR + ""notfound.txt"",1)", "errcatch");
-assert_checkequal(ierr, 999);
index b48c729..7db3c5c 100644 (file)
@@ -5,6 +5,8 @@
 //  This file is distributed under the same license as the Scilab package.
 // =============================================================================
 // <-- CLI SHELL MODE -->
+// <-- NO CHECK REF -->
+//
 // =============================================================================
 // Unitary tests for mgetl function
 //==============================================================================
@@ -121,5 +123,24 @@ assert_checkequal(ierr, 999);
 ierr = execstr("mgetl(TMPDIR + ""notfound.txt"",1)", "errcatch");
 assert_checkequal(ierr, 999);
 
-
-
+//test long file with CRLF
+tmp = tempname();
+fd = mopen(tmp, "wb");
+txt = "a":"z"
+for i = 1:10000;
+    mfprintf(fd, "%s %s %s %s %s %s %s %s %s %s" + ascii(13) + ascii(10), txt, txt, txt, txt, txt, txt, txt, txt, txt, txt);
+end
+mclose(fd);
+x = mgetl(tmp);
+assert_checktrue(length(x) == 27 * 10 - 1);
+
+//test long file with LF
+tmp = tempname();
+fd = mopen(tmp, "wb");
+txt = "a":"z"
+for i = 1:10000;
+    mfprintf(fd, "%s %s %s %s %s %s %s %s %s %s" + ascii(10), txt, txt, txt, txt, txt, txt, txt, txt, txt, txt);
+end
+mclose(fd);
+x = mgetl(tmp);
+assert_checktrue(length(x) == 27 * 10 - 1);