--- devel/octave/scripts/io/strread.m	2012-07-06 23:16:06.000000000 +0200
+++ strread.m	2012-07-09 21:22:28.000000000 +0200
@@ -47,12 +47,41 @@
 ## @itemx %u
 ## The word is parsed as a number and converted to int32.
 ##
-## @item %*', '%*f', '%*s
+## @item %q
+## Read a text string enclosed in double quotes. NOTE: this is experimental
+## and may not work as expected if the matching data text strings contain
+## whitespace and/or delimiter characters. Multiple consecutive whitespaces
+## will be folded into one space in the returned strings.
+##
+## @item %*d, %*n, %*u, %*f, %*s, %*q
 ## The word is skipped.
 ##
 ## For %s and %d, %f, %n, %u and the associated %*s @dots{} specifiers an
-## optional width can be specified as %Ns, etc. where N is an integer > 1.
-## For %f, format specifiers like %N.Mf are allowed.
+## optional character 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 %[ ]
+## Pattern matching: read only characters matching those enclosed between the
+## brackets til the first non-matching character or delimiter.
+## @item %[^ ]
+## Read only characters NOT matching those between %[^ and ] til the first
+## matching character.
+##
+## @item %*[ ], %*[^ ]
+## Skip matching or non-matching characters, respectively.
+##
+## If ']' should be part of the pattern it must be the first character of the
+## actual pattern after the starting [ or [^: %[] ] or %[^] ]
+##
+## NOTE: patterns containing whitespace may not work as expected due to
+## the way strread currently works. Patterns containing delimiters may not
+## work at all, especially if the number of enclosed delimiters differs from 
+## line to line. endofline (EOL) characters have a better chance to work.
 ##
 ## @item literals
 ## In addition the format may contain literal character strings; these will be
@@ -168,7 +197,7 @@
 ##
 ## @end table
 ##
-# @seealso{textscan, textread, load, dlmread, fscanf}
+## @seealso{textscan, textread, load, dlmread, fscanf}
 ## @end deftypefn
 
 function varargout = strread (str, format = "%f", varargin)
@@ -293,31 +322,93 @@
     ## Default format specified.  Expand it (to desired nargout)
     fmt_words = cell (nargout, 1);
     fmt_words (1:nargout) = format;
+    idw = zeros (1, numel (fmt_words));
   else
     ## Determine the number of words per line as a first guess.  Forms
     ## 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 = '^%\*?c';
     if (! all (cellfun ("isempty", regexp (fmt_words(idy2), errpat))))
-      error ("strread: %q, %c, %[] or bit width format specifiers are not supported yet.");
+      error ("strread: %c format specifiers are not supported. Try %1s instead");
     endif
 
