## Copyright (C) 2009,2010,2011,2012 Philip Nienhuis <prnienhuis at users.sf.net>
## 
## 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 2 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 Octave; see the file COPYING.  If not, see
## <http://www.gnu.org/licenses/>.

## -*- texinfo -*-
## @deftypefn {Function File} @var{xls} = xlsopen (@var{filename})
## @deftypefnx {Function File} @var{xls} = xlsopen (@var{filename}, @var{readwrite})
## @deftypefnx {Function File} @var{xls} = xlsopen (@var{filename}, @var{readwrite}, @var{reqintf})
## Get a pointer to an Excel spreadsheet in the form of return argument
## (file pointer struct) @var{xls}. After processing the spreadsheet,
## the file pointer must be explicitly closed by calling xlsclose().
##
## Calling xlsopen without specifying a return argument is fairly useless!
##
## To make this function work at all, you need MS-Excel (95 - 2003), and/or
## the Java package >= 1.2.8 plus Apache POI >= 3.5 and/or JExcelAPI and/or
## OpenXLS and/or OpenOffice.org (or clones) installed on your computer +
## proper javaclasspath set. These interfaces are referred to as COM, POI,
## JXL, OXS, and UNO, resp., and are preferred in that order by default
## (depending on their presence).
## For OOXML support, in addition to Apache POI support you also need the
## following jars in your javaclasspath: poi-ooxml-schemas-3.5.jar,
## xbean.jar and dom4j-1.6.1.jar (or later versions). Later OpenOffice.org
## versions (UNO) have support for OOXML as well.
## Excel'95 spreadsheets can only be read by JExcelAPI and OpenOffice.org.
##
## @var{filename} should be a valid .xls or xlsx Excel file name (including
## extension). But if you use the COM interface you can specify any extension
## that your installed Excel version can read AND write; the same goes for UNO
## (OpenOffice.org). Using the other Java interfaces, only .xls or .xlsx are
## allowed. If @var{filename} does not contain any directory path, the file
## is saved in the current directory.
##
## If @var{readwrite} is set to 0 (default value) or omitted, the Excel file
## is opened for reading. If @var{readwrite} is set to True or 1, an Excel
## file is opened (or created) for reading & writing.
##
## Optional input argument @var{reqintf} can be used to override the Excel
## interface that otherwise is automatically selected by xlsopen. Currently
## implemented interfaces (in order of preference) are 'COM' (Excel/COM),
## 'POI' (Java/Apache POI), 'JXL' (Java/JExcelAPI), 'OXS' (Java/OpenXLS), or
## 'UNO' (Java/OpenOffice.org - EXPERIMENTAL!).
## In most situations this parameter is unneeded as xlsopen automatically
## selects the most useful interface present.
##
## Beware: Excel invocations may be left running invisibly in case of COM
## errors or forgetting to close the file pointer. Similarly for OpenOffice.org
## which may even prevent Octave from being closed.
##
## Examples:
##
## @example
##   xls = xlsopen ('test1.xls');
##   (get a pointer for reading from spreadsheet test1.xls)
##
##   xls = xlsopen ('test2.xls', 1, 'POI');
##   (as above, indicate test2.xls will be written to; in this case using Java
##    and the Apache POI interface are requested)
## @end example
##
## @seealso {xlsclose, xlsread, xlswrite, xls2oct, oct2xls, xlsfinfo}
##
## @end deftypefn

