aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--NEWS9
-rw-r--r--src/Makefile.am2
-rw-r--r--src/gpg-error.c46
-rw-r--r--src/gpg-error.def.in1
-rw-r--r--src/gpg-error.h.in8
-rw-r--r--src/gpg-error.vers1
-rw-r--r--src/gpgrt-int.h5
-rw-r--r--src/init.c4
-rw-r--r--src/init.h3
-rw-r--r--src/stringutils.c110
-rw-r--r--src/visibility.c17
-rw-r--r--src/visibility.h2
-rw-r--r--src/w32-utils.c164
13 files changed, 344 insertions, 28 deletions
diff --git a/NEWS b/NEWS
index 626e7d9..3251b09 100644
--- a/NEWS
+++ b/NEWS
@@ -1,6 +1,15 @@
Noteworthy changes in version 1.57 (unreleased) [C40/A40/R_]
-----------------------------------------------
+ * New function gpgrt_fconcat and improved the existing
+ gpgrt_fnameconcat and gpgrt_absfnameconcat. [T7894]
+
+ * Interface changes relative to the 1.55 release:
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ gpgrt_fconcat NEW.
+ GPGRT_FCONCAT_ABS NEW.
+ GPGRT_FCONCAT_TILDE NEW.
+ GPGRT_FCONCAT_SYSCONF NEW.
Release-info: https://dev.gnupg.org/T7847
diff --git a/src/Makefile.am b/src/Makefile.am
index 5f06a29..82b4193 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -129,7 +129,7 @@ TESTS = gpg-error-config-test.sh
#
if HAVE_W32_SYSTEM
arch_sources = w32-gettext.c w32-lock.c w32-lock-obj.h w32-thread.c \
- w32-iconv.c w32-estream.c w32-reg.c spawn-w32.c
+ w32-iconv.c w32-estream.c w32-reg.c spawn-w32.c w32-utils.c
RCCOMPILE = $(RC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) \
$(libgpg_error_la_CPPFLAGS) $(AM_CPPFLAGS) $(CPPFLAGS)
LTRCCOMPILE = $(LIBTOOL) --mode=compile --tag=RC $(RCCOMPILE)
diff --git a/src/gpg-error.c b/src/gpg-error.c
index 8562342..e0b5920 100644
--- a/src/gpg-error.c
+++ b/src/gpg-error.c
@@ -563,6 +563,7 @@ main (int argc, char *argv[])
CMD_GETREG,
CMD_FOPEN,
OPT_OPENMODE,
+ CMD_FCONCAT,
CMD_CHDIR,
CMD_MKDIR
};
@@ -588,10 +589,12 @@ main (int argc, char *argv[])
ARGPARSE_c (CMD_FOPEN, "fopen", "Open the given file"),
ARGPARSE_c (CMD_CHDIR, "chdir", "Try a chdir to the given directory"),
ARGPARSE_c (CMD_MKDIR, "mkdir", "Create the given directory"),
+ ARGPARSE_c (CMD_FCONCAT, "fconcat", "Construct a file name"),
ARGPARSE_group (301, ("@\nOptions:\n ")),
ARGPARSE_s_n (OPT_QUIET, "quiet", "Silence some output"),
ARGPARSE_s_n (OPT_DESC, "desc", "Print with error description"),
ARGPARSE_s_s (OPT_OPENMODE, "openmode", "Specify the open mode"),
+
ARGPARSE_end()
};
gpgrt_argparse_t pargs = { &argc, &argv, ARGPARSE_FLAG_COMMAND };
@@ -629,6 +632,7 @@ main (int argc, char *argv[])
case CMD_FOPEN:
case CMD_CHDIR:
case CMD_MKDIR:
+ case CMD_FCONCAT:
mycmd = pargs.r_opt;
break;
@@ -650,6 +654,17 @@ main (int argc, char *argv[])
if (argc > 1)
gpgrt_usage (1);
}
+ else if (mycmd == CMD_FCONCAT)
+ {
+ if (argc < 2)
+ {
+ es_fprintf (es_stderr,
+ "Usage: %s fconcat <flags> <part1> [<part2> ...]\n\n"
+ " For <flags> see GPGRT_FCONCAT_*\n",
+ gpgrt_strusage (11));
+ exit(2);
+ }
+ }
else if (getregmode || mycmd)
{
if (argc != 1)
@@ -718,6 +733,37 @@ main (int argc, char *argv[])
gpgrt_fclose (fp);
}
}
+ else if (mycmd == CMD_FCONCAT)
+ {
+ unsigned int fconcatflags;
+ char *myfname;
+
+ fconcatflags = strtoul (argv[0], NULL, 0);
+
+ if (argc == 2)
+ myfname = gpgrt_fconcat (fconcatflags, argv[1], NULL);
+ else if (argc == 3)
+ myfname = gpgrt_fconcat (fconcatflags, argv[1], argv[2], NULL);
+ else if (argc == 4)
+ myfname = gpgrt_fconcat (fconcatflags, argv[1], argv[2], argv[3], NULL);
+ else if (argc == 5)
+ myfname = gpgrt_fconcat (fconcatflags, argv[1], argv[2], argv[3],
+ argv[4], NULL);
+ else if (argc == 6)
+ myfname = gpgrt_fconcat (fconcatflags, argv[1], argv[2], argv[3],
+ argv[4], argv[5], NULL);
+ else
+ myfname = NULL;
+
+ if (argc > 6)
+ log_error ("error fconcat: too many parts\n");
+ else if (!myfname)
+ log_error ("error fconcat: %s\n",
+ gpg_strerror (gpg_error_from_syserror ()));
+ else if (!quiet)
+ printf ("%s\n", myfname);
+ gpgrt_free (myfname);
+ }
else if (mycmd == CMD_CHDIR)
{
err = gpgrt_chdir (*argv);
diff --git a/src/gpg-error.def.in b/src/gpg-error.def.in
index a54aa69..53c21d8 100644
--- a/src/gpg-error.def.in
+++ b/src/gpg-error.def.in
@@ -284,5 +284,6 @@ EXPORTS
gpgrt_nvc_get_string @221
gpgrt_nvc_get_bool @222
+ gpgrt_fconcat @223
;; end of file with public symbols for Windows.
diff --git a/src/gpg-error.h.in b/src/gpg-error.h.in
index 7672b71..9d5b94f 100644
--- a/src/gpg-error.h.in
+++ b/src/gpg-error.h.in
@@ -1583,6 +1583,14 @@ int gpgrt_cmp_version (const char *a, const char *b, int level);
char *gpgrt_fnameconcat (const char *first, ...) GPGRT_ATTR_SENTINEL(0);
char *gpgrt_absfnameconcat (const char *first, ...) GPGRT_ATTR_SENTINEL(0);
+/* Same as gpgrt_fnameconcat but using flags for extensibility. */
+#define GPGRT_FCONCAT_ABS 1 /* Expand to an absolute name. */
+#define GPGRT_FCONCAT_TILDE 2 /* Enable tilde expansion. */
+#define GPGRT_FCONCAT_SYSCONF 4 /* Prepend the sysconfdir. */
+char *gpgrt_fconcat (unsigned int flags, const char *first,
+ ...) GPGRT_ATTR_SENTINEL(0);
+
+
#ifdef __cplusplus
}
diff --git a/src/gpg-error.vers b/src/gpg-error.vers
index 199a0b9..b047f8b 100644
--- a/src/gpg-error.vers
+++ b/src/gpg-error.vers
@@ -207,6 +207,7 @@ GPG_ERROR_1.0 {
gpgrt_fnameconcat;
gpgrt_absfnameconcat;
+ gpgrt_fconcat;
gpgrt_access;
diff --git a/src/gpgrt-int.h b/src/gpgrt-int.h
index 8d2a936..b805eed 100644
--- a/src/gpgrt-int.h
+++ b/src/gpgrt-int.h
@@ -840,7 +840,7 @@ char *_gpgrt_getpwdir (const char *name);
char *_gpgrt_getusername (void);
/* Expand and concat file name parts. */
-char *_gpgrt_vfnameconcat (int want_abs, const char *first_part,
+char *_gpgrt_vfnameconcat (unsigned int flags, const char *first_part,
va_list arg_ptr);
char *_gpgrt_fnameconcat (const char *first_part,
... ) GPGRT_ATTR_SENTINEL(0);
@@ -870,6 +870,9 @@ enum gpgrt_windows_feature
};
int _gpgrt_windows_feature (int);
+const char *_gpgrt_w32_get_sysconfdir (void);
+const char *_gpgrt_w32_get_profile (void);
+
#endif /*HAVE_W32_SYSTEM*/
/*
diff --git a/src/init.c b/src/init.c
index 7f970d9..5a218ac 100644
--- a/src/init.c
+++ b/src/init.c
@@ -143,6 +143,7 @@ _gpg_err_init (void)
_gpgrt_abort ();
}
_gpg_w32__init_gettext_module ();
+ _gpgrt_w32__init_utils ();
real_init ();
}
# endif /*!DLL_EXPORT*/
@@ -728,8 +729,9 @@ DllMain (HINSTANCE hinst, DWORD reason, LPVOID reserved)
if (tls_index == TLS_OUT_OF_INDEXES)
return FALSE;
#ifndef _GPG_ERR_HAVE_CONSTRUCTOR
- /* If we have not constructors (e.g. MSC) we call it here. */
+ /* If we have no constructors (e.g. MSC) we call it here. */
_gpg_w32__init_gettext_module ();
+ _gpgrt_w32__init_utils ();
#endif
/* fallthru. */
case DLL_THREAD_ATTACH:
diff --git a/src/init.h b/src/init.h
index 269e2c2..96c2281 100644
--- a/src/init.h
+++ b/src/init.h
@@ -54,9 +54,10 @@ void _gpgrt_w32_utf8_for_new_threads (void);
struct tls_space_s *get_tls (void);
-/* Explicit constructor for w32-gettext.c */
+/* Explicit constructors for w32-gettext.c and w32-utils.c */
#ifndef DLL_EXPORT
void _gpg_w32__init_gettext_module (void);
+void _gpgrt_w32__init_utils (void);
#endif
#endif /*HAVE_W32_SYSTEM*/
diff --git a/src/stringutils.c b/src/stringutils.c
index 9cba1c2..f153be1 100644
--- a/src/stringutils.c
+++ b/src/stringutils.c
@@ -59,17 +59,21 @@ _gpgrt_trim_spaces (char *str)
}
-/* Helper for _gpgrt_fnameconcat. The additional flag WANT_ABS tells
- * whether an absolute file name is requested. */
+/* Helper for _gpgrt_fnameconcat. FLAGS controls special features of
+ * the function. See GPGRT_FCONCAT_ constants for details. */
char *
-_gpgrt_vfnameconcat (int want_abs, const char *first_part, va_list arg_ptr)
+_gpgrt_vfnameconcat (unsigned int flags,
+ const char *first_part, va_list arg_ptr)
{
const char *argv[32];
int argc;
size_t n;
- int skip = 1; /* Characters to skip from FIRST_PART. */
+ int skip = 1; /* Characters to skip from FIRST_PART. Only used if
+ * HOME is NULL */
char *home_buffer = NULL;
- char *name, *home, *p;
+ char *name, *p;
+ const char *home;
+ const char *extradelim = "";
/* Put all args into an array because we need to scan them twice. */
n = strlen (first_part) + 1;
@@ -87,15 +91,39 @@ _gpgrt_vfnameconcat (int want_abs, const char *first_part, va_list arg_ptr)
n++;
home = NULL;
- if (*first_part == '~')
+ if ((flags & GPGRT_FCONCAT_SYSCONF))
+ {
+#ifdef HAVE_W32_SYSTEM
+ home = _gpgrt_w32_get_sysconfdir ();
+ if (!home)
+ return NULL;
+#else
+ home = SYSCONFDIR;
+#endif
+ skip = 0;
+ n += strlen (home);
+ if (n && home[n-1] != '/' && *first_part != '/')
+ {
+ extradelim = "/";
+ n++;
+ }
+ }
+ else if (*first_part == '~' && (flags & GPGRT_FCONCAT_TILDE))
{
if (first_part[1] == '/' || !first_part[1])
{
/* This is the "~/" or "~" case. */
- home_buffer = _gpgrt_getenv ("HOME");
- if (!home_buffer)
- home_buffer = _gpgrt_getpwdir (NULL);
- home = home_buffer;
+#ifdef HAVE_W32_SYSTEM
+ home = _gpgrt_w32_get_profile ();
+ /* Note that we fallback to $HOME if the above failed. */
+#endif
+ if (!home)
+ {
+ home_buffer = _gpgrt_getenv ("HOME");
+ if (!home_buffer)
+ home_buffer = _gpgrt_getpwdir (NULL);
+ home = home_buffer;
+ }
if (home && *home)
n += strlen (home);
}
@@ -130,7 +158,7 @@ _gpgrt_vfnameconcat (int want_abs, const char *first_part, va_list arg_ptr)
}
if (home)
- p = stpcpy (stpcpy (name, home), first_part + skip);
+ p = stpcpy (stpcpy (stpcpy (name, home), extradelim), first_part + skip);
else
p = stpcpy (name, first_part);
@@ -146,7 +174,7 @@ _gpgrt_vfnameconcat (int want_abs, const char *first_part, va_list arg_ptr)
p = stpcpy (stpcpy (p, "/"), argv[argc]);
}
- if (want_abs)
+ if ((flags & GPGRT_FCONCAT_ABS))
{
#ifdef HAVE_W32_SYSTEM
p = strchr (name, ':');
@@ -163,18 +191,18 @@ _gpgrt_vfnameconcat (int want_abs, const char *first_part, va_list arg_ptr)
#endif
)
{
- home = _gpgrt_getcwd ();
- if (!home)
+ char *mycwd = _gpgrt_getcwd ();
+ if (!mycwd)
{
xfree (name);
return NULL;
}
- n = strlen (home) + 1 + strlen (name) + 1;
+ n = strlen (mycwd) + 1 + strlen (name) + 1;
home_buffer = xtrymalloc (n);
if (!home_buffer)
{
- xfree (home);
+ xfree (mycwd);
xfree (name);
return NULL;
}
@@ -188,12 +216,12 @@ _gpgrt_vfnameconcat (int want_abs, const char *first_part, va_list arg_ptr)
}
/* Avoid a leading double slash if the cwd is "/". */
- if (home[0] == '/' && !home[1])
+ if (mycwd[0] == '/' && !mycwd[1])
strcpy (stpcpy (p, "/"), name);
else
- strcpy (stpcpy (stpcpy (p, home), "/"), name);
+ strcpy (stpcpy (stpcpy (p, mycwd), "/"), name);
- xfree (home);
+ xfree (mycwd);
xfree (name);
name = home_buffer;
/* Let's do a simple compression to catch the common case of
@@ -202,7 +230,44 @@ _gpgrt_vfnameconcat (int want_abs, const char *first_part, va_list arg_ptr)
if (n > 2 && name[n-2] == '/' && name[n-1] == '.')
name[n-2] = 0;
}
- }
+#ifdef HAVE_W32_SYSTEM
+ else if (name[0] && name[1] != ':'
+ && !(name[0] == '/' && name[1] == '/')
+ && !(name[0] == '\\' && name[1] == '\\'))
+ {
+ /* Absolute name but no drive letter but also no UNC. */
+ char *mycwd = _gpgrt_getcwd ();
+ if (!mycwd)
+ {
+ xfree (name);
+ return NULL;
+ }
+ if (mycwd[0] && mycwd[1] == ':')
+ {
+ home_buffer = xtrymalloc (2 + strlen (name) + 1);
+ if (!home_buffer)
+ {
+ xfree (mycwd);
+ xfree (name);
+ return NULL;
+ }
+ home_buffer[0] = mycwd[0];
+ home_buffer[1] = mycwd[1];
+ strcpy (home_buffer + 2, name);
+ xfree (name);
+ name = home_buffer;
+ }
+ xfree (mycwd);
+ }
+#endif /* W32 */
+
+#ifdef HAVE_W32_SYSTEM
+ /* Fix "c://foo" to c:/foo". */
+ n = strlen (name);
+ if (name[0] && name[1] == ':' && name[2] == '/' && name[3] == '/')
+ memmove (name+3, name+4, strlen (name+4)+1);
+#endif
+ } /* end GPGRT_FCONCAT_ABS */
#ifdef HAVE_W32_SYSTEM
for (p=name; *p; p++)
@@ -224,7 +289,7 @@ _gpgrt_fnameconcat (const char *first_part, ... )
char *result;
va_start (arg_ptr, first_part);
- result = _gpgrt_vfnameconcat (0, first_part, arg_ptr);
+ result = _gpgrt_vfnameconcat (GPGRT_FCONCAT_TILDE, first_part, arg_ptr);
va_end (arg_ptr);
return result;
}
@@ -241,7 +306,8 @@ _gpgrt_absfnameconcat (const char *first_part, ... )
char *result;
va_start (arg_ptr, first_part);
- result = _gpgrt_vfnameconcat (1, first_part, arg_ptr);
+ result = _gpgrt_vfnameconcat (GPGRT_FCONCAT_TILDE| GPGRT_FCONCAT_ABS,
+ first_part, arg_ptr);
va_end (arg_ptr);
return result;
}
diff --git a/src/visibility.c b/src/visibility.c
index 54c4e2a..98d4721 100644
--- a/src/visibility.c
+++ b/src/visibility.c
@@ -1326,7 +1326,7 @@ gpgrt_fnameconcat (const char *first, ... )
char *result;
va_start (arg_ptr, first);
- result = _gpgrt_vfnameconcat (0, first, arg_ptr);
+ result = _gpgrt_vfnameconcat (GPGRT_FCONCAT_TILDE, first, arg_ptr);
va_end (arg_ptr);
return result;
}
@@ -1338,7 +1338,20 @@ gpgrt_absfnameconcat (const char *first, ... )
char *result;
va_start (arg_ptr, first);
- result = _gpgrt_vfnameconcat (1, first, arg_ptr);
+ result = _gpgrt_vfnameconcat (GPGRT_FCONCAT_TILDE|GPGRT_FCONCAT_ABS,
+ first, arg_ptr);
+ va_end (arg_ptr);
+ return result;
+}
+
+char *
+gpgrt_fconcat (unsigned int flags, const char *first, ... )
+{
+ va_list arg_ptr;
+ char *result;
+
+ va_start (arg_ptr, first);
+ result = _gpgrt_vfnameconcat (flags, first, arg_ptr);
va_end (arg_ptr);
return result;
}
diff --git a/src/visibility.h b/src/visibility.h
index 8729c01..fa2a990 100644
--- a/src/visibility.h
+++ b/src/visibility.h
@@ -228,6 +228,7 @@ MARK_VISIBLE (gpgrt_cmp_version)
MARK_VISIBLE (gpgrt_fnameconcat)
MARK_VISIBLE (gpgrt_absfnameconcat)
+MARK_VISIBLE (gpgrt_fconcat)
MARK_VISIBLE (gpgrt_strlist_free)
MARK_VISIBLE (gpgrt_strlist_add)
@@ -474,6 +475,7 @@ MARK_VISIBLE (gpgrt_spawn_actions_set_atfork)
#define gpgrt_cmp_version _gpgrt_USE_UNDERSCORED_FUNCTION
#define gpgrt_fnameconcat _gpgrt_USE_UNDERSCORED_FUNCTION
#define gpgrt_absfnameconcat _gpgrt_USE_UNDERSCORED_FUNCTION
+#define gpgrt_fconcat _gpgrt_USE_UNDERSCORED_FUNCTION
#define gpgrt_strlist_free _gpgrt_USE_UNDERSCORED_FUNCTION
#define gpgrt_strlist_add _gpgrt_USE_UNDERSCORED_FUNCTION
diff --git a/src/w32-utils.c b/src/w32-utils.c
new file mode 100644
index 0000000..d8f4896
--- /dev/null
+++ b/src/w32-utils.c
@@ -0,0 +1,164 @@
+/* w32-utils.c - Further Windows utility functions.
+ * Copyright (C) 2025 g10 Code GmbH
+ *
+ * This file is part of Libgpg-error.
+ *
+ * Libgpg-error is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * Libgpg-error 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this program; if not, see <https://www.gnu.org/licenses/>.
+ * SPDX-License-Identifier: LGPL-2.1+
+ */
+
+#if HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#ifndef HAVE_W32_SYSTEM
+# error This module may only be build for Windows.
+#endif
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <errno.h>
+#include <gpg-error.h>
+#define WIN32_LEAN_AND_MEAN
+#include <windows.h>
+#include <shlobj.h>
+#ifndef CSIDL_APPDATA
+#define CSIDL_APPDATA 0x001a
+#endif
+#ifndef CSIDL_LOCAL_APPDATA
+#define CSIDL_LOCAL_APPDATA 0x001c
+#endif
+#ifndef CSIDL_COMMON_APPDATA
+#define CSIDL_COMMON_APPDATA 0x0023
+#endif
+#ifndef CSIDL_PROFILE
+#define CSIDL_PROFILE 0x0028
+#endif
+#ifndef CSIDL_FLAG_CREATE
+#define CSIDL_FLAG_CREATE 0x8000
+#endif
+
+#include "gpgrt-int.h"
+
+
+/* This is a helper function to load and call and the Windows Shell
+ * function SHGetFolderPathW and return the value as UTF-8. On error
+ * NULL is returned and ERRNO is set. */
+static char *
+w32_shgetfolderpath (HWND a, int b, HANDLE c, DWORD d)
+{
+ static int initialized;
+ static HRESULT (WINAPI * func)(HWND,int,HANDLE,DWORD,LPWSTR);
+ wchar_t wfname[MAX_PATH];
+ char *buf, *p;
+
+ if (!initialized)
+ {
+ void *handle;
+
+ handle = LoadLibraryEx ("shell32.dll", NULL, 0);
+ if (!handle)
+ {
+ _gpgrt_w32_set_errno (-1);
+ return NULL;
+ }
+ func = (void*)GetProcAddress (handle, "SHGetFolderPathW");
+ if (!func)
+ {
+ _gpgrt_w32_set_errno (-1);
+ CloseHandle (handle);
+ handle = NULL;
+ return NULL;
+ }
+
+ initialized = 1;
+ }
+
+ /* We call FUNC only if B != 0 so that it is possible to call this
+ * function just for initializing. */
+ if (b && func (a,b,c,d,wfname) >= 0)
+ {
+ buf = _gpgrt_wchar_to_utf8 (wfname, -1);
+ if (buf)
+ {
+ for (p=buf; *p; p++)
+ if (*p == '\\')
+ *p = '/';
+ }
+ return buf;
+ }
+ else
+ return NULL;
+}
+
+
+const char *
+_gpgrt_w32_get_sysconfdir (void)
+{
+ static char *dir;
+
+ if (!dir)
+ {
+ char *tmp = w32_shgetfolderpath (NULL, CSIDL_COMMON_APPDATA, NULL, 0);
+ if (!tmp)
+ return NULL;
+ dir = _gpgrt_strconcat (tmp, "/GNU/etc", NULL);
+ xfree (tmp);
+ if (dir)
+ gpgrt_annotate_leaked_object (dir);
+ }
+
+ return dir;
+}
+
+
+const char *
+_gpgrt_w32_get_profile (void)
+{
+ static char *dir;
+
+ if (!dir)
+ {
+ dir = w32_shgetfolderpath (NULL, CSIDL_PROFILE, NULL, 0);
+ if (dir)
+ gpgrt_annotate_leaked_object (dir);
+ }
+
+ return dir;
+}
+
+
+
+/* Constructor for this module. This can only be used if we are a
+ * DLL. If used as a static lib we can't control the process set; for
+ * example it might be used with a main module which is not build with
+ * mingw and thus does not know how to call the constructors. */
+#ifdef DLL_EXPORT
+static void w32_utils_init (void) _GPG_ERR_CONSTRUCTOR;
+#endif
+
+static void
+w32_utils_init (void)
+{
+ w32_shgetfolderpath (NULL, 0, NULL, 0);
+}
+
+#if !defined(DLL_EXPORT) || !defined(_GPG_ERR_HAVE_CONSTRUCTOR)
+void
+_gpgrt_w32__init_utils (void)
+{
+ w32_utils_init ().
+}
+#endif