blob: a184c77f89a351f3aa21928f429ba408d5d53fde [file] [log] [blame]
/*
* R : A Computer Language for Statistical Data Analysis
* file registry.c
* Copyright (C) 2005--2013 The R Core Team
*
* 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 this program; if not, a copy is available at
* https://www.R-project.org/Licenses/
*/
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif
#include "win-nls.h"
#include "Defn.h"
/* Mingw-w64 defines this to be 0x0502 */
#ifndef _WIN32_WINNT
# define _WIN32_WINNT 0x0502 /* for KEY_WOW64_64KEY */
#endif
#include <windows.h>
const static struct {
const char * reg;
HKEY key;
}
KeyTable[] = {
{ "HCC", HKEY_CURRENT_CONFIG },
{ "HCR", HKEY_CLASSES_ROOT },
{ "HCU", HKEY_CURRENT_USER },
{ "HLM", HKEY_LOCAL_MACHINE },
{ "HPD", HKEY_PERFORMANCE_DATA },
{ "HU" , HKEY_USERS },
{NULL, NULL}
};
const char *formatError(DWORD res)
{
static char buf[1000], *p;
FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
NULL, res,
MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
buf, 1000, NULL);
p = buf+strlen(buf) -1;
if(*p == '\n') *p = '\0';
p = buf+strlen(buf) -1;
if(*p == '\r') *p = '\0';
p = buf+strlen(buf) -1;
if(*p == '.') *p = '\0';
return buf;
}
static HKEY find_hive(const char *hkey)
{
int i;
for(i = 0; KeyTable[i].reg; i++)
if(!strcmp(hkey, KeyTable[i].reg)) return KeyTable[i].key;
error(_("invalid '%s' value"), "hive");
return HKEY_LOCAL_MACHINE; /* -Wall */
}
static SEXP mkCharUcs(wchar_t *name)
{
int n = wcslen(name), N = 3*n+1;
char buf[N];
R_CheckStack();
wcstombs(buf, name, N); buf[N-1] = '\0';
return mkCharCE(buf, CE_UTF8);
}
static SEXP readRegistryKey1(HKEY hkey, const wchar_t *name)
{
SEXP ans = R_NilValue;
LONG res;
DWORD type, size0 = 10000, size = size0;
BYTE data[10000], *d = data;
res = RegQueryValueExW(hkey, name, NULL, &type, d, &size);
while (res == ERROR_MORE_DATA) {
size0 *= 10;
size = size0;
d = (BYTE *) R_alloc(size0, sizeof(char));
res = RegQueryValueExW(hkey, name, NULL, &type, d, &size);
}
if (res != ERROR_SUCCESS) return ans;
switch(type) {
case REG_NONE:
/* NULL */
break;
case REG_DWORD:
ans = allocVector(INTSXP, 1);
memcpy(INTEGER(ans), d, 4);
break;
case REG_DWORD_BIG_ENDIAN:
{
BYTE d4[4];
int i;
for(i = 0; i < 4; i++) d4[3-i] = d[i];
ans = allocVector(INTSXP, 1);
memcpy(INTEGER(ans), d4, 4);
break;
}
case REG_SZ:
case REG_EXPAND_SZ:
{
PROTECT(ans = allocVector(STRSXP, 1));
SET_STRING_ELT(ans, 0, mkCharUcs((wchar_t *)d));
UNPROTECT(1);
break;
}
case REG_BINARY:
ans = allocVector(RAWSXP, size);
memcpy(RAW(ans), d, size);
break;
case REG_MULTI_SZ:
{
int i, n;
wchar_t *p = (wchar_t *)d;
for (n = 0; *p; n++) { for(; *p; p++) {}; p++; }
PROTECT(ans = allocVector(STRSXP, n));
for (i = 0, p = (wchar_t *)d; i < n; i++) {
SET_STRING_ELT(ans, i, mkCharUcs(p));
for(; *p; p++) {};
p++;
}
UNPROTECT(1);
break;
}
case REG_LINK:
warning("unhandled key type %s\n", "REG_LINK");
ans = mkString("<REG_LINK>");
break;
case REG_RESOURCE_LIST:
warning("unhandled key type %s\n", "REG_RESOURCE_LIST");
ans = mkString("<REG_RESOURCE_LIST>");
break;
default:
warning("unhandled key type %d\n", type);
}
return ans;
}
static SEXP readRegistryKey(HKEY hkey, int depth, int view)
{
int i, k = 0, size0, *indx;
SEXP ans, nm, ans0, nm0, tmp, sind;
DWORD res, nsubkeys, maxsubkeylen, nval, maxvalnamlen, size;
wchar_t *name;
HKEY sub;
REGSAM acc = KEY_READ;
if (depth <= 0) return mkString("<subkey>");
if(view == 2) acc |= KEY_WOW64_32KEY;
else if(view == 3) acc |= KEY_WOW64_64KEY;
res = RegQueryInfoKey(hkey, NULL, NULL, NULL,
&nsubkeys, &maxsubkeylen, NULL, &nval,
&maxvalnamlen, NULL, NULL, NULL);
if (res != ERROR_SUCCESS)
error("RegQueryInfoKey error code %d: '%s'", (int) res,
formatError(res));
size0 = max(maxsubkeylen, maxvalnamlen) + 1;
name = (wchar_t *) R_alloc(size0, sizeof(wchar_t));
PROTECT(ans = allocVector(VECSXP, nval + nsubkeys));
PROTECT(nm = allocVector(STRSXP, nval+ nsubkeys));
if (nval > 0) {
PROTECT(ans0 = allocVector(VECSXP, nval));
PROTECT(nm0 = allocVector(STRSXP, nval));
for (i = 0; i < nval; i++) {
size = size0;
res = RegEnumValueW(hkey, i, (LPWSTR) name, &size,
NULL, NULL, NULL, NULL);
if (res != ERROR_SUCCESS) break;
SET_VECTOR_ELT(ans0, i, readRegistryKey1(hkey, name));
SET_STRING_ELT(nm0, i, mkCharUcs(name));
}
/* now sort by name */
PROTECT(sind = allocVector(INTSXP, nval)); indx = INTEGER(sind);
for (i = 0; i < nval; i++) indx[i] = i;
orderVector1(indx, nval, nm0, TRUE, FALSE, R_NilValue);
for (i = 0; i < nval; i++, k++) {
SET_VECTOR_ELT(ans, k, VECTOR_ELT(ans0, indx[i]));
if (LENGTH(tmp = STRING_ELT(nm0, indx[i])))
SET_STRING_ELT(nm, k, tmp);
else
SET_STRING_ELT(nm, k, mkChar("(Default)"));
}
UNPROTECT(3);
}
if (nsubkeys > 0) {
PROTECT(ans0 = allocVector(VECSXP, nsubkeys));
PROTECT(nm0 = allocVector(STRSXP, nsubkeys));
for (i = 0; i < nsubkeys; i++) {
size = size0;
res = RegEnumKeyExW(hkey, i, (LPWSTR) name, &size,
NULL, NULL, NULL, NULL);
if (res != ERROR_SUCCESS) break;
res = RegOpenKeyExW(hkey, (LPWSTR) name, 0, acc, &sub);
if (res != ERROR_SUCCESS) break;
SET_VECTOR_ELT(ans0, i, readRegistryKey(sub, depth-1, view));
SET_STRING_ELT(nm0, i, mkCharUcs(name));
RegCloseKey(sub);
}
/* now sort by name */
PROTECT(sind = allocVector(INTSXP, nsubkeys)); indx = INTEGER(sind);
for (i = 0; i < nsubkeys; i++) indx[i] = i;
orderVector1(indx, nsubkeys, nm0, TRUE, FALSE, R_NilValue);
for (i = 0; i < nsubkeys; i++, k++) {
SET_VECTOR_ELT(ans, k, VECTOR_ELT(ans0, indx[i]));
SET_STRING_ELT(nm, k, STRING_ELT(nm0, indx[i]));
}
UNPROTECT(3);
}
setAttrib(ans, R_NamesSymbol, nm);
UNPROTECT(2);
return ans;
}
SEXP readRegistry(SEXP call, SEXP op, SEXP args, SEXP env)
{
SEXP ans;
HKEY hive, hkey;
LONG res;
const wchar_t *key;
int maxdepth, view;
REGSAM acc = KEY_READ;
args = CDR(args);
if(!isString(CAR(args)) || LENGTH(CAR(args)) != 1)
error(_("invalid '%s' value"), "key");
key = filenameToWchar(STRING_ELT(CAR(args), 0), 0);
if(!isString(CADR(args)) || LENGTH(CADR(args)) != 1)
error(_("invalid '%s' value"), "hive");
maxdepth = asInteger(CADDR(args));
if(maxdepth == NA_INTEGER || maxdepth < 1)
error(_("invalid '%s' value"), "maxdepth");
hive = find_hive(CHAR(STRING_ELT(CADR(args), 0)));
view = asInteger(CADDDR(args));
/* Or KEY_READ with KEY_WOW64_64KEY or KEY_WOW64_32KEY to
explicitly access the 64- or 32- bit registry view. See
http://msdn.microsoft.com/en-us/library/aa384129(VS.85).aspx
*/
if(view == 2) acc |= KEY_WOW64_32KEY;
else if(view == 3) acc |= KEY_WOW64_64KEY;
res = RegOpenKeyExW(hive, key, 0, acc, &hkey);
if (res == ERROR_FILE_NOT_FOUND)
error(_("Registry key '%ls' not found"), key);
if (res != ERROR_SUCCESS)
error("RegOpenKeyEx error code %d: '%s'", (int) res, formatError(res));
ans = readRegistryKey(hkey, maxdepth, view);
RegCloseKey(hkey);
return ans;
}