subsurface/windows.c
Lubomir I. Ivanov 30e10183ca windows.c: Use a zeroed buffer when retrieving the module path
There is a small API note on GetModuleFileName(), which says:
"Windows XP: The string is truncated to nSize characters
and is not null-terminated."

Which means that on XP it will be only safe if we pass a zeroed
buffer to it, otherwise the next call to wcsrchr (which is
a strchr for wchar_t) may not find a relative terminating \0 in
the buffer, returning a wrong pointer and resulting in a corrupted
string.

Signed-off-by: Lubomir I. Ivanov <neolit123@gmail.com>
Signed-off-by: Dirk Hohndel <dirk@hohndel.org>
2013-03-05 12:27:32 -08:00

328 lines
8.7 KiB
C

/* windows.c */
/* implements Windows specific functions */
#include "dive.h"
#include "display-gtk.h"
#include <windows.h>
#include <shlobj.h>
const char system_divelist_default_font[] = "Sans 8";
static HKEY hkey;
void subsurface_open_conf(void)
{
LONG success;
success = RegCreateKeyEx(HKEY_CURRENT_USER, (LPCTSTR)TEXT("Software\\subsurface"),
0L, NULL, REG_OPTION_NON_VOLATILE, KEY_ALL_ACCESS,
NULL, &hkey, NULL);
if (success != ERROR_SUCCESS)
printf("CreateKey Software\\subsurface failed %ld\n", success);
}
void subsurface_unset_conf(char *name)
{
RegDeleteValue(hkey, (LPCTSTR)name);
}
void subsurface_set_conf(char *name, const char *value)
{
/* since we are using the pointer 'value' as both an actual
* pointer to the string setting and as a way to pass the
* numbers 0 and 1 to this function for booleans, one of the
* calls to RegSetValueEx needs to pass &value (when we want
* to pass the boolean value), the other one passes value (the
* address of the string. */
int wlen;
wchar_t *wname, *wstring;
wname = (wchar_t *)g_utf8_to_utf16(name, -1, NULL, NULL, NULL);
if (!wname)
return;
wlen = g_utf8_strlen((char *)value, -1);
wstring = (wchar_t *)g_utf8_to_utf16((char *)value, -1, NULL, NULL, NULL);
if (!wstring || !wlen) {
free(wname);
return;
}
RegSetValueExW(hkey, (LPCWSTR)wname, 0, REG_SZ, (const BYTE *)wstring,
wlen * sizeof(wchar_t));
free(wstring);
free(wname);
}
void subsurface_set_conf_int(char *name, int value)
{
RegSetValueEx(hkey, (LPCTSTR)name, 0, REG_DWORD, (const BYTE *)&value, 4);
}
void subsurface_set_conf_bool(char *name, int value)
{
subsurface_set_conf_int(name, value);
}
const void *subsurface_get_conf(char *name)
{
const int csize = 64;
int blen = 0;
LONG ret = ERROR_MORE_DATA;
wchar_t *wstring = NULL, *wname;
char *utf8_string;
wname = (wchar_t *)g_utf8_to_utf16(name, -1, NULL, NULL, NULL);
if (!wname)
return NULL;
blen = 0;
/* lest try to load the string in chunks of 'csize' bytes until it fits */
while (ret == ERROR_MORE_DATA) {
blen += csize;
wstring = (wchar_t *)realloc(wstring, blen * sizeof(wchar_t));
ret = RegQueryValueExW(hkey, (LPCWSTR)wname, NULL, NULL,
(LPBYTE)wstring, (LPDWORD)&blen);
}
/* that's what happens the first time we start - just return NULL */
if (ret != ERROR_SUCCESS) {
free(wname);
free(wstring);
return NULL;
}
/* convert the returned string into utf-8 */
utf8_string = g_utf16_to_utf8(wstring, -1, NULL, NULL, NULL);
free(wstring);
free(wname);
if (!utf8_string)
return NULL;
if (!g_utf8_validate(utf8_string, -1, NULL)) {
free(utf8_string);
return NULL;
}
return utf8_string;
}
int subsurface_get_conf_int(char *name)
{
DWORD value = -1, len = 4;
LONG ret = RegQueryValueEx(hkey, (LPCTSTR)TEXT(name), NULL, NULL,
(LPBYTE)&value, (LPDWORD)&len);
if (ret != ERROR_SUCCESS)
return -1;
return value;
}
int subsurface_get_conf_bool(char *name)
{
int ret = subsurface_get_conf_int(name);
if (ret == -1)
return ret;
return ret != 0;
}
void subsurface_flush_conf(void)
{
/* this is a no-op */
}
void subsurface_close_conf(void)
{
RegCloseKey(hkey);
}
int subsurface_fill_device_list(GtkListStore *store)
{
const int bufdef = 512;
const char *dlabels[] = {"UEMISSDA", NULL};
const char *devdef = "COM1";
GtkTreeIter iter;
int index = -1, nentries = 0, ret, i;
char bufname[bufdef], bufval[bufdef], *p;
DWORD nvalues, bufval_len, bufname_len;
HKEY key;
/* add serial ports */
ret = RegOpenKeyExA(HKEY_LOCAL_MACHINE, "HARDWARE\\DEVICEMAP\\SERIALCOMM",
0, KEY_READ, &key);
if (ret == ERROR_SUCCESS) {
ret = RegQueryInfoKeyA(key, NULL, NULL, NULL, NULL, NULL, NULL, &nvalues,
NULL, NULL, NULL, NULL);
if (ret == ERROR_SUCCESS)
for (i = 0; i < nvalues; i++) {
memset(bufval, 0, bufdef);
memset(bufname, 0, bufdef);
bufname_len = bufdef;
bufval_len = bufdef;
ret = RegEnumValueA(key, i, bufname, &bufname_len, NULL, NULL, bufval,
&bufval_len);
if (ret == ERROR_SUCCESS) {
gtk_list_store_append(store, &iter);
gtk_list_store_set(store, &iter, 0, bufval, -1);
if (is_default_dive_computer_device(bufval))
index = nentries;
nentries++;
}
}
}
/* add drive letters that match labels */
memset(bufname, 0, bufdef);
bufname_len = bufdef;
if (GetLogicalDriveStringsA(bufname_len, bufname)) {
p = bufname;
while (*p) {
memset(bufval, 0, bufdef);
if (GetVolumeInformationA(p, bufval, bufdef, NULL, NULL, NULL, NULL, 0)) {
for (i = 0; dlabels[i] != NULL; i++)
if (!strcmp(bufval, dlabels[i])) {
char name[80];
snprintf(name, sizeof(name), "%s (%s)", p, dlabels[i]);
gtk_list_store_append(store, &iter);
gtk_list_store_set(store, &iter, 0, name, -1);
if (is_default_dive_computer_device(p))
index = nentries;
nentries++;
}
}
p = &p[strlen(p) + 1];
}
}
/* if we can't find anything, use the default */
if (!nentries) {
gtk_list_store_append(store, &iter);
gtk_list_store_set(store, &iter,
0, devdef, -1);
if (is_default_dive_computer_device(devdef))
index = 0;
}
return index;
}
const char *subsurface_icon_name()
{
return "subsurface.ico";
}
const char *system_default_filename(void)
{
char datapath[MAX_PATH];
const char *user;
char *buffer;
int len;
user = g_get_user_name();
if (! SUCCEEDED(SHGetFolderPath(NULL, CSIDL_APPDATA, NULL, 0, datapath))) {
datapath[0] = '.';
datapath[1] = '\0';
}
len = strlen(datapath) + strlen(user) + 17;
buffer = malloc(len);
snprintf(buffer, len, "%s\\Subsurface\\%s.xml", datapath, user);
return buffer;
}
const char *subsurface_gettext_domainpath(char *argv0)
{
/* first hackishly make sure that the LANGUAGE information is correctly set up
* in the environment */
char buffer[80];
gchar *locale = g_win32_getlocale();
snprintf(buffer, sizeof(buffer), "LANGUAGE=%s.UTF-8", locale);
putenv(buffer);
g_free(locale);
/* always use translation directory relative to install location, regardless of argv0 */
return "./share/locale";
}
void subsurface_ui_setup(GtkSettings *settings, GtkWidget *menubar,
GtkWidget *vbox, GtkUIManager *ui_manager)
{
gtk_box_pack_start(GTK_BOX(vbox), menubar, FALSE, FALSE, 0);
}
/* barely documented API */
extern int __wgetmainargs(int *, wchar_t ***, wchar_t ***, int, int *);
/* expand-convert the UTF-16 argument list to a list of UTF-8 strings */
void subsurface_command_line_init(gint *argc, gchar ***argv)
{
wchar_t **wargv, **wenviron, *p, path[MAX_PATH] = {0};
gchar **argv_new;
gchar *s;
/* for si we assume that a struct address will equal the address
* of its first and only int member */
gint i, n, ret, si;
/* change the current process path to the module path, so that we can
* access relative folders such as ./share and ./xslt */
GetModuleFileNameW(NULL, path, MAX_PATH - 1);
p = wcsrchr(path, '\\');
*(p + 1) = '\0';
SetCurrentDirectoryW(path);
/* memory leak tools may reports a potential issue here at a call
* to strcpy_s in msvcrt, wich should be a false positive. but even if there
* is some kind of a leak, it should be unique and have the same
* lifespan as the process heap. */
ret = __wgetmainargs(&n, &wargv, &wenviron, TRUE, &si);
if (ret < 0) {
g_warning("Cannot convert command line");
return;
}
argv_new = g_malloc(sizeof(gchar *) * (n + 1));
for (i = 0; i < n; ++i) {
s = g_utf16_to_utf8((gunichar2 *)wargv[i], -1, NULL, NULL, NULL);
if (!s) {
g_warning("Cannot convert command line argument (%d) to UTF-8", (i + 1));
s = "\0";
} else if (!g_utf8_validate(s, -1, NULL)) {
g_warning("Cannot validate command line argument '%s' (%d)", s, (i + 1));
g_free(s);
s = "\0";
}
argv_new[i] = s;
}
argv_new[n] = NULL;
/* update the argument list and count */
if (argv && argc) {
*argv = argv_new;
*argc = n;
}
}
/* once done, free the argument list */
void subsurface_command_line_exit(gint *argc, gchar ***argv)
{
int i;
for (i = 0; i < *argc; i++)
g_free((*argv)[i]);
g_free(*argv);
}
gboolean subsurface_launch_for_uri(const char* uri)
{
gboolean ret = FALSE;
wchar_t *wuri = (wchar_t *)g_utf8_to_utf16(uri, -1, NULL, NULL, NULL);
if (wuri) {
if ((INT_PTR)ShellExecuteW(NULL, L"open", wuri, NULL, NULL, SW_SHOWNORMAL) > 32)
ret = TRUE;
free(wuri);
}
if (!ret)
g_message("ShellExecute failed for: %s", uri);
return ret;
}
/* check if we are running a newer OS version */
gboolean subsurface_os_feature_available(os_feature_t f)
{
switch (f) {
case UTF8_FONT_WITH_STARS:
if ((GetVersion() & 0xff) < 6)
return FALSE; /* version less than Vista */
else
return TRUE;
break;
default:
return TRUE;
}
}