# HG changeset patch
# User Júlio Hoffimann <julio.hoffimann@gmail.com>
# Date 1362863886 10800
# Node ID 8e27cb7dc86f74b534119e87903db2aadefdb64b
# Parent  28be9be86240bedb763b2bf51c0f9c7b1757fa7b
textread.m, textscan.m: Organized the code for better error notification.

diff -r 28be9be86240 -r 8e27cb7dc86f scripts/io/textread.m
--- a/scripts/io/textread.m	Fri Mar 08 13:16:09 2013 -0500
+++ b/scripts/io/textread.m	Sat Mar 09 18:18:06 2013 -0300
@@ -1,4 +1,5 @@
 ## Copyright (C) 2009-2013 Eric Chassande-Mottin, CNRS (France)
+## Copyright (C) 2013 Júlio Hoffimann Mendes
 ##
 ## This file is part of Octave.
 ##
@@ -49,9 +50,10 @@
 
 function varargout = textread (filename, format = "%f", varargin)
 
-  BUFLENGTH = 4096;       # Read buffer to speed up processing @var{n}
+  ## The actual implementation is a simple wrapper for strread().
+  ## We'll properly set `format' and `varargin', and pass the file
+  ## contents as a big string.
 
-  ## Check input
   if (nargin < 1)
     print_usage ();
   endif
@@ -60,13 +62,18 @@
     error ("textread: FILENAME and FORMAT arguments must be strings");
   endif
 
+  ## We assume the first (non-header) line of the file is no longer
+  ## than BUFLENGTH so that we can determine the EOL character.
+  BUFLENGTH = 4096;
+
   if (! isempty (varargin) && isnumeric (varargin{1}))
-    nlines = varargin{1};
+    nlines = floor (varargin{1});
   else
     nlines = Inf;
   endif
+
   if (nlines < 1)
-    printf ("textread: N = 0, no data read\n");
+    printf ("textread: no lines to read\n");
     varargout = cell (1, nargout);
     return
   endif
@@ -79,41 +86,53 @@
 
   ## Skip header lines if requested
   headerlines = find (strcmpi (varargin, "headerlines"), 1);
-  if (! isempty (headerlines))
-    ## Beware of missing or wrong headerline value
-    if (headerlines  == numel (varargin)
-       || ! isnumeric (varargin{headerlines + 1}))
-      error ("missing or illegal value for 'headerlines'" );
+  if (headerlines && isindex (headerlines+1, numel (varargin)))
+    # we can read a value in that position
+    if (isnumeric (varargin{headerlines+1}))
+      varargin{headerlines+1} = floor (varargin{headerlines+1});
+      if (varargin{headerlines+1} > 0)
+        # remember we can skip the entire file (headerlines = Inf)
+        fskipl (fid, varargin{headerlines+1});
+        varargin(headerlines:headerlines+1) = [];
+        nargin = nargin-2;
+      else
+        warning ("textread: 'headerlines' value ignored");
+      endif
+    else
+      error ("textread: 'headerlines' must be a number");
     endif
-    ## Avoid conveying floats to fskipl
-    varargin{headerlines + 1} = round (varargin{headerlines + 1});
-    ## Beware of zero valued headerline, fskipl would skip to EOF
-    if (varargin{headerlines + 1} > 0)
-      fskipl (fid, varargin{headerlines + 1});
-      varargin(headerlines:headerlines+1) = [];
-      nargin = nargin - 2;
-    elseif (varargin{headerlines + 1} < 0)
-      warning ("textread: negative headerline value ignored");
+  else
+    # check if the user specified the keyword, but not the value
+    if (! isnull (headerlines) && headerlines+1 > numel (varargin))
+      error ("textread: missing value for 'headerlines'");
     endif
   endif
   st_pos = ftell (fid);
 
-  ## Read a first file chunk. Rest follows after endofline processing
+  ## Read the first file chunk. Rest follows after endofline processing
   [str, count] = fscanf (fid, "%c", BUFLENGTH);
   if (isempty (str) || count < 1)
-    warning ("textread: empty file");
+    # the file has no lines other than header lines
     varargout = cell (1, nargout);
     return;
   endif
 
   endofline = find (strcmpi (varargin, "endofline"), 1);