## Author: Philip Nienhuis
## Created: 2009-11-29
## Updates:
## 2010-01-03 Added OOXML support
## 2010-01-10 Changed (java) interface preference order to COM->POI->JXL
## 2010-01-16 Removed echoeing debug info in POI stanza
## 2010-03-01 Removed javaclasspath check for rt.jar
## 2010-03-14 Fixed check on xwrite flag lines 204+, if xlsopen fails xls ptr
##            should be []
## 2010-08-25 Improved help text
## 2010-09-27 Improved POI help message for unrecognized .xls format to hint for BIFF5/JXL
## 2010-10-20 Improved code for tracking changes to new/existing files
##     ''     Lots of code cleanup, improved error checking and catching
##     ''     Implemented fallback to JXL if POI can't read a file.
## 2010-10-30 More fine-grained file existence/writable checks
## 2010-11-01 Added <COM>.Application.DisplayAlerts=0 in COM section to avoid Excel pop-ups
## 2010-11-05 Option for multiple requested interface types (cell array)
##     ''     Bug fix: JXL fallback from POI for BIFF5 is only useful for reading
## 2010-11-05 Slight change to reporting to screen
## 2010-11-08 Tested with POI 3.7 (OK)
## 2010-11-10 Texinfo header updated
## 2010-12-01 Small bugfix - reset xlssupport in l. 102
## 2010-12-06 Textual changes to info header 
## 2011-03-26 OpenXLS support added
## 2011-05-18 Experimental UNO support added, incl. creating new spreadsheets
## 2011-05-22 Textual changes in header
## 2011-05-29 Cleanup of comments & messages
## 2011-09-03 Reset chkintf to [] if no xls support was discovered (to allow
##            rediscovery of interfaces between xlsopen calls, e.g. javaclasspath changes)
## 2011-09-08 Minor code cleanup
## 2012-01-26 Fixed "seealso" help string
## 2012-06-06 Improved interface detection logic. No more messages if same interface is
##            requested & used consecutively
## 2012-06-07 Fixed mixed-up lastintf assignments for POI and JXL
##
## Latest subfunction update: 2012-06-06

