--- octave/scripts/io/strread.m	2012-07-06 23:16:06.000000000 +0200
+++ strread.m	2012-07-06 23:11:30.000000000 +0200
@@ -54,6 +54,10 @@
 ## optional width can be specified as %Ns, etc. where N is an integer > 1.
 ## For %f, format specifiers like %N.Mf are allowed.
 ##
+## For %f, %u, and %d specifiers, a bit width can be specified and the
+## relating read numbers will be casted to that bit width: %f32 to single,
+## %f64 to double, %d8, %d16, %d32 and %d64 to int8, int16, int32, and int64,
+## respectively; same for %u8 ... %u64 (uint8 ... uint64).
 ## @item literals
 ## In addition the format may contain literal character strings; these will be
 ## skipped during reading.
@@ -298,16 +302,17 @@
     ## like %f<literal>) (w/o delimiter in between) are fixed further on
     format = strrep (format, "%", " %");
     fmt_words = regexp (format, '[^ ]+', "match");
-    
+
     ## Find position of conversion specifiers (they start with %)
     idy2 = find (! cellfun ("isempty", regexp (fmt_words, '^%')));
 
     ## Check for unsupported format specifiers
-    errpat = '(\[.*\]|[cq]|[nfdu]8|[nfdu]16|[nfdu]32|[nfdu]64)';
+    errpat = '(\[.*\]|[cq])';
     if (! all (cellfun ("isempty", regexp (fmt_words(idy2), errpat))))
-      error ("strread: %q, %c, %[] or bit width format specifiers are not supported yet.");
+      error ("strread: %q, %c and %[] format specifiers are not supported yet.");
     endif
 
+    allwptrn = '([nfdu]8|[nfdu]16|[nfdu]32|[nfdu]64)';
     ## Format conversion specifiers following literals w/o space/delim
     ## in between are separate now.  Separate those w trailing literals
     a = strfind (fmt_words(idy2), "%");
@@ -315,6 +320,11 @@
     for jj = 1:numel (a)
       ## From right to left to avoid losing track
       ii = numel (a) - jj + 1;
+      [re] = regexp (fmt_words{idy2(ii)}, allwptrn, "end");
+      if (!isempty (re))
+        ## Add bit width specifier to conv. specifier
+        b{ii}(1) = re;
+      endif
       if (! (length (fmt_words{idy2(ii)}) == b{ii}(1)))
         ## Split fmt_words(ii) into % conv specifier and trailing literal
         fmt_words(idy2(ii)+1 : end+1) = fmt_words(idy2(ii) : end);
@@ -383,7 +393,7 @@
     ## Add eol_char to delimiter collection
     delimiter_str = unique ([delimiter_str eol_char]);
     ## .. and remove it from whitespace collection
-    white_spaces = strrep (white_spaces, eol_char, '');
+    white_spaces = strrep (white_spaces, eol_char, "");
   endif
 
   pad_out = 0;
@@ -397,7 +407,7 @@
     ## Condense all repeated whitespace into one single space
     ## FIXME: this will also fold repeated whitespace in a char field
     rxp_wsp = sprintf ("[%s]+", white_spaces);
-    str = regexprep (str, rxp_wsp, ' ');
+    str = regexprep (str, rxp_wsp, " ");
     ## Remove possible leading space at string
     if (str(1) == 32)
        str = str(2:end);
@@ -432,6 +442,7 @@
   ## Replace TreatAsEmpty char sequences by empty strings
   if (! isempty (empty_str))
     for ii = 1:numel (empty_str)
+      ## "words" matching empty_str may still be bordered by literals
       idz = strncmp (empty_str{ii}, words, length (empty_str{ii}));
       words(idz) = {""};
     endfor
@@ -568,7 +579,7 @@
           if (idf(ii))
             s = strfind (words(icol, 1), fmt_words{ii});
             if (isempty (s{:}))
-              error ("strread: Literal '%s' not found in column %d", fmt_words{ii}, icol);
+              error ("strread: literal '%s' not found in column %d", fmt_words{ii}, icol);
             endif
             s = s{:}(1);
             e = s(1) + length (fmt_words{ii}) - 1;
@@ -583,7 +594,7 @@
             if (! idg(ii) && ! isempty (s) && s(1) == 1)
               ## Leading literal.  Assign literal to icol, paste rest in icol + 1
               ## Apply only to those cells that do have something beyond literal