+    ## Check for whitespace or delimiters in %[] style fmt_words.
+    ## After all, patterns like "%[] [%ab\ncdefg]" are allowed...
+    ii = 1; num_words_per_line = numel (fmt_words);
+    ## idw keeps track of which %[] fmt_words contain whitespace/delims
+    idw = zeros (1, num_words_per_line);
+    while ii <= num_words_per_line
+      if (! isempty (regexp (fmt_words{ii}, '%\*?\[')))
+        ## %[] style conv specifier. Find begin pos of actual pattern chars
+        ## 1. Check if pattern characters contains ']' 
+        es = regexp (fmt_words{ii}, '%(\*?\[\]|\*?\[\^\])', 'end');
+        if (isempty (es))
+          ## 2. No, assume pattern w/o ']'
+          es = regexp (fmt_words{ii}, '%(\*?\[|\*?\[\^)', 'end');
+        endif
+        ## Check if closing ']' of pattern is in fmt_words{ii}.
+        ## If fmt_words{i} ends after es we can skip searching ']'
+        no_close_brckt = (es == length (fmt_words{ii}));
+        if (no_close_brckt || isempty (strfind (fmt_words{ii}(es+1:end), ']')))
+          ## Apparently one or more spaces / delimiters in pattern as fmt_words
+          ## got split up there. Check next fmt_words where matching ] is
+          jj = ii + 1; mtch_ptrn = 0;
+          while jj <= num_words_per_line
+            ## Even "%", "*", "[", "^" and "]" (in that order) could be in pattern
+            if (! isempty (regexp (fmt_words{ii+1}, '%\*?\[\^')))
+              if (mtch_ptrn)
+                error ("strread - ambiguous format specifier # %d", ii);
+              else
+                mtch_ptrn = 1;
+              endif
+            endif
+            if (isempty (regexp (fmt_words{jj}, '\]')))
+              ## Found no matching ] yet, search next fmt_words
+              ++jj;
+            else 
+              ## Found the missing ]. Concatenate adjacent fmt_words
+              fmt_words{ii} = [fmt_words{ii:jj}];
+              fmt_words(ii+1:jj) = [];
+              num_words_per_line -= jj - ii;
+              ## For %[^..], the delim/whitesp behavior is inverted
+              idw(ii) = 1;
+              idw(ii+1:jj) = [];
+              jj = num_words_per_line + 1;
+            endif
+          endwhile
+        else
+          idw(ii) = ! isempty (regexp (fmt_words{ii}, '%\*?\[\^?'));
+        endif
+      endif
+      ++ii;
+    endwhile
+    ## Just renew idy2
+    idy2 = find (! cellfun ("isempty", regexp (fmt_words, '^%')));
+
+    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), "%");
-    b = regexp (fmt_words(idy2), '[nfdus]', "end");
+    b = regexp (fmt_words(idy2), '(\[.*\]|[nfdusq])', 'end');
     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);
+        idw(idy2(ii)+1 : end+1) = idw(idy2(ii) : end);
+        idw(idy2(ii)+1) = 0;
         fmt_words{idy2(ii)} = fmt_words{idy2(ii)}(a{ii} : b{ii}(1));
         fmt_words{idy2(ii)+1} = fmt_words{idy2(ii)+1}(b{ii}+1:end);
       endif
@@ -383,7 +474,26 @@
     ## 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
+
+  ## Check if pattern contains EOL (a special one as EOL can be used
+  ## to reading until EOL while skipping other delimiters
+  ideol = zeros (1, num_words_per_line);
+  if (! isempty (eol_char))
+    for ii=1:num_words_per_line
+      if (idw(ii))
+        ideol(ii) = ! isempty (strfind (fmt_words{ii}, eol_char));
+      endif
+    endfor
+  endif
+  if (sum(ideol))
+    ## Prepend char(253) to all EOL in str so some trace of EOL is left
+    ## FIXME this assumes char(253) doesn't occur in str
+    str = strrep (str, eol_char, [char(253) eol_char]);
+    ## Ensure last char was eol_char
+    if (str(end) != char(253)); str(end+1) = char(253); endif
+    fmt_words = strrep (fmt_words, eol_char, char(253));
   endif
 
   pad_out = 0;
@@ -397,7 +507,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 +542,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
@@ -440,7 +551,9 @@
   ## fmt_words has been split properly now, but words{} has only been split on
   ## delimiter positions. As numeric fields can also be separated by
   ## whitespace, more splits may be needed.
-  ## We also don't know the number of lines (as EndOfLine may have been set to
+  ## In addition, words may have been split within %[ ] matches if the matched
+  ## strings contain delimiters. These splits must be undone.
+  ## Also we don't know the number of lines (as EndOfLine may have been set to
   ## "" (empty) by the caller).
   ##
   ## We also may have to cope with 3 cases as far as literals go:
@@ -455,7 +568,13 @@
   idf = cellfun ("isempty", strfind (fmt_words, "%"));
   ## Find indices and pointers to conversion specifiers with fixed width
   idg = ! cellfun ("isempty", regexp (fmt_words, '%\*?\d'));