function [ xls ] = xlsopen (filename, xwrite=0, reqinterface=[])

  persistent xlsinterfaces; persistent chkintf; persistent lastintf;
  # xlsinterfaces.<intf> = [] (not yet checked), 0 (found to be unsupported) or 1 (OK)
  if (isempty (chkintf));
      chkintf = 1;
      xlsinterfaces = struct ('COM', [], 'POI', [], 'JXL', [], 'OXS', [], 'UNO', []);
  endif
  if (isempty (lastintf))
    lastintf = "---";
  endif

  xlssupport = 0;

  if (nargout < 1)
      usage ("XLS = xlsopen (Xlfile [, Rw] [, reqintf]). But no return argument specified!"); 
  endif

  if (~(islogical (xwrite) || isnumeric (xwrite)))
      usage ("Numerical or logical value expected for arg # 2")
  endif

  if (~isempty (reqinterface))
    if ~(ischar (reqinterface) || iscell (reqinterface)), usage ("Arg # 3 not recognized"); endif
    # Turn arg3 into cell array if needed
    if (~iscell (reqinterface)), reqinterface = {reqinterface}; endif
    ## Check if previously used interface matches a requested interface
    if (isempty (regexpi (reqinterface, lastintf, 'once'){1}))
      ## New interface requested
      xlsinterfaces.COM = 0; xlsinterfaces.POI = 0; xlsinterfaces.JXL = 0;
      xlsinterfaces.OXS = 0; xlsinterfaces.UNO = 0;
      for ii=1:numel (reqinterface)
        reqintf = toupper (reqinterface {ii});
        # Try to invoke requested interface(s) for this call. Check if it
        # is supported anyway by emptying the corresponding var.
        if     (strcmpi (reqintf, 'COM'))
          xlsinterfaces.COM = [];
        elseif (strcmpi (reqintf, 'POI'))
          xlsinterfaces.POI = [];
        elseif (strcmpi (reqintf, 'JXL'))
          xlsinterfaces.JXL = [];
        elseif (strcmpi (reqintf, 'OXS'))
          xlsinterfaces.OXS = [];
        elseif (strcmpi (reqintf, 'UNO'))
          xlsinterfaces.UNO = [];
        else 
          usage (sprintf ("Unknown .xls interface \"%s\" requested. Only COM, POI, JXL, OXS or UNO supported\n", reqinterface{}));
        endif
      endfor
      printf ("Checking requested interface(s):\n");
      xlsinterfaces = getxlsinterfaces (xlsinterfaces);
      # Well, is/are the requested interface(s) supported on the system?
      # FIXME check for multiple interfaces
      xlsintf_cnt = 0;
      for ii=1:numel (reqinterface)
        if (~xlsinterfaces.(toupper (reqinterface{ii})))
          # No it aint
          printf ("%s is not supported.\n", upper (reqinterface{ii}));
        else
          ++xlsintf_cnt;
        endif
      endfor
      # Reset interface check indicator if no requested support found
      if (~xlsintf_cnt)
        chkintf = [];
        xls = [];
        return
      endif
    endif
  endif

  # Var xwrite is really used to avoid creating files when wanting to read, or
  # not finding not-yet-existing files when wanting to write.

  # Check if Excel file exists. Adapt file open mode for readwrite argument
  if (xwrite), fmode = 'r+b'; else fmode = 'rb'; endif
  fid = fopen (filename, fmode);
  if (fid < 0)            # File doesn't exist...
    if (~xwrite)        # ...which obviously is fatal for reading...
      error ( sprintf ("File %s not found\n", filename));
    else              # ...but for writing, we need more info:
      fid = fopen (filename, 'rb');  # Check if it exists at all...
      if (fid < 0)      # File didn't exist yet. Simply create it
        printf ("Creating file %s\n", filename);
        xwrite = 3;
      else            # File exists, but is not writable => Error
        fclose (fid);  # Do not forget to close the handle neatly
        error (sprintf ("Write mode requested but file %s is not writable\n", filename))
      endif
    endif
  else
    # Close file anyway to avoid COM or Java errors
    fclose (fid);
  endif
  
  # Check for the various Excel interfaces. No problem if they've already
  # been checked, getxlsinterfaces (far below) just returns immediately then.
  xlsinterfaces = getxlsinterfaces (xlsinterfaces);

  # Supported interfaces determined; Excel file type check moved to seperate interfaces.
  chk1 = strcmpi (filename(end-3:end), '.xls');      # Regular (binary) BIFF 
  chk2 = strcmpi (filename(end-4:end-1), '.xls');    # Zipped XML / OOXML
  
  # Initialize file ptr struct
  xls = struct ("xtype", 'NONE', "app", [], "filename", [], "workbook", [], "changed", 0, "limits", []); 

  # Keep track of which interface is selected
  xlssupport = 0;

  # Interface preference order is defined below: currently COM -> POI -> JXL -> OXS -> UNO
  if (xlsinterfaces.COM && ~xlssupport)
    # Excel functioning has been tested above & file exists, so we just invoke it
    app = actxserver ("Excel.Application");
    try      # Because Excel itself can still crash on file formats etc.
      app.Application.DisplayAlerts = 0;
      if (xwrite < 2)
        # Open workbook
        wb = app.Workbooks.Open (canonicalize_file_name (filename));
      elseif (xwrite > 2)
        # Create a new workbook
        wb = app.Workbooks.Add ();
        ### Uncommenting the below statement can be useful in multi-user environments.
        ### Be sure to uncomment correspondig stanza in xlsclose to avoid zombie Excels
        # wb.SaveAs (canonicalize_file_name (filename))
      endif
      xls.app = app;
      xls.xtype = 'COM';
      xls.workbook = wb;
      xls.filename = filename;
      xlssupport += 1;
      lastintf = 'COM';
    catch
      warning ( sprintf ("ActiveX error trying to open or create file %s\n", filename));
      app.Application.DisplayAlerts = 1;
      app.Quit ();
      delete (app);
    end_try_catch
  endif
  
  if (xlsinterfaces.POI && ~xlssupport)
    if ~(chk1 || chk2)
      error ("Unsupported file format for Apache POI.")
    endif
    # Get handle to workbook
    try
      if (xwrite > 2)
        if (chk1)
          wb = java_new ('org.apache.poi.hssf.usermodel.HSSFWorkbook');
        elseif (chk2)
          wb = java_new ('org.apache.poi.xssf.usermodel.XSSFWorkbook');
        endif
          xls.app = 'new_POI';
      else
        xlsin = java_new ('java.io.FileInputStream', filename);
        wb = java_invoke ('org.apache.poi.ss.usermodel.WorkbookFactory', 'create', xlsin);
        xls.app = xlsin;
      endif
      xls.xtype = 'POI';
      xls.workbook = wb;
      xls.filename = filename;
      xlssupport += 2;
      lastintf = 'POI';
    catch
      clear xlsin;
      if (xlsinterfaces.JXL)
        printf ('Couldn''t open file %s using POI; trying Excel''95 format with JXL...\n', filename);
      endif
    end_try_catch
  endif
      
  if (xlsinterfaces.JXL && ~xlssupport)
    if (~chk1)
      error ("JXL can only read reliably from .xls files")
    endif
    try
      xlsin = java_new ('java.io.File', filename);
      if (xwrite > 2)
        # Get handle to new xls-file
        wb = java_invoke ('jxl.Workbook', 'createWorkbook', xlsin);
      else
        # Open existing file
        wb = java_invoke ('jxl.Workbook', 'getWorkbook', xlsin);
      endif
      xls.xtype = 'JXL';
      xls.app = xlsin;
      xls.workbook = wb;
      xls.filename = filename;
      xlssupport += 4;
      lastintf = 'JXL';
    catch
      clear xlsin;
      if (xlsinterfaces.POI)
        printf ('... No luck with JXL either, unsupported file format.\n', filename);
      endif
    end_try_catch
  endif

  if (xlsinterfaces.OXS && ~xlssupport)
    if (~chk1)
      error ("OXS can only read from .xls files")
    endif
    try
      wb = javaObject ('com.extentech.ExtenXLS.WorkBookHandle', filename);
      xls.xtype = 'OXS';
      xls.app = 'void - OpenXLS';
      xls.workbook = wb;
      xls.filename = filename;
      xlssupport += 8;
      lastintf = 'OXS';
    catch
      printf ('Unsupported file format for OpenXLS - %s\n');
    end_try_catch
  endif

  if (xlsinterfaces.UNO && ~xlssupport)
    # First, the file name must be transformed into a URL
    if (~isempty (strmatch ("file:///", filename)) || ~isempty (strmatch ("http:///", filename))...
      || ~isempty (strmatch ("ftp:///", filename)) || ~isempty (strmatch ("www:///", filename)))
      # Seems in proper shape for OOo (at first sight)
    else
      # Transform into URL form
      fname = canonicalize_file_name (strsplit (filename, filesep){end});
      # On Windows, change backslash file separator into forward slash
      if (strcmp (filesep, "\\"))
        tmp = strsplit (fname, filesep);
        flen = numel (tmp);
        tmp(2:2:2*flen) = tmp;
        tmp(1:2:2*flen) = '/';
        fname = [ tmp{:} ];
      endif
      filename = [ 'file://' fname ];
    endif
    try
      xContext = java_invoke ("com.sun.star.comp.helper.Bootstrap", "bootstrap");
      xMCF = xContext.getServiceManager ();
      oDesktop = xMCF.createInstanceWithContext ("com.sun.star.frame.Desktop", xContext);
      # Workaround for <UNOruntime>.queryInterface():
      unotmp = java_new ('com.sun.star.uno.Type', 'com.sun.star.frame.XComponentLoader');
      aLoader = oDesktop.queryInterface (unotmp);
      # Some trickery as Octave Java cannot create initialized arrays
      lProps = javaArray ('com.sun.star.beans.PropertyValue', 1);
      lProp = java_new ('com.sun.star.beans.PropertyValue', "Hidden", 0, true, []);
      lProps(1) = lProp;
      if (xwrite > 2)
        xComp = aLoader.loadComponentFromURL ("private:factory/scalc", "_blank", 0, lProps);
      else
        xComp = aLoader.loadComponentFromURL (filename, "_blank", 0, lProps);
      endif
      # Workaround for <UNOruntime>.queryInterface():
      unotmp = java_new ('com.sun.star.uno.Type', 'com.sun.star.sheet.XSpreadsheetDocument');
      xSpdoc = xComp.queryInterface (unotmp);
      # save in ods struct:
      xls.xtype = 'UNO';
      xls.workbook = xSpdoc;        # Needed to be able to close soffice in odsclose()
      xls.filename = filename;
      xls.app.xComp = xComp;        # Needed to be able to close soffice in odsclose()
      xls.app.aLoader = aLoader;      # Needed to be able to close soffice in odsclose()
      xls.odfvsn = 'UNO';
      xlssupport += 16;
      lastintf = 'UNO';
    catch
      error ('Couldn''t open file %s using UNO', filename);
    end_try_catch
  endif

  # if 
  #  ---- other interfaces
  # endif

  # Rounding up. If none of the xlsinterfaces is supported we're out of luck.
  if (~xlssupport)
    if (isempty (reqinterface))
      printf ("None.\n");
      warning ("No support for Excel .xls I/O"); 
    else
      warning ("File type not supported by %s %s %s %s %s", reqinterface{:});
    endif
    xls = [];
    # Reset found interfaces for re-testing in the next call. Add interfaces if needed.
    chkintf = [];
  else
    # From here on xwrite is tracked via xls.changed in the various lower
    # level r/w routines and it is only used to determine if an informative
    # message is to be given when saving a newly created xls file.
    xls.changed = xwrite;

    # Until something was written to existing files we keep status "unchanged".
    # xls.changed = 0 (existing/only read from), 1 (existing/data added), 2 (new,
    # data added) or 3 (pristine, no data added).
    if (xls.changed == 1), xls.changed = 0; endif
  endif
  
