changeset 17370:8a930cffa978

Merged with main
author Andrej Lojdl <andrej.lojdl@gmail.com>
date Mon, 02 Sep 2013 00:06:21 +0200
parents 0e14b25c5f0f (current diff) 95412dcfa707 (diff)
children dc103ce7c8cf
files libinterp/corefcn/module.mk libinterp/corefcn/txt-eng-ft.cc
diffstat 12 files changed, 258 insertions(+), 240 deletions(-) [+]
line wrap: on
line diff
--- a/bootstrap.conf
+++ b/bootstrap.conf
@@ -49,6 +49,7 @@
   link
   lstat
   malloc-gnu
+  mbrtowc
   mkdir
   mkfifo
   mkostemp
--- a/libgui/src/resource-manager.cc
+++ b/libgui/src/resource-manager.cc
@@ -308,7 +308,6 @@
       "__display_tokens__ "
       "__dsearchn__ "
       "__dump_symtab_info__ "
-      "__end__ "
       "__error_text__ "
       "__finish__ "
       "__fltk_ginput__ "
--- a/libinterp/corefcn/module.mk
+++ b/libinterp/corefcn/module.mk
@@ -305,11 +305,11 @@
 corefcn/oct-tex-lexer.cc: LEX_OUTPUT_ROOT := lex.octave_tex_
 corefcn/oct-tex-parser.h: corefcn/oct-tex-parser.yy
 
-corefcn/oct-tex-lexer.ll: corefcn/oct-tex-lexer.in.ll corefcn/oct-tex-symbols.in Makefile
+corefcn/oct-tex-lexer.ll: corefcn/oct-tex-lexer.in.ll corefcn/oct-tex-symbols.in Makefile.am
 	$(AWK) 'BEGIN { print "/* DO NOT EDIT. AUTOMATICALLY GENERATED FROM oct-tex-lexer.in.ll and oct-tex-symbols.in. */"; } /^@SYMBOL_RULES@$$/ { count = 0; while (getline < "$(srcdir)/corefcn/oct-tex-symbols.in") { if ($$0 !~ /^#.*/ && NF == 3) { printf("\"\\\\%s\" { yylval->sym = %d; return SYM; }\n", $$1, count); count++; } } getline } ! /^@SYMBOL_RULES@$$/ { print }' $< > $@-t
 	mv $@-t $@
 
-corefcn/oct-tex-symbols.cc: corefcn/oct-tex-symbols.in Makefile
+corefcn/oct-tex-symbols.cc: corefcn/oct-tex-symbols.in Makefile.am
 	$(AWK) 'BEGIN { print "// DO NOT EDIT. AUTOMATICALLY GENERATED FROM oct-tex-symbols.in."; print "static uint32_t symbol_codes[][2] = {"; count = 0; } END { print "};"; printf("static int num_symbol_codes = %d;\n", count); } /^#/ { } { if (NF == 3) { printf("  { %s, %s },\n", $$2, $$3); count++; } }' $< > $@-t
 	mv $@-t $@
 
--- a/libinterp/corefcn/txt-eng-ft.cc
+++ b/libinterp/corefcn/txt-eng-ft.cc
@@ -30,6 +30,8 @@
 #include <fontconfig/fontconfig.h>
 #endif
 
+#include <clocale>
+#include <cwchar>
 #include <iostream>
 #include <map>
 #include <utility>