-  idy = find (idf | idg);
+  ## Find indices and pointers to regexp-like conversion specifiers
+  idr = ! cellfun ("isempty", regexp (fmt_words, '%\*?\[.*\]', 'end'));
+  ## Find %q (double-space) conversion specifiers
+  idq = ! cellfun ("isempty", regexp (fmt_words, '^%\*?q'));
+  
+  idy = find (idf | idg | idr | idq);
+
   ## Find indices to numeric conversion specifiers
   idn = ! cellfun ("isempty", regexp (fmt_words, '%[dnfu]'));
 
@@ -470,7 +589,7 @@
       ## and matching fmt_words to words until the fmt_words array is exhausted.
       ## iwrd: ptr to current analyzed word; iwrdp: ptr to pos before analyzed char
       iwrd = 1; iwrdp = 0; iwrdl = length (words{1});
-      fwptr = zeros (1, numel (fmt_words));
+      sttc = endc = fwptr = zeros (1, numel (fmt_words));
       ii = 1;
       while ii <= numel (fmt_words)
 
@@ -478,7 +597,89 @@
         ## Keep track of which words nr. every fmt_words{} is (starts) in.
         fwptr(ii) = iwrd;
 
-        if (idf(ii))
+        if (idr(ii))
+          ## Regexp-like specifier. Search current + next words until satisfied
+          ## sttc & endc keep track of columns to be concatenated later on
+          sttc(ii) = jj = iwrd;
+          ptrn = fmt_words{ii}(index (fmt_words{ii}, '['): end);
+          while jj <= numel (words)
+            [rs, re, ~, ~, ~, ~, nm] = regexp (words{jj}(iwrdp+1:end), ptrn);
+            if (isempty (rs))
+              ## Apparently an empty match; stop search & leave iwrdp untouched
+              iwrd = jj;
+              iwrdl = length (words{jj});
+              jj = numel(words) + 1;
+            else
+              if (! length ([nm{:}]) == 0)
+                ## Match before end of word
+                ipos = find (! cellfun ("isempty", nm)) - 1;
+                iwrdp += ipos(1);
+                ## Provision for EOL: in that case bump {words} ptr
+                if (words{jj}(iwrdp+1) == char(253)); nxt_wrd = 1; endif
+                iwrd = jj;
+                jj = numel (words) + 1;
+              else
+                ## Match includes at last char of words{jj}
+                if (idw(ii))
+                  ## Pattern contains whitespace/delims
+                  ## => next word may contain more matches. Continue there
+                  ++jj;
+                  if (jj <= numel (words))
+                    iwrdl = length (words{jj});
+                    iwrdp = 0;
+                  else
+                    iwrd = jj;
+                    nxt_wrd = 1;
+                  endif
+                else
+                  ## Match should end at delimiter
+                  iwrd = jj;
+                  jj = numel (words) + 1;
+                  nxt_wrd = 1;
+                endif
+              endif
+            endif
+          endwhile
+          endc(ii) = min (iwrd, numel (words));
+
+        elseif (idq(ii))
+          ## double quote specifier (%q). Check if matching words contain delims
+          ## sttc & endc keep track of columns to be concatenated later on
+          sttc(ii) = jj = iwrd;
+          ## Find position of first '"' (might occur after whitespace)
+          ## Consume '"' & set iwrdp before first expected matching '"' position
+          iwrdp += regexp (words{iwrd}(iwrdp+1:end), '[^ ]*"');
+          ## Find word containing next '"'
+          while jj <= numel (words)
+            ## Find position of matching double quote
+            if (iwrdp < length (words{jj}))
+              qtepos = strfind (words{jj}(iwrdp+1:end), '"');
+              if (! isempty (qtepos))
+                ## Found. First dbl quote of next %q string may also be here
+                iwrdp += qtepos(1);
+                iwrd = jj;
+                if (iwrdp == length (words{jj})); 
+                  nxt_wrd = 1; 
+                endif
+                jj = numel (words) + 1;
+              else
+                ## Matching '"' must be in a next word.
+                ++jj; iwrdp = 0;
+                if (jj <= numel (words))
+                  iwrdl = length (words{jj});
+                endif
+              endif
+            else
+              ## Matching '"' must be in a next word.
+              ++jj; iwrdp = 0;
+              if (jj <= numel (words))
+                iwrdl = length (words{jj});
+              endif
+            endif
+          endwhile
+          endc(ii) = min (iwrd, numel (words));
+
+        elseif (idf(ii))
           ## Literal expected
           if (isempty (strfind (fmt_words{ii}, words(iwrd))))
             ## Not found in current word; supposed to be in next word
