diff options
author | Werner Koch <[email protected]> | 2020-02-18 07:53:30 +0000 |
---|---|---|
committer | Werner Koch <[email protected]> | 2020-02-18 07:56:33 +0000 |
commit | 5742b8eaf3fa9cda3dfb6b3ad0fea7485fff1a12 (patch) | |
tree | 7bb1347295569e10184f65ccfb3aea8e44495275 /src | |
parent | w32: Support static link with -lws2_32. (diff) | |
download | libgpg-error-5742b8eaf3fa9cda3dfb6b3ad0fea7485fff1a12.tar.gz libgpg-error-5742b8eaf3fa9cda3dfb6b3ad0fea7485fff1a12.zip |
core: Add gpgrt_fnameconcat and gpgrt_absfnameconcat.
* src/gpg-error.h.in (gpgrt_fnameconcat): New.
(gpgrt_absfnameconcat): New.
* src/visibility.c (gpgrt_fnameconcat, gpgrt_absfnameconcat): New.
* src/stringutils.c: New file.
(_gpgrt_vfnameconcat): New.
(_gpgrt_fnameconcat, _gpgrt_absfnameconcat): New.
* src/gpg-error.def.in: Add new functions.
* src/gpg-error.vers: Ditto.
* src/sysutils.c: Include pwd.h.
(_gpgrt_getpwdir): New.
* configure.ac: Test for pwd.h, getpwnam, getpwuid, and their _r
variants.
* src/Makefile.am (libgpg_error_la_SOURCES): Add new file.
* tests/t-stringutils.c: New.
* tests/t-common.h (xmalloc, xstrdup, xfree): New.
(die): Kludge to avoid compiler warnings.
--
The new functions are based on the code of make_filename from GnuPG.
They have been written by me ages ago with only minor modifications by
David Shaw. I re-license them from LGPL-3.0+ OR GPL-2.0+ to
LGPL-2.1-or-later.
Signed-off-by: Werner Koch <[email protected]>
Diffstat (limited to 'src')
-rw-r--r-- | src/Makefile.am | 1 | ||||
-rw-r--r-- | src/gpg-error.def.in | 3 | ||||
-rw-r--r-- | src/gpg-error.h.in | 6 | ||||
-rw-r--r-- | src/gpg-error.vers | 3 | ||||
-rw-r--r-- | src/gpgrt-int.h | 11 | ||||
-rw-r--r-- | src/stringutils.c | 224 | ||||
-rw-r--r-- | src/sysutils.c | 39 | ||||
-rw-r--r-- | src/visibility.c | 27 | ||||
-rw-r--r-- | src/visibility.h | 4 |
9 files changed, 318 insertions, 0 deletions
diff --git a/src/Makefile.am b/src/Makefile.am index e9c057f..336fe2a 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -198,6 +198,7 @@ libgpg_error_la_SOURCES = gettext.h $(arch_sources) \ strsource.c strerror.c code-to-errno.c code-from-errno.c \ visibility.c visibility.h \ sysutils.c \ + stringutils.c \ syscall-clamp.c \ logging.c \ b64dec.c b64enc.c \ diff --git a/src/gpg-error.def.in b/src/gpg-error.def.in index 0a584c4..4e3b5d7 100644 --- a/src/gpg-error.def.in +++ b/src/gpg-error.def.in @@ -229,4 +229,7 @@ EXPORTS gpgrt_add_emergency_cleanup @174 gpgrt_abort @175 + gpgrt_fnameconcat @178 + gpgrt_absfnameconcat @179 + ;; end of file with public symbols for Windows. diff --git a/src/gpg-error.h.in b/src/gpg-error.h.in index d118e90..470021d 100644 --- a/src/gpg-error.h.in +++ b/src/gpg-error.h.in @@ -1309,6 +1309,12 @@ void gpgrt_set_fixed_string_mapper (const char *(*f)(const char*)); * numbering scheme a LEVEL of 3 is suitable; see the manual. */ int gpgrt_cmp_version (const char *a, const char *b, int level); +/* Construct a filename from the NULL terminated list of parts. Tilde + * expansion is done for the first argument. The caller must release + * the result using gpgrt_free; on error ERRNO is set and NULL + * returned. The second function returns an absolute filename. */ +char *gpgrt_fnameconcat (const char *first, ...) GPGRT_ATTR_SENTINEL(0); +char *gpgrt_absfnameconcat (const char *first, ...) GPGRT_ATTR_SENTINEL(0); #ifdef __cplusplus diff --git a/src/gpg-error.vers b/src/gpg-error.vers index eef4cbc..594342b 100644 --- a/src/gpg-error.vers +++ b/src/gpg-error.vers @@ -199,6 +199,9 @@ GPG_ERROR_1.0 { gpgrt_add_emergency_cleanup; gpgrt_abort; + gpgrt_fnameconcat; + gpgrt_absfnameconcat; + local: *; }; diff --git a/src/gpgrt-int.h b/src/gpgrt-int.h index 718b62c..97f0533 100644 --- a/src/gpgrt-int.h +++ b/src/gpgrt-int.h @@ -787,6 +787,17 @@ gpg_err_code_t _gpgrt_chdir (const char *name); /* Return the current WD as a malloced string. */ char *_gpgrt_getcwd (void); +/* Return the home directory of user NAME. */ +char *_gpgrt_getpwdir (const char *name); + +/* Expand and concat file name parts. */ +char *_gpgrt_vfnameconcat (int want_abs, const char *first_part, + va_list arg_ptr); +char *_gpgrt_fnameconcat (const char *first_part, + ... ) GPGRT_ATTR_SENTINEL(0); +char *_gpgrt_absfnameconcat (const char *first_part, + ... ) GPGRT_ATTR_SENTINEL(0); + /* * Platform specific functions (Windows) diff --git a/src/stringutils.c b/src/stringutils.c new file mode 100644 index 0000000..d92398d --- /dev/null +++ b/src/stringutils.c @@ -0,0 +1,224 @@ +/* stringutils.c - String helper functions. + * Copyright (C) 1997, 2014 Werner Koch + * Copyright (C) 2020 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-or-later + */ + +#include <config.h> +#include <stdlib.h> +#include <stdint.h> +#include <string.h> +#include <stdarg.h> +#include <errno.h> +#ifdef HAVE_STAT +# include <sys/stat.h> +#endif +#include <sys/types.h> +#ifdef HAVE_PWD_H +# include <pwd.h> +#endif + +#include "gpgrt-int.h" + + +/* Helper for _gpgrt_fnameconcat. The additional flag WANT_ABS tells + * whether an absolute file name is requested. */ +char * +_gpgrt_vfnameconcat (int want_abs, 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. */ + char *home_buffer = NULL; + char *name, *home, *p; + + /* Put all args into an array becuase we need to scan them twice. */ + n = strlen (first_part) + 1; + argc = 0; + while ((argv[argc] = va_arg (arg_ptr, const char *))) + { + n += strlen (argv[argc]) + 1; + if (argc >= DIM (argv)-1) + { + _gpg_err_set_errno (EINVAL); + return NULL; + } + argc++; + } + n++; + + home = NULL; + if (*first_part == '~') + { + 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; + if (home && *home) + n += strlen (home); + } + else + { + /* This is the "~username/" or "~username" case. */ + char *user; + + user = _gpgrt_strdup (first_part+1); + if (!user) + return NULL; + + p = strchr (user, '/'); + if (p) + *p = 0; + skip = 1 + strlen (user); + + home = home_buffer = _gpgrt_getpwdir (user); + xfree (user); + if (home) + n += strlen (home); + else + skip = 1; + } + } + + name = xtrymalloc (n); + if (!name) + { + _gpgrt_free (home_buffer); + return NULL; + } + + if (home) + p = stpcpy (stpcpy (name, home), first_part + skip); + else + p = stpcpy (name, first_part); + + xfree (home_buffer); + home_buffer = NULL; + + for (argc=0; argv[argc]; argc++) + { + /* Avoid a leading double slash if the first part was "/". */ + if (!argc && name[0] == '/' && !name[1]) + p = stpcpy (p, argv[argc]); + else + p = stpcpy (stpcpy (p, "/"), argv[argc]); + } + + if (want_abs) + { +#ifdef HAVE_W32_SYSTEM + p = strchr (name, ':'); + if (p) + p++; + else + p = name; +#else + p = name; +#endif + if (*p != '/' +#ifdef HAVE_W32_SYSTEM + && *p != '\\' +#endif + ) + { + home = _gpgrt_getcwd (); + if (!home) + { + xfree (name); + return NULL; + } + + n = strlen (home) + 1 + strlen (name) + 1; + home_buffer = xtrymalloc (n); + if (!home_buffer) + { + xfree (home); + xfree (name); + return NULL; + } + + if (p == name) + p = home_buffer; + else /* Windows case. */ + { + memcpy (home_buffer, p, p - name + 1); + p = home_buffer + (p - name + 1); + } + + /* Avoid a leading double slash if the cwd is "/". */ + if (home[0] == '/' && !home[1]) + strcpy (stpcpy (p, "/"), name); + else + strcpy (stpcpy (stpcpy (p, home), "/"), name); + + xfree (home); + xfree (name); + name = home_buffer; + /* Let's do a simple compression to catch the common case of + * a trailing "/.". */ + n = strlen (name); + if (n > 2 && name[n-2] == '/' && name[n-1] == '.') + name[n-2] = 0; + } + } + +#ifdef HAVE_W32_SYSTEM + for (p=name; *p; p++) + if (*p == '\\') + *p = '/'; +#endif + return name; +} + + +/* Construct a filename from the NULL terminated list of parts. Tilde + * expansion is done for the first argument. The caller must release + * the result using gpgrt_free; on error ERRNO is set and NULL + * returned. */ +char * +_gpgrt_fnameconcat (const char *first_part, ... ) +{ + va_list arg_ptr; + char *result; + + va_start (arg_ptr, first_part); + result = _gpgrt_vfnameconcat (0, first_part, arg_ptr); + va_end (arg_ptr); + return result; +} + + +/* Construct a filename from the NULL terminated list of parts. Tilde + * expansion is done for the first argument. The caller must release + * the result using gpgrt_free; on error ERRNO is set and NULL + * returned. This version returns an absolute filename. */ +char * +_gpgrt_absfnameconcat (const char *first_part, ... ) +{ + va_list arg_ptr; + char *result; + + va_start (arg_ptr, first_part); + result = _gpgrt_vfnameconcat (1, first_part, arg_ptr); + va_end (arg_ptr); + return result; +} diff --git a/src/sysutils.c b/src/sysutils.c index bc31d92..6bdd76f 100644 --- a/src/sysutils.c +++ b/src/sysutils.c @@ -32,6 +32,9 @@ #endif #include <sys/types.h> #include <fcntl.h> +#ifdef HAVE_PWD_H +# include <pwd.h> +#endif #include "gpgrt-int.h" @@ -337,3 +340,39 @@ _gpgrt_getcwd (void) #endif } } + + +/* Get the standard home directory for user NAME. If NAME is NULL the + * directory for the current user is retruned. Caller must release + * the returned string. */ +char * +_gpgrt_getpwdir (const char *name) +{ + char *result = NULL; +#ifdef HAVE_PWD_H + struct passwd *pwd = NULL; + + if (name) + { +#ifdef HAVE_GETPWNAM + /* Fixme: We should use getpwnam_r if available. */ + pwd = getpwnam (name); +#endif + } + else + { +#ifdef HAVE_GETPWUID + /* Fixme: We should use getpwuid_r if available. */ + pwd = getpwuid (getuid()); +#endif + } + if (pwd) + { + result = _gpgrt_strdup (pwd->pw_dir); + } +#else /*!HAVE_PWD_H*/ + /* No support at all. */ + (void)name; +#endif /*HAVE_PWD_H*/ + return result; +} diff --git a/src/visibility.c b/src/visibility.c index d754032..5f88aad 100644 --- a/src/visibility.c +++ b/src/visibility.c @@ -1180,6 +1180,33 @@ gpgrt_cmp_version (const char *a, const char *b, int level) +/* String utilities. */ +char * +gpgrt_fnameconcat (const char *first, ... ) +{ + va_list arg_ptr; + char *result; + + va_start (arg_ptr, first); + result = _gpgrt_vfnameconcat (0, first, arg_ptr); + va_end (arg_ptr); + return result; +} + +char * +gpgrt_absfnameconcat (const char *first, ... ) +{ + va_list arg_ptr; + char *result; + + va_start (arg_ptr, first); + result = _gpgrt_vfnameconcat (1, first, arg_ptr); + va_end (arg_ptr); + return result; +} + + + /* For consistency reasons we use function wrappers also for Windows * specific function despite that they are technically not needed. */ #ifdef HAVE_W32_SYSTEM diff --git a/src/visibility.h b/src/visibility.h index 28038d0..192f733 100644 --- a/src/visibility.h +++ b/src/visibility.h @@ -216,6 +216,10 @@ MARK_VISIBLE (gpgrt_set_usage_outfnc); MARK_VISIBLE (gpgrt_cmp_version); +MARK_VISIBLE (gpgrt_fnameconcat); +MARK_VISIBLE (gpgrt_absfnameconcat); + + #undef MARK_VISIBLE #else /*!_GPGRT_INCL_BY_VISIBILITY_C*/ |