=head1 NAME

CFX5::LIST - routines for accessing the CFX LIST tools

=head1 SYNOPSIS

  use CFX5::LIST;

=head1 DESCRIPTION

A Perl module for retrieving information from the CFX LIST tools.
This includes (and is currently limited to!) the license manager.  It
assumes a valid installation of the LIST tools, and may not work in
all cases which do not have such an installation.

=cut

package CFX5::LIST;

use strict;

use CFX5::FileTests;
use CFX5::PathManip qw(fullpath dirname join_file);
use CFX5::Config qw(%Config query_conf);
use CFX5::Output qw(:all);
use CFX5::Win32::Registry qw(registry_get registry_get_all registry_list_subkeys);

=head2 License Manager

The functions in this section deal with the license manager, in the
context of a CFX-5 installation.

=head3 license_dir

  my $dir=CFX5::LIST::license_dir();

Returns the "license directory", the directory in which the licensing
tools are installed.

The setting is taken from the C<CFX_LICENSING_ROOT> (or
CFX_LICENSE_ROOT, oops) variable if possible.  If that is not set, it
is found in the registry on Windows.

If that fails, on both Windows and UNIX, it will look at the directory
one above the CFX-5 root directory for a directory called C<licensing>
or C<Licensing> -- this is the new license manager.  This will be
returned if found.

If all that fails, it will then look in I<CFX5ROOT>C</license> for a
copy of the old licensing tools.

If the licensing tools cannot be found, returns C<undef>.

=cut

sub license_dir {
  my $c=query_conf('CFX_LICENSING_ROOT') || query_conf('CFX_LICENSE_ROOT');
  if (defined $c and -d $c) {
    # use this for preference
    return $c;
  }

  if ($Config{windows_nt}) {
    # the new license manager writes a useful key, but failing that we
    # can try the funny one the old license manager wrote
    for my $path ("SOFTWARE\\CFX\\CFX License Manager\\CurrentVersion\\Installation Root",
                  'SOFTWARE\Microsoft\Windows\CurrentVersion\App Paths\cfxlm.exe\Path') {
      my $cfxlm=registry_get($path);
      if (defined $cfxlm and -d $cfxlm) {
        return fullpath($cfxlm);
      } elsif (defined $cfxlm) {
        _note("CFX Licensing Tools home directory in the registry is\n\t", $cfxlm,
              "\nbut this directory does not exist.\n\n");
      }
    }
  }

  # now look relative to our root directory.
  for my $licdir (
                  # new-style
                  join_file(dirname($Config{CFX_ROOT}),'Licensing'),
                  join_file(dirname($Config{CFX_ROOT}),'licensing'),
                  # new-style but installed into the product installation
                  join_file($Config{CFX_ROOT},'Licensing'),
                  join_file($Config{CFX_ROOT},'licensing'),
                  # if both of these fail, pick it up from the
                  # previous directory
                  join_file($Config{CFX_ROOT},'license')) {
    if (-d $licdir) {
      return $licdir;
    }
  }

  undef;
}

use CFX5::Config::def
  (
   CFX_LICENSING_ROOT => sub { license_dir(); },
   CFX_LICMAN_EXEC => sub {
     my $root=$Config{CFX_LICENSING_ROOT};
     return undef unless defined $root;
     my $cfxlm=$Config{windows_nt} ? 'cfxlm.exe' : 'cfxlm';
     join_file($root, $cfxlm);
   },
  );

=head3 license_file

  my $file=CFX5::LIST::license_file();

Returns a good value for CFX_LICENSE_FILE (aka LM_LICENSE_FILE) if one
can be found, either from the registry on Windows, or by looking in
various known directories.  Return value is C<undef> if no such file
can be found.  Note that we do not check any existing variables such
as C<CFX_LICENSE_FILE>; this is the responsibility of the caller.

=cut