-              jptr = find (cellfun("length", words(icol+1, jptr), ...
+              jptr = find (cellfun ("length", words(icol+1, jptr), ...
                             "UniformOutput", false) > e(1));
               words(icol+1, :) = {""};
               words(icol+1, jptr) = cellfun ...
@@ -685,7 +696,26 @@
           if (! isempty (regexp (fmt_words{m}, "%[du]")))
             ## Cast to integer
             ## FIXME: NaNs will be transformed into zeros
-            data = int32 (data);
+            switch fmt_words{m}
+              case '%d8'
+                data = int8 (data);
+              case '%d16'
+                  data = int16 (data);
+              case '%d64'
+                  data = int64 (data);
+              case '%u8'
+                data = uint8 (data);
+              case '%u16'
+                  data = uint16 (data);
+              case {'%u32', '%u'}
+                  data = uint32 (data);
+              case '%u64'
+                  data = uint64 (data);
+              otherwise
+                data = int32 (data);
+            endswitch
+          elseif (regexp (fmt_words{m}, '%f32'))
+            data = single (data);
           endif
           data(n) = numeric_fill_value;
           if (pad_out)
@@ -701,21 +731,52 @@
           switch fmt_words{m}(ew+1)
             case {"d", "u", "f", "n"}
               n = cellfun ("isempty", data);
-              ### FIXME - erroneously formatted data lead to NaN, not an error
-              ###         => ReturnOnError can't be implemented for numeric data
-              data = str2double (strtrunc (data, swidth));
-              data(n) = numeric_fill_value;
+              ## FIXME - erroneously formatted data lead to NaN, not an error
+              ##         => ReturnOnError can't be implemented for numeric data
+              data = str2double (data);
+              if (! isempty (regexp (fmt_words{m}, "f32")))
+                data = single (data);
+              endif
               if (pad_out)
                 data(end+1:num_lines) = numeric_fill_value;
               endif
-              if (numel (nfmt) > 1)
-                sprec = str2double (nfmt{2});
-                data = 10^-sprec * round (10^sprec * data);
-              elseif (! isempty (regexp (fmt_words{m}, "[du]")))
+              if (! isempty (regexp (fmt_words{m}, "[du]")))
                 ## Cast to integer
                 ## FIXME: NaNs will be transformed into zeros
-                data = int32 (data);
+                s = regexp (fmt_words{m}, "[du]", "start");
+                switch fmt_words{m}(s : end)
+                  case "d8"
+                    data = int8 (data);
+                  case "d16"
+                    data = int16 (data);
+                  case "d64"
+                    data = int64 (data);
+                  case "u8"
+                    data = uint8 (data);
+                  case "u16"
+                    data = uint16 (data);
+                  case {"u32", "u"}
+                    data = uint32 (data);
+                  case "u64"
+                    data = uint64 (data);
+                  otherwise
+                    data = int32 (data);
+                endswitch
+              else
+                if (! isempty (strfind (fmt_words{m}, "f32")))
+                  data = single (data);
+                  if (numel (nfmt) > 1)
+                    sprec = str2double (nfmt{2});
+                    data = 10^-sprec * single (round (10^sprec * data));
+                  endif
+                else
+                  if (numel (nfmt) > 1)
+                    sprec = str2double (nfmt{2});
+                    data = 10^-sprec * double (round (10^sprec * data));
+                  endif
+                endif
               endif
+              data(n) = numeric_fill_value;
               varargout{k} = data.';
               k++;
             case "s"
@@ -956,6 +1017,26 @@
 %! assert (isempty (b));
 %! assert (isempty (c));
 
+%% Bit width, fixed width conv. specifiers
+%!test
+%! str2 = "123456789012345 ";
+%! str2 = [str2 str2 str2 str2 str2 str2 str2 str2];
+%! str2 = [str2 " 123456789.01234 1234567890.1234 12345.678901234 12345.678901234"];
+%! pttrn = "%3u8%*s %5u16%*s %10u32%*s %15u64 %3d8%*s %5d16%*s %10d32%*s %15d64 %9f32%*s %14f64%*s %10.2f32%*s %12.2f64%*s";
+%! [a b c d e f g h i j k l] = strread (str2, pttrn, "delimiter", " ");
+%! assert (a, uint8 (123));
+%! assert (b, uint16 (12345));
+%! assert (c, uint32 (1234567890));
+%! assert (d, uint64 (123456789012345));
+%! assert (e, int8 (123));
+%! assert (f, int16 (12345));
+%! assert (g, int32 (1234567890));
+%! assert (h, int64 (123456789012345));
+%! assert (i, single (123456789), 1e-12);
+%! assert (j, double (1234567890.123), 1e-15);
+%! assert (k, single (12345.68), 1e-5);
+%! assert (l, double (12345.68), 1e-15);
+
 %% Unsupported format specifiers
 %!test
 %!error <format specifiers are not supported> strread ("a", "%c")
@@ -966,9 +1047,3 @@
 %!error <format specifiers are not supported> strread ("a", "%*[a] %d")
 %!error <format specifiers are not supported> strread ("a", "%[^a]")
 %!error <format specifiers are not supported> strread ("a", "%*[^a] %d")
-%!error <format specifiers are not supported> strread ("a", "%d8")
-%!error <format specifiers are not supported> strread ("a", "%*d8 %s")
-%!error <format specifiers are not supported> strread ("a", "%f64")
-%!error <format specifiers are not supported> strread ("a", "%*f64 %s")
-%!error <format specifiers are not supported> strread ("a", "%u32")
-%!error <format specifiers are not supported> strread ("a", "%*u32 %d")