@@ -501,7 +702,7 @@
         elseif (idg(ii))
           ## Fixed width specifier (%N or %*N): read just a part of word
           sw = regexp (fmt_words{ii}, '\d', "once");
-          ew = regexp (fmt_words{ii}, '[nfuds]') - 1;
+          ew = regexp (fmt_words{ii}, '[nfudsq]') - 1;
           iwrdp += floor (str2double (fmt_words{ii}(sw:ew)));
           if (iwrdp > iwrdl)
             ## Match error. Field extends beyond word boundary.
@@ -559,7 +760,41 @@
       endif
       words = reshape (words, words_period, num_lines);
 
-      ## 3. Do the column splitting on rectangular words array
+      ## 3. Concatenate columns contained within single %q or %[] specifiers
+      ## Proceed backward (right to left) to avoid mixing up pointers
+      icol = length (fmt_words);
+      while (icol > 0)
+        if (sttc(icol) > 0)
+          ## For this fmt specifier some columns must be combined
+          ncols = endc(icol) - sttc(icol) + 1;
+          if (ncols > 1)
+            ## Combine columns. Transpose as combining later on is columnwise
+            tmpc = words(sttc(icol):endc(icol), :);
+            ## For %q specifiers, insert space columns
+            if (idq(icol))
+              for ii=1:ncols-1
+                jj = ncols - ii;
+                tmpc = [tmpc(1:jj+1, :); tmpc(jj+1:end, :)];
+                tmpc(jj+1, :) = " ";
+              endfor
+            endif
+            mcols = size (tmpc, 1);
+            ## Add split to row below
+            ## FIXME: this assumes data doesn't contain char(255) 
+            tmpc(mcols+1, :) = char(255);
+            ## Assign combined columns to start column
+            words(sttc(icol), :) = strsplit ([tmpc{1:end-1}], char (255));
+            words(sttc(icol)+1:endc(icol), :) = [];
+            ## Update fmt_words pointers to words
+            if (icol < length (fmt_words))
+              fwptr(icol+1:end) = fwptr(icol+1:end) - ncols + 1;
+            endif
+          endif
+        endif
+        --icol;
+      endwhile
+
+      ## 4. Do the column splitting on rectangular words array
       icol = 1; ii = 1;    # icol = current column, ii = current fmt_word
       while (ii <= num_words_per_line)
 
@@ -568,7 +803,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 +818,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 ...
@@ -599,7 +834,7 @@
               elseif (idg(ii))
                 ## Current field = fixed width. Strip into icol, rest in icol+1
                 sw = regexp (fmt_words{ii}, '\d', "once");
-                ew = regexp (fmt_words{ii}, '[nfuds]') - 1;
+                ew = regexp (fmt_words{ii}, '[nfudsq]') - 1;
                 wdth = floor (str2double (fmt_words{ii}(sw:ew)));
                 words(icol+1, jptr) = cellfun (@(x) x(wdth+1:end),
                      words(icol,jptr), "UniformOutput", false);
@@ -632,6 +867,55 @@
             endif
           endif
 