endfunction


## Copyright (C) 2009,2010,2011,2012 Philip Nienhuis <prnienhuis at users.sf.net>
## 
## 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 2 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 Octave; see the file COPYING.  If not, see
## <http://www.gnu.org/licenses/>.

## -*- texinfo -*-
## @deftypefn {Function File} @var{xlsinterfaces} = getxlsinterfaces (@var{xlsinterfaces})
## Get supported Excel .xls file read/write interfaces from the system.
## Each interface for which the corresponding field is set to empty
## will be checked. So by manipulating the fields of input argument
## @var{xlsinterfaces} it is possible to specify which
## interface(s) should be checked.
##
## Currently implemented interfaces comprise:
## - ActiveX / COM (native Excel in the background)
## - Java & Apache POI
## - Java & JExcelAPI
## - Java & OpenXLS (only JRE >= 1.4 needed)
## - Java & UNO bridge (native OpenOffice.org in background) - EXPERIMENTAL!!
##
## Examples:
##
## @example
##   xlsinterfaces = getxlsinterfaces (xlsinterfaces);
## @end example

## Author: Philip Nienhuis
## Created: 2009-11-29
## Last updates: 
## 2009-12-27 Make sure proper dimensions are checked in parsed javaclasspath
## 2010-09-11 Rearranged code and clarified messages about missing classes
## 2010-09-27 More code cleanup
## 2010-10-20 Added check for minimum Java version (should be >= 6 / 1.6)
## 2010-11-05 Slight change to reporting to screen
## 2011-02-15 Adapted to javaclasspath calling style of java-1.2.8 pkg
## 2011-03-26 OpenXLS support added
##      ''    Bug fix: javaclasspath change wasn't picked up between calls with req.intf
## 2011-05-18 Experimental UNO support added
## 2011-05-29 Reduced verbosity
## 2011-06-06 Fix for javaclasspath format in *nix w java-1.2.8 pkg
## 2011-06-13 Fixed potentially faulty tests for java classlib presence
## 2011-09-03 Fixed order of xlsinterfaces.<member> statements in Java detection try-catch
##      ''    Reset tmp1 (always allow interface rediscovery) for empty xlsinterfaces arg
## 2011-09-08 Minor code cleanup
## 2011-09-18 Added temporary warning about UNO interface
## 2012-03-01 Changed UNO warning so that it is suppressed when UNO is not yet chosen
## 2012-03-07 Only check for COM if run on Windows
## 2012-03-21 Print newline if COM found but no Java support
##     ''     Improved logic for finding out what interfaces to check
##     ''     Fixed bugs with Java interface checking (tmp1 initialization)
## 2012-06-06 Improved & simplified Java check code