@@ -629,18 +631,39 @@
 {
   if (font.is_valid ())
     {
-      std::string str = e.string_value ();
       FT_UInt glyph_index, previous = 0;
 
-      for (size_t i = 0; i < str.length (); i++)
+      std::string str = e.string_value ();
+      size_t n = str.length (), curr = 0;
+      mbstate_t ps = { 0 };
+      wchar_t wc;
+
+      while (n > 0)
         {
-          glyph_index = process_character (static_cast<unsigned char> (str[i]),
-                                           previous);
+          size_t r = gnulib::mbrtowc (&wc, str.data () + curr, n, &ps);
+
+          if (r > 0
+              && r != static_cast<size_t> (-1)
+              && r != static_cast<size_t> (-2))
+            {
+              n -= r;
+              curr += r;
 
-          if (str[i] == '\n')
-            previous = 0;
+              glyph_index = process_character (wc, previous);
+
+              if (wc == L'\n')
+                previous = 0;
+              else
+                previous = glyph_index;
+            }
           else
-            previous = glyph_index;
+            {
+              if (r != 0)
+                ::warning ("ft_render: failed to decode string `%s' with "
+                           "locale `%s'", str.c_str (),
+                           std::setlocale (LC_CTYPE, NULL));
+              break;
+            }
         }
     }
 }
--- a/libinterp/corefcn/variables.cc
+++ b/libinterp/corefcn/variables.cc
@@ -401,6 +401,8 @@
       struct_elts = name.substr (pos+1);
       symbol_name = name.substr (0, pos);
     }
+  else if (is_keyword (symbol_name))
+    return retval;
 
   // We shouldn't need to look in the global symbol table, since any
   // name that is visible in the current scope will be in the local
--- a/libinterp/parse-tree/lex.ll
+++ b/libinterp/parse-tree/lex.ll
@@ -2723,11 +2723,7 @@
       return kw_token;
     }
 
-  // Find the token in the symbol table.  Beware the magic
-  // transformation of the end keyword...
-
-  if (tok == "end")
-    tok = "__end__";
+  // Find the token in the symbol table.
 
   symbol_table::scope_id sid = symtab_context.curr_scope ();
 
@@ -2754,7 +2750,9 @@
 
   current_input_column += flex_yyleng ();
 
-  if (tok != "__end__")
+  // The magic end index can't be indexed.
+
+  if (tok != "end")
     looking_for_object_index = true;
 
   at_beginning_of_statement = false;
--- a/libinterp/parse-tree/pt-arg-list.cc
+++ b/libinterp/parse-tree/pt-arg-list.cc
@@ -126,7 +126,7 @@
 static int index_position = 0;
 static int num_indices = 0;
 
