aboutsummaryrefslogtreecommitdiff
path: root/xdico
diff options
context:
space:
mode:
authorSergey Poznyakoff <gray@gnu.org>2019-02-20 17:02:27 +0200
committerSergey Poznyakoff <gray@gnu.org>2019-02-22 12:23:15 +0200
commit4da4ddea3a40049f90c6d2bfb27bc3de564160c8 (patch)
tree839a49d9a3d1cb75aae16acf0348825c0c760715 /xdico
parentc8a6eb71afdf2c9572fcdc7b617b1356b2f75daa (diff)
downloaddico-4da4ddea3a40049f90c6d2bfb27bc3de564160c8.tar.gz
dico-4da4ddea3a40049f90c6d2bfb27bc3de564160c8.tar.bz2
Avoid introducing gnulib dependencies to libdico
After pulling updates from gnulib, it was discovered that it had added an unwanted dependency to the libdico. The dependency was added b redefining fseek to rpl_fseek in the config.h. Of course, that's quite OK for user programs and in fact that's one of the purposes of gnulib, so it's doing its job all right. However, this kind of depedencies is not acceptable in installable libraries, such as libdico. There are two ways of fixing this: (1) by including libgnu to the libdico, or (2) by creating pristine (i.e. stripped of any gnulib stuff) version of config.h and using it in the library sources. The first approach was used in mailutils for the libmu_aux convenience library. However, in dico I wouldn't like to introduce additional dependencies to the installable libraries and modules, therefore second approach has been chosen. Two separare configuration headers are introduced: include/prog/config.h Configuration header with gnulib dependencies. It is used by libxdico, and user-space programs: dico, dicod, idxgcide and some test programs. include/lib/config.h Pristine header, free from any gnulib additions. It is used by all installable libraries, including dicod modules. In order to create pristine header, bootstrap uses specially prepared temporary edition of configure.ac, which is stripped off any gl_.* statements. This approach introduces additional difficulties. The two header files are of course listed in the AC_CONFIG_HEADERS statement in configure.ac. Autotools give precedence to the first one. It is the only file for which autoheader creates the .h.in template. Further, automake creates in each Makefile.am the DEFAULT_INCLUDES statement, which lists the directory part of the first header, and this value is used in the compiler command line. This means that to include the pristine header, its path prefix needs to be specified. To avoid this, the nostdinc option is passed to AM_INIT_AUTOMAKE. As a side effect, it makes it impossible to build libltdl in the recursive mode (although its Makefile.am pretends otherwise). Thus, libltdl is built in nonrecursive mode from the main Makefile.am. Similar problem occurs with the gnulib itself, although in this case its bootstrapping tool provides sufficient mechanisms to help overcome it. Gnulib is built as a sub-library of libxdico. As a side effect, the libextra library is no longer needed. Another side effect is that the rewritten bootstrap script made it possible to use gnulib-tool directly, instead of using gnulib/build-aux/bootstrap wrapper. Following bugs are fixed by this change: https://puszcza.gnu.org.ua/bugs/?416 https://puszcza.gnu.org.ua/bugs/?417 https://puszcza.gnu.org.ua/bugs/?418 Almost all files in the project are affected by this change. However, some changes are pure bugfixes. These are: * lib/mergesort.c (mergesort): Rename to dico_mergesort to avoid name clash on Free and OpenBSD. * lib/stream.c (dico_stream_getdelim): Fix memory reallocation algorithm. * lib/utf8.c (utf8_wc_strstr): Reset errno to 0. * lib/libi18n.c: Don't depend on gnu/configmake.h * modules/wordnet/module.ac: Move definition of WORDNET_COND out of conditional context. This fixes the functionality of the configure --without-wordnet option. Some obsolete and unused files have been removed. These are: * include/gjdict.xbm * lib/bushu.dat * lib/bushu.h * lib/cursor.xbm * lib/jiscvt.c * makedict/ Main set of changes is: * bootstrap: Mostly rewritten as described above. * GNUmakefile: New file for the ease of maintenance. * maint/bootstrap.mk: New file. * gnulib.modules: Moved to maint/ * maint/printflike: New file. Lists printf-like functions for gettext. * configure.boot: Use AC_CONFIG_HEADERS with two header files. Use nostdinc option to AM_INIT_AUTOMAKE. (LTDL_INIT): Use nonrecursive option. (DICO_PROG_CONFIG,DICO_LIB_CONFIG) (DICO_PROG_INCLUDES,DICO_MODULE_INCLUDES): New substitution variables. (GRECS_HOST_PROJECT_INCLUDES): Fix value. (COND_LIBDICOSASL): New conditional. * Makefile.am: Build libltdl. * lib/Makefile.am: Move convenience libraries to another directory. Build only libdico. * dico/Makefile.am: Fix LDADD and includes. * dicod/Makefile.am: Likewise. * dicod/tests/Makefile.am: Likewise. * lib/tests/Makefile.am: Likewise. * modules/*/Makefile.am: Likewise. The directory struture is reorganized and the sources of convenience libraries moved to a separate directory: * xdico/Makefile.am: New file * xdico/appi18n.c * xdico/gsaslstr.c * xdico/iputil.c * xdico/timer.c * xdico/userprivs.c * xdico/xalloc-die.c * xdico/xhostname.c * xdico/xscript.c * xdico/xstream.c * xdico/xtkn.c * xdico/xutil.c: All moved from lib * xdico/gnu/Makefile.am: New file. * xdico/gnu: Gnulib sources imported to this directory.
Diffstat (limited to 'xdico')
-rw-r--r--xdico/.gitignore2
-rw-r--r--xdico/Makefile.am48
-rw-r--r--xdico/appi18n.c27
-rw-r--r--xdico/gnu/Makefile.am19
-rw-r--r--xdico/gsaslstr.c297
-rw-r--r--xdico/iputil.c127
-rw-r--r--xdico/timer.c199
-rw-r--r--xdico/userprivs.c138
-rw-r--r--xdico/xalloc-die.c25
-rw-r--r--xdico/xhostname.c52
-rw-r--r--xdico/xscript.c204
-rw-r--r--xdico/xstream.c67
-rw-r--r--xdico/xtkn.c27
-rw-r--r--xdico/xutil.c161
14 files changed, 1393 insertions, 0 deletions
diff --git a/xdico/.gitignore b/xdico/.gitignore
new file mode 100644
index 0000000..983b627
--- /dev/null
+++ b/xdico/.gitignore
@@ -0,0 +1,2 @@
+/gnu
+!/gnu/Makefile.am
diff --git a/xdico/Makefile.am b/xdico/Makefile.am
new file mode 100644
index 0000000..ce7c896
--- /dev/null
+++ b/xdico/Makefile.am
@@ -0,0 +1,48 @@
+# This file is part of GNU Dico
+# Copyright (C) 1998-2019 Sergey Poznyakoff
+#
+# GNU Dico is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 3, or (at your option)
+# any later version.
+#
+# GNU Dico 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 General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with GNU Dico. If not, see <http://www.gnu.org/licenses/>.
+
+SUBDIRS = gnu
+
+AM_CPPFLAGS = \
+ @DICO_PROG_CONFIG@\
+ -I${top_srcdir}/include\
+ -I${srcdir}/gnu\
+ -I${builddir}/gnu\
+ -DLOCALEDIR=\"$(localedir)\"
+
+noinst_LTLIBRARIES=libxdico.la
+if COND_LIBDICOSASL
+ noinst_LIBRARIES=libdicosasl.a
+endif
+
+libxdico_la_SOURCES=\
+ appi18n.c\
+ iputil.c\
+ timer.c\
+ userprivs.c\
+ xalloc-die.c\
+ xhostname.c\
+ xscript.c\
+ xstream.c\
+ xtkn.c\
+ xutil.c
+
+libxdico_la_LIBADD = gnu/libgnu.la
+
+libdicosasl_a_SOURCES=\
+ gsaslstr.c
+
+
diff --git a/xdico/appi18n.c b/xdico/appi18n.c
new file mode 100644
index 0000000..ed4e9ff
--- /dev/null
+++ b/xdico/appi18n.c
@@ -0,0 +1,27 @@
+/* This file is part of GNU Dico.
+ Copyright (C) 2008-2019 Sergey Poznyakoff
+
+ GNU Dico is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3, or (at your option)
+ any later version.
+
+ GNU Dico 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 General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with GNU Dico. If not, see <http://www.gnu.org/licenses/>. */
+
+#include <config.h>
+#include <gettext.h>
+#include <locale.h>
+
+void
+appi18n_init(void)
+{
+ setlocale(LC_ALL, "");
+ bindtextdomain(PACKAGE, LOCALEDIR);
+ textdomain(PACKAGE);
+}
diff --git a/xdico/gnu/Makefile.am b/xdico/gnu/Makefile.am
new file mode 100644
index 0000000..f2969cb
--- /dev/null
+++ b/xdico/gnu/Makefile.am
@@ -0,0 +1,19 @@
+AUTOMAKE_OPTIONS = 1.11 gnits subdir-objects
+
+SUBDIRS =
+noinst_HEADERS =
+noinst_LIBRARIES =
+noinst_LTLIBRARIES =
+EXTRA_DIST =
+BUILT_SOURCES =
+SUFFIXES =
+MOSTLYCLEANFILES =
+MOSTLYCLEANDIRS =
+CLEANFILES =
+DISTCLEANFILES =
+MAINTAINERCLEANFILES =
+
+AM_CPPFLAGS = @DICO_PROG_CONFIG@ -I$(srcdir) -I$(builddir)
+AM_CFLAGS =
+
+include gnulib.am
diff --git a/xdico/gsaslstr.c b/xdico/gsaslstr.c
new file mode 100644
index 0000000..83fdc8e
--- /dev/null
+++ b/xdico/gsaslstr.c
@@ -0,0 +1,297 @@
+/* This file is part of GNU Dico.
+ Copyright (C) 2008-2019 Sergey Poznyakoff
+
+ GNU Dico is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3, or (at your option)
+ any later version.
+
+ GNU Dico 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 General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with GNU Dico. If not, see <http://www.gnu.org/licenses/>. */
+
+#include <config.h>
+#include <dico.h>
+#include <libi18n.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include <errno.h>
+#include <gsasl.h>
+
+#ifdef WITH_GSASL
+
+#define _ERR_SASL 0x1
+#define _ERR_TRANSPORT 0x2
+
+struct g_buf {
+ char *buffer;
+ size_t pos;
+ size_t size;
+ size_t level;
+};
+
+#define g_buf_ptr(p) ((p).buffer + (p).pos)
+#define g_buf_level(p) ((p).level - (p).pos)
+#define g_buf_drop(p) (p).level = (p).pos = 0
+#define g_buf_free(p) free((p).buffer)
+#define g_buf_advance(p, s) \
+ do { \
+ if (((p).pos += s) >= (p).level) \
+ g_buf_drop(p); \
+ } while (0)
+
+int
+g_buf_grow(struct g_buf *pb, const char *ptr, size_t size)
+{
+ if (!pb->buffer) {
+ pb->buffer = malloc(size);
+ pb->size = size;
+ pb->level = 0;
+ } else if (pb->size - pb->level < size) {
+ size_t newsize = pb->size + size;
+ pb->buffer = realloc(pb->buffer, newsize);
+ if (pb->buffer)
+ pb->size = newsize;
+ }
+
+ if (!pb->buffer)
+ return 1;
+
+ memcpy(pb->buffer + pb->level, ptr, size);
+ pb->level += size;
+ return 0;
+}
+
+struct _gsasl_str {
+ Gsasl_session *sess; /* Session context */
+ int last_err; /* Last GSASL error code */
+ int errflag;
+ dico_stream_t transport;
+ struct g_buf buf;
+};
+
+#define set_error(s, flag, code) \
+ do { \
+ (s)->last_err = code; \
+ (s)->errflag = flag; \
+ } while (0)
+
+static int
+_gsasl_read(void *data, char *buf, size_t size, size_t *pret)
+{
+ struct _gsasl_str *s = data;
+ int rc;
+ size_t len;
+ char *bufp = NULL;
+
+ if ((len = g_buf_level(s->buf))) {
+ if (len > size)
+ len = size;
+ memcpy(buf, g_buf_ptr(s->buf), len);
+ g_buf_advance(s->buf, len);
+ if (pret)
+ *pret = len;
+ return 0;
+ }
+
+ do {
+ char tmp[80];
+ size_t sz;
+ int status;
+
+ status = dico_stream_read(s->transport, tmp, sizeof(tmp), &sz);
+ if (status == EINTR)
+ continue;
+ else if (status) {
+ free (bufp);
+ return status;
+ }
+ rc = g_buf_grow(&s->buf, tmp, sz);
+ if (rc)
+ return rc;
+
+ rc = gsasl_decode(s->sess,
+ g_buf_ptr(s->buf),
+ g_buf_level(s->buf),
+ &bufp, &len);
+ } while (rc == GSASL_NEEDS_MORE);
+
+ if (rc != GSASL_OK) {
+ set_error(s, _ERR_SASL, rc);
+ free(bufp);
+ errno = EIO;
+ return 1;
+ }
+
+ g_buf_drop(s->buf);
+ if (len > size) {
+ memcpy(buf, bufp, size);
+ g_buf_grow(&s->buf, bufp + size, len - size);
+ len = size;
+ } else {
+ memcpy(buf, bufp, len);
+ }
+
+ if (pret)
+ *pret = len;
+
+ free(bufp);
+ return 0;
+}
+
+static int
+_gsasl_write(void *data, const char *buf, size_t size, size_t *pret)
+{
+ struct _gsasl_str *s = data;
+ char *obuf = NULL;
+ size_t olen = 0;
+
+ int rc = gsasl_encode(s->sess, buf, size, &obuf, &olen);
+ if (rc != GSASL_OK) {
+ set_error(s, _ERR_SASL, rc);
+ errno = EIO;
+ return 1;
+ }
+ rc = dico_stream_write(s->transport, obuf, olen);
+ free(obuf);
+ if (rc) {
+ set_error(s, _ERR_TRANSPORT, errno);
+ return 1;
+ }
+ if (pret)
+ *pret = olen;
+ return 0;
+}
+
+static int
+_gsasl_destroy(void *data)
+{
+ struct _gsasl_str *s = data;
+ g_buf_free(s->buf);
+ dico_stream_destroy(&s->transport);
+ return 0;
+}
+
+/*
+ FIXME:
+static const char *
+_gsasl_error_string(void *data, int code)
+{
+
+}
+*/
+
+static int
+_gsasl_flush(void *data)
+{
+ struct _gsasl_str *s = data;
+ return dico_stream_flush(s->transport);
+}
+
+static int
+_gsasl_close(void *data)
+{
+ struct _gsasl_str *s = data;
+ return dico_stream_close(s->transport);
+}
+
+static int
+_gsasl_ioctl(void *data, int code, void *call_data)
+{
+ struct _gsasl_str *s = data;
+
+ switch (code) {
+ case DICO_IOCTL_GET_TRANSPORT:
+ *(dico_stream_t*)call_data = s->transport;
+ break;
+
+ case DICO_IOCTL_SET_TRANSPORT:
+ s->transport = call_data;
+ break;
+
+ case DICO_IOCTL_BYTES_IN:
+ *(off_t*)call_data = dico_stream_bytes_in(s->transport);
+ break;
+
+ case DICO_IOCTL_BYTES_OUT:
+ *(off_t*)call_data = dico_stream_bytes_out(s->transport);
+ break;
+
+ default:
+ errno = EINVAL;
+ return -1;
+ }
+ return 0;
+}
+
+dico_stream_t
+dico_gsasl_stream(Gsasl_session *sess, dico_stream_t transport)
+{
+ int rc;
+ struct _gsasl_str *s;
+ dico_stream_t str;
+
+ s = malloc(sizeof(*s));
+ if (!s)
+ return NULL;
+ memset(s, 0, sizeof(*s));
+ rc = dico_stream_create(&str, DICO_STREAM_READ|DICO_STREAM_WRITE, s);
+ if (rc) {
+ free(s);
+ return NULL;
+ }
+ s->sess = sess;
+ s->transport = transport;
+ dico_stream_set_write(str, _gsasl_write);
+ dico_stream_set_read(str, _gsasl_read);
+ dico_stream_set_flush(str, _gsasl_flush);
+ dico_stream_set_close(str, _gsasl_close);
+ dico_stream_set_destroy(str, _gsasl_destroy);
+ dico_stream_set_ioctl(str, _gsasl_ioctl);
+ dico_stream_set_buffer(str, dico_buffer_line, 1024);
+ return str;
+}
+
+int
+insert_gsasl_stream(Gsasl_session *sess, dico_stream_t *pstr)
+{
+ int rc;
+ dico_stream_t gsasl_str;
+ dico_stream_t str = *pstr;
+ dico_stream_t prev = NULL, s;
+
+ while ((rc = dico_stream_ioctl(str, DICO_IOCTL_GET_TRANSPORT, &s)) == 0
+ && s) {
+ prev = str;
+ str = s;
+ }
+ if (rc && errno != EINVAL && errno != ENOSYS) {
+ dico_log(L_ERR, errno, _("Cannot get transport stream"));
+ return rc;
+ }
+ if (!str) {
+ dico_log(L_ERR, 0, _("Cannot get transport stream"));
+ return 1;
+ }
+
+ gsasl_str = dico_gsasl_stream(sess, str);
+ if (!gsasl_str) {
+ dico_log(L_ERR, errno, _("Cannot create GSASL stream"));
+ return 1;
+ }
+
+ if (!prev)
+ *pstr = gsasl_str;
+ else if (dico_stream_ioctl(prev, DICO_IOCTL_SET_TRANSPORT, gsasl_str)) {
+ dico_log(L_ERR, errno, _("Cannot install GSASL stream"));
+ return 1;
+ }
+ return 0;
+}
+
+#endif
diff --git a/xdico/iputil.c b/xdico/iputil.c
new file mode 100644
index 0000000..cf11ab6
--- /dev/null
+++ b/xdico/iputil.c
@@ -0,0 +1,127 @@
+/* This file is part of GNU Dico.
+ Copyright (C) 1998-2019 Sergey Poznyakoff
+
+ GNU Dico is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3, or (at your option)
+ any later version.
+
+ GNU Dico 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 General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with GNU Dico. If not, see <http://www.gnu.org/licenses/>. */
+
+#include <config.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/time.h>
+#include <sys/un.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <string.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <netdb.h>
+
+#include <intprops.h>
+#include <inttostr.h>
+
+#include <xdico.h>
+#include <xalloc.h>
+
+static size_t
+mu_stpcpy (char **pbuf, size_t *psize, const char *src)
+{
+ size_t slen = strlen (src);
+ if (pbuf == NULL || *pbuf == NULL)
+ return slen;
+ else {
+ char *buf = *pbuf;
+ size_t size = *psize;
+ if (size > slen)
+ size = slen;
+ memcpy (buf, src, size);
+ *psize -= size;
+ *pbuf += size;
+ if (*psize)
+ **pbuf = 0;
+ else
+ (*pbuf)[-1] = 0;
+ return size;
+ }
+}
+
+#define S_UN_NAME(sa, salen) \
+ ((salen < offsetof (struct sockaddr_un,sun_path)) ? "" : (sa)->sun_path)
+
+void
+sockaddr_to_str (const struct sockaddr *sa, int salen,
+ char *bufptr, size_t buflen,
+ size_t *plen)
+{
+ char buf[INT_BUFSIZE_BOUND (uintmax_t)]; /* FIXME: too much */
+ size_t len = 0;
+ switch (sa->sa_family) {
+ case AF_INET:
+ case AF_INET6:
+ {
+ char host[NI_MAXHOST];
+ char srv[NI_MAXSERV];
+ if (getnameinfo(sa, salen,
+ host, sizeof(host), srv, sizeof(srv),
+ NI_NUMERICHOST|NI_NUMERICSERV) == 0) {
+ if (sa->sa_family == AF_INET6) {
+ len += mu_stpcpy(&bufptr, &buflen, "inet6://[");
+ len += mu_stpcpy(&bufptr, &buflen, host);
+ len += mu_stpcpy(&bufptr, &buflen, "]:");
+ len += mu_stpcpy(&bufptr, &buflen, srv);
+ } else {
+ len += mu_stpcpy(&bufptr, &buflen, "inet://");
+ len += mu_stpcpy(&bufptr, &buflen, host);
+ len += mu_stpcpy(&bufptr, &buflen, ":");
+ len += mu_stpcpy(&bufptr, &buflen, srv);
+ }
+ } else {
+ if (sa->sa_family == AF_INET6)
+ len += mu_stpcpy(&bufptr, &buflen, "inet6://[getnameinfo failed]");
+ else
+ len += mu_stpcpy(&bufptr, &buflen, "inet://[getnameinfo failed]");
+ }
+ break;
+ }
+
+ case AF_UNIX:
+ {
+ struct sockaddr_un *s_un = (struct sockaddr_un *)sa;
+ if (S_UN_NAME(s_un, salen)[0] == 0)
+ len += mu_stpcpy (&bufptr, &buflen, "anonymous socket");
+ else {
+ len += mu_stpcpy (&bufptr, &buflen, "socket ");
+ len += mu_stpcpy (&bufptr, &buflen, s_un->sun_path);
+ }
+ break;
+ }
+
+ default:
+ len += mu_stpcpy (&bufptr, &buflen, "{Unsupported family: ");
+ len += mu_stpcpy (&bufptr, &buflen, umaxtostr (sa->sa_family, buf));
+ len += mu_stpcpy (&bufptr, &buflen, "}");
+ }
+ if (plen)
+ *plen = len + 1;
+}
+
+char *
+sockaddr_to_astr (const struct sockaddr *sa, int salen)
+{
+ size_t size;
+ char *p;
+
+ sockaddr_to_str(sa, salen, NULL, 0, &size);
+ p = xmalloc(size);
+ sockaddr_to_str(sa, salen, p, size, NULL);
+ return p;
+}
diff --git a/xdico/timer.c b/xdico/timer.c
new file mode 100644
index 0000000..1239108
--- /dev/null
+++ b/xdico/timer.c
@@ -0,0 +1,199 @@
+/* This file is part of GNU Dico
+ Copyright (C) 2008-2019 Sergey Poznyakoff
+
+ GNU Dico is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3, or (at your option)
+ any later version.
+
+ GNU Dico 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 General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with GNU Dico. If not, see <http://www.gnu.org/licenses/>. */
+
+#include <config.h>
+#include <xdico.h>
+#include <sys/time.h>
+#include <sys/resource.h>
+#include <unistd.h>
+#include <string.h>
+#include <hash.h>
+#include <ctype.h>
+#include <xalloc.h>
+
+struct timer_slot {
+ char *name;
+ double real;
+ double self_user; /* user time in sec */
+ double self_system; /* system time in sec */
+ double children_user; /* user time in sec */
+ double children_system; /* system time in sec */
+ struct timeval real_mark;
+ struct rusage self_mark;
+ struct rusage children_mark;
+};
+
+
+static Hash_table *timer_table;
+
+static size_t
+hash_string_ci (const char *string, size_t n_buckets)
+{
+ size_t value = 0;
+ unsigned char ch;
+
+ for (; (ch = *string); string++)
+ value = (value * 31 + tolower(ch)) % n_buckets;
+ return value;
+}
+
+/* Calculate the hash of a string. */
+static size_t
+timer_hasher(void const *data, size_t n_buckets)
+{
+ const struct timer_slot *t = data;
+ return hash_string_ci(t->name, n_buckets);
+}
+
+/* Compare two strings for equality. */
+static bool
+timer_compare(void const *data1, void const *data2)
+{
+ const struct timer_slot *t1 = data1;
+ const struct timer_slot *t2 = data2;
+ return strcasecmp(t1->name, t2->name) == 0;
+}
+
+/* Allocate a timer structure */
+static xdico_timer_t
+timer_alloc(const char *name)
+{
+ xdico_timer_t t = xmalloc(sizeof(*t) + strlen(name) + 1);
+ memset(t, 0, sizeof(*t));
+ t->name = (char*)(t + 1);
+ strcpy(t->name, name);
+ return t;
+}
+
+/* Lookup a timer by its name. If it does not exist, create it. */
+xdico_timer_t
+timer_get(const char *name)
+{
+ xdico_timer_t tp, ret;
+
+ tp = timer_alloc(name);
+
+ if (! ((timer_table
+ || (timer_table = hash_initialize(0, 0,
+ timer_hasher,
+ timer_compare, 0)))
+ && (ret = hash_insert(timer_table, tp))))
+ xalloc_die ();
+
+ if (ret != tp)
+ free(tp);
+ return ret;
+}
+
+xdico_timer_t
+timer_start(const char *name)
+{
+ xdico_timer_t t = timer_get(name);
+
+ gettimeofday(&t->real_mark, NULL);
+ getrusage(RUSAGE_SELF, &t->self_mark);
+ getrusage(RUSAGE_CHILDREN, &t->children_mark);
+ return t;
+}
+
+#define DIFFTIME(now,then)\
+ (((now).tv_sec - (then).tv_sec) \
+ + ((double)((now).tv_usec - (then).tv_usec))/1000000)
+
+static void
+_timer_compute(xdico_timer_t t)
+{
+ struct timeval real;
+ struct rusage rusage;
+
+ gettimeofday(&real, NULL);
+ t->real = DIFFTIME(real, t->real_mark);
+ getrusage(RUSAGE_SELF, &rusage);
+ t->self_user = DIFFTIME(rusage.ru_utime, t->self_mark.ru_utime);
+ t->self_system = DIFFTIME(rusage.ru_stime, t->self_mark.ru_stime);
+
+ getrusage(RUSAGE_CHILDREN, &rusage);
+ t->children_user = DIFFTIME(rusage.ru_utime, t->children_mark.ru_utime);
+ t->children_system = DIFFTIME(rusage.ru_stime, t->children_mark.ru_stime);
+}
+
+xdico_timer_t
+timer_stop(const char *name)
+{
+ xdico_timer_t t = timer_get(name);
+ _timer_compute(t);
+ return t;
+}
+
+xdico_timer_t
+timer_reset(const char *name)
+{
+ xdico_timer_t t = timer_get(name);
+ t->real = 0.0;
+ t->self_user = 0.0;
+ t->self_system = 0.0;
+ t->children_user = 0.0;
+ t->children_system = 0.0;
+ return t;
+}
+
+double
+timer_get_real(xdico_timer_t t)
+{
+ return t->real;
+}
+
+double
+timer_get_user(xdico_timer_t t)
+{
+ return t->self_user + t->children_user;
+}
+
+double
+timer_get_system(xdico_timer_t t)
+{
+ return t->self_system + t->children_system;
+}
+
+void
+timer_format_time(dico_stream_t stream, double t)
+{
+ static char buf[128];
+
+ if (t < 600)
+ snprintf(buf, sizeof(buf), "%0.3f", t );
+ else {
+ long int s, m, h, d;
+
+ s = (long int)t;
+ d = s / (3600*24);
+ s -= d * 3600 * 24;
+ h = s / 3600;
+ s -= h * 3600;
+ m = s / 60;
+ s -= m * 60;
+
+ if (d)
+ snprintf(buf, sizeof(buf), "%ld+%02ld:%02ld:%02ld", d, h, m, s);
+ else if (h)
+ snprintf(buf, sizeof(buf), "%02ld:%02ld:%02ld", h, m, s);
+ else
+ snprintf(buf, sizeof(buf), "%02ld:%02ld", m, s);
+ }
+ dico_stream_write(stream, buf, strlen(buf));
+}
+
+
diff --git a/xdico/userprivs.c b/xdico/userprivs.c
new file mode 100644
index 0000000..cafa237
--- /dev/null
+++ b/xdico/userprivs.c
@@ -0,0 +1,138 @@
+/* This file is part of GNU Dico.
+ Copyright (C) 2007-2019 Sergey Poznyakoff
+
+ GNU Dico is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3, or (at your option)
+ any later version.
+
+ GNU Dico 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 General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with GNU Dico. If not, see <http://www.gnu.org/licenses/>. */
+
+#include <config.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <grp.h>
+#include <errno.h>
+#include <xalloc.h>
+#include <xdico.h>
+#include <libi18n.h>
+
+/* Switch to the given UID/GID */
+int
+switch_to_privs (uid_t uid, gid_t gid, dico_list_t retain_groups)
+{
+ int rc = 0;
+ gid_t *emptygidset;
+ size_t size = 1, j = 1;
+ dico_iterator_t itr;
+ void *gp;
+
+ _dico_libi18n_init();
+
+ if (uid == 0) {
+ dico_log(L_ERR, 0, _("Refusing to run as root"));
+ return 1;
+ }
+
+ /* Create a list of supplementary groups */
+ size = dico_list_count(retain_groups);
+ size++;
+ emptygidset = xcalloc(size, sizeof emptygidset[0]);
+ emptygidset[0] = gid ? gid : getegid();
+
+ itr = dico_list_iterator(retain_groups);
+ for (gp = dico_iterator_first(itr); gp;
+ gp = dico_iterator_next(itr))
+ emptygidset[j++] = *(gid_t*) gp;
+ dico_iterator_destroy(&itr);
+
+ /* Reset group permissions */
+ if (geteuid() == 0 && setgroups(j, emptygidset)) {
+ dico_log(L_ERR, errno, _("setgroups(1, %lu) failed"),
+ (unsigned long) emptygidset[0]);
+ rc = 1;
+ }
+ free(emptygidset);
+
+ /* Switch to the user's gid. On some OSes the effective gid must
+ be reset first */
+
+#if defined(HAVE_SETEGID)
+ if ((rc = setegid(gid)) < 0)
+ dico_log(L_ERR, errno, _("setegid(%lu) failed"),
+ (unsigned long) gid);
+#elif defined(HAVE_SETREGID)
+ if ((rc = setregid(gid, gid)) < 0)
+ dico_log(L_ERR, errno, _("setregid(%lu,%lu) failed"),
+ (unsigned long) gid, (unsigned long) gid);
+#elif defined(HAVE_SETRESGID)
+ if ((rc = setresgid(gid, gid, gid)) < 0)
+ dico_log(L_ERR, errno, _("setresgid(%lu,%lu,%lu) failed"),
+ (unsigned long) gid,
+ (unsigned long) gid,
+ (unsigned long) gid);
+#endif
+
+ if (rc == 0 && gid != 0) {
+ if ((rc = setgid(gid)) < 0 && getegid() != gid)
+ dico_log(L_ERR, errno, _("setgid(%lu) failed"),
+ (unsigned long) gid);
+ if (rc == 0 && getegid() != gid) {
+ dico_log(L_ERR, errno, _("Cannot set effective gid to %lu"),
+ (unsigned long) gid);
+ rc = 1;
+ }
+ }
+
+ /* Now reset uid */
+ if (rc == 0 && uid != 0) {
+ uid_t euid;
+
+ if (setuid(uid)
+ || geteuid() != uid
+ || (getuid() != uid
+ && (geteuid() == 0 || getuid() == 0))) {
+
+#if defined(HAVE_SETREUID)
+ if (geteuid() != uid) {
+ if (setreuid(uid, -1) < 0) {
+ dico_log(L_ERR, errno, _("setreuid(%lu,-1) failed"),
+ (unsigned long) uid);
+ rc = 1;
+ }
+ if (setuid(uid) < 0) {
+ dico_log(L_ERR, errno, _("second setuid(%lu) failed"),
+ (unsigned long) uid);
+ rc = 1;
+ }
+ } else
+#endif
+ {
+ dico_log(L_ERR, errno, _("setuid(%lu) failed"),
+ (unsigned long) uid);
+ rc = 1;
+ }
+ }
+
+ euid = geteuid();
+ if (uid != 0 && setuid(0) == 0) {
+ dico_log(L_ERR, 0, _("seteuid(0) succeeded when it should not"));
+ rc = 1;
+ } else if (uid != euid && setuid(euid) == 0) {
+ dico_log(L_ERR, 0, _("Cannot drop non-root setuid privileges"));
+ rc = 1;
+ }
+
+ }
+
+ return rc;
+}
+
+
diff --git a/xdico/xalloc-die.c b/xdico/xalloc-die.c
new file mode 100644
index 0000000..4f42be6
--- /dev/null
+++ b/xdico/xalloc-die.c
@@ -0,0 +1,25 @@
+/* This file is part of GNU Dico.
+ Copyright (C) 1998-2019 Sergey Poznyakoff
+
+ GNU Dico is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3, or (at your option)
+ any later version.
+
+ GNU Dico 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 General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with GNU Dico. If not, see <http://www.gnu.org/licenses/>. */
+
+#include <config.h>
+#include <xdico.h>
+#include <sysexits.h>
+
+void
+xalloc_die(void)
+{
+ dico_die(EX_OSERR, L_CRIT, 0, "Not enough memory");
+}
diff --git a/xdico/xhostname.c b/xdico/xhostname.c
new file mode 100644
index 0000000..51b9c6d
--- /dev/null
+++ b/xdico/xhostname.c
@@ -0,0 +1,52 @@
+/* This file is part of GNU Dico
+ Copyright (C) 2008-2019 Sergey Poznyakoff
+
+ GNU Dico is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3, or (at your option)
+ any later version.
+
+ GNU Dico 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 General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with GNU Dico. If not, see <http://www.gnu.org/licenses/>. */
+
+#include <config.h>
+#include <stdlib.h>
+#include <string.h>
+#include <netdb.h>
+#include <xdico.h>
+#include <xalloc.h>
+#include <xgethostname.h>
+#include <xgetdomainname.h>
+
+char *
+xdico_local_hostname(void)
+{
+ struct hostent *hp;
+ char *hostpart = xgethostname();
+ char *ret;
+
+ hp = gethostbyname(hostpart);
+ if (hp)
+ ret = xstrdup(hp->h_name);
+ else {
+ char *domainpart = xgetdomainname();
+
+ if (domainpart && domainpart[0] && strcmp(domainpart, "(none)")) {
+ ret = xmalloc(strlen(hostpart) + 1
+ + strlen(domainpart) + 1);
+ strcpy(ret, hostpart);
+ strcat(ret, ".");
+ strcat(ret, domainpart);
+ free(hostpart);
+ } else
+ ret = hostpart;
+ free(domainpart);
+ }
+ return ret;
+}
+
diff --git a/xdico/xscript.c b/xdico/xscript.c
new file mode 100644
index 0000000..47f0d5d
--- /dev/null
+++ b/xdico/xscript.c
@@ -0,0 +1,204 @@
+/* This file is part of GNU Dico.
+ Copyright (C) 2008-2019 Sergey Poznyakoff
+
+ GNU Dico is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3, or (at your option)
+ any later version.
+
+ GNU Dico is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or