+        elseif (idr(ii) && ii < num_words_per_line)
+          ## Leading %[..] type specifier. Split into two columns. This split
+          ## is simply unavoidable as we don't know what is left to follow %[]
+          ## 1. Amend pattern
+          ptrn = ['^' fmt_words{ii}(index (fmt_words{ii}, '['): end) '*'];
+          ## 2. Extract leading column and remove optional trailing char(253)
+          tmpwrds = words(icol, :);
+          words(icol, :) = regexp (words(icol, :), ptrn, 'match', 'once');
+          if (words{icol, 1}(end) == char(253))
+            words(icol, :) = strrep (words(icol, :), char(253), '');
+          endif
+          ## 3. Extract trailing column
+          tmpwrds = regexprep (tmpwrds, ptrn, '');
+          ## 4. This split may not have been needed; delete temp column if empty
+          if (isempty ([tmpwrds{:}]))
+            clear tmpwrds;
+          else
+            ## 5. Insert a column and add tmpwrds after present column 
+            words(icol+2:end+1, :) = words(icol+1:end, :);
+            words(icol+1, :) = tmpwrds;
+            fwptr = [fwptr(1:ii) (++fwptr(ii+1:end))];
+          endif
+
+        elseif (idr(ii))
+          ## Just check for trailing char(253) and if present remove it
+          if (words{icol, 1}(end) == char(253))
+            words(icol, :) = strrep (words(icol, :), char(253), '');
+          endif
+
+        elseif (idq(ii))
+          ## In all cases remove left '"' 
+          words(icol, :) = regexprep (words(icol, :), '^"', "");
+          if  (ii < num_words_per_line)
+            ## Check if a column to the right must be split off
+            if (fwptr(icol) == fwptr(icol+1))
+              ## Replace only first occurrence of '"', next %q may be in same col
+              words(icol, :) = regexprep (words(icol, :), '"', char(255), "once");
+              words(icol+1:end+1, :) = words(icol:end, :);
+              ptrn = ["^.*" char(255)];
+              ## Right column
+              words(icol+1, :) = regexprep (words(icol+1, :), ptrn, "", "once");
+              ## Left column
+              words(icol, :) = regexprep (words(icol, :), [char(255) ".*$"], "");
+              fwptr = [fwptr(1:ii) (++fwptr(ii+1:end))];
+            endif
+          endif
+          ## Replace matching occurrence of '"'
+          words(icol, :) = regexprep (words(icol, :), '"', "", "once");
+
         else
           ## Conv. specifier.  Peek if next fmt_word needs split from current column
           if (ii < num_words_per_line)
@@ -672,7 +956,8 @@
       ## FIXME - add support for formats like "<%s>", "%[a-zA-Z]"
       ##         Someone with regexp experience is needed.
       switch fmt_words{m}(1:min (2, length (fmt_words{m})))
-        case "%s"
+        case {"%s", "%[", "%q"}
+          ## for %[] & %q, column has been singled out above; return as string
           if (pad_out)
             data(end+1:num_lines) = {""};
           endif
@@ -685,7 +970,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 +1005,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,19 +1291,109 @@
 %! 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);