-  if (! isempty (endofline))
-    ## 'endofline' option set by user.
-    if (! ischar (varargin{endofline + 1}));
-      error ("character value required for EndOfLine");
+  if (endofline && isindex (endofline+1, numel (varargin)))
+    # we can read a value in that position
+    if (ischar (varargin{endofline+1}))
+      switch (varargin{endofline+1})
+        case {"\n","\r\n","\r"}
+          continue;
+        otherwise
+          error ("textread: invalid 'endofline'");
+      endswitch
+    else
+      error ("textread: 'endofline must be a char'");
     endif
   else
-    ## Determine EOL from file.  Search for EOL candidates in first BUFLENGTH chars
+    ## Either the user specified some crazy value or did not specified at all.
+    ## Ignore him and determine the correct EOL from the file chunk just read.
     eol_srch_len = min (length (str), BUFLENGTH);
     ## First try DOS (CRLF)
     if (! isempty (strfind ("\r\n", str(1 : eol_srch_len))))
@@ -125,13 +144,12 @@
     else
       eol_char = "\n";
     endif
-    ## Set up default endofline param value
     varargin(end+1:end+2) = {"endofline", eol_char};
   endif
- 
+
   ## Now that we know what EOL looks like, we can process format_repeat_count.
   ## FIXME The below isn't ML-compatible: counts lines, not format string uses
-  if (isfinite (nlines) && (nlines > 0))
+  if (isfinite (nlines))
     l_eol_char = length (eol_char);
     eoi = findstr (str, eol_char);
     n_eoi = length (eoi);
@@ -161,12 +179,13 @@
     fseek (fid, st_pos, "bof");
     str = fscanf (fid, "%c", eoi_pos);
   else
-    fseek (fid, st_pos, "bof");
-    str = fread (fid, "char=>char").';
+    fseek (fid, st_pos);
+    str = fread (fid, "*char").';
   endif
+
   fclose (fid);
- 
-  ## Set up default whitespace param value if needed
+
+  ## Set up default whitespace value if needed
   if (isempty (find (strcmpi ("whitespace", varargin))))
     varargin(end+1:end+2) = {"whitespace", " \b\t"};
   endif
@@ -200,6 +219,6 @@
 %!error textread (1)
 %!error <arguments must be strings> textread (1, "%f")
 %!error <arguments must be strings> textread ("fname", 1)
-%!error <missing or illegal value for> textread (file_in_loadpath ("textread.m"), "", "headerlines")
-%!error <missing or illegal value for> textread (file_in_loadpath ("textread.m"), "", "headerlines", 'hh')
-%!error <character value required for> textread (file_in_loadpath ("textread.m"), "%s", "endofline", true)
+%!error <missing value for> textread (file_in_loadpath ("textread.m"), "", "headerlines")
+%!error <must be a number> textread (file_in_loadpath ("textread.m"), "", "headerlines", 'hh')
+%!error <must be a char> textread (file_in_loadpath ("textread.m"), "%s", "endofline", true)
diff -r 28be9be86240 -r 8e27cb7dc86f scripts/io/textscan.m
--- a/scripts/io/textscan.m	Fri Mar 08 13:16:09 2013 -0500
+++ b/scripts/io/textscan.m	Sat Mar 09 18:18:06 2013 -0300
@@ -73,7 +73,7 @@
 function [C, position] = textscan (fid, format = "%f", varargin)
 
   BUFLENGTH = 4096;               ## Read buffer
-  
+
   ## Check input
   if (nargin < 1)
     print_usage ();
@@ -507,6 +507,6 @@
 %!   assert (strcmp (lh, rh));
 %! end
 
-%!error <missing or illegal value for> textread (file_in_loadpath ("textscan.m"), "", "headerlines")
-%!error <missing or illegal value for> textread (file_in_loadpath ("textscan.m"), "", "headerlines", 'hh')
-%!error <character value required for> textread (file_in_loadpath ("textscan.m"), "", "endofline", true)
+%!error <missing value for> textread (file_in_loadpath ("textscan.m"), "", "headerlines")
+%!error <must be a number> textread (file_in_loadpath ("textscan.m"), "", "headerlines", 'hh')
+%!error <must be a char> textread (file_in_loadpath ("textscan.m"), "", "endofline", true)
