diff options
| -rw-r--r-- | NEWS | 9 | ||||
| -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 |
13 files changed, 344 insertions, 28 deletions
@@ -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*/ /* @@ -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 |
