From a5d6bb6e7babc10eb75831a585eed9b6fe424525 Mon Sep 17 00:00:00 2001 From: Takahiro Ueda Date: Sat, 4 May 2024 14:11:38 +0900 Subject: [PATCH 1/7] test(check.rb): improve Windows compatibility - Remove Unix-dependent code to enhance portability. - Replace guess_term_width() with io/console/size available in Ruby 1.9. - Use File::PATH_SEPARATOR. - Replace RUBY_PLATFORM with RbConfig::CONFIG["host_os"]. - On Windows, convert CR+LF to LF in output to align with test cases. - Add windows? method. - Add --show-newlines option to show CR/LF. --- check/check.rb | 103 +++++++++++++++++++++++++++++-------------------- 1 file changed, 62 insertions(+), 41 deletions(-) diff --git a/check/check.rb b/check/check.rb index 5e9e1450..bc166a05 100755 --- a/check/check.rb +++ b/check/check.rb @@ -14,9 +14,11 @@ end require "fileutils" +require "io/console/size" require "open3" require "optparse" require "ostruct" +require "rbconfig" require "set" require "thread" require "tmpdir" @@ -72,12 +74,16 @@ def read_env_positive_int(key, default_value) # Get the total size of the physical memory available on the host machine. def get_total_physical_memory - if RUBY_PLATFORM.downcase.include?("linux") + platform = RbConfig::CONFIG["host_os"].downcase + if platform.include?("linux") mem_info = `free -b | grep Mem` mem_info.split[1].to_i - elsif RUBY_PLATFORM.downcase.include?("darwin") + elsif platform.include?("darwin") mem_info = `sysctl -n hw.memsize` mem_info.to_i + elsif platform.include?("mingw") || platform.include?("mswin") + mem_info = `wmic ComputerSystem get TotalPhysicalMemory` + mem_info.split[1].to_i else nil end @@ -168,7 +174,7 @@ def which(name) result = File.expand_path(name) else # Search from $PATH. - ENV["PATH"].split(":").each do |path| + ENV["PATH"].split(File::PATH_SEPARATOR).each do |path| candidate = File.join(path, name) if File.executable?(candidate) result = File.expand_path(candidate) @@ -240,19 +246,24 @@ def wordsize # Host environment. def cygwin? - RUBY_PLATFORM =~ /cygwin/i + RbConfig::CONFIG["host_os"] =~ /cygwin/i end def mac? - RUBY_PLATFORM =~ /darwin/i + RbConfig::CONFIG["host_os"] =~ /darwin|mac os/i end def linux? - RUBY_PLATFORM =~ /linux/i + RbConfig::CONFIG["host_os"] =~ /linux/i end def unix? - cygwin? || mac? || linux? + cygwin? || mac? || linux? || RbConfig::CONFIG["host_os"] =~ /solaris|bsd/i + end + + def windows? + # NOTE: "cygwin" is intentionally excluded. + RbConfig::CONFIG["host_os"] =~ /mswin|msys|mingw|bccwin|wince|emc/i end def travis? @@ -281,6 +292,14 @@ def total_memory @@cached_total_memory = nil @@total_memory_mutex = Mutex.new + def reveal_newlines(str) + if FormTest.cfg.show_newlines + str.gsub(/\r/, "").gsub(/\n/, "\n") + else + str + end + end + # Override methods in Test::Unit::TestCase. def setup @@ -356,6 +375,14 @@ def do_test(&block) break end end + # On Windows, we convert newline characters in stdout/stderr into + # the Unix-style newline characters used in our test cases. + @raw_stdout = @stdout + @raw_stderr = @stderr + if windows? + @stdout = @stdout.gsub(/\r\n/, "\n") + @stderr = @stderr.gsub(/\r\n/, "\n") + end # MesPrint inevitably inserts newline characters when a line exceeds # its length limit. To verify error/warning messages, here we remove # newline characters that seem to be part of continuation lines @@ -379,7 +406,7 @@ def do_test(&block) $stderr.puts("=" * 79) $stderr.puts("#{info.desc} FAILED") $stderr.puts("=" * 79) - $stderr.puts(@stdout) + $stderr.puts(reveal_newlines(@raw_stdout)) $stderr.puts("=" * 79) $stderr.puts if info.status.nil? @@ -397,7 +424,7 @@ def do_test(&block) $stderr.puts("=" * 79) $stderr.puts("#{info.desc} SUCCEEDED") $stderr.puts("=" * 79) - $stderr.puts(@stdout) + $stderr.puts(reveal_newlines(@raw_stdout)) $stderr.puts("=" * 79) $stderr.puts end @@ -585,7 +612,13 @@ def bytesize(exprname, index = -1) def file(filename) begin File.open(File.join(@tmpdir, filename), "r") do |f| - return f.read + result = f.read + # On Windows, we convert newline characters in the file into + # the Unix-style newline characters used in our test cases. + if windows? + result = result.gsub(/\r\n/, "\n") + end + return result end rescue StandardError $stderr.puts("warning: failed to read '#{filename}'") @@ -1088,7 +1121,7 @@ def delete(classname) # FORM configuration. class FormConfig - def initialize(form, mpirun, mpirun_opts, valgrind, valgrind_opts, wordsize, ncpu, timeout, retries, stat, full, verbose) + def initialize(form, mpirun, mpirun_opts, valgrind, valgrind_opts, wordsize, ncpu, timeout, retries, stat, full, verbose, show_newlines) @form = form @mpirun = mpirun @mpirun_opts = mpirun_opts @@ -1100,6 +1133,7 @@ def initialize(form, mpirun, mpirun_opts, valgrind, valgrind_opts, wordsize, ncp @stat = stat @full = full @verbose = verbose + @show_newlines = show_newlines @form_bin = nil @mpirun_bin = nil @@ -1114,7 +1148,7 @@ def initialize(form, mpirun, mpirun_opts, valgrind, valgrind_opts, wordsize, ncp @form_cmd = nil end - attr_reader :form, :mpirun, :mpirun_opts, :valgrind, :valgrind_opts, :ncpu, :timeout, :retries, :stat, :full, :verbose, + attr_reader :form, :mpirun, :mpirun_opts, :valgrind, :valgrind_opts, :ncpu, :timeout, :retries, :stat, :full, :verbose, :show_newlines, :form_bin, :mpirun_bin, :valgrind_bin, :valgrind_supp, :head, :wordsize, :form_cmd def serial? @@ -1131,9 +1165,7 @@ def mpi? def check_bin(name, bin) # Check if the executable is available. - system("cd #{TempDir.root}; type #{bin} >/dev/null 2>&1") - if $? == 0 - # OK. + if File.executable?(bin) return end @@ -1166,7 +1198,8 @@ def check end @head = "" - `#{@form_bin} #{frmname} 2>/dev/null`.split("\n").each do |output_line| + out, _status = Open3.capture2e("#{@form_bin} #{frmname}") + out.split("\n").each do |output_line| if output_line =~ /FORM/ @head = output_line break @@ -1236,14 +1269,16 @@ def check end @form_cmd = cmdlist.join(" ") # Check the output header. - @head = `#{@form_cmd} #{frmname} 2>/dev/null`.split("\n").first - if $? != 0 - system("#{form_cmd} #{frmname}") + out, _err, status = Open3.capture3("#{@form_cmd} #{frmname}") + if status.success? + @head = out.split("\n").first + else fatal("failed to execute '#{@form_cmd}'") end if !@valgrind.nil? # Include valgrind version information. - @head += "\n#{`#{@form_cmd} @{frmname} 2>&1 >/dev/null | grep Valgrind`.split("\n")[0]}" + out, _status = Open3.capture2e("#{@form_cmd} #{frmname}") + @head += "\n" + out.split("\n").select { |line| line.include?("Valgrind") }.first end ensure FileUtils.rm_rf(tmpdir) @@ -1344,6 +1379,7 @@ def main opts.group_count = nil opts.files = [] opts.verbose = false + opts.show_newlines = false parser = OptionParser.new parser.banner = "Usage: #{File.basename($0)} [options] [--] [binname] [files|tests..]" @@ -1387,6 +1423,8 @@ def main "Split tests and run only one group") { |group| opts.group_id, opts.group_count = parse_group(group) } parser.on("-v", "--verbose", "Enable verbose output") { opts.verbose = true } + parser.on("--show-newlines", + "Show newline characters") { opts.show_newlines = true } parser.on("-D TEST=NAME", "Alternative way to run tests NAME") { |pat| opts.name_patterns << parse_def(pat) } begin @@ -1479,7 +1517,7 @@ def main # --path option. if !opts.path.nil? - ENV["PATH"] = "#{opts.path}:#{ENV['PATH']}" + ENV["PATH"] = "#{opts.path}#{File::PATH_SEPARATOR}#{ENV['PATH']}" end # Set FORMPATH @@ -1517,7 +1555,8 @@ def main opts.retries > 1 ? opts.retries : 1, opts.stat, opts.full, - opts.verbose) + opts.verbose, + opts.show_newlines) FormTest.cfg.check puts("Check #{FormTest.cfg.form_bin}") puts(FormTest.cfg.head) @@ -1532,7 +1571,7 @@ def finalize # Print detailed statistics. - term_width = guess_term_width + term_width = IO.console_size[1] max_foldname_width = infos.map { |info| info.foldname.length }.max max_where_width = infos.map { |info| info.where.length }.max + 2 @@ -1628,24 +1667,6 @@ def format_time(time, max_time) format("%s%02d:%02d:%02d.%03d", overflow ? ">" : " ", h, m, s, ms) end -# Return a guessed terminal width. -def guess_term_width - require "io/console" - IO.console.winsize[1] -rescue LoadError, NoMethodError - system("type tput >/dev/null 2>&1") - if $? == 0 - cols = `tput cols 2>/dev/null` - else - cols = ENV["COLUMNS"] || ENV["TERM_WIDTH"] - end - begin - Integer(cols) - rescue ArgumentError, TypeError - 80 - end -end - if $0 == __FILE__ main end From 37999c4465c14a92be095fc3d8f490a7cd53baeb Mon Sep 17 00:00:00 2001 From: Takahiro Ueda Date: Sat, 28 Dec 2024 14:56:13 +0900 Subject: [PATCH 2/7] build: improve Windows compatibility - Append $(EXEEXT) to TEST_BINS. - Always enclose $SHELL in quotes to accommodate spaces. --- check/Makefile.am | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/check/Makefile.am b/check/Makefile.am index 5cec3eb3..6178d805 100644 --- a/check/Makefile.am +++ b/check/Makefile.am @@ -1,12 +1,12 @@ TEST_BINS = if BUILD_FORM -TEST_BINS += $(top_builddir)/sources/form +TEST_BINS += $(top_builddir)/sources/form$(EXEEXT) endif if BUILD_TFORM -TEST_BINS += $(top_builddir)/sources/tform +TEST_BINS += $(top_builddir)/sources/tform$(EXEEXT) endif if BUILD_PARFORM -TEST_BINS += $(top_builddir)/sources/parform +TEST_BINS += $(top_builddir)/sources/parform$(EXEEXT) endif TEST_OPTS = @@ -17,12 +17,12 @@ TESTS_ENVIRONMENT = \ RUBY="$(RUBY)" \ TEST_BINS="$(TEST_BINS)" \ TEST_OPTS="$(TEST_OPTS)" \ - $(SHELL) + "$(SHELL)" TESTS += check-help.sh else TESTS_ENVIRONMENT = \ TEST_BINS="$(TEST_BINS)" \ - $(SHELL) + "$(SHELL)" endif TESTS += benchmark-fu.sh From e4b4ef4ba4f20ece0bbea36db82974e3fbdf2e86 Mon Sep 17 00:00:00 2001 From: Takahiro Ueda Date: Sat, 28 Dec 2024 14:59:11 +0900 Subject: [PATCH 3/7] ci(deploy): publish Windows binaries to GitHub Releases - Now the workflow creates the Windows distribution that is included in the release. - Use "mingw-w64-x86_64-ruby" instead of "ruby". The former gives "mingw32" for RbConfig::CONFIG["host_os"] but the latter gives "cygwin". - Refactor preparation of distributions. --- .github/workflows/deploy.yml | 88 ++++++++++++++++-------------------- 1 file changed, 40 insertions(+), 48 deletions(-) diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml index 6fecc0ef..e0c8b1a8 100644 --- a/.github/workflows/deploy.yml +++ b/.github/workflows/deploy.yml @@ -89,7 +89,7 @@ jobs: mingw-w64-x86_64-gmp mingw-w64-x86_64-mpfr mingw-w64-x86_64-zlib - ruby + mingw-w64-x86_64-ruby - name: Download tarball uses: actions/download-artifact@v4 @@ -332,57 +332,34 @@ jobs: mv artifacts/src/*.tar.gz dist mv artifacts/doc-html/*.tar.gz dist mv artifacts/doc-pdf/*.pdf dist - if ls artifacts/*-ubuntu-*/*form >/dev/null 2>&1; then - pkgname=$distname-x86_64-linux - mkdir $pkgname - mv artifacts/*-ubuntu-*/*form $pkgname - chmod +x $pkgname/*form - tar -c $pkgname/* | gzip -c -9 >dist/$pkgname.tar.gz - rm -rf $pkgname - fi - if ls artifacts/*-macos-13/*form >/dev/null 2>&1; then - pkgname=$distname-x86_64-osx - mkdir $pkgname - mv artifacts/*-macos-13/*form $pkgname - chmod +x $pkgname/*form - tar -c $pkgname/* | gzip -c -9 >dist/$pkgname.tar.gz - rm -rf $pkgname - fi - if ls artifacts/*-macos-14/*form >/dev/null 2>&1; then - pkgname=$distname-arm64-osx - mkdir $pkgname - mv artifacts/*-macos-14/*form $pkgname - chmod +x $pkgname/*form - tar -c $pkgname/* | gzip -c -9 >dist/$pkgname.tar.gz - rm -rf $pkgname - fi - # Do not include Windows binaries into the distribution, for now. - # - # if ls artifacts/*-windows-*/*form.exe >/dev/null 2>&1; then - # pkgname=$distname-x86_64-windows - # mkdir $pkgname - # mv artifacts/*-windows-*/*form.exe $pkgname - # chmod +x $pkgname/*form.exe - # # Zip may be more popular than tar.gz for Windows(?) - # # tar -c $pkgname/* | gzip -c -9 >dist/$pkgname.tar.gz - # zip -9 dist/$pkgname.zip $pkgname/* - # rm -rf $pkgname - # fi + make_tar_gz() { + if ls artifacts/$2 >/dev/null 2>&1; then + pkgname=$distname-$1 + mkdir $pkgname + mv artifacts/$2 $pkgname + chmod +x $pkgname/*form* + tar -c $pkgname/* | gzip -c -9 >dist/$pkgname.tar.gz + rm -rf $pkgname + fi + } + make_zip() { + if ls artifacts/$2 >/dev/null 2>&1; then + pkgname=$distname-$1 + mkdir $pkgname + mv artifacts/$2 $pkgname + chmod +x $pkgname/*form* + zip -9 dist/$pkgname.zip $pkgname/* + rm -rf $pkgname + fi + } + make_tar_gz x86_64-linux '*-ubuntu-*/*form' + make_tar_gz x86_64-osx '*-macos-13/*form' + make_tar_gz arm64-osx '*-macos-14/*form' + make_zip x86_64-windows '*-windows-*/*form.exe' - name: Print distributions run: ls -l dist - # Publish the distributions to GitHub Releases, only if the commit has - # a versioning tag. - - name: Publish distributions - if: startsWith(github.ref, 'refs/tags/v') - uses: softprops/action-gh-release@v1 - with: - files: | - dist/*.tar.gz - dist/*.zip - dist/*.pdf - # Upload the distributions as an artifact, regardless of whether # the commit has a versioning tag. This makes checking and debugging easy. - name: Upload distributions as artifacts @@ -394,3 +371,18 @@ jobs: dist/*.zip dist/*.pdf if-no-files-found: error + + # To prevent the distribution of Windows binaries, uncomment the following lines. + # - name: Delete Windows binaries + # run: rm -fv dist/*windows* + + # Publish the distributions to GitHub Releases, only if the commit has + # a versioning tag. + - name: Publish distributions + if: startsWith(github.ref, 'refs/tags/v') + uses: softprops/action-gh-release@v1 + with: + files: | + dist/*.tar.gz + dist/*.zip + dist/*.pdf From 433a29c1598e43b0d802f94fad9acbbb8ca687d1 Mon Sep 17 00:00:00 2001 From: Takahiro Ueda Date: Mon, 29 Apr 2024 19:00:03 +0900 Subject: [PATCH 4/7] fix(windows): use binary mode for stdout on Windows On Windows, the standard output is opened with text mode. This patch sets the mode to binary mode, which partially resolves #418. --- sources/form3.h | 1 + sources/startup.c | 3 +++ 2 files changed, 4 insertions(+) diff --git a/sources/form3.h b/sources/form3.h index 52553b85..1f3f3926 100644 --- a/sources/form3.h +++ b/sources/form3.h @@ -195,6 +195,7 @@ #define WIN32_LEAN_AND_MEAN #include #include +#include /* Undefine/rename conflicted symbols. */ #undef VOID /* WinNT.h */ #undef MAXLONG /* WinNT.h */ diff --git a/sources/startup.c b/sources/startup.c index 8aafdf03..351f805d 100644 --- a/sources/startup.c +++ b/sources/startup.c @@ -1624,6 +1624,9 @@ int main(int argc, char **argv) #ifdef TRAPSIGNALS setSignalHandlers(); #endif +#ifdef WINDOWS + _setmode(_fileno(stdout),O_BINARY); +#endif #ifdef WITHPTHREADS AB = ABdummy; From 8002d6478e99afdd612472bd91ca7ab297a484cc Mon Sep 17 00:00:00 2001 From: Takahiro Ueda Date: Sat, 28 Dec 2024 15:37:15 +0900 Subject: [PATCH 5/7] test(windows): adapt test cases to Windows - "ExtComm_1" requires Unix. - "Sta_Fill_1" is pending on Windows. - "AppendPath" and "TempSortDir" are divided into Unix and Windows cases because off the different path separators. --- check/examples.frm | 2 ++ check/features.frm | 50 ++++++++++++++++++++++++++++++++++++++++++---- 2 files changed, 48 insertions(+), 4 deletions(-) diff --git a/check/examples.frm b/check/examples.frm index 6533690a..7aa9ee6e 100644 --- a/check/examples.frm +++ b/check/examples.frm @@ -654,6 +654,7 @@ assert result("G") =~ expr(" Local F = B(1); Print; .end +#pend_if windows? assert finished? assert warning? *--#] Sta_Fill_1 : @@ -1813,6 +1814,7 @@ Local aPLUSbTO3= Print; .end +#require unix? # This gives Valgrind errors (3 memory leaks) on Travis CI # (osx-gcc-valgrind-parvorm), but cleanly works on Linux with mpich 3.2. # Might be an OS- or implementation-specific bug. diff --git a/check/features.frm b/check/features.frm index 05665e7a..5a1ff3c8 100644 --- a/check/features.frm +++ b/check/features.frm @@ -220,7 +220,7 @@ assert result("F2") =~ expr("0") assert result("F3") =~ expr("0") assert result("F4") =~ expr("0") *--#] partitions_ : -*--#[ AppendPath : +*--#[ AppendPath_unix : #include foo/foo1.h * foo/bar/p1.prc #call p1 @@ -238,6 +238,36 @@ P; #call p1 P; .end +#require unix? +#prepare write "foo/foo1.h", "#prependpath bar\n" +#prepare write "foo/foo2.h", "#appendpath bar\n" +#prepare write "foo/bar/p1.prc", "#procedure p1()\nL F=1234;\n#endprocedure\n" +#prepare write "foo/bar/p2.prc", "#procedure p2()\nL G=5678;\n#endprocedure\n" +#prepare write "bar/p1.prc", "#procedure p1()\nL H=9012;\n#endprocedure\n" +assert succeeded? +assert result("F") =~ expr("1234") +assert result("G") =~ expr("5678") +assert result("H") =~ expr("9012") +*--#] AppendPath_unix : +*--#[ AppendPath_windows : +#include foo\foo1.h +* foo/bar/p1.prc +#call p1 +P; +.end +#:path foo;bar +#include foo1.h +* foo/bar/p2.prc +#call p2 +P; +.end +#:path foo;bar +#include foo2.h +* bar/p1.prc +#call p1 +P; +.end +#require windows? #prepare write "foo/foo1.h", "#prependpath bar\n" #prepare write "foo/foo2.h", "#appendpath bar\n" #prepare write "foo/bar/p1.prc", "#procedure p1()\nL F=1234;\n#endprocedure\n" @@ -247,7 +277,7 @@ assert succeeded? assert result("F") =~ expr("1234") assert result("G") =~ expr("5678") assert result("H") =~ expr("9012") -*--#] AppendPath : +*--#] AppendPath_windows : *--#[ TimeoutAfter_1 : #procedure problematicprocedure * Do nothing. @@ -1135,13 +1165,25 @@ Print +s; assert succeeded? assert result("F") =~ expr("0"); *--#] Sortrealloc_2 : -*--#[ TempSortDir : +*--#[ TempSortDir_unix : #: TempSortDir bad/path Local test = 1; .end +#require unix? if mpi? assert runtime_error?("Could not create sort file: bad/path/0formxxx.sor") else assert runtime_error?("Could not create sort file: bad/path/xformxxx.sor") end -*--#] TempSortDir : +*--#] TempSortDir_unix : +*--#[ TempSortDir_windows : +#: TempSortDir bad_path +Local test = 1; +.end +#require windows? +if mpi? + assert runtime_error?('Could not create sort file: bad_path\0formxxx.sor') +else + assert runtime_error?('Could not create sort file: bad_path\xformxxx.sor') +end +*--#] TempSortDir_windows : From 9161839ca06ea28dbb32f496add142f9e2db53f1 Mon Sep 17 00:00:00 2001 From: Takahiro Ueda Date: Sat, 28 Dec 2024 15:52:08 +0900 Subject: [PATCH 6/7] fix(tform): restrict sort file creation check to main thread Restrict the sort file creation check at startup to the main thread in TFORM. This change prevents unnecessary checks and potential race conditions caused by multiple threads. Close #602. --- sources/startup.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/sources/startup.c b/sources/startup.c index 351f805d..adc14d2e 100644 --- a/sources/startup.c +++ b/sources/startup.c @@ -804,6 +804,9 @@ classic:; /* Try to create the sort file already, so we can Terminate earlier if this fails. */ +#ifdef WITHPTHREADS + if ( par <= 1 ) { +#endif if ( ( AM.S0->file.handle = CreateFile((char *)AM.S0->file.name) ) < 0 ) { MesPrint("Could not create sort file: %s", AM.S0->file.name); Terminate(-1); @@ -812,6 +815,9 @@ classic:; CloseFile(AM.S0->file.handle); AM.S0->file.handle = -1; remove(AM.S0->file.name); +#ifdef WITHPTHREADS + } +#endif /* With the stage4 and scratch file names we have to be a bit more careful. They are to be allocated after the threads are initialized when there From d49daaaa149bf844c3a7b35d2e2c1cc23ed1d6c6 Mon Sep 17 00:00:00 2001 From: Takahiro Ueda Date: Sat, 28 Dec 2024 15:56:59 +0900 Subject: [PATCH 7/7] fix(tform): avoid segfault when TempSortDir is unavailable Avoid null pointer dereferences during early termination before sort buffer allocation. Close #605. --- sources/startup.c | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/sources/startup.c b/sources/startup.c index adc14d2e..58e6ade4 100644 --- a/sources/startup.c +++ b/sources/startup.c @@ -1727,6 +1727,9 @@ VOID CleanUp(WORD par) int i; if ( FG.fname ) { +#ifdef WITHPTHREADS + if ( B ) { +#endif CleanUpSort(0); for ( i = 0; i < 3; i++ ) { if ( AR.Fscr[i].handle >= 0 ) { @@ -1742,6 +1745,9 @@ VOID CleanUp(WORD par) AR.Fscr[i].POfill = 0; } } +#ifdef WITHPTHREADS + } +#endif if ( par > 0 ) { /* Close all input levels above the lowest? @@ -1759,12 +1765,18 @@ VOID CleanUp(WORD par) } } CloseFile(AC.StoreHandle); +#ifdef WITHPTHREADS + if ( B ) +#endif if ( par >= 0 || AR.StoreData.Handle < 0 || AM.ClearStore ) { remove(FG.fname); } dontremove:; #else CloseFile(AC.StoreHandle); +#ifdef WITHPTHREADS + if ( B ) +#endif if ( par >= 0 || AR.StoreData.Handle < 0 || AM.ClearStore > 0 ) { remove(FG.fname); }