exporting patch:
# HG changeset patch
# User Daniel J Sebald <daniel.sebald@ieee.org>
# Date 1536734101 18000
#      Wed Sep 12 01:35:01 2018 -0500
# Node ID a74c52eefde83e9e81f613a75ac47ca941729236
# Parent  11f63a33732eff5b1694b07284dfdd81b287a5b8
Allow processing standalone i/j/I/J in dlmread() routine (bug #54622)

* dlmread.cc (dlmread): Convert the I/J of the whole string hunk to lower case
  before looking for numbers.  Make use of an existing routine extract_num()
  originating from str2double function that skips white space, reads standalone
  imaginary symbols, etc.  Do the above for the imaginary portion of the
  possibly complex number if the first number was real portion, not pure
  imaginary.  Apply logic to make sense of valid (Re,Im) combinations, allowing
  blank space between and no imaginary symbol.  Change one of the BIST to test
  these scenarios.
  (IJtolower): Added.  Helper routine for use in std::transform().

* str2double.cc (extract_num): Move this function to lo-utils.cc.
  (is_image_unit): Ditto.
  (single_num): Ditto.

* lo-utils.cc (extract_num): Moved function from str2double.cc to here.
  (is_image_unit): Ditto.
  (single_num): Ditto.

* lo-utils.h (extract_num): Declaration added to the header file.

* importdata.m: Change a BIST file element from "Inf" to "Infj".

diff --git a/libinterp/corefcn/dlmread.cc b/libinterp/corefcn/dlmread.cc
--- a/libinterp/corefcn/dlmread.cc
+++ b/libinterp/corefcn/dlmread.cc
@@ -161,6 +161,8 @@ parse_range_spec (const octave_value& ra
   return stat;
 }
 
+int IJtolower (int i) { if (i == 'I') return 'i'; else if (i == 'J') return 'j'; else return i; }
+
 DEFMETHOD (dlmread, interp, args, ,
            doc: /* -*- texinfo -*-
 @deftypefn  {} {@var{data} =} dlmread (@var{file})
@@ -431,37 +433,65 @@ such as text, are also replaced by the @
                 rdata.resize (rmax, cmax, empty_value);
             }
 
-          tmp_stream.str (str);
+          // extract_num() does not handle upper case I or J
+          std::string enstr = str;
+          std::transform (enstr.begin(), enstr.end(), enstr.begin(), IJtolower);
+          tmp_stream.str (enstr);
           tmp_stream.clear ();
 
-          double x = octave_read_double (tmp_stream);
+          double x;
+          bool have_imag;
+          bool have_sign;
+          extract_num (tmp_stream, x, have_imag, have_sign);
+
           if (tmp_stream)
             {
-              if (tmp_stream.eof ())
+              if (have_imag)
                 {
-                  if (iscmplx)
-                    cdata(i,j++) = x;
+                  if (tmp_stream.eof ())
+                    {
+                      if (! iscmplx)
+                        {
+                          iscmplx = true;
+                          cdata = ComplexMatrix (rdata);
+                        }
+
+                      cdata(i,j++) = Complex (0, x);
+                    }
                   else
-                    rdata(i,j++) = x;
+                    {
+                      // Imaginary portion must be last, i.e., (Re,Im)
+                      j++;  // Leave data initialized to empty_value
+                    }
                 }
               else
                 {
-                  int next_char = tmp_stream.peek ();
-                  if (next_char == 'i' || next_char == 'j'
-                      || next_char == 'I' || next_char == 'J')
+                  if (tmp_stream.eof ())
                     {
-                      // Process pure imaginary numbers.
-                      tmp_stream.get ();
-                      next_char = tmp_stream.peek ();
-                      if (next_char == std::istringstream::traits_type::eof ())
+                      if (iscmplx)
+                        cdata(i,j++) = Complex (x, 0);
+                      else
+                        rdata(i,j++) = x;
+                    }
+                  else
+                    {
+                      double y;
+                      extract_num (tmp_stream, y, have_imag, have_sign);
+
+                      // Don't require a sign or imaginary symbol; just space OK
+
+                      if (tmp_stream)
                         {
-                          if (! iscmplx)
+                          if (! iscmplx && y != 0.)
                             {
                               iscmplx = true;
                               cdata = ComplexMatrix (rdata);
                             }
 
-                          cdata(i,j++) = Complex (0, x);
+                          if (iscmplx)
+                            cdata(i,j++) = Complex (x, y);
+                          else
+                            rdata(i,j++) = x;
                         }
                       else
                         {
@@ -469,31 +499,11 @@ such as text, are also replaced by the @
                           j++;  // Leave data initialized to empty_value
                         }
                     }
-                  else if (std::isalpha (next_char) && ! std::isfinite (x))
-                    {
-                      // Parsing failed, <Inf|NA|NaN><extra text>
-                      j++;  // Leave data initialized to empty_value
-                    }
-                  else
-                    {
-                      double y = octave_read_double (tmp_stream);
-
-                      if (! iscmplx && y != 0.)
-                        {
-                          iscmplx = true;
-                          cdata = ComplexMatrix (rdata);
-                        }
-
-                      if (iscmplx)
-                        cdata(i,j++) = Complex (x, y);
-                      else
-                        rdata(i,j++) = x;
-                    }
                 }
             }
           else
             {
-              // octave_read_double() parsing failed
+              // Parsing failed, <Inf|NA|NaN><extra text>
               j++;  // Leave data initialized to empty_value
             }
 
@@ -696,10 +706,10 @@ such as text, are also replaced by the @
 %! file = tempname ();
 %! unwind_protect
 %!   fid = fopen (file, "wt");
-%!   fwrite (fid, "1,2jack,3");
+%!   fwrite (fid, "1,2jack,3,J,NaN+I");
 %!   fclose (fid);
 %!
-%!   assert (dlmread (file), [1, 0, 3]);
+%!   assert (dlmread (file), [1, 0, 3, complex(0,1), complex(NaN,1)]);
 %! unwind_protect_cleanup
 %!   unlink (file);
 %! end_unwind_protect
diff --git a/libinterp/corefcn/str2double.cc b/libinterp/corefcn/str2double.cc
--- a/libinterp/corefcn/str2double.cc
+++ b/libinterp/corefcn/str2double.cc
@@ -38,206 +38,6 @@ along with Octave; see the file COPYING.
 #include "errwarn.h"
 #include "utils.h"
 
-static inline bool
-is_imag_unit (int c)
-{ return c == 'i' || c == 'j'; }
-
-static double
-single_num (std::istringstream& is)
-{
-  double num = 0.0;
-
-  char c = is.peek ();
-
-  // Skip spaces.
-  while (isspace (c))
-    {
-      is.get ();
-      c = is.peek ();
-    }
-
-  if (std::toupper (c) == 'I')
-    {
-      // It's infinity.
-      is.get ();
-      char c1 = is.get ();
-      char c2 = is.get ();
-      if (std::tolower (c1) == 'n' && std::tolower (c2) == 'f')
-        {
-          num = octave::numeric_limits<double>::Inf ();
-          is.peek (); // May set EOF bit.
-        }
-      else
-        is.setstate (std::ios::failbit); // indicate that read has failed.
-    }
-  else if (c == 'N')
-    {
-      // It's NA or NaN
-      is.get ();
-      char c1 = is.get ();
-      if (c1 == 'A')
-        {
-          num = octave_NA;
-          is.peek (); // May set EOF bit.
-        }
-      else
-        {
-          char c2 = is.get ();
-          if (c1 == 'a' && c2 == 'N')
-            {
-              num = octave::numeric_limits<double>::NaN ();
-              is.peek (); // May set EOF bit.
-            }
-          else
-            is.setstate (std::ios::failbit); // indicate that read has failed.
-        }
-    }
-  else
-    is >> num;
-
-  return num;
-}
-
-static std::istringstream&
-extract_num (std::istringstream& is, double& num, bool& imag, bool& have_sign)
-{
-  have_sign = imag = false;
-
-  char c = is.peek ();
-
-  // Skip leading spaces.
-  while (isspace (c))
-    {
-      is.get ();
-      c = is.peek ();
-    }
-
-  bool negative = false;
-
-  // Accept leading sign.
-  if (c == '+' || c == '-')
-    {
-      have_sign = true;
-      negative = c == '-';
-      is.get ();
-      c = is.peek ();
-    }
-
-  // Skip spaces after sign.
-  while (isspace (c))
-    {
-      is.get ();
-      c = is.peek ();
-    }
-
-  // Imaginary number (i*num or just i), or maybe 'inf'.
-  if (c == 'i')
-    {
-      // possible infinity.
-      is.get ();
-      c = is.peek ();
-
-      if (is.eof ())
-        {
-          // just 'i' and string is finished.  Return immediately.
-          imag = true;
-          num = (negative ? -1.0 : 1.0);
-          return is;
-        }
-      else
-        {
-          if (std::tolower (c) != 'n')
-            imag = true;
-          is.unget ();
-        }
-    }
-  else if (c == 'j')
-    imag = true;
-
-  // It's i*num or just i
-  if (imag)
-    {
-      is.get ();
-      c = is.peek ();
-      // Skip spaces after imaginary unit.
-      while (isspace (c))
-        {
-          is.get ();
-          c = is.peek ();
-        }
-
-      if (c == '*')
-        {
-          // Multiplier follows, we extract it as a number.
-          is.get ();
-          num = single_num (is);
-          if (is.good ())
-            c = is.peek ();
-        }
-      else
-        num = 1.0;
-    }
-  else
-    {
-      // It's num, num*i, or numi.
-      num = single_num (is);
-      if (is.good ())
-        {
-          c = is.peek ();
-
-          // Skip spaces after number.
-          while (isspace (c))
-            {
-              is.get ();
-              c = is.peek ();
-            }
-
-          if (c == '*')
-            {
-              is.get ();
-              c = is.peek ();
-
-              // Skip spaces after operator.
-              while (isspace (c))
-                {
-                  is.get ();
-                  c = is.peek ();
-                }
-
-              if (is_imag_unit (c))
-                {
-                  imag = true;
-                  is.get ();
-                  c = is.peek ();
-                }
-              else
-                is.setstate (std::ios::failbit); // indicate read has failed.
-            }
-          else if (is_imag_unit (c))
-            {
-              imag = true;
-              is.get ();
-              c = is.peek ();
-            }
-        }
-    }
-
-  if (is.good ())
-    {
-      // Skip trailing spaces.
-      while (isspace (c))
-        {
-          is.get ();
-          c = is.peek ();
-        }
-    }
-
-  if (negative)
-    num = -num;
-
-  return is;
-}
-
 static inline void
 set_component (Complex& c, double num, bool imag)
 {
diff --git a/liboctave/util/lo-utils.cc b/liboctave/util/lo-utils.cc
--- a/liboctave/util/lo-utils.cc
+++ b/liboctave/util/lo-utils.cc
@@ -440,3 +440,203 @@ octave_write_float_complex (std::ostream
   octave_write_float (os, imag (c));
   os << ')';
 }
+
+static inline bool
+is_imag_unit (int c)
+{ return c == 'i' || c == 'j'; }
+
+static double
+single_num (std::istringstream& is)
+{
+  double num = 0.0;
+
+  char c = is.peek ();
+
+  // Skip spaces.
+  while (isspace (c))
+    {
+      is.get ();
+      c = is.peek ();
+    }
+
+  if (std::toupper (c) == 'I')
+    {
+      // It's infinity.
+      is.get ();
+      char c1 = is.get ();
+      char c2 = is.get ();
+      if (std::tolower (c1) == 'n' && std::tolower (c2) == 'f')
+        {
+          num = octave::numeric_limits<double>::Inf ();
+          is.peek (); // May set EOF bit.
+        }
+      else
+        is.setstate (std::ios::failbit); // indicate that read has failed.
+    }
+  else if (c == 'N')
+    {
+      // It's NA or NaN
+      is.get ();
+      char c1 = is.get ();
+      if (c1 == 'A')
+        {
+          num = octave_NA;
+          is.peek (); // May set EOF bit.
+        }
+      else
+        {
+          char c2 = is.get ();
+          if (c1 == 'a' && c2 == 'N')
+            {
+              num = octave::numeric_limits<double>::NaN ();
+              is.peek (); // May set EOF bit.
+            }
+          else
+            is.setstate (std::ios::failbit); // indicate that read has failed.
+        }
+    }
+  else
+    is >> num;
+
+  return num;
+}
+
+std::istringstream&
+extract_num (std::istringstream& is, double& num, bool& imag, bool& have_sign)
+{
+  have_sign = imag = false;
+
+  char c = is.peek ();
+
+  // Skip leading spaces.
+  while (isspace (c))
+    {
+      is.get ();
+      c = is.peek ();
+    }
+
+  bool negative = false;
+
+  // Accept leading sign.
+  if (c == '+' || c == '-')
+    {
+      have_sign = true;
+      negative = c == '-';
+      is.get ();
+      c = is.peek ();
+    }
+
+  // Skip spaces after sign.
+  while (isspace (c))
+    {
+      is.get ();
+      c = is.peek ();
+    }
+
+  // Imaginary number (i*num or just i), or maybe 'inf'.
+  if (c == 'i')
+    {
+      // possible infinity.
+      is.get ();
+      c = is.peek ();
+
+      if (is.eof ())
+        {
+          // just 'i' and string is finished.  Return immediately.
+          imag = true;
+          num = (negative ? -1.0 : 1.0);
+          return is;
+        }
+      else
+        {
+          if (std::tolower (c) != 'n')
+            imag = true;
+          is.unget ();
+        }
+    }
+  else if (c == 'j')
+    imag = true;
+
+  // It's i*num or just i
+  if (imag)
+    {
+      is.get ();
+      c = is.peek ();
+      // Skip spaces after imaginary unit.
+      while (isspace (c))
+        {
+          is.get ();
+          c = is.peek ();
+        }
+
+      if (c == '*')
+        {
+          // Multiplier follows, we extract it as a number.
+          is.get ();
+          num = single_num (is);
+          if (is.good ())
+            c = is.peek ();
+        }
+      else
+        num = 1.0;
+    }
+  else
+    {
+      // It's num, num*i, or numi.
+      num = single_num (is);
+      if (is.good ())
+        {
+          c = is.peek ();
+
+          // Skip spaces after number.
+          while (isspace (c))
+            {
+              is.get ();
+              c = is.peek ();
+            }
+
+          if (c == '*')
+            {
+              is.get ();
+              c = is.peek ();
+
+              // Skip spaces after operator.
+              while (isspace (c))
+                {
+                  is.get ();
+                  c = is.peek ();
+                }
+
+              if (is_imag_unit (c))
+                {
+                  imag = true;
+                  is.get ();
+                  c = is.peek ();
+                }
+              else
+                is.setstate (std::ios::failbit); // indicate read has failed.
+            }
+          else if (is_imag_unit (c))
+            {
+              imag = true;
+              is.get ();
+              c = is.peek ();
+            }
+        }
+    }
+
+  if (is.good ())
+    {
+      // Skip trailing spaces.
+      while (isspace (c))
+        {
+          is.get ();
+          c = is.peek ();
+        }
+    }
+
+  if (negative)
+    num = -num;
+
+  return is;
+}
diff --git a/liboctave/util/lo-utils.h b/liboctave/util/lo-utils.h
--- a/liboctave/util/lo-utils.h
+++ b/liboctave/util/lo-utils.h
@@ -136,4 +136,7 @@ octave_write_float (std::ostream& os, fl
 extern OCTAVE_API void
 octave_write_float_complex (std::ostream& os, const FloatComplex& cval);
 
+extern OCTAVE_API std::istringstream&
+extract_num (std::istringstream& is, double& num, bool& imag, bool& have_sign);
+
 #endif
diff --git a/scripts/io/importdata.m b/scripts/io/importdata.m
--- a/scripts/io/importdata.m
+++ b/scripts/io/importdata.m
@@ -492,10 +492,10 @@ endfunction
 
 %!test
 %! ## Exceptional values (Inf, NaN, NA)
-%! A = [3.1 Inf NA; -Inf NaN 128];
+%! A = [3.1 complex(0,Inf) NA; -Inf NaN 128];
 %! fn  = tempname ();
 %! fid = fopen (fn, "w");
-%! fputs (fid, "3.1\tInf\tNA\n-Inf\tNaN\t128");
+%! fputs (fid, "3.1\tInfj\tNA\n-Inf\tNaN\t128");
 %! fclose (fid);
 %! [a,d,h] = importdata (fn, '\t');
 %! unlink (fn);
