# HG changeset patch
# User Markus Mützel <markus.muetzel@gmx.de>
# Date 1516567491 -3600
#      Sun Jan 21 21:44:51 2018 +0100
# Node ID a7cb3ae01de585b6fba80b4fc262579076b4d4fa
# Parent  be37df6e1fb4fd1e7e2b31f977702928ec8b065b
Add function "winqueryreg" (bug #52935).

* sysdep.cc: Add new function. Add BIST tests.

* system.txi: Add DOCSTRING entry.

* __unimplemented__.m: Remove function from list.

diff -r be37df6e1fb4 -r a7cb3ae01de5 doc/interpreter/system.txi
--- a/doc/interpreter/system.txi	Sat Jan 20 09:29:07 2018 +0100
+++ b/doc/interpreter/system.txi	Sun Jan 21 21:44:51 2018 +0100
@@ -405,6 +405,8 @@
 
 @DOCSTRING(get_home_directory)
 
+@DOCSTRING(winqueryreg)
+
 @node Current Working Directory
 @section Current Working Directory
 
diff -r be37df6e1fb4 -r a7cb3ae01de5 libinterp/corefcn/sysdep.cc
--- a/libinterp/corefcn/sysdep.cc	Sat Jan 20 09:29:07 2018 +0100
+++ b/libinterp/corefcn/sysdep.cc	Sun Jan 21 21:44:51 2018 +0100
@@ -712,6 +712,306 @@
 ## Test for unsetenv is in setenv test
 */
 
+#if defined (OCTAVE_USE_WINDOWS_API)
+LONG
+get_regkey_value (HKEY h_rootkey, const std::string subkey,
+                  const std::string name, octave_value& value)
+{
+  DWORD type = 0;
+  DWORD length = 0;
+  LONG result = RegGetValueA (h_rootkey, subkey.c_str (), name.c_str (),
+                              RRF_RT_ANY, &type, nullptr, &length);
+  if (result != ERROR_SUCCESS)
+    return result;
+  
+  if (type == REG_DWORD)
+    {
+      void *data = malloc (length * sizeof (DWORD));
+      result = RegGetValueA (h_rootkey, subkey.c_str (), name.c_str (),
+                             RRF_RT_ANY, &type, data, &length);
+      if (result == ERROR_SUCCESS)
+        {
+          value = octave_int32 (*(static_cast<DWORD *> (data)));
+          free (data);
+        }
+    }
+  else if ((type == REG_SZ) || (type == REG_EXPAND_SZ))
+    {
+      void *data = malloc (length * sizeof (char));
+      result = RegGetValueA (h_rootkey, subkey.c_str (), name.c_str (),
+                             RRF_RT_ANY, &type, data, &length);
+      if (result == ERROR_SUCCESS)
+        {
+          char *str = static_cast<char *> (data);
+          charNDArray name_array (dim_vector (1, length));
+          for (octave_idx_type i = 0; i < length; i++)
+            name_array.xelem(i) = str[i];
+          free (data);
+          value = name_array;
+        }
+    }
+
+  return result;
+}
+
+LONG
+get_regkey_names (HKEY h_rootkey, const std::string subkey,
+                  std::list<std::string> &fields)
+{
+  LONG retval;
+  HKEY h_subkey;
+
+  fields.clear ();
+
+  retval = RegOpenKeyEx (h_rootkey, subkey.c_str (), 0, KEY_READ, &h_subkey);
+  if (retval != ERROR_SUCCESS)
+    return retval;
+
+  DWORD idx = 0;
+  #define MAX_VALUE_NAME_SIZE 32767
+  char value_name[MAX_VALUE_NAME_SIZE+1];
+  DWORD value_name_size = MAX_VALUE_NAME_SIZE;
+
+  while (true)
+    {
+      retval = RegEnumValue (h_subkey, idx, value_name, &value_name_size,
+                             NULL, NULL, NULL, NULL);
+      if (retval != ERROR_SUCCESS)
+        break;
+      fields.push_back (value_name);
+      value_name_size = MAX_VALUE_NAME_SIZE;
+      idx++;
+    }
+
+  if (retval == ERROR_NO_MORE_ITEMS)
+    retval = ERROR_SUCCESS;
+
+  RegCloseKey (h_subkey);
+
+  return retval;
+}
+#endif
+
+DEFUN (winqueryreg, args, ,
+       doc: /* -*- texinfo -*-
+@deftypefn  {} {@var{value} =} winqueryreg (@var{rootkey}, @var{subkey}, @var{valuename})
+@deftypefnx {} {@var{value} =} winqueryreg (@var{rootkey}, @var{subkey})
+@deftypefnx {} {@var{valnames} =} winqueryreg (@code{"name"}, @var{rootkey}, @var{subkey})
+
+Query names or value from the Windows registry.
+
+On Windows, return the value of a registry key @var{subkey} at the root key
+@var{rootkey}.
+You can specify the name of the queried registry value with the optional
+argument @var{valuename}.  Otherwise, if called with two arguments or if
+@var{valuename} is empty, the default value of that key is returned.
+If the value is of the type "REG_DWORD", @var{value} is of the class int32.  If
+the value is of the type "REG_SZ" or "REG_EXPAND_SZ" a string is returned.
+
+If the first argument is @code{"name"}, a cell array of strings with the names
+of the values at that key is returned.
+
+The variable @var{rootkey} must be a string with a valid root key identifier:
+@table @asis
+@item HKCR
+HKEY_CLASSES_ROOT
+@item HKCU
+HKEY_CURRENT_USER
+@item HKLM
+HKEY_LOCAL_MACHINE
+@item HKU
+HKEY_USERS
+@item HKEY_PERFORMANCE_DATA
+@end table
+
+Examples:
+
+Get a list of value names at the key @code{"HKCU\Environment"}:
+@example
+@var{valuenames} = winqueryreg ("name", "HKEY_CURRENT_USER", ...
+                          "Environment");
+@end example
+
+For each @var{valuenames}, display the value:
+@example
+for @var{k} = 1:length (@var{valuenames})
+  @var{val} = winqueryreg ("HKEY_CURRENT_USER", "Environment", ...
+                     @var{valuenames}@{@var{k}@});
+  @var{str} = sprintf ("%s = %s", @var{valuenames}@{@var{k}@}, num2str(@var{val}));
+  disp (@var{str});
+endfor
+@end example
+
+On non-Windows platforms, this function fails with an error.
+@end deftypefn */)
+{
+#if defined (OCTAVE_USE_WINDOWS_API)
+  if ((args.length () < 2) || (args.length () > 3))
+    print_usage ();
+
+  // Input check
+  std::string rootkey_name =
+      args(0).xstring_value ("winqueryreg: The first argument must be 'name' "
+                             "or a valid ROOTKEY identifier.");
+  std::string subkey_name = "";
+  std::string value_name = "";
+  bool get_names = false;
+  if (rootkey_name.compare ("name") == 0)
+    {
+      if (args.length () < 3)
+        error ("winqueryreg: If the first argument is 'name', ROOTKEY and "
+               "SUBKEY must be given.");
+      get_names = true;
+      rootkey_name =
+          args(1).xstring_value ("winqueryreg: ROOTKEY must be a string.");
+      subkey_name =
+          args(2).xstring_value ("winqueryreg: SUBKEY must be a string.");
+    }
+  else
+    {
+      subkey_name =
+          args(1).xstring_value ("winqueryreg: SUBKEY must be a string.");
+      if (args.length () == 3)
+        value_name =
+            args(2).xstring_value ("winqueryreg: VALUENAME must be a string.");
+    }
+
+  // Get rootkey handle
+  HKEY h_rootkey;
+  if ((rootkey_name.compare ("HKEY_CLASSES_ROOT") == 0) ||
+      (rootkey_name.compare ("HKCR") == 0))
+    h_rootkey = HKEY_CLASSES_ROOT;
+  else if (rootkey_name.compare ("HKEY_CURRENT_CONFIG") == 0)
+    h_rootkey = HKEY_CURRENT_CONFIG;
+  else if ((rootkey_name.compare ("HKEY_CURRENT_USER") == 0) ||
+           (rootkey_name.compare ("HKCU") == 0))
+    h_rootkey = HKEY_CURRENT_USER;
+  else if ((rootkey_name.compare ("HKEY_LOCAL_MACHINE") == 0) ||
+           (rootkey_name.compare ("HKLM") == 0))
+    h_rootkey = HKEY_LOCAL_MACHINE;
+  else if (rootkey_name.compare ("HKEY_PERFORMANCE_DATA") == 0)
+    h_rootkey = HKEY_PERFORMANCE_DATA;
+  else if ((rootkey_name.compare ("HKEY_USERS") == 0) ||
+           (rootkey_name.compare ("HKU") == 0))
+    h_rootkey = HKEY_USERS;
+  else
+    error ("winqueryreg: ROOTKEY is no valid root key identifier.");
+
+  if (get_names)
+    {
+      std::list<std::string> fields;
+
+      LONG retval = get_regkey_names (h_rootkey, subkey_name, fields);
+      if (retval != ERROR_SUCCESS)
+          error ("winqueryreg: Error %d reading names from registry.", retval);
+
+      Cell fieldnames (dim_vector (1, fields.size ()));
+      size_t i;
+      std::list<std::string>::const_iterator it;
+      for (i = 0, it = fields.begin ();
+           it != fields.end (); ++it, ++i)
+        {
+          fieldnames(i) = *it;
+        }
+      return ovl (fieldnames);
+    }
+  else
+    {
+      octave_value key_val;
+      LONG retval = get_regkey_value (h_rootkey, subkey_name, value_name,
+                                      key_val);
+      if (retval != ERROR_SUCCESS)
+        error ("winqueryreg: Error %d reading the specified key.", retval);
+      return ovl (key_val);
+    }
+#else
+  octave_unused_parameter (args);
+  error ("winqueryreg: Function is only supported on Windows platforms.");
+#endif
+}
+
+/*
+%!testif ; ispc ()
+%! assert (ischar (winqueryreg ("HKEY_LOCAL_MACHINE", ...
+%!                              'SOFTWARE\Microsoft\Windows\CurrentVersion', ...
+%!                              "ProgramFilesDir")));
+%!testif ; ispc ()
+%! assert (isa (winqueryreg ("HKEY_LOCAL_MACHINE", ...
+%!                           'SYSTEM\CurrentControlSet\Control\FileSystem', ...
+%!                           "Win31FileSystem"), "int32"));
+%!testif ; ispc ()
+%! assert (iscellstr (winqueryreg ("name", "HKEY_LOCAL_MACHINE", ...
+%!                              'SOFTWARE\Microsoft\Windows\CurrentVersion')));
+%!testif ; ispc ()
+%! try
+%!   winqueryreg (1, 'SOFTWARE\Microsoft\Windows\CurrentVersion');
+%!   error ("Not the one we expect.");
+%! catch err
+%!   assert (err.message, ["winqueryreg: The first argument must be 'name'", ...
+%!                         " or a valid ROOTKEY identifier."]);
+%! end_try_catch
+%!testif ; ispc ()
+%! try
+%!   winqueryreg ("HKEY_LOCAL_MACHINE", 1);
+%!   error ("Not the one we expect.");
+%! catch err
+%!   assert (err.message, "winqueryreg: SUBKEY must be a string.");
+%! end_try_catch
+%!testif ; ispc ()
+%! try
+%!   winqueryreg ("HKEY_LOCAL_MACHINE", ...
+%!                'SOFTWARE\Microsoft\Windows\CurrentVersion', 1);
+%!   error ("Not the one we expect.");
+%! catch err
+%!   assert (err.message, "winqueryreg: VALUENAME must be a string.");
+%! end_try_catch
+%!testif ; ispc ()
+%! try
+%!   winqueryreg ("FOO", ...
+%!                'SOFTWARE\Microsoft\Windows\CurrentVersion');
+%!   error ("Not the one we expect.");
+%! catch err
+%!   assert (err.message, ...
+%!           "winqueryreg: ROOTKEY is no valid root key identifier.");
+%! end_try_catch
+%!testif ; ispc ()
+%! try
+%!   winqueryreg ("HKEY_LOCAL_MACHINE", 'FOO\bar');
+%!   error ("Not the one we expect.");
+%! catch err
+%!   assert (err.message, "winqueryreg: Error 2 reading the specified key.");
+%! end_try_catch
+%!testif ; ispc ()
+%! try
+%!   winqueryreg ("HKEY_LOCAL_MACHINE", ...
+%!                'SOFTWARE\Microsoft\Windows\CurrentVersion', "foo");
+%!   error ("Not the one we expect.");
+%! catch err
+%!   assert (err.message, "winqueryreg: Error 2 reading the specified key.");
+%! end_try_catch
+%!testif ; ispc ()
+%! try
+%!   winqueryreg ("name", "HKEY_LOCAL_MACHINE");
+%!   error ("Not the one we expect.");
+%! catch err
+%!   assert (err.message, ["winqueryreg: If the first argument is 'name', ", ...
+%!                         "ROOTKEY and SUBKEY must be given."]);
+%! end_try_catch
+%!testif ; ispc ()
+%! try
+%!   winqueryreg ("name", 1, 'SOFTWARE\Microsoft\Windows\CurrentVersion');
+%!   error ("Not the one we expect.");
+%! catch err
+%!   assert (err.message, "winqueryreg: ROOTKEY must be a string.");
+%! end_try_catch
+%! try
+%!   winqueryreg ("name", "HKEY_LOCAL_MACHINE", 1);
+%!   error ("Not the one we expect.");
+%! catch err
+%!   assert (err.message, "winqueryreg: SUBKEY must be a string.");
+%! end_try_catch
+*/
+
 // FIXME: perhaps kbhit should also be able to print a prompt?
 
 DEFUN (kbhit, args, ,
diff -r be37df6e1fb4 -r a7cb3ae01de5 scripts/help/__unimplemented__.m
--- a/scripts/help/__unimplemented__.m	Sat Jan 20 09:29:07 2018 +0100
+++ b/scripts/help/__unimplemented__.m	Sun Jan 21 21:44:51 2018 +0100
@@ -1349,7 +1349,6 @@
   "whatsnew",
   "width",
   "winopen",
-  "winqueryreg",
   "withtol",
   "wordcloud",
   "workspace",