+
+%% %[] tests
+%% Plain [..] and *[..]
+%!test
+%! ar = "abcdefguvwxAny\nacegxyzTrailing\nJunk";
+%! [a, b] = strread (ar, "%[abcdefg] %*[uvwxyz] %s");
+%! assert (a, {"abcdefg"; "aceg"; ""});
+%! assert (b, {"Any"; "Trailing"; "Junk"});
+
+%!test
+%! assert (strread ("A2 B2 C3", "%*[ABC]%d", 3), int32([2; 2; 3]));
+
+%% [^..] and *[^..]
+%!test
+%! br = "abcdefguvwx1Any\nacegxyz2Trailing\n3Junk";
+%! [a, b] = strread (br, "%[abcdefg] %*[^0123456789] %s");
+%! assert (a, {"abcdefg"; "aceg"; ""});
+%! assert (b, {"1Any"; "2Trailing"; "3Junk"});
+
+%% [..] and [^..] crossing delimiters
+%!test
+%! cr = "ab cd efguv wx1Any\na ce gx yz2Trailing\n   3Junk";
+%! [a, b] = strread (cr, "%[ abcdefg] %*[^0123456789] %s", "delimiter", " \n", "whitespace", "");
+%! assert (a, {"abcdefg"; "aceg"; ""});
+%! assert (b, {"1Any"; "2Trailing"; "3Junk"});
+
+%% Bug #36464
+%!test
+%! assert (strread ('1 2 3 4 5 6', "%*n%n%*[^\n]"), 2);
+
+%% test %[]] and %[^]]
+%!test
+%! assert (strread ('345]', "%*[123456]%[]]"){1}, "]");
+%! assert (strread ('345]', "%*[^]]%s"){1}, "]");
+
+%% Textscan example
+%!test
+%! st4 = {"1st string  123.45 MoreText 1953-05-14 60 82+i15 abcdefg", "\n";
+%!        "2nd string  678.90 LessText 1961-02-01 52 53-i03 aceg",    "\n";
+%!        "3rd string  246.08 SameText 1973-12-13 40 12+i66 bdfh",    "\n";
+%!        "4th string  135.79 NoText 1982-09-27 29 13+i81 iiiiii",    "\n"};
+%! C = textscan ([st4'{:}], "%s %*[^\n]");
+%! D = textscan ([st4'{:}], "%s string %n %*[^\n]");
+%! E = textscan ([st4'{:}], "%s string %n %[^T]Text %*[^\n]");
+%! assert (C{1}, {"1st"; "2nd"; "3rd"; "4th"});
+%! assert (D{1}, {"1st"; "2nd"; "3rd"; "4th"});
+%! assert (D{2}, [123.45; 678.90; 246.08; 135.79], 1e-10);
+%! assert (E{1}, {"1st"; "2nd"; "3rd"; "4th"});
+%! assert (E{2}, [123.45; 678.90; 246.08; 135.79], 1e-10);
+%! assert (E{3}, {"More"; "Less"; "Same"; "No"});
+
+%% %q test, simple case
+%! str = 'aaa "Notalongtext" bbb "AnotherShortText"';
+%! [a, b] = strread (str, "%s %q");
+%! assert (a, {"aaa"; "bbb"});
+%! assert (b, {"Notalongtext"; "AnotherShortText"});
+
+%% %q, with consecutive white space and delims between double quotes
+%!test
+%! str = ['25 "ab' "\t" 'cd" 34.2' "\n" '8 "dggs\\ ' "\n" 'fdf" 101.3'];
+%! [a, b, c]  = strread (str, "%d %q %f");
+%! assert (a, int32([25; 8]));
+%! assert (b, {"ab cd"; 'dggs\\ fdf'});   ## Single quotes, double backslash 
+%! assert (c, [34.2; 101.3], 1e-15);
+
+%% %q test, consecutive %q words
+%!test
+%! str = '" a a""bb" " c c""dd"';
+%! [a, b] = strread (str, "%q%q");
+%! assert (a, {" a a"; " c c"});
+%! assert (b, {"bb"; "dd"});
+
+%% %q with delimiters (whitespace) contained within column w immediately adjacent col.
+%!test
+%! str =          '1 Harry "17  Station St, Milw"02/05/1973';
+%! str = [str "\n" '2 Doug "1A  Through Rd, Merl"22/08/1981' "\n"];
+%! str = [str "\n" '3 John "581 Main St,    Lard"03/01/1979' "\n"];
+%! [a, b, c, d] = strread (str, "%d %s %q%s");
+%! assert (a', int32([1 2 3]));
+%1 assert (b, {"Harry"; "Doug"; "John"});
+%! assert (c, {"17 Station St, Milw"; "1A Through Rd, Merl"; "581 Main St, Lard"});
+%! assert (d, {"02/05/1973"; "22/08/1981"; "03/01/1979"});
+
 %% Unsupported format specifiers
 %!test
 %!error <format specifiers are not supported> strread ("a", "%c")
 %!error <format specifiers are not supported> strread ("a", "%*c %d")
-%!error <format specifiers are not supported> strread ("a", "%q")
-%!error <format specifiers are not supported> strread ("a", "%*q %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", "%[^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")
