diff options
| author | Werner Koch <[email protected]> | 2025-12-09 11:46:22 +0100 |
|---|---|---|
| committer | Werner Koch <[email protected]> | 2025-12-09 11:46:22 +0100 |
| commit | 34dba88757fedb61d1ea63616a3e03837ccf50ab (patch) | |
| tree | 55a160118e218037e0edfa42fd49b4e66ad3f970 /src | |
| parent | po: Update Portuguese Translation. (diff) | |
| download | libgpg-error-34dba88757fedb61d1ea63616a3e03837ccf50ab.tar.gz libgpg-error-34dba88757fedb61d1ea63616a3e03837ccf50ab.zip | |
New function gpgrt_fconcat.
* src/w32-utils.c: New.
* src/init.c (_gpg_err_init, DllMain): Call new init function.
* src/visibility.c (gpgrt_fconcat): New.
* src/gpg-error.h.in (gpgrt_fconcat): New prototype.
(GPGRT_FCONCAT_ABS): New.
(GPGRT_FCONCAT_TILDE): New.
(GPGRT_FCONCAT_SYSCONF): New.
* src/gpg-error.def.in (gpgrt_fconcat): New.
* src/gpg-error.vers: (gpgrt_fconcat): New.
* src/stringutils.c (_gpgrt_vfnameconcat): Repalce want-abs but a new
flags arg. Change to support GPGRT_FCONCAT_SYSCONF. Fix tilde
expansion under Windows to use CISDL_PROFILE instead of $HOME. Adjust
all callers.
* src/gpg-error.c (main): New command "fconcat".
--
This function allows to get the sysconfig directory in a portable way.
On Windows this is CISDL_COMMON_APPDATA\GNU\etc. On Unix this is the
directory specified at the configure run.
This patch also changes the behaviour of tilde expansion in Windows
which was not well defined. It does now use CISDL_PROFILE instead of
the HOME envvar which is uncommon under Windows. Note that under
Windows "~user/foo" is still not defined. Tilde expansion is not done
when GPGRT_FCONCAT_SYSCONF is used.
GnuPG-bug-id: 7894
Diffstat (limited to 'src')
| -rw-r--r-- | src/Makefile.am | 2 | ||||
| -rw-r--r-- | src/gpg-error.c | 46 | ||||
| -rw-r--r-- | src/gpg-error.def.in | 1 | ||||
| -rw-r--r-- | src/gpg-error.h.in | 8 | ||||
| -rw-r--r-- | src/gpg-error.vers | 1 | ||||
| -rw-r--r-- | src/gpgrt-int.h | 5 | ||||
| -rw-r--r-- | src/init.c | 4 | ||||
| -rw-r--r-- | src/init.h | 3 | ||||
| -rw-r--r-- | src/stringutils.c | 110 | ||||
| -rw-r--r-- | src/visibility.c | 17 | ||||
| -rw-r--r-- | src/visibility.h | 2 | ||||
| -rw-r--r-- | src/w32-utils.c | 164 |
12 files changed, 335 insertions, 28 deletions
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*/ /* @@ -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: @@ -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 |
