## Copyright (C) 2009 Eric Chassande-Mottin, CNRS (France)
##
## This program is free software; you can redistribute it and/or modify
## it under the terms of the GNU General Public License as published by
## the Free Software Foundation; either version 3 of the License, or
## (at your option) any later version.
##
## This program is distributed in the hope that it will be useful,
## but WITHOUT ANY WARRANTY; without even the implied warranty of
## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
## GNU General Public License for more details.
##
## You should have received a copy of the GNU General Public License
## along with this program; if not, see
## <http://www.gnu.org/licenses/>.

## -*- texinfo -*-
## @deftypefn {Function File}  {[@var{a} @var{b} ...]=}textread(@var{filename},@var{format})
## @deftypefnx {Function File} {[@var{a} @var{b} ...] =}textread(@var{filename},@var{format},@var{prop},@var{value})
## Read data from a text file.
## The string @var{format} describes the different columns of the text file and
## It may continue the following specifiers:
## @table @code
## @item %s
## for a string,
##
## @item %d,%f
## for a double, floating-point or integer number and
##
## @item %*
## to ignore a column.
## @end table
##
## For example, the textfile containing
##
## @example
## @group
## Bunny Bugs   5.5
## Duck Daffy  -7.5e-5
## Penguin Tux   6
## @end group
## @end example
##
## can be read using
##
## @example
## @code{[a,b,c] = textread("test.txt", "%s %s %f").}
## @end example
##
## Currently implemented @var{prop} arguments are:
## @itemize
## @item "headerlines":
## @var{value} represents the number of header lines to skip.
## @item "commentstyle":
## @var{value} is the style and can be 
## @itemize
## @item "shell": comment specifier is #
## @item "c": comment specifier is /*
## @item "c++": comment specifier is //
## @item "matlab": comment specifier is %
## @end itemize
## @end itemize
##
## @seealso{load, dlmread, fscanf}
## @end deftypefn

function varargout = textread(varargin)

 if nargchk(2,inf,nargin)
   print_usage ();
 end

 file=varargin{1};
 formatstr=varargin{2};

 comment_flag=false;
 header_skip=0;
 if nargin>2
   n=3;
   while (n<=nargin)
     switch varargin{n}
       case "commentstyle"
	 comment_flag=true;
	 switch varargin{n+1}
           case "c"
             comment_specif="/*";
           case "c++"
             comment_specif="//";
           case "shell"
             comment_specif="#";
           case "matlab"
             comment_specif="%";
           otherwise
             error("textread: unknown comment style %s",val);
         endswitch
       case "headerlines"
	 header_skip=varargin{n+1};
       otherwise
	 print_usage ();	 
     endswitch
     n+=2;
   endwhile
 endif

 # open file
 fid = fopen(file,"r");

 # parse format string
 idx=strfind(formatstr,"%")';
 specif=formatstr([idx,idx+1]);
 nspecif=length(idx)
 idx_star=strfind(formatstr,"%*");
 nfields=length(idx)-length(idx_star);

 if (nargout!=nfields)
   error("textread: the number of output variables must match that of format specifiers");
 endif

 ## init buffer
 res=cell;

 ## read line
 header_count=1; line_count=1;
 while ~feof(fid)

   ## read line
   this = fgetl(fid);

   ## ignore line if empty
   if isempty(this)
     continue
   endif

   this=deblank(this);

   ## ignore line if it is a comment
   if comment_flag
     buffer=strjust(this,"left");
     if strcmp(buffer(1:length(comment_specif)),comment_specif)
       continue
     endif
   endif

   ## ignore line if it is a header
   if (header_count <= header_skip)
     header_count++;
     continue
   endif

   while ~isempty(this)

     m=1;
     while (m <= nspecif)

       ## read data
       data=sscanf(this,"%s",1);

       ## if no data
       if isempty(data)

         switch specif(m,:)
           case "%s"
             res{m,line_count}="";
           case {"%d", "%f"}
             res{m,line_count}=[];
           case "%*"
	     ## do nothing
         endswitch
	 m++;
         continue
       endif

       ## map to format
       switch specif(m,:)
         case "%s"
           res{m,line_count}=char(data);
         case {"%d", "%f"}
           res{m,line_count}=str2num(data);
	 case "%*"
	   ## do nothing
       endswitch

       ## suppress read data from buffer
       this=this(length(data)+2:end);

       m++;
     endwhile  ## m <= n
     line_count++;
   endwhile  ## ~isempty(this)

 endwhile ## ~feof(fid)

 ## if empty file
 if isempty(res)
   varargout{1:n}=cell;
   return
 endif

 ## map to output structures
 mm=1;
 for m=1:nspecif
   switch specif(m,:)
     case "%s"
       varargout{mm} = res (m, :); mm++;
     case {"%d", "%f"}
       varargout{mm} = [res{m,:}]; mm++;
     case "%*"
       continue
   endswitch
 endfor

 ## close file
 fclose(fid);
 
endfunction
