* Bug 15600 fixed: savematfile(File) was unstable and stiff 14/20514/14
Samuel GOUGEON [Sat, 22 Sep 2018 14:46:27 +0000 (16:46 +0200)]
  http://bugzilla.scilab.org/15600

  Merging first https://codereview.scilab.org/20476 is required.

  - savematfile(File) considered and tried to save all current variables
    (including Scilab macros etc) instead of only user ones.
  - Variables with unsupported types yielded an error and broke the
    whole saving, instead of being filtered out before saving.
  - When an error occurred, the file was not closed and stayed locked.
  - When no input variable was actually saved, the output file was
    created anyway.
  - Variables named flag, head, ij, it, it1, mn, v, vars, x, x1, M, O,
    P, T, or MOPT were corrupted by internal values before being saved.
  - In -v4, encoded integers could not be saved.
  - In -v4, an error message refered to the ASCII output file instead of
    a binary one.
  - In -v4, saving some hypermatrices corrupted the reloading of next
    stored variables.
  - In ASCII: UTF-8 output File pathname was not accepted.
  - Code factorized and cleaned

Change-Id: I7382ad748600c29b65f1788157c670af0f280541

scilab/CHANGES.md
scilab/modules/matio/macros/savematfile.sci
scilab/modules/matio/sci_gateway/matio_gateway.xml
scilab/modules/matio/tests/nonreg_tests/bug_15600.tst [new file with mode: 0644]

index 66e410d..32362d5 100644 (file)
@@ -248,6 +248,12 @@ input and output arguments.
 * `surf(x,y,fun)` and `surf(x,y,list(fun,params))` syntaxes are now supported, with a function identifier `fun`.
 * Figure editor's terms have been clarify to ease usage.
 * `plot(x, list(fun, params))` is now supported.
+* `savematfile` has been upgraded:
+   - `savematfile(File)` now saves only user variables instead of all current ones.
+   - In formats 6, 7 and 7.3, `savematfile()` no longer breaks the saving when an unsupported type is encountered. A warning is now softly yielded.
+   - Input variables can no longer be corrupted by internal values before being saved.
+   - When an error occurs, the output file is now closed and unlocked.
+   - Encoded integers can now be saved in `-v4` format.
 
 Help pages:
 -----------