sub license_file {
  my $file;

  if ($Config{windows_nt}) {
    #
    # we're on Windows NT, so check the registry
    #
    my $kp='SOFTWARE\FLEXlm License Manager';
    for my $ks ('CFX License Service','CFX-5 License Service') {
      _debug("looking for $ks service");
      $file = registry_get("$kp/$ks/License");
      if (defined $file) {
	if ($file !~ m!^\s*$!) {
	  _debug("Got $file");
	  last;
	}
	_debug("Read empty filename");
      }
    }
    
    if (defined $file and -f $file) {
      return fullpath($file);
    } elsif (defined $file and $file =~ m!^\s*$!) {
      # empty
      1;
    } elsif (defined $file) {
      _warn("CFX license file in the registry is\n\t", $file,
	    "\nbut this file does not exist.");
      return undef;
    }
  }

  #
  # check in the licensing tools directory
  #
  my $dir=license_dir();
  if (defined $dir) {
    $file=join_file($dir,'license.dat');
    if (-f $file) {
      return $file;
    }
  }

  #
  # now, and this will probably only be different on NT, look in the
  # CFX_ROOT/license directory too
  #
  $file=join_file($Config{CFX_ROOT},'license','license.dat');
  if (-f $file) {
    return $file;
  }

  #
  # nope, no luck
  #
  return undef;
}

=head3 license_info_text

  my $string=CFX5::LIST::license_info_text();

Returns information about installed licenses, for use by the cfx5info
command.

=cut

sub license_info_text {
  my $file;

  my @rv;

  push @rv, "Licensing tools directory: ";
  if (defined license_dir()) {
    push @rv, license_dir();
  } else {
    push @rv, "(unset)";
  }
  push @rv, "\n\n";

  if ($Config{windows_nt}) {
    #
    # we're on Windows NT, so check the registry
    #
    push @rv, "The following license daemons are set up on this machine:\n\n";

    my $kp='SOFTWARE\FLEXlm License Manager';
    for my $ks (registry_list_subkeys($kp)) {
      my %data = registry_get_all("$kp/$ks");
      
      push @rv, "$ks:\n";
      # lowercase all the key names
      for my $k (keys %data) {
	if (lc($k) ne $k) {
	  $data{lc($k)}=$data{$k};
	  delete $data{$k};
	}
      }
      for my $p (['service', 'Name'],
		 ['license', 'License file'],
		 ['lmgrd',   'lmgrd executable'],
		 ['lmgrd_log_file', 'lmgrd log file location']) {
	my ($k,$n)=@$p;
	if (exists $data{$k}) {
	  if (defined $data{$k}) {
	    push @rv, " * $n: $data{$k}\n";
	  } else {
	    push @rv, " * $n: (no setting)\n";
	  }
	  delete $data{$k};
	}
      }
      for my $k (sort keys %data) {
	if (defined $data{$k}) {
	  push @rv, " * $k: $data{$k}\n";
	} else {
	  push @rv, " * $k: (no setting)\n";
	}
      }
      push @rv, "\n";
    }
  }

  if ($Config{ANSYSLIC_DIR}) {
    my $inifile=join_file($Config{ANSYSLIC_DIR},'ansyslmd.ini');
    if (-f $inifile) {
      local *FILE;
      if (open FILE, "<$inifile") {
        push @rv, "Contents of $inifile:\n";
        while (defined (my $l=<FILE>)) {
          push @rv, "  $l";
          if ($l =~ m!\r\@!) {
            # problem with reading files containing CRs on UNIX --
            # everything seems to install OK, but the ansyslmd.ini is
            # corrupt and none of the licensing will work!  Caused by
            # reading the CR after the port number and inserting it
            # into this file.
            _error("The following line in $inifile is corrupted, and has a carriage return character before the @ sign:",
                   "\n\n  $l",
                   "\n\nIf you are experiencing licensing problems, please delete and manually recreate this server specification using the ANSYS license management tools.");
          }
        }
        push @rv, "\n";
        close FILE;
      } else {
        push @rv, "\nWarning: $inifile could not be read: $!\n\n";
      }
    }
  }

  return @rv;
}


1;