function [xlsinterfaces] = getxlsinterfaces (xlsinterfaces)

  # tmp1 = [] (not initialized), 0 (No Java detected), or 1 (Working Java found)
  persistent tmp1 = []; persistent jcp;  # Java class path
  persistent uno_1st_time = 0;

  if (isempty (xlsinterfaces.COM) && isempty (xlsinterfaces.POI) && isempty (xlsinterfaces.JXL)
   && isempty (xlsinterfaces.OXS) && isempty (xlsinterfaces.UNO))
    # Looks like first call to xlsopen. Check Java support
    printf ("Detected XLS interfaces: ");
    tmp1 = [];
  elseif (isempty (xlsinterfaces.POI) || isempty (xlsinterfaces.JXL)
       || isempty (xlsinterfaces.OXS) || isempty (xlsinterfaces.UNO))
    # Can't be first call. Here one of the Java interfaces is requested
    if (~tmp1)
      # Check Java support again
      tmp1 = [];
    endif
  endif
  deflt = 0;

  # Check if MS-Excel COM ActiveX server runs (only on Windows!)
  if (isempty (xlsinterfaces.COM))
    xlsinterfaces.COM = 0;
    if (ispc)
      try
        app = actxserver ("Excel.application");
        # If we get here, the call succeeded & COM works.
        xlsinterfaces.COM = 1;
        # Close Excel. Yep this is inefficient when we need only one r/w action,
        # but it quickly pays off when we need to do more with the same file
        # (+, MS-Excel code is in OS cache anyway after this call so no big deal)
        app.Quit();
        delete(app);
        printf ("COM");
        if (deflt), printf ("; "); else, printf ("*; "); deflt = 1; endif
      catch
        # COM non-existent. Only print message if COM is explicitly requested (tmp1==[])
        if (~isempty (tmp1))
          printf ("ActiveX not working; no Excel installed?\n"); 
        endif
      end_try_catch
    endif
  endif

  if (isempty (tmp1))
    # Check Java support. First try javaclasspath
    try
      jcp = javaclasspath ('-all');              # For java pkg > 1.2.7
      if (isempty (jcp)), jcp = javaclasspath; endif  # For java pkg < 1.2.8
      # If we get here, at least Java works. Now check for proper version (>= 1.6)
      jver = char (java_invoke ('java.lang.System', 'getProperty', 'java.version'));
      cjver = strsplit (jver, '.');
      if (sscanf (cjver{2}, '%d') < 6)
        warning ("\nJava version might be too old - you need at least Java 6 (v. 1.6.x.x)\n");
        return
      endif
      # Now check for proper entries in class path. Under *nix the classpath
      # must first be split up. In java 1.2.8+ javaclasspath is already a cell array
      if (isunix && ~iscell (jcp)); jcp = strsplit (char (jcp), ":"); endif
      tmp1 = 1;
    catch
      # No Java support found
      tmp1 = 0;
      if (isempty (xlsinterfaces.POI) || isempty (xlsinterfaces.JXL)...
        || isempty (xlsinterfaces.OXS) || isempty (xlsinterfaces.UNO))
        # Some or all Java-based interface(s) explicitly requested but no Java support
        warning (' No Java support found (no Java JRE? no Java pkg installed AND loaded?');
      endif
      # Set Java interfaces to 0 anyway as there's no Java support
      xlsinterfaces.POI = 0;
      xlsinterfaces.JXL = 0;
      xlsinterfaces.OXS = 0;
      xlsinterfaces.UNO = 0;
      printf ("\n");
      # No more need to try any Java interface
      return
    end_try_catch
  endif

  # Try Java & Apache POI
  if (isempty (xlsinterfaces.POI))
    xlsinterfaces.POI = 0;
    # Check basic .xls (BIFF8) support
    jpchk1 = 0; entries1 = {"poi-3", "poi-ooxml-3"};
    # Only under *nix we might use brute force: e.g., strfind (classname, classpath);
    # under Windows we need the following more subtle, platform-independent approach:
    for ii=1:length (jcp)
      for jj=1:length (entries1)
        if (isempty (strfind (tolower (jcp{ii}), entries1{jj}))), ++jpchk1; endif
      endfor
    endfor
    if (jpchk1 > 1)
      xlsinterfaces.POI = 1;
      printf ("POI");
    endif
    # Check OOXML support
    jpchk2 = 0; entries2 = {"xbean", "poi-ooxml-schemas", "dom4j"};
    for ii=1:length (jcp)
      for jj=1:length (entries2)
        if (isempty (strfind (lower (jcp{ii}), entries2{jj}))), ++jpchk2; endif
      endfor
    endfor
    if (jpchk2 > 2), printf (" (& OOXML)"); endif
    if (xlsinterfaces.POI)
      if (deflt), printf ("; "); else, printf ("*; "); deflt = 1; endif
    endif
  endif

  # Try Java & JExcelAPI
  if (isempty (xlsinterfaces.JXL))
    xlsinterfaces.JXL = 0;
    jpchk = 0; entries = {"jxl"};
    for ii=1:length (jcp)
      for jj=1:length (entries)
        if (isempty (strfind (lower (jcp{ii}), entries{jj}))), ++jpchk; endif
      endfor
    endfor
    if (jpchk > 0)
      xlsinterfaces.JXL = 1;
      printf ("JXL");
      if (deflt), printf ("; "); else, printf ("*; "); deflt = 1; endif
    endif
  endif

  # Try Java & OpenXLS
  if (isempty (xlsinterfaces.OXS))
    xlsinterfaces.OXS = 0;
    jpchk = 0; entries = {"openxls"};
    for ii=1:length (jcp)
      for jj=1:length (entries)
        if (isempty (strfind (lower (jcp{ii}), entries{jj}))), ++jpchk; endif
      endfor
    endfor
    if (jpchk > 0)
      xlsinterfaces.OXS = 1;
      printf ("OXS");
      if (deflt), printf ("; "); else, printf ("*; "); deflt = 1; endif
    endif
  endif

  # Try Java & UNO
  if (isempty (xlsinterfaces.UNO))
    xlsinterfaces.UNO = 0;
    # entries0(1) = not a jar but a directory (<00o_install_dir/program/>)
    jpchk = 0; entries = {'program', 'unoil', 'jurt', 'juh', 'unoloader', 'ridl'};
    for jj=1:numel (entries)
      for ii=1:numel (jcp)
        jcplst = strsplit (jcp{ii}, filesep);
        jcpentry = jcplst {end};
        if (~isempty (strfind (lower (jcpentry), lower (entries{jj})))); ++jpchk; endif
      endfor
    endfor
    if (jpchk >= numel (entries))
      xlsinterfaces.UNO = 1;
      printf ('UNO');
      if (deflt), printf ("; "); else, printf ("*; "); deflt = 1; uno_1st_time = min (++uno_1st_time, 2); endif
    endif
  endif

  # ---- Other interfaces here, similar to the ones above

  if (deflt), printf ("(* = active interface)\n"); endif

  ## FIXME the below stanza should be dropped once UNO is stable.
  # Echo a suitable warning about experimental status:
  if (uno_1st_time == 1)
    ++uno_1st_time;
    printf ("\nPLEASE NOTE: UNO (=OpenOffice.org-behind-the-scenes) is EXPERIMENTAL\n");
    printf ("After you've opened a spreadsheet file using the UNO interface,\n");
    printf ("xlsclose on that file will kill ALL OpenOffice.org invocations,\n");
    printf ("also those that were started outside and/or before Octave!\n");
    printf ("Trying to quit Octave w/o invoking xlsclose will only hang Octave.\n\n");
  endif

endfunction