@@ -580,6 +586,7 @@ Known issues
 * [#15594](http://bugzilla.scilab.org/show_bug.cgi?id=15594): The `bool2s` page was alone in a main chapter instead of being with the `boolean` page.
 * [#15598](http://bugzilla.scilab.org/show_bug.cgi?id=15598): `string(handle)` returned "" instead of calling `%h_string()`
 * [#15599](http://bugzilla.scilab.org/show_bug.cgi?id=15599): degree of zero polynomial was 0 instead of -Inf.
+* [#15600](http://bugzilla.scilab.org/show_bug.cgi?id=15600): `savematfile(File)` was fragile and had many pitfalls.
 * [#15601](http://bugzilla.scilab.org/show_bug.cgi?id=15601): `tbx_generate_pofile` failed when the absolute toolbox path includes some spaces, or when the toolbox has no XML files.
 * [#15605](http://bugzilla.scilab.org/show_bug.cgi?id=15605): CLR and DLR Xcos blocks did not accept expressions including "%" like "%i"
 * [#15609](http://bugzilla.scilab.org/show_bug.cgi?id=15609): (1:1):2 crashed Scilab.
index 8b69049..758d052 100644 (file)
@@ -2,8 +2,8 @@
 // Copyright (C) 2014 - Scilab Enterprises - Paul Bignier: bug #13102 fixed
 // Copyright (C) 2002-2004 - INRIA - Vincent COUVERT
 // Copyright (C) ???? - INRIA - Serge STEER
-//
 // Copyright (C) 2012 - 2016 - Scilab Enterprises
+// Copyright (C) 2018 - Samuel GOUGEON
 //
 // This file is hereby licensed under the terms of the GNU GPL v2.0,
 // pursuant to article 5.3.4 of the CeCILL v.2.1.
@@ -17,29 +17,31 @@ function savematfile(varargin)
     // This function has been developed following the 'MAT-File Format' description:
     // www.mathworks.com/access/helpdesk/help/pdf_doc/matlab/matfile_format.pdf
 
-    vars=who("get");
-    // Verify that all inputs are character strings
-    for k=1:size(varargin)
-        if type(varargin(k))<>10 then
-            error(gettext("All inputs must be character strings."));
-        end
-    end
+    // INITIALIZATIONS
+    // ===============
+    ?vars? = who_user(%f); // On first line => ignores variables inner to savematfile()
 
-    [lhs,rhs]=argn(0);
+    [lhs,rhs] = argn(0);
 
-    mtlb_opts=[]; // Options for ASCII format
-    mtlb_thefile=[]; // Name of file to write
-    mtlb_names=[]; // Variable names to save
-    version=[]; // MAT-file version: 4 or 6 or 7 (7.3 not yet implemented)
-    bin=[]; // %T is binary file %F if ASCII file
+    mtlb_opts = [];     // Options for ASCII format
+    mtlb_thefile = [];  // Name of file to write
+    mtlb_names = [];    // Variables names to save
+    version = [];       // MAT-file version: 4 or 6 or 7 or 7.3
+    bin = %T;           // %T is binary file %F if ASCII file
 
-    // Default format is binary
-    if rhs==1 then
-        bin=%T;
+    // ==================================
+    // CHECKING INPUTS & SETTING DEFAULTS
+    // ==================================
+    // Verify that all inputs are character strings
+    for k = 1:size(varargin)
+        if type(varargin(k))<>10 then
+            msg = _("%s: Argument #%d: String expected.\n")
+            error(msprintf(msg, "savematfile", k));
+        end
     end
 
     // Sort all inputs (Options/Names/Filename)
-    k=1
+    k = 1
     while k <= size(varargin)
         // All options are converted to lower case
         if part(varargin(k),1)=="-" then
@@ -51,11 +53,10 @@ function savematfile(varargin)
             warning(msprintf(gettext("Option %s not implemented: IGNORED."),"-append"));
             k=k+1
         case "-mat"
-            bin=%T
             k=k+1
         case "-ascii"
             mtlb_opts=[mtlb_opts varargin(k)];
-            bin=%F
+            bin = %F
             k=k+1
         case "-struct"
             k=k+1;
@@ -79,26 +80,22 @@ function savematfile(varargin)
             end
         case "-v4"
             version=4;
-            bin=%T;
             k=k+1
         case "-v6"
             version=6;
-            bin=%T;
             k=k+1
         case "-v7"
             version=7;
-            bin=%T;
             k=k+1
         case "-v7.3"
             version=7.3;
-            bin=%T;
             k=k+1
         case "-tabs"
-            bin=%F;
+            bin = %F;
             mtlb_opts=[mtlb_opts varargin(k)];
             k=k+1
         case "-double"
-            bin=%F;
+            bin = %F;
             mtlb_opts=[mtlb_opts varargin(k)];
             k=k+1
         case "-regexp"
@@ -108,10 +105,7 @@ function savematfile(varargin)
             end
         else
             if isempty(mtlb_thefile) then // Filename
-                mtlb_thefile=pathconvert(varargin(k),%f,%t);
-                if fileparts(mtlb_thefile,"extension")==".mat" & isempty(bin) then // extension .mat and bin not already fixed by options
-                    bin=%T
-                end
+                mtlb_thefile = pathconvert(varargin(k),%f,%t);
             else // Variable names
                 mtlb_names=[mtlb_names;varargin(k)]
             end
@@ -121,20 +115,35 @@ function savematfile(varargin)
 
     // Default version 7 for binary files
     if isempty(version) & bin then
-        version=7;
+        version = 7;
         warning(gettext("Option -v7 added."));
     end
 
-    // If no name given then all workspace saved
+    // If no name given then all user workspace saved
     if isempty(mtlb_names) then
-        mtlb_names=vars;
+        mtlb_names = ?vars?;
 
         // Part to delete Scilab variables from mtlb_names (should be improved)
         predef_names = [predef("names");"savematfile";"varargin"];
-        for i=1:size(predef_names, "*")
-            mtlb_names(mtlb_names==predef_names(i))=[];
+        for k=1:size(predef_names, "*")
+            mtlb_names(mtlb_names==predef_names(k))=[];
+        end
+        // polynomials, graphic handles, macros, builtin, libraries, lists.. are ignored:
+        for k = size(mtlb_names,"*"):-1:1
+             execstr("x="+mtlb_names(k));
+             if and(type(x)<>[1 4 5 6 7 8 10 17])
+                 mtlb_names(k) = []
+             end
+         end
+    end
+    clear ?vars?
+
+    // Clear variable which are no more used to avoid name conflicts
+    for k = ["varargin","mtlb_names","mtlb_fmt","mtlb_fd"]
+        if or(mtlb_names==k) then
+            msg = gettext("%s: Name conflict: it is not possible to save variable with name ""%s"" (ignored).")
+            warning(msprintf(msg, "savematfile", k));
         end
-        //mtlb_names(($-predef()+1):$)=[]; // clear predefined variables
     end
 
     // If binary format and no extension for filename, .mat is added
@@ -145,227 +154,194 @@ function savematfile(varargin)
     // Do not handle function redefinition
     funcprot(0);
 
-    // Binary save
+    // ------------------------------------------------------------------------
+
+    // ===========
+    // BINARY SAVE
+    // ===========
     if bin then
         // LEVEL 4 MAT-file (This part comes from mtlb_save.sci)
+        // -----------------------------------------------------
         if version==4 then
             // Matlab 5 types are not saved (structs...)
-            for k=size(mtlb_names,"*"):-1:1
-                execstr("x="+mtlb_names(k))
-                if and(type(x)<>[1 4 5 6 10]) then
-                    warning(msprintf(gettext("Variable %s can not be saved in level 4 MAT-file: IGNORED."),mtlb_names(k)));
-                    mtlb_names(k)=[]
-                end
-            end
+            mtlb_names = checkTypeBeforeMatSave(mtlb_names, [1 4 5 6 8 10], "4")
 
             // Open file for writing
-            [mtlb_fd,err]=mopen(mtlb_thefile,"wb",0)
+            [mtlb_fd,err] = mopen(mtlb_thefile,"wb",0)
 
             // Clear variable wich are no more used to avoid name conflicts
-            for k=["varargin","mtlb_names","mtlb_fmt","mtlb_fd"]
-                if or(mtlb_names==k) then
-                    error(msprintf(gettext("Name conflict: it is not possible to save variable with name %s."),k));
-                end
-            end
-            clear("x","k","rhs","lhs","kk","err","bin","version","mtlb_thefile","mtlb_opts");
+            clear k x rhs lhs kk err bin version mtlb_thefile mtlb_opts
 
             // Following 'for loop' from SS
-            for mtlb_k=1:size(mtlb_names,"*")
+            for mtlb_k = 1:size(mtlb_names,"*")
                 // perform changes on variables
-                execstr("x="+mtlb_names(mtlb_k))
-                it=0
-                select type(x)
+                execstr("__x__="+mtlb_names(mtlb_k))
+
+                // hypermatrix => we concatenate its pages horizontally:
+                __s__ = size(__x__);
+                if length(__s__)>2    
+                    __x__ = matrix(__x__,__s__(1),-1);
+                end
+                //
+                __it__ = 0  // == has imaginary part
+                __P__ = 0   // v4 encoding type
+                __T__ = 0
+                select type(__x__)
                 case 1 then
-                    P=0
-                    T=0
-                    if norm(imag(x),1)<>0 then it=1,end
-                case 4 then
-                    x=bool2s(x)
-                    P=5
-                    T=0
-                case 5 then
-                    if norm(imag(x),1)<>0 then it1=1,else it1=0,end
-                    P=0
-                    T=2
+                    if norm(imag(__x__),1)<>0
+                        __it__ = 1
+                    end
+                case 4 then     // boolean
+                    __x__ = bool2s(__x__)
+                    __P__ = 5
+                case 5 then     // sparse
+                    if norm(imag(__x__),1)<>0 then
+                        __it1__ = 1
+                    else
+                        __it1__ = 0
+                    end
+                    __T__ = 2
                     // We transpose the sparse matrix so as to ease the conversion to the matlab sparse format
-                    [x,v,mn]=spget(x);
-                    if it1==0 then
-                        x=[x real(v);[mn 0]]
+                    [__x__, __v__, __mn__] = spget(__x__);
+                    if __it1__==0 then
+                        __x__ = [__x__ real(__v__);[__mn__ 0]]
                     else
-                        x=[x real(v) imag(v);[mn 0 0]]
+                        __x__ = [__x__ real(__v__) imag(__v__);[__mn__ 0 0]]
                     end
-                case 6 then
-                    x=bool2s(x)
-                    P=0
-                    T=2
-                    [x,v,mn]=spget(x);
-                    x=[x v;[mn 0]]
+                case 6 then     // sparse boolean
+                    __x__ = bool2s(__x__)
+                    __P__ = 0
+                    __T__ = 2
+                    [__x__, __v__, __mn__] = spget(__x__);
+                    __x__ = [__x__ __v__ ; [__mn__ 0]]
                 case 8 then
-                    T=0
-                    select inttype(x)
-                    case 4 then P=2,
-                    case 14 then P=2,
-                    case 2 then P=3
-                    case 12 then P=4
-                    case 1 then P=5,
-                    case 11 then P=5,
+                    // Supported Matlab v4 encodings:
+                    // 0 double
+                    // 2 32-bit signed integers
+                    // 3 16-bit signed integers
+                    // 4 16-bit unsigned integers
+                    // 5 8-bit unsigned integers
+                    select inttype(__x__)   
+                    case 8  then    // int64  = l
+                        __P__ = 0
+                        __x__ = double(__x__);
+                    case 18  then   // uint64  = ul
+                        __P__ = 0
+                        __x__ = double(__x__);
+                    case 4  then __P__ = 2, // int32  = i
+                    case 14 then            // uint32 = ui
+                        if find(__x__>int32(%inf))==[]
+                            __P__ = 2
+                        else
+                            __P__ = 0
+                            __x__ = double(__x__);
+                        end
+                    case 2  then __P__ = 3  // int16  = s
+                    case 12 then __P__ = 4  // uint16 = us
+                    case 1  then    // int8
+                        if find(__x__<0,1)==[]
+                            __P__ = 5,      // uc
+                        else
+                            __x__ = int16(__x__);
+                            __P__ = 3,      // s
+                        end
+                    case 11 then __P__ = 5, // uint8 = uc
                     end
-                    x=double(x)
-                case 10 then
-                    x1=part(x(:),1:max(length(x)))
-                    x=[]
-                    for l=1:size(x1,1)
-                        x=[x;ascii(x1(l))]
+
+                case 10 then    // Text
+                    __x1__ = part(__x__(:), 1:max(length(__x__)))
+                    __x__ = []
+                    for l = 1:size(__x1__,1)
+                        __x__ = [__x__; ascii(__x1__(l))]
                     end
-                    P=5
-                    T=1
+                    __P__ = 5
+                    __T__ = 1
                 else
-                    error(gettext("Attempt to write an unsupported data type to an ASCII file."));
+                    // Should never happen:
+                    mclose(mtlb_fd);
+                    error(gettext("Attempt to write an unsupported data type to a binary file."));
                 end
-                [m,n]=size(x)
-
-
-                M = 0 //little endian
-                O = 0
-                MOPT=[M O P T]
 
-                [m,n]=size(x)
-
-                head=[MOPT*[1000;100;10;1] m,n,it,length(mtlb_names(mtlb_k))+1]
-
-                mput(head,"uil",mtlb_fd);
-                mput([ascii(mtlb_names(mtlb_k)) 0],"c",mtlb_fd);
-                select P
-                case 0 then
-                    flag="dl"
-                case 1 then
-                    flag="fl"
-                case 2 then
-                    flag="il"
-                case 3 then
-                    flag="sl"
-                case 4 then
-                    flag="usl"
-                case 5 then
-                    flag="uc"
-                end
-                if T==0 then
-                    if x<>[] then
-                        mput(real(x(:).'),flag,mtlb_fd);
-                        if it==1
-                            mput(imag(x(:).'),flag,mtlb_fd);
+                //          v.....little endian forced
+                __MOPT__ = [0  0 __P__ __T__]
+
+                __head__ = [__MOPT__*[1000;100;10;1] size(__x__)(1:2), __it__, length(mtlb_names(mtlb_k))+1]
+
+                mput(__head__,"uil",mtlb_fd);
+                mput([ascii(mtlb_names(mtlb_k)) 0],"uc", mtlb_fd);
+                __flag__ = ["dl" "fl" "il" "sl" "usl" "uc"]
+                __flag__ = __flag__(__P__+1);
+                if __T__==0 then
+                    if __x__<>[] then
+                        if type(__x__)<>8
+                            mput(real(__x__(:).'), __flag__, mtlb_fd);
+                            if __it__==1
+                                mput(imag(__x__(:).'), __flag__, mtlb_fd);
+                            end
+                        else
+                            mput(__x__(:).', __flag__, mtlb_fd);
                         end
                     end
-                elseif T==1
-                    v=mput(x(:).',flag,mtlb_fd);
-                elseif T==2 then  //sparse
-                    mput(x(:).',flag,mtlb_fd);
+                elseif __T__==1         // Text
+                    mput(__x__(:).', __flag__, mtlb_fd);
+                elseif __T__==2         // sparse and sparse boolean
+                    mput(__x__(:).', __flag__, mtlb_fd);
                 end
             end
             mclose(mtlb_fd);
-            // End of loop written by SS
-            // LEVEL 6 MAT-file
-        elseif version==6 then
-            // Open file for writing
-            mtlb_fd=matfile_open(mtlb_thefile, "w");
-            if mtlb_fd == -1 then
-                error(msprintf(gettext("%s: Could not open file ''%s''.\n"),"savematfile",mtlb_thefile))
-            end
-            // Clear variable wich are no more used to avoid name conflicts
-            for k=["varargin","mtlb_names","mtlb_fmt","mtlb_fd"]
-                if or(mtlb_names==k) then
-                    error(msprintf(gettext("Name conflict: it is not possible to save variable with name %s."),k))
-                end
-            end
-            clear("x","k","rhs","lhs","kk","err","sep","bin","version","mtlb_thefile","mtlb_opts");
 
-            // Write variables as miMATRIX data type
-            for k=1:size(mtlb_names,"*")
-                %var=evstr(mtlb_names(k));
-                // We transpose the sparse matrix so as to ease the conversion to the matlab sparse format
-                if type(%var)==5 then %var = %var'; end
-                if and(type(%var)<>[9 13]) then
-                    if ~matfile_varwrite(mtlb_fd, mtlb_names(k), %var, %F) then
-                        error(msprintf(gettext("savematfile: could not save variable named %s.\n"), mtlb_names(k)));
-                    end
-                else
-                    error(msprintf(gettext("savematfile: could not save variable named %s.\n"), mtlb_names(k)));
-                end
+        // Versions 6, 7 and 7.3
+        // ---------------------
+        else
+            // Filtering supported types
+            // Unsupported : handles 9, macros 13, primitives 130, Others 128, 129
+            // Unsupported : booleans 4 : http://bugzilla.scilab.org/15568
+            mtlb_names = checkTypeBeforeMatSave(mtlb_names, [1 5 7 8 10 17], version)
+            if mtlb_names==[]
+                msg = gettext("savematfile: No variable to save => No file written.")
+                warning(msg);
+                return
             end
 
-            matfile_close(mtlb_fd);
-        elseif version==7
             // Open file for writing
-            mtlb_fd=matfile_open(mtlb_thefile, "w");
-            if mtlb_fd == -1 then
-                error(msprintf(gettext("%s: Could not open file ''%s''.\n"),"savematfile",mtlb_thefile))
+            if version < 7.3
+                mtlb_fd = matfile_open(mtlb_thefile, "w");
+            else
+                mtlb_fd = matfile_open(mtlb_thefile, "w", "7.3");
             end
-            // Clear variable wich are no more used to avoid name conflicts
-            for k=["varargin","mtlb_names","mtlb_fmt","mtlb_fd"]
-                if or(mtlb_names==k) then
-                    error(msprintf(gettext("Name conflict: it is not possible to save variable with name %s."),k))
-                end
-            end
-            clear("x","k","rhs","lhs","kk","err","sep","bin","version","mtlb_thefile","mtlb_opts");
-
-            // Write variables as miCOMPRESSED data type
-            for k=1:size(mtlb_names,"*")
-                %var=evstr(mtlb_names(k));
-                // We transpose the sparse matrix so as to ease the conversion to the matlab sparse format
-                if type(%var)==5 then %var = %var'; end
-                if and(type(%var)<>[9 13]) then
-                    if ~matfile_varwrite(mtlb_fd, mtlb_names(k), %var, %T) then
-                        error(msprintf(gettext("savematfile: could not save variable named %s.\n"), mtlb_names(k)));
-                    end
-                else
-                    error(msprintf(gettext("savematfile: could not save variable named %s.\n"), mtlb_names(k)));
-                end
-            end
-
-            matfile_close(mtlb_fd);
-        else // Version 7.3
-            // Open file for writing
-            mtlb_fd=matfile_open(mtlb_thefile, "w", "7.3"); // "7.3" specifies a Matlab 7.3 file
             if mtlb_fd == -1 then
-                error(msprintf(gettext("%s: Could not open file ''%s''.\n"),"savematfile",mtlb_thefile))
+                msg = gettext("%s: Could not open file ''%s''.\n")
+                error(msprintf(msg, "savematfile", mtlb_thefile))
             end
-            // Clear variable wich are no more used to avoid name conflicts
-            for k=["varargin","mtlb_names","mtlb_fmt","mtlb_fd"]
-                if or(mtlb_names==k) then
-                    error(msprintf(gettext("Name conflict: it is not possible to save variable with name %s."),k))
+            ?compression? = version>6
+            // Clear variable which are no more used to avoid name conflicts
+            clear k x rhs lhs kk err bin version mtlb_thefile mtlb_opts
+
+            // Write variables as miMATRIX data type
+            for ?k? = 1:size(mtlb_names,"*")
+                ?var? = evstr(mtlb_names(?k?));
+                // We transpose the sparse matrix so as to ease the conversion
+                //  to the matlab sparse format
+                if type(?var?)==5 then
+                    ?var? = ?var?'
                 end
-            end
-            clear("x","k","rhs","lhs","kk","err","sep","bin","version","mtlb_thefile","mtlb_opts");
-
-            // Write variables as miCOMPRESSED data type
-            for k=1:size(mtlb_names,"*")
-                %var=evstr(mtlb_names(k));
-                // We transpose the sparse matrix so as to ease the conversion to the matlab sparse format
-                if type(%var)==5 then %var = %var'; end
-                if and(type(%var)<>[9 13]) then
-                    if ~matfile_varwrite(mtlb_fd, mtlb_names(k), %var, %T) then
-                        error(msprintf(gettext("savematfile: could not save variable named %s.\n"), mtlb_names(k)));
-                    end
-                else
-                    error(msprintf(gettext("savematfile: could not save variable named %s.\n"), mtlb_names(k)));
+                if ~matfile_varwrite(mtlb_fd, mtlb_names(?k?), ?var?, ?compression?) then
+                    msg = gettext("savematfile: could not write variable ""%s"".\n")
+                    warning(msprintf(msg, mtlb_names(?k?)));
                 end
             end
-
             matfile_close(mtlb_fd);
         end
 
+        // --------------------------------------------------------------------
+        // ==========
         // ASCII save
+        // ==========
     else
         // The end of this function has been adapted from mtlb_save.sci
 
         // Matlab 5 types are not saved (structs...)
-        for k=size(mtlb_names,"*"):-1:1
-            execstr("x="+mtlb_names(k))
-            if and(type(x)<>[1 4 5 6 10]) then
-                warning(msprintf(gettext("Variable %s can not be saved in ASCII file: IGNORED."),mtlb_names(k)));
-                mtlb_names(k)=[]
-            end
-        end
+        mtlb_names = checkTypeBeforeMatSave(mtlb_names, [1 4 5 6 10], "ASCII")
         if ( (mtlb_opts <> []) & (strindex("-tabs",mtlb_opts)<>[]) ) then
             sep = ascii(9);
         else
@@ -377,40 +353,50 @@ function savematfile(varargin)
             mtlb_fmt="(2x,1pe23.15"+sep+")"
         end
 
-        mtlb_fd=file("open",mtlb_thefile,"unknown")
+        mtlb_fd = file("open", getshortpathname(mtlb_thefile), "unknown")
 
         // Clear variable wich are no more used to avoid name conflicts
-        for k=["varargin","mtlb_names","mtlb_fmt","mtlb_fd"]
-            if or(mtlb_names==k) then
-                error(msprintf(gettext("Name conflict: it is not possible to save variable with name %s."),k));
-            end
-        end
-        clear("x","k","rhs","lhs","kk","err","sep","bin","version","mtlb_thefile","mtlb_opts");
+        clear i k x rhs lhs kk err sep bin version mtlb_thefile mtlb_opts
 
         for mtlb_k=1:size(mtlb_names,"*")
             // perform changes on variables
-            execstr("x="+mtlb_names(mtlb_k))
-            select type(x)
+            execstr("__x__="+mtlb_names(mtlb_k))
+            select type(__x__)
             case 1 then
-                write(mtlb_fd,real(x),"("+string(size(x,2))+mtlb_fmt+")")
+                write(mtlb_fd,real(__x__),"("+string(size(__x__,2))+mtlb_fmt+")")
             case 4 then
-                write(mtlb_fd,bool2s(x),"("+string(size(x,2))+mtlb_fmt+")")
+                write(mtlb_fd,bool2s(__x__),"("+string(size(__x__,2))+mtlb_fmt+")")
             case 5 then
                 // We need to transpose to conform to the matlab sparse format
-                [ij,x]=spget(real(x'));x=[ij x];
-                write(mtlb_fd,real(x),"(2f8.0,1x"+string(size(x,2))+mtlb_fmt+")")
+                [__ij__, __x__] = spget(real(__x__'));
+                __x__ = [__ij__ __x__];
+                write(mtlb_fd, real(__x__), "(2f8.0,1x"+string(size(__x__,2))+mtlb_fmt+")")
             case 6 then
-                [ij,x]=spget(bool2s(x));x=[ij x];
-                write(mtlb_fd,real(x),"(2f8.0,1x"+string(size(x,2))+mtlb_fmt+")")
+                [__ij__, __x__] = spget(bool2s(__x__));
+                __x__ = [__ij__ __x__];
+                write(mtlb_fd, real(__x__), "(2f8.0,1x"+string(size(__x__,2))+mtlb_fmt+")")
             case 10 then
-                x=part(x(:),1:max(length(x)))
-                x1=[]
-                for l=1:size(x,1)
-                    x1=[x1;ascii(x(l))]
+                __x__ = part(__x__(:),1:max(length(__x__)))
+                __x1__ = []
+                for l = 1:size(__x__,1)
+                    __x1__ = [__x1__; ascii(__x__(l))] // will fail with UTF-8 (variable length)
                 end
-                write(mtlb_fd,x1,"("+string(size(x1,2))+mtlb_fmt+")")
+                write(mtlb_fd, __x1__, "("+string(size(__x1__,2))+mtlb_fmt+")")
             end
         end
         file("close",mtlb_fd)
     end
 endfunction
+
+// ----------------------------------------------------------------------------
+
+function ?names? = checkTypeBeforeMatSave(?names?, ?typesOK?, ?version?)
+    for ?k? = size(?names?,"*"):-1:1
+        execstr("?x?="+?names?(?k?))
+        if and(type(?x?)<>?typesOK?) then
+            ?msg? = gettext("%s: Variable ""%s"" can not be saved in MAT-file version %s (type %d unsupported): IGNORED.\n")
+            warning(msprintf(?msg?, "savematfile", ?names?(?k?), string(?version?), type(?x?)));
+            ?names?(?k?) = []
+        end
+    end
+endfunction
index fd46685..0dc2520 100644 (file)
@@ -21,7 +21,7 @@
 
  Don't touch if you do not know what you are doing
 -->
-<module name="scinotes">
+<module name="matio">
     <gateway name="sci_matfile_open"        function="matfile_open"         type="0"/>
     <gateway name="sci_matfile_close"       function="matfile_close"        type="0"/>
     <gateway name="sci_matfile_listvar"     function="matfile_listvar"      type="0"/>
diff --git a/scilab/modules/matio/tests/nonreg_tests/bug_15600.tst b/scilab/modules/matio/tests/nonreg_tests/bug_15600.tst
new file mode 100644 (file)
index 0000000..77d778d
--- /dev/null
@@ -0,0 +1,137 @@
+// =============================================================================
+// Scilab ( http://www.scilab.org/ ) - This file is part of Scilab
+// Copyright (C) 2018 - Samuel GOUGEON
+//
+//  This file is distributed under the same license as the Scilab package.
+// =============================================================================
+//
+// <-- CLI SHELL MODE -->
+// <-- ENGLISH IMPOSED -->
+// <-- NO CHECK REF -->
+
+//
+// <-- Non-regression test for bug 15600 -->
+//
+// <-- Bugzilla URL -->
+// http://bugzilla.scilab.org/15600
+//
+// <-- Short Description -->
+// savematfile(filename) included many environment variables and variables
+//  of unsupported types.
+
+// Preparing data of various types
+// -------------------------------
+clear
+b = %t;                         // Skipped : http://bugzilla.scilab.org/15568
+i = int8((rand(2,3)-0.5)*200);
+k = uint16(rand(1,4)*100);
+d = %pi;
+c = 1-%i;
+h = rand(1,3,2);
+t = "text";
+L = list(%t, "abc");            // Ignored : http://bugzilla.scilab.org/15729
+p = %z;                         // Ignored
+rational = %z/(1-%z);           // Ignored
+Sparse = sprand(10,10,0.05);
+SparseB = Sparse<0.5;           // Ignored
+Struct = struct("age",30, "type","software");
+Cell = {1-%i, int8(-5); rand(2,3), "Abc"};
+
+// Saving the environment
+// ----------------------
+savematfile(TMPDIR+"/tmp.mat");
+savematfile(TMPDIR+"/tmp_73.mat","-v7.3");
+
+// Testing the actually saved set of variables
+// -------------------------------------------
+// version 7
+File = TMPDIR+"/tmp.mat";
+fd = matfile_open(File, "r");
+[names, classes, types] = matfile_listvar(fd);
+matfile_close(fd);
+ref = ["Cell" "Sparse" "Struct" "c" "d" "h" "i" "k" "t"]';
+assert_checkequal(names, ref);
+mdelete(File);
+
+// version 7.3
+File = TMPDIR+"/tmp_73.mat";
+fd = matfile_open(File, "r");
+[names, classes, types] = matfile_listvar(fd);
+matfile_close(fd);
+ref = ["Cell" "Sparse" "Struct" "c" "d" "h" "i" "k" "t"]';
+assert_checkequal(names, ref);
+mdelete(File);
+
+// ==========================================
+File = TMPDIR+"/tmp.mat";
+
+// When the output file is actually empty, it is now not created
+// -------------------------------------------------------------
+savematfile(File, "%z", "-v7")
+assert_checkfalse(isfile(File));
+
+// Some input variables were corrupted by savematfile() internal values before being saved
+// ---------------------------------------------------------------------------------------
+// -v4: Saving the variables flag, head, it, it1, mn, v, x, x1, M, O, P, T, MOPT
+V = rand(1);
+[flag, head, it, x, x1, M, O, P, T, MOPT] = (V,V,V,V,V,V,V,V,V,V);
+// Uncomment after fixing http://bugzilla.scilab.org/15731
+//it1 = sparse(V);
+//[mn, v, it10, mn0, v0] = (it1, it1, it1, it1, it1);
+[it1, mn, v] = (V,V,V);
+
+vnames = list("flag","head","it","it1","mn","v","x","x1","M","O","P","T","MOPT");
+savematfile(File,"-v4", vnames(:));
+clear(vnames(:))
+loadmatfile(File);
+for n = vnames
+    assert_checktrue(isdef(n,"l"));
+    y = evstr(n);
+    yref = V;
+    if issparse(y), yref = sparse(V), end
+    assert_checkequal(y, yref);
+end
+mdelete(File);
+
+// -v6 | 7|7.3: Saving the "vars" variable
+vars = rand(2,3);
+varsRef = vars;
+for v = ["-v6" "-v7" "-v7.3"]
+    vars = varsRef;
+    savematfile(File,"vars", v);
+    sleep(1000)
+    clear vars
+    loadmatfile(File);
+    assert_checktrue(isdef("vars","l"));
+    assert_checkequal(vars, varsRef);
+    mdelete(File);
+end
+
+// Encoded integers were not saved in v4 format
+// --------------------------------------------
+T = ["int8" "uint8" "int16" "uint16" "int32" "uint32" "int64" "uint64"];
+bm = list(int8(-%inf), 0, int16(-%inf), 0, -1e9, 0, -1e9, 0);
+bp = list(int8(%inf), uint8(%inf), int16(%inf), uint16(%inf), -1e9, 1e9, -1e9, 1e9);
+S.s = "1,1";
+S.v = "1,3";
+S.m = "2,3";
+for i = 1:8
+    for f = ["s" "v" "m"]
+        tmp = T(i)+f+" = "+T(i)+"(grand("+S(f)+",""uin"",double(bm(i)),double(bp(i)))); "+..
+              T(i)+f+"0 = "+T(i)+f+";"; // Reference copy
+        execstr(tmp);
+    end
+end
+vnames = ["int8s" "uint8s" "int8v" "uint8v" "int8m" "uint8m" ..
+          "int16s" "uint16s" "int16v" "uint16v" "int16m" "uint16m" ..
+          "int32s" "uint32s" "int32v" "uint32v" "int32m" "uint32m" ..
+          "int64s" "uint64s" "int64v" "uint64v" "int64m" "uint64m"
+          ];
+savematfile(File,"-v4", vnames);
+clear(vnames)
+loadmatfile(File);  // variables are well stored as the given inttype,
+                    // but are reloaded as doubles!
+for v = vnames
+    assert_checktrue(isdef(v,"l"));
+    assert_checkequal(evstr(v), double(evstr(v+"0")));
+end