-DEFCONSTFUN (__end__, , ,
+DEFCONSTFUN (end, , ,
   "internal function")
 {
   octave_value retval;
--- a/libinterp/parse-tree/pt-id.h
+++ b/libinterp/parse-tree/pt-id.h
@@ -55,7 +55,7 @@
 
   ~tree_identifier (void) { }
 
-  bool has_magic_end (void) const { return (name () == "__end__"); }
+  bool has_magic_end (void) const { return (name () == "end"); }
 
   bool is_identifier (void) const { return true; }
 
--- a/libinterp/parse-tree/pt-idx.cc
+++ b/libinterp/parse-tree/pt-idx.cc
@@ -360,7 +360,7 @@
                   // contains the second (or third, etc.) "end" token,
                   // so we must evaluate everything up to the point of
                   // that argument list so we can pass the appropriate
-                  // value to the built-in __end__ function.
+                  // value to the built-in end function.
 
                   const octave_value_list tmp_list
                     = tmp.subsref (type.substr (tmpi, i - tmpi), idx, nargout);
--- a/scripts/image/imread.m
+++ b/scripts/image/imread.m
@@ -47,7 +47,7 @@
 ## independent if @var{map} is a requested output.  To obtain the actual
 ## RGB image, use @code{ind2rgb}.  When more than one indexed image is being
 ## read, @var{map} is obtained from the first.  In some rare cases this
-## may be incorrect and @code{imfinfo] can be used to obtain the colormap of
+## may be incorrect and @code{imfinfo} can be used to obtain the colormap of
 ## each image.
 ##
 ## See the Octave manual for more information in representing images.
--- a/scripts/io/importdata.m
+++ b/scripts/io/importdata.m
@@ -22,22 +22,21 @@
 ## @deftypefnx {Function File} {@var{A} =} importdata (@var{fname}, @var{delimiter}, @var{header_rows})
 ## @deftypefnx {Function File} {[@var{A}, @var{delimiter}] =} importdata (@dots{})
 ## @deftypefnx {Function File} {[@var{A}, @var{delimiter}, @var{header_rows}] =} importdata (@dots{})
-## Importing data from file.
-##
-## Importing the contents of file @var{fname} into workspace.
+## Import data from the file @var{fname}.
 ##
 ## Input parameters:
 ##
 ## @itemize
 ## @item @var{fname}
-## The file name for the file to import.
+## The name of the file containing data.
 ## 
 ## @item @var{delimiter}
 ## The character separating columns of data.  Use @code{\t} for tab.
-## (Only valid for ascii files)
+## (Only valid for ASCII files)
 ##
 ## @item @var{header_rows}
-## Number of header rows before the data begins.  (Only valid for ascii files)
+## The number of header rows before the data begins.  (Only valid for ASCII
+## files)
 ## @end itemize
 ##
 ## Different file types are supported:
@@ -45,7 +44,7 @@
 ## @itemize
 ## @item ASCII table
 ##
-## Importing ASCII table using the specified number of header rows and
+## Import ASCII table using the specified number of header rows and
 ## the specified delimiter.
 ##
 ## @item Image file
@@ -63,79 +62,57 @@
 
 ## Author: Erik Kjellson <erikiiofph7@users.sourceforge.net>
 
-function [output, delimiter, header_rows] = importdata (varargin)
+function [output, delimiter, header_rows] = importdata (fname, delimiter = "", header_rows = -1)
 
-  ## Default values
-  fname   = "";
-  delimiter  = "";
-  header_rows = -1;
-
-  ##########
-
-  ## Check input arguments
-
-  if (nargin < 1)
+  if (nargin < 1 || nargin > 3)
     print_usage ();
   endif
 
-  fname = varargin{1};
-  ## Check that the file name really is a string
   if (! ischar (fname))
-    error ("importdata: file name needs to be a string");
-  endif
-  if ( strcmpi (fname, "-pastespecial"))
+    error ("importdata: FNAME must be a string");
+  elseif (strcmpi (fname, "-pastespecial"))
     error ("importdata: option -pastespecial not implemented");
   endif
 
   if (nargin > 1)
-    delimiter = varargin{2};
-    ## Check that the delimiter really is a string
-    if (!ischar (delimiter))
-      error("importdata: delimiter needs to be a character");
+    if (! ischar (delimiter)
+        || (length (delimiter) > 1 && ! strcmp (delimiter, '\t')))
+      error("importdata: DELIMITER must be a single character");
     endif
-    if (length (delimiter) > 1 && !strcmpi (delimiter, "\\t"))
-      error("importdata: delimiter cannot be longer than 1 character");
-    endif
-    if (strcmpi (delimiter, "\\"))
-      delimiter = "\\\\";
+    if (strcmp (delimiter, '\t'))
+      delimiter = "\t";
     endif
   endif
 
   if (nargin > 2)
-    header_rows = varargin{3};
-    if (!isnumeric (header_rows) || header_rows < 0)
-      error ("importdata: number of header rows needs to be an integer number >= 0");
+    if (! isnumeric (header_rows) || header_rows < 0
+        || header_rows != fix (header_rows))
+      error ("importdata: HEADER_ROWS must be an integer >= 0");
     endif
   endif
 
-  if (nargin > 3)
-    error ("importdata: too many input arguments");
-  endif
-
-  ##########
-
   ## Check file format
   ## Get the extension from the file name.
-  [d n fileExt v] = fileparts (fname);
-  ## Make sure file extension is in lower case.
-  fileExt = lower (fileExt);
+  [~, ~, ext, ~] = fileparts (fname);
+  ext = lower (ext);
 
-  switch (fileExt)
-    case {".au", ".snd"}
-      error ("importdata: not implemented for file format %s", fileExt);
-    case ".avi"
-      error ("importdata: not implemented for file format %s", fileExt);
+  switch (ext)
+    case {".au", ".snd", ".flac", ".ogg"}
+      error ("importdata: not implemented for file format %s", ext);
+    case {".avi", ".mj2", ".mpg", ".asf", ".asx", ".wmv", ".mp4", ".m4v", ...
+          ".mov"} 
+      error ("importdata: not implemented for file format %s", ext);
     case {".bmp", ".cur", ".gif", ".hdf", ".ico", ".jpe", ".jpeg", ".jpg", ...
-          ".pbm", ".pcx", ".pgm", ".png", ".pnm", ".ppm", ".ras", ...
-          ".tif", ".tiff", ".xwd"}
-      delimiter  = NaN;
+          ".jp2", ".jpf", ".jpx", ".j2c", ".j2k", ".pbm", ".pcx", ".pgm", ...
+          ".png", ".pnm", ".ppm", ".ras", ".tif", ".tiff", ".xwd"}
+      delimiter = NaN;
       header_rows = 0;
       [output.cdata, output.colormap, output.alpha] = imread (fname);
     case ".mat"
-      delimiter  = NaN;
+      delimiter = NaN;
       header_rows = 0;
       output = load (fname);
-    case {".wk1", ".xls", ".xlsx", ".dbf", ".pxl"}
+    case {".xls", ".xlsx", ".wk1", ".dbf", ".pxl"}
       ## If there's no Excel file support simply fall back to unimplemented.m
       output = xlsread (fname);
     case {".ods", ".sxc", ".fods", ".uos", ".xml"}
@@ -147,168 +124,147 @@
         output = xlsread (fname);
       end_try_catch
     case {".wav", ".wave"}
-      delimiter  = NaN;
+      delimiter = NaN;
       header_rows = 0;
       [output.data, output.fs] = wavread (fname);
     otherwise
-      ## Assume the file is in ascii format.
+      ## Assume the file is in ASCII format.
       [output, delimiter, header_rows]  = ...
           importdata_ascii (fname, delimiter, header_rows);
   endswitch
 
   ## If there are any empty fields in the output structure, then remove them
-  if (isstruct (output) && length (output) == 1)
+  if (isstruct (output) && numel (output) == 1)
     fields = fieldnames (output);
     for i=1:length (fields)
-      if (isempty (getfield (output, fields{i})))
+      if (isempty (output.(fields{i})))
         output = rmfield (output, fields{i});
       endif
     endfor
 
     ## If only one field is left, replace the structure with the field,
-    ## i.e. output = output.onlyFieldLeft
+    ## i.e., output = output.onlyFieldLeft
 
     ## Update the list of fields
     fields = fieldnames (output);
-    if (length (fields) == 1)
-      output = getfield (output, fields{1});
+    if (numel (fields) == 1)
+      output = output.(fields{1});
     endif
   endif
-endfunction
-
-
-########################################
-
-function [output, delimiter, header_rows] = ...
-      importdata_ascii (fname, delimiter, header_rows)
-
-  ## Define the fields in the output structure so that the order will be
-  ## correct.
-
-  output.data       = [];
-  output.textdata   = [];
-  output.rowheaders = [];
-  output.colheaders = [];
-
-  ## Read file into string and count the number of header rows
-  file_content = fileread (fname);
-
-  ## Split the file into rows (using \n and/or \r as delimiters between rows).
-  file_content_rows = regexp (file_content, "\n|\n\r|\r|\r\n", "split");
-
-  ## FIXME: guess delimiter, if it isn't defined
-  if (isempty (delimiter))
-    error ("importdata: Guessing delimiter is not implemented yet, you have to specify it.");
-  endif
-
-  ## FIXME: A more intelligent way to count number of header rows. This
-  ## is needed e.g. when delimiter=' ' and the header contains spaces...
-
-  ## If number of header rows is undefined, then count the number of
-  ## header rows by step through row by row and look for the delimiter.
-  ## Assume that the header can't contain any delimiter.
-  if (header_rows < 0)
-    header_rows = 0;
-    for i=1:length (file_content_rows)
-      if (isempty (regexp(file_content_rows{i}, delimiter, "once")))
-        header_rows++;
-      else
-        ## Data part has begun and therefore no more header rows can be
-        ## found
-        break;
-      endif
-    endfor
-  endif
-
-  ## Put the header rows in output.textdata.
-  if (header_rows > 0)
-    output.textdata   = file_content_rows (1:header_rows)';
-  endif
-
-  ## If space is the delimiter, then remove spaces in the beginning of
-  ## each data row.
-  if (strcmpi (delimiter, " "))
-    for i=(header_rows+1):length (file_content_rows)
-      ## strtrim does not only remove the leading spaces but also the
-      ## tailing spaces, but that doesn't really matter.
-      file_content_rows{i} = strtrim (file_content_rows{i});
-    endfor
-  endif
-
-  ## Remove empty data rows. Go through them backwards so that you wont
-  ## get out of bounds.
-  for i=length (file_content_rows):-1:(header_rows + 1)
-    if (length (file_content_rows{i}) < 1)
-      file_content_rows = [file_content_rows(1:i-1), ...
-                           file_content_rows(i+1:length(file_content_rows))];
-    endif
-  endfor
-
-  ## Count the number of data columns. If there are different number of
-  ## columns, use the greatest value.
-  data_columns = 0;
-  delimiter_pattern = delimiter;
-  ## If space is the delimiter, then multiple spaces should count as ONE
-  ## delimiter. Also ignore leading spaces.
-  if (strcmpi (delimiter, " "))
-    delimiter_pattern = ' +';
-  endif
-  for i=(header_rows+1):length(file_content_rows)
-    data_columns = max (data_columns,
-                        length (regexp (file_content_rows{i},
-                                        delimiter_pattern, "split")));
-  endfor
-
-  ## FIXME: Make it behave like Matlab when importing a table where a whole
-  ## column is text only. E.g.
-  ##    abc  12  34
-  ##    def  56  78
-  ## This would give a 3x2 data matrix with the left column = nan(2,1), and 
-  ## the text would end up in textdata.
-  ## In Matlab the data matrix would only be a 2x2 matrix, see example at:
-  ## http://www.mathworks.se/help/matlab/import_export/import-numeric-data-and-header-text-from-a-text-file.html
-
-  ## Go through the data and put it in either output.data or
-  ## output.textdata depending on if it is numeric or not.
-  output.data = NaN (length (file_content_rows) - header_rows, data_columns);
-  for i=(header_rows+1):length(file_content_rows)
-    ## Only use the row if it contains anything other than white-space
-    ## characters.
-    if (any (file_content_rows{i} != " "))
-      row_data = regexp (file_content_rows{i}, delimiter_pattern, "split");
-
-      for j=1:length(row_data)
-        ## Try to convert the column to a number, if it works put it in
-        ## output.data, otherwise in output.textdata
-        if (!isempty (row_data{j}))
-          data_numeric = str2double (row_data{j});
-          if (!isnan (data_numeric))
-            output.data(i-header_rows, j) = data_numeric;
-          else
-            output.textdata{i,j} = row_data{j};
-          endif
-        endif
-      endfor
-
-    endif
-  endfor
-
-  ## Check wether rowheaders or colheaders should be used
-  if ((header_rows == data_columns) && (size (output.textdata, 2) == 1))
-    output.rowheaders = output.textdata;
-  elseif (size (output.textdata, 2) == data_columns)
-    output.colheaders = output.textdata(end,:);
-  endif
-
-  ## When delimiter = "\\t" convert it to a tab, done for Matlab compatibility.
-  if (strcmp (delimiter, '\t'))
-    delimiter = "\t";
-  endif
 
 endfunction
 
+function [output, delimiter, header_rows] = importdata_ascii (fname, delimiter, num_header_rows)
 
-########################################
+  ## Define fields in the output structure so that the order will be correct.
+  output.data       = [];
+  output.textdata   = {};
+  output.rowheaders = {};
+  output.colheaders = {};
+
+  [fid, msg] = fopen (fname, "r");
+  if (fid == -1)
+    error (msg);
+  endif
+
+  header_rows = 0;
+  header_cols = 0;
+    
+  ## Work through first few rows line by line until a delimiter is found.
+  while (ischar (row = fgetl (fid)))
+
+    ## If no delimiter determined yet, make a guess.
+    if (isempty (delimiter))
+      ## This pattern can be fooled, but mostly does the job just fine.
+      delim = regexp (row, '[+-\d.eE\*ij ]+([^+-\d.ij])[+-\d.ij]',
+                           'tokens', 'once');
+      if (! isempty (delim))
+        delimiter = delim{1};
+      endif
+    endif
+
+    if (delimiter == " ")
+      row_entries = regexp (strtrim (row), ' +', 'split');
+    else
+      row_entries = ostrsplit (row, delimiter);
+    endif
+    row_data = str2double (row_entries);
+    if (all (isnan (row_data)) || header_rows < num_header_rows)
+      header_rows++;
+      output.textdata{end+1, 1} = row;
+    else
+      if (! isempty (output.textdata))
+        if (delimiter == " ")
+          output.colheaders = regexp (strtrim (output.textdata{end}),
+                                      ' +', 'split');
+        else
+          output.colheaders = ostrsplit (output.textdata{end}, delimiter);
+        endif
+      endif
+      header_cols = find (! isnan (row_data), 1) - 1;
+      ## The number of header rows and header columns is now known.
+      break;
+    endif
+
+  endwhile
+
+  fclose (fid);
+
+  if (row == -1)
+    error ("importdata: Unable to determine delimiter");
+  endif
+  if (num_header_rows >= 0)
+    header_rows = num_header_rows;
+  endif
+
+  ## Now, let the efficient built-in routine do the bulk of the work.
+  if (delimiter == " ")
+    output.data = dlmread (fname, "", header_rows, header_cols,
+                           "emptyvalue", NA);
+  else
+    output.data = dlmread (fname, delimiter, header_rows, header_cols,
+                           "emptyvalue", NA);
+  endif
+
+  ## Go back and correct any individual values that did not convert.
+  na_idx = isna (output.data);
+  if (header_cols > 0)
+    na_idx = [(true (rows (na_idx), header_cols)), na_idx];
+  endif
+  if (any (na_idx(:)))
+
+    file_content = ostrsplit (fileread (fname), "\n");
+
+    na_rows = find (any (na_idx, 2));
+    for ridx = na_rows(:)'
+      row = file_content{ridx+header_rows};
+      if (delimiter == " ")
+        fields = regexp (strtrim (row), ' +', 'split');
+      else
+        fields = ostrsplit (row, delimiter);
+      endif
+      
+      text = fields(na_idx(ridx,:));
+      text = text(! strcmpi (text, "NA"));  #  Remove valid "NA" entries
+      if (! isempty (text))
+        output.textdata(end+1:end+numel (text), 1) = text;
+      endif
+      if (header_cols)
+        output.rowheaders(end+1, :) = fields(1:header_cols);
+      endif
+    endfor
+
+  endif
+
+  ## Final cleanup to satisfy output configuration
+  if (all (cellfun ("isempty", output.textdata)))
+    output = output.data;
+  elseif (! isempty (output.rowheaders) && ! isempty (output.colheaders))
+    output = struct ("data", {output.data}, "textdata", {output.textdata});
+  endif
+
+endfunction
+
 
 %!test
 %! ## Comma separated values
@@ -317,11 +273,15 @@
 %! fid = fopen (fn, "w");
 %! fputs (fid, "3.1,-7.2,0\n0.012,6.5,128");
 %! fclose (fid);
-%! [a,d,h] = importdata (fn, ",");
+%! [a1,d1,h1] = importdata (fn, ",");
+%! [a2,d2,h2] = importdata (fn);
 %! unlink (fn);
-%! assert (a, A);
-%! assert (d, ",");
-%! assert (h, 0);
+%! assert (a1, A);
+%! assert (d1, ",");
+%! assert (h1, 0);
+%! assert (a2, A);
+%! assert (d2, ",");
+%! assert (h2, 0);
 
 %!test
 %! ## Tab separated values
@@ -330,11 +290,15 @@
 %! fid = fopen (fn, "w");
 %! fputs (fid, "3.1\t-7.2\t0\n0.012\t6.5\t128");
 %! fclose (fid);
-%! [a,d,h] = importdata (fn, "\t");
+%! [a1,d1,h1] = importdata (fn, "\t");
+%! [a2,d2,h2] = importdata (fn);
 %! unlink (fn);
-%! assert (a, A);
-%! assert (d, "\t");
-%! assert (h, 0);
+%! assert (a1, A);
+%! assert (d1, "\t");
+%! assert (h1, 0);
+%! assert (a2, A);
+%! assert (d2, "\t");
+%! assert (h2, 0);
 
 %!test
 %! ## Space separated values, using multiple spaces to align in columns.
@@ -343,17 +307,39 @@
 %! fid = fopen (fn, "w");
 %! fprintf (fid, "%10.3f %10.3f %10.3f\n", A');
 %! fclose (fid);
-%! [a,d,h] = importdata (fn, " ");
+%! [a1,d1,h1] = importdata (fn, " ");
+%! [a2,d2,h2] = importdata (fn);
 %! unlink (fn);
-%! assert (a, A);
-%! assert (d, " ");
-%! assert (h, 0);
+%! assert (a1, A);
+%! assert (d1, " ");
+%! assert (h1, 0);
+%! assert (a2, A);
+%! assert (d2, " ");
+%! assert (h2, 0);
+
+%!test
+%! ## No separator, 1 column of data only
+%! A = [3.1;-7.2;0;0.012;6.5;128];
+%! fn  = tmpnam ();
+%! fid = fopen (fn, "w");
+%! fprintf (fid, "%f\n", A);
+%! fclose (fid);
+%! [a1,d1,h1] = importdata (fn, "");
+%! [a2,d2,h2] = importdata (fn);
+%! unlink (fn);
+%! assert (a1, A);
+%! assert (d1, "");
+%! assert (h1, 0);
+%! assert (a2, A);
+%! assert (d2, "");
+%! assert (h2, 0);
 
 %!test
 %! ## Header text
 %! A.data = [3.1 -7.2 0; 0.012 6.5 128];
 %! A.textdata = {"This is a header row."; ...
 %!               "this row does not contain any data, but the next one does."};
+%! A.colheaders = A.textdata (2);
 %! fn  = tmpnam ();
 %! fid = fopen (fn, "w");
 %! fprintf (fid, "%s\n", A.textdata{:});
@@ -368,9 +354,9 @@
 %!test
 %! ## Column headers, only last row is returned in colheaders
 %! A.data = [3.1 -7.2 0; 0.012 6.5 128];
-%! A.textdata = {"Label1 Label2 Label3";
-%!               "col1 col2 col3"};
-%! A.colheaders = {"col1", "col2", "col3"};
+%! A.textdata = {"Label1\tLabel2\tLabel3";
+%!               "col 1\tcol 2\tcol 3"};
+%! A.colheaders = {"col 1", "col 2", "col 3"};
 %! fn  = tmpnam ();
 %! fid = fopen (fn, "w");
 %! fprintf (fid, "%s\n", A.textdata{:});
@@ -385,9 +371,8 @@
 %!test
 %! ## Row headers
 %! A.data = [3.1 -7.2 0; 0.012 6.5 128];
-%! ## FIXME: Does Matlab even create field "textdata" if it is null?
-%! A.textdata = {""};
-%! A.rowheaders = {"row1", "row2"};
+%! A.textdata = {"row1"; "row2"};
+%! A.rowheaders = A.textdata;
 %! fn  = tmpnam ();
 %! fid = fopen (fn, "w");
 %! fputs (fid, "row1\t3.1\t-7.2\t0\nrow2\t0.012\t6.5\t128");
@@ -396,7 +381,7 @@
 %! unlink (fn);
 %! assert (a, A);
 %! assert (d, "\t");
-%! assert (h, 2);
+%! assert (h, 0);
 
 %!test
 %! ## Row/Column headers and Header Text
@@ -457,9 +442,7 @@
 %! assert (d, "\t");
 %! assert (h, 0);
 
-## FIXME: Currently commented out (8/23/13) because I can't determine whether
-## Matlab processes exceptional values.
-%!#test
+%!test
 %! ## Exceptional values (Inf, NaN, NA)
 %! A = [3.1 Inf NA; -Inf NaN 128];
 %! fn  = tmpnam ();
@@ -473,11 +456,13 @@
 %! assert (h, 0);
 
 %!test
-%! ## Missing values
-%! A = [3.1 NaN 0; 0.012 6.5 128];
+%! ## Missing values and Text Values
+%! A.data = [3.1 NA 0; 0.012 NA 128];
+%! A.textdata = {char(zeros(1,0))
+%!               "NO DATA"};
 %! fn  = tmpnam ();
 %! fid = fopen (fn, "w");
-%! fputs (fid, "3.1\t\t0\n0.012\t6.5\t128");
+%! fputs (fid, "3.1\t\t0\n0.012\tNO DATA\t128");
 %! fclose (fid);
 %! [a,d,h] = importdata (fn, '\t');
 %! unlink (fn);
@@ -485,7 +470,7 @@
 %! assert (d, "\t");
 %! assert (h, 0);
 
-%!test
+%!#test
 %! ## CRLF for line breaks
 %! A = [3.1 -7.2 0; 0.012 6.5 128];
 %! fn  = tmpnam ();
@@ -498,7 +483,7 @@
 %! assert (d, "\t");
 %! assert (h, 0);
 
-%!test
+%!#test
 %! ## CR for line breaks
 %! A = [3.1 -7.2 0; 0.012 6.5 128];
 %! fn  = tmpnam ();
@@ -511,3 +496,14 @@
 %! assert (d, "\t");
 %! assert (h, 0);
 
+%!error importdata ()
+%!error importdata (1,2,3,4)
+%!error <FNAME must be a string> importdata (1)
+%!error <option -pastespecial not implemented> importdata ("-pastespecial")
+%!error <DELIMITER must be a single character> importdata ("foo", 1)
+%!error <DELIMITER must be a single character> importdata ("foo", "ab")
+%!error <HEADER_ROWS must be an integer> importdata ("foo", " ", "1")
+%!error <HEADER_ROWS must be an integer> importdata ("foo", " ", 1.5)
+%!error <not implemented for file format .au> importdata ("foo.au")
+%!error <not implemented for file format .avi> importdata ("foo.avi")
+
--- a/scripts/plot/private/__patch__.m
+++ b/scripts/plot/private/__patch__.m
@@ -29,7 +29,6 @@
 
 function [h, failed] = __patch__ (p, varargin)
 
-  keyboard
   h = NaN;
   failed = false;