summaryrefslogtreecommitdiff
path: root/src/stringutils.c
diff options
context:
space:
mode:
authorWerner Koch <wk@gnupg.org>2020-02-18 08:53:30 +0100
committerWerner Koch <wk@gnupg.org>2020-02-18 08:56:33 +0100
commit5742b8eaf3fa9cda3dfb6b3ad0fea7485fff1a12 (patch)
tree7bb1347295569e10184f65ccfb3aea8e44495275 /src/stringutils.c
parenta6ab8e3a710b899ecfb2ff518725314a1e0e64ff (diff)
downloadlibgpg-error-5742b8eaf3fa9cda3dfb6b3ad0fea7485fff1a12.tar.gz
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 <wk@gnupg.org>
Diffstat (limited to 'src/stringutils.c')
-rw-r--r--src/stringutils.c224
1 files changed, 224 insertions, 0 deletions
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;
+}