aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSergey Poznyakoff <gray@gnu.org.ua>2005-06-04 17:24:04 +0000
committerSergey Poznyakoff <gray@gnu.org.ua>2005-06-04 17:24:04 +0000
commit447f6ddce61e287c6b95a403ddbbe827f8c17ce8 (patch)
tree3d4fbe863ae81dbab8209af48decf04bf6205f9f
parente462754c736fbf815b20acea417333564f6b9981 (diff)
downloadmailfromd-447f6ddce61e287c6b95a403ddbbe827f8c17ce8.tar.gz
mailfromd-447f6ddce61e287c6b95a403ddbbe827f8c17ce8.tar.bz2
Moved from ../
git-svn-id: file:///svnroot/mailfromd/trunk@19 7a8a7f39-df28-0410-adc6-e0d955640f24
-rw-r--r--src/daemon.c195
-rw-r--r--src/dns.c194
-rw-r--r--src/mailfrom.h75
-rw-r--r--src/obstack.c498
-rw-r--r--src/obstack.h519
-rw-r--r--src/snprintf.c830
-rw-r--r--src/snprintf.h203
7 files changed, 2514 insertions, 0 deletions
diff --git a/src/daemon.c b/src/daemon.c
new file mode 100644
index 00000000..25e51a62
--- /dev/null
+++ b/src/daemon.c
@@ -0,0 +1,195 @@
+/*-
+ * Copyright (c) 1990, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+#include <stdio.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <signal.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+
+#ifdef HAVE_PATHS_H
+# include <paths.h>
+#endif
+
+#ifndef _PATH_DEVNULL
+# define _PATH_DEVNULL "/dev/null"
+#endif
+
+/*
+ According to Unix-FAQ maintained by Andrew Gierth:
+
+ 1.fork() so the parent can exit, this returns control to the command
+ line or shell invoking your program. This step is required so that the
+ new process is guaranteed not to be a process group leader. The next
+ step, setsid(), fails if you're a process group leader.
+
+ 2.setsid() to become a process group and session group leader. Since a
+ controlling terminal is associated with a session, and this new session
+ has not yet acquired a controlling terminal our process now has no
+ controlling terminal, which is a Good Thing for daemons.
+
+ 3.fork() again so the parent, (the session group leader), can exit. This
+ means that we, as a non-session group leader, can never regain a
+ controlling terminal.
+
+ 4.chdir("/") to ensure that our process doesn't keep any directory in use.
+ Failure to do this could make it so that an administrator couldn't unmount
+ a filesystem, because it was our current directory.
+ [Equivalently, we could change to any directory containing files important
+ to the daemon's operation.]
+
+ 5.umask(0) so that we have complete control over the permissions of
+ anything we write. We don't know what umask we may have inherited.
+ [This step is optional]
+
+ 6.close() fds 0, 1, and 2. This releases the standard in, out, and error
+ we inherited from our parent process. We have no way of knowing where
+ these fds might have been redirected to. Note that many daemons use
+ sysconf() to determine the limit _SC_OPEN_MAX. _SC_OPEN_MAX tells you the
+ maximun open files/process. Then in a loop, the daemon can close all
+ possible file descriptors. You have to decide if you need to do this or not.
+ If you think that there might be file-descriptors open you should close
+ them, since there's a limit on number of concurrent file descriptors.
+
+ 7.Establish new open descriptors for stdin, stdout and stderr. Even if
+ you don't plan to use them, it is still a good idea to have them open.
+ The precise handling of these is a matter of taste; if you have a logfile,
+ for example, you might wish to open it as stdout or stderr, and open
+ `/dev/null' as stdin; alternatively, you could open `/dev/console' as
+ stderr and/or stdout, and `/dev/null' as stdin, or any other combination
+ that makes sense for your particular daemon. */
+
+#define MAXFD 64
+
+void
+waitdaemon_timeout (int signo ARG_UNUSED)
+{
+ int left;
+
+ left = alarm (0);
+ signal (SIGALRM, SIG_DFL);
+ if (left == 0)
+ {
+ fprintf (stderr, "timed out waiting for child\n");
+ exit (1);
+ }
+}
+
+/* waitdaemon is like daemon, but optionally the parent pause up
+ until maxwait before exiting. Return -1, on error, otherwise
+ waitdaemon will return the pid of the parent. */
+
+int
+waitdaemon (int nochdir, int noclose, int maxwait)
+{
+ int fd;
+ pid_t childpid;
+ pid_t ppid;
+
+ ppid = getpid ();
+
+ switch (childpid = fork ())
+ {
+ case -1: /* Something went wrong. */
+ return (-1);
+
+ case 0: /* In the child. */
+ break;
+
+ default: /* In the parent. */
+ if (maxwait > 0)
+ {
+ signal (SIGALRM, waitdaemon_timeout);
+ alarm (maxwait);
+ pause ();
+ }
+ _exit(0);
+ }
+
+ if (setsid () == -1)
+ return -1;
+
+ /* SIGHUP is ignore because when the session leader terminates
+ all process in the session (the second child) are sent the SIGHUP. */
+ signal (SIGHUP, SIG_IGN);
+
+ switch (fork ())
+ {
+ case 0:
+ break;
+
+ case -1:
+ return -1;
+
+ default:
+ _exit (0);
+ }
+
+ if (!nochdir)
+ chdir ("/");
+
+ if (!noclose)
+ {
+ int i;
+ long fdlimit = -1;
+
+#if defined (HAVE_SYSCONF) && defined (_SC_OPEN_MAX)
+ fdlimit = sysconf (_SC_OPEN_MAX);
+#elif defined (HAVE_GETDTABLESIZE)
+ fdlimit = getdtablesize ();
+#endif
+
+ if (fdlimit == -1)
+ fdlimit = MAXFD;
+
+ for (i = 0; i < fdlimit; i++)
+ close (i);
+
+ fd = open (_PATH_DEVNULL, O_RDWR, 0);
+ if (fd != -1)
+ {
+ dup2 (fd, STDIN_FILENO);
+ dup2 (fd, STDOUT_FILENO);
+ dup2 (fd, STDERR_FILENO);
+ if (fd > 2)
+ close (fd);
+ }
+ }
+ return ppid;
+}
+
+int
+daemon (int nochdir, int noclose)
+{
+ return (waitdaemon (nochdir, noclose, 0) == -1) ? -1 : 0;
+}
diff --git a/src/dns.c b/src/dns.c
new file mode 100644
index 00000000..2cbe4e92
--- /dev/null
+++ b/src/dns.c
@@ -0,0 +1,194 @@
+/* This file is part of mailfrom filter.
+ Copyright (C) 2005, Sergey Poznyakoff
+
+ This program 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 2, or (at your option)
+ any later version.
+
+ This program 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 this program; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
+ MA 02110-1301 USA */
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+#include <stdlib.h>
+#include <resolv.h>
+#include <arpa/nameser.h>
+#include <netdb.h>
+#include <pthread.h>
+#include <mailutils/mailutils.h>
+#include "mailfrom.h"
+
+struct mx_buffer {
+ unsigned pref;
+ char *name;
+};
+
+static int
+comp_pref(const void *a, const void *b)
+{
+ const struct mx_buffer *ma = a, *mb = b;
+ if (ma->pref > mb->pref)
+ return 1;
+ else if (ma->pref < mb->pref)
+ return -1;
+ return 0;
+}
+
+static void
+free_mx_buffer(struct mx_buffer *mx_buffer, int nmx)
+{
+ int i;
+ for (i = 0; i < nmx; i++)
+ free(mx_buffer[i].name);
+}
+
+static getmx_status
+_getmx(char *host, char *answer, size_t answer_size, mxbuf_t mxbuf)
+{
+ int n, nmx, i, count;
+ ns_msg msg;
+ struct mx_buffer mx_buffer[MAXMXCOUNT];
+
+ debug(100,("Getting MX records for %s", host));
+ n = res_query (host, C_IN, T_MX, answer, answer_size);
+ if (n < 0) {
+ debug(10, ("res_query failed (errno=%s, h_errno=%d)",
+ mu_strerror(errno), h_errno));
+ switch (h_errno) {
+ case NO_DATA:
+ case NO_RECOVERY:
+ case HOST_NOT_FOUND:
+ return getmx_not_found;
+
+ case TRY_AGAIN:
+ case -1:
+ return getmx_temp_failure;
+
+ default:
+ mu_error("res_nsearch(%s) failed with unexpected h_errno %d",
+ host, h_errno);
+ return getmx_failure;
+ }
+ }
+
+ if (ns_initparse(answer, n, &msg) < 0) {
+ debug(2,("ns_initparse(host %s) failed", host));
+ return getmx_failure;
+ }
+
+ /* Get number of MXs */
+ count = ns_msg_count(msg, ns_s_an);
+ if (count == 0)
+ return getmx_not_found;
+
+ /* Collect MX records */
+ for (i = nmx = 0; i < count; i++) {
+ ns_rr rr;
+
+ if (ns_parserr(&msg, ns_s_an, i, &rr) < 0) {
+ debug(2,("ns_parserrd(%d) failed (host %s)",
+ i, host));
+ free_mx_buffer(mx_buffer, nmx);
+ return getmx_failure;
+ }
+ if (ns_rr_type(rr) == ns_t_mx) {
+ int s;
+ char tname[NS_MAXDNAME];
+ const u_char *rdata;
+ int rdlen;
+
+ rdata = ns_rr_rdata(rr);
+ rdlen = ns_rr_rdlen(rr);
+
+ s = dn_expand(ns_msg_base(msg), ns_msg_end(msg),
+ rdata+NS_INT16SZ, tname, sizeof tname);
+ if (s < 0) {
+ debug(2,("dn_expand failed"));
+ free_mx_buffer(mx_buffer, nmx);
+ return getmx_failure;
+ }
+ mx_buffer[nmx].pref = ns_get16(rdata);
+ mx_buffer[nmx].name = strdup(tname);
+ debug(100,("MX %u %s", mx_buffer[nmx].pref,
+ mx_buffer[nmx].name));
+ if (nmx++ > MAXMXCOUNT)
+ break;
+ }
+ }
+
+ /* Sort according to preference value */
+ qsort(mx_buffer, nmx, sizeof mx_buffer[0], comp_pref);
+
+ /* Prepare return value */
+ memset(mxbuf, 0, sizeof(mxbuf_t));
+ for (i = 0; i < nmx; i++)
+ mxbuf[i] = mx_buffer[i].name;
+ return getmx_success;
+}
+
+static int
+is_ipaddr(char *str)
+{
+ for (; *str; str++)
+ if (!(isdigit(*str) || *str == '.'))
+ return 0;
+ return 1;
+}
+
+static pthread_mutex_t getmx_mutex = PTHREAD_MUTEX_INITIALIZER;
+
+getmx_status
+getmx(char *host, mxbuf_t mxbuf)
+{
+ char hbuf[NI_MAXHOST];
+ getmx_status status = getmx_failure;
+
+ pthread_mutex_lock(&getmx_mutex);
+ if (is_ipaddr(host)) {
+ struct sockaddr_in sa;
+ memset(&sa, 0, sizeof sa);
+ sa.sin_family = AF_INET;
+ if (inet_aton(host, &sa.sin_addr)
+ && getnameinfo((struct sockaddr *)&sa, sizeof sa,
+ hbuf, sizeof(hbuf),
+ NULL, 0,
+ 0) == 0
+ && !is_ipaddr(hbuf))
+ host = hbuf;
+ else
+ host = NULL;
+ }
+
+ if (host) {
+ unsigned char *answer = malloc(MAXPACKET);
+ if (!answer)
+ status = getmx_failure;
+ else {
+ char *p;
+
+ for (p = hbuf; p; ) {
+ status = _getmx(p, answer, MAXPACKET, mxbuf);
+ if (status == getmx_success)
+ break;
+ p = strchr(p, '.');
+ if (!p)
+ break;
+ p++;
+ }
+ free(answer);
+ }
+ }
+ pthread_mutex_unlock(&getmx_mutex);
+ return status;
+}
+
diff --git a/src/mailfrom.h b/src/mailfrom.h
new file mode 100644
index 00000000..86f63a6a
--- /dev/null
+++ b/src/mailfrom.h
@@ -0,0 +1,75 @@
+/* This file is part of mailfrom filter.
+ Copyright (C) 2005, Sergey Poznyakoff
+
+ This program 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 2, or (at your option)
+ any later version.
+
+ This program 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 this program; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
+ MA 02110-1301 USA */
+
+#ifndef MAXPACKET
+# define MAXPACKET 8192 /* max packet size used internally by BIND */
+#endif
+
+typedef enum {
+ getmx_success,
+ getmx_not_found,
+ getmx_failure,
+ getmx_temp_failure,
+} getmx_status;
+
+#define getmx_resolved(c) ((c) == getmx_success || (c) == getmx_not_found)
+
+#define MAXMXCOUNT 32
+typedef char *mxbuf_t[MAXMXCOUNT];
+
+#define DEFAULT_PIDFILE LOCALSTATEDIR "/mailfromd.pid"
+#define DEFAULT_DATABASE LOCALSTATEDIR "/mailfromd"
+
+#define debug(lev,c) do if (debug_level >= lev) {\
+ debug_log("%s:%lu:%s: ", __FILE__, __LINE__, __FUNCTION__); \
+ debug_log c; \
+} while (0)
+
+extern int debug_level;
+extern time_t expire_interval;
+
+extern getmx_status getmx(char *ipstr, mxbuf_t mxbuf);
+
+#include "mu_dbm.h"
+
+#ifdef USE_DBM
+# define cache_get _mailfrom_cache_get
+# define cache_insert _mailfrom_cache_insert
+# define cache_get2 _mailfrom_cache_get2
+# define cache_insert2 _mailfrom_cache_insert2
+# define cache_delete _mailfrom_cache_delete
+# define cache_list_item _mailfrom_cache_list_item
+# define cache_list_db _mailfrom_cache_list_db
+getmx_status cache_get(char *email);
+void cache_insert(char *email, getmx_status rc);
+void cache_delete(char *email);
+getmx_status cache_get2(char *email, char *client_addr);
+void cache_insert2(char *email, char *client_addr, getmx_status rc);
+void cache_list_item(char *email);
+void cache_list_db(void);
+#else
+# define cache_get(email) getmx_failure
+# define cache_insert(email, rc)
+# define cache_get2(email, client_addr) getmx_failure
+# define cache_insert2(email, client_addr, rc)
+# define cache_delete(email)
+# define cache_list_item(email)
+# define cache_list_db()
+#endif
+
+
diff --git a/src/obstack.c b/src/obstack.c
new file mode 100644
index 00000000..18937c43
--- /dev/null
+++ b/src/obstack.c
@@ -0,0 +1,498 @@
+/* obstack.c - subroutines used implicitly by object stack macros
+ Copyright (C) 1988, 89, 90, 91, 92, 93, 94 Free Software Foundation, Inc.
+
+This program 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 2, or (at your option) any
+later version.
+
+This program 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
+Licensealong with GNU Radius; if not, write to the Free Software
+Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 USA. */
+
+#include "obstack.h"
+
+/* NOTE BEFORE MODIFYING THIS FILE: This version number must be
+ incremented whenever callers compiled using an old obstack.h can no
+ longer properly call the functions in this obstack.c. */
+#define OBSTACK_INTERFACE_VERSION 1
+
+/* Comment out all this code if we are using the GNU C Library, and are not
+ actually compiling the library itself, and the installed library
+ supports the same library interface we do. This code is part of the GNU
+ C Library, but also included in many other GNU distributions. Compiling
+ and linking in this code is a waste when using the GNU C library
+ (especially if it is a shared library). Rather than having every GNU
+ program understand `configure --with-gnu-libc' and omit the object
+ files, it is simpler to just do this in the source for each such file. */
+
+#include <stdio.h> /* Random thing to get __GNU_LIBRARY__. */
+#if !defined (_LIBC) && defined (__GNU_LIBRARY__) && __GNU_LIBRARY__ > 1
+#include <gnu-versions.h>
+#if _GNU_OBSTACK_INTERFACE_VERSION == OBSTACK_INTERFACE_VERSION
+#define ELIDE_CODE
+#endif
+#endif
+
+
+#ifndef ELIDE_CODE
+
+
+#if defined (__STDC__) && __STDC__
+#define POINTER void *
+#else
+#define POINTER char *
+#endif
+
+/* Determine default alignment. */
+struct fooalign {char x; double d;};
+#define DEFAULT_ALIGNMENT \
+ ((PTR_INT_TYPE) ((char *)&((struct fooalign *) 0)->d - (char *)0))
+/* If malloc were really smart, it would round addresses to DEFAULT_ALIGNMENT.
+ But in fact it might be less smart and round addresses to as much as
+ DEFAULT_ROUNDING. So we prepare for it to do that. */
+union fooround {long x; double d;};
+#define DEFAULT_ROUNDING (sizeof (union fooround))
+
+/* When we copy a long block of data, this is the unit to do it with.
+ On some machines, copying successive ints does not work;
+ in such a case, redefine COPYING_UNIT to `long' (if that works)
+ or `char' as a last resort. */
+#ifndef COPYING_UNIT
+#define COPYING_UNIT int
+#endif
+
+/* The non-GNU-C macros copy the obstack into this global variable
+ to avoid multiple evaluation. */
+
+struct obstack *_obstack;
+
+/* Define a macro that either calls functions with the traditional malloc/free
+ calling interface, or calls functions with the mmalloc/mfree interface
+ (that adds an extra first argument), based on the state of use_extra_arg.
+ For free, do not use ?:, since some compilers, like the MIPS compilers,
+ do not allow (expr) ? void : void. */
+
+#define CALL_CHUNKFUN(h, size) \
+ (((h) -> use_extra_arg) \
+ ? (*(h)->chunkfun) ((h)->extra_arg, (size)) \
+ : (*(h)->chunkfun) ((size)))
+
+#define CALL_FREEFUN(h, old_chunk) \
+ do { \
+ if ((h) -> use_extra_arg) \
+ (*(h)->freefun) ((h)->extra_arg, (old_chunk)); \
+ else \
+ (*(h)->freefun) ((old_chunk)); \
+ } while (0)
+
+
+/* Initialize an obstack H for use. Specify chunk size SIZE (0 means default).
+ Objects start on multiples of ALIGNMENT (0 means use default).
+ CHUNKFUN is the function to use to allocate chunks,
+ and FREEFUN the function to free them.
+
+ Return nonzero if successful, zero if out of memory.
+ To recover from an out of memory error,
+ free up some memory, then call this again. */
+
+int
+_obstack_begin (h, size, alignment, chunkfun, freefun)
+ struct obstack *h;
+ int size;
+ int alignment;
+ POINTER (*chunkfun) ();
+ void (*freefun) ();
+{
+ register struct _obstack_chunk* chunk; /* points to new chunk */
+
+ if (alignment == 0)
+ alignment = DEFAULT_ALIGNMENT;
+ if (size == 0)
+ /* Default size is what GNU malloc can fit in a 4096-byte block. */
+ {
+ /* 12 is sizeof (mhead) and 4 is EXTRA from GNU malloc.
+ Use the values for range checking, because if range checking is off,
+ the extra bytes won't be missed terribly, but if range checking is on
+ and we used a larger request, a whole extra 4096 bytes would be
+ allocated.
+
+ These number are irrelevant to the new GNU malloc. I suspect it is
+ less sensitive to the size of the request. */
+ int extra = ((((12 + DEFAULT_ROUNDING - 1) & ~(DEFAULT_ROUNDING - 1))
+ + 4 + DEFAULT_ROUNDING - 1)
+ & ~(DEFAULT_ROUNDING - 1));
+ size = 4096 - extra;
+ }
+
+ h->chunkfun = (struct _obstack_chunk * (*)()) chunkfun;
+ h->freefun = freefun;
+ h->chunk_size = size;
+ h->alignment_mask = alignment - 1;
+ h->use_extra_arg = 0;
+
+ chunk = h->chunk = CALL_CHUNKFUN (h, h -> chunk_size);
+ if (!chunk)
+ {
+ h->alloc_failed = 1;
+ return 0;
+ }
+ h->alloc_failed = 0;
+ h->next_free = h->object_base = chunk->contents;
+ h->chunk_limit = chunk->limit
+ = (char *) chunk + h->chunk_size;
+ chunk->prev = 0;
+ /* The initial chunk now contains no empty object. */
+ h->maybe_empty_object = 0;
+ return 1;
+}
+
+int
+_obstack_begin_1 (h, size, alignment, chunkfun, freefun, arg)
+ struct obstack *h;
+ int size;
+ int alignment;
+ POINTER (*chunkfun) ();
+ void (*freefun) ();
+ POINTER arg;
+{
+ register struct _obstack_chunk* chunk; /* points to new chunk */
+
+ if (alignment == 0)
+ alignment = DEFAULT_ALIGNMENT;
+ if (size == 0)
+ /* Default size is what GNU malloc can fit in a 4096-byte block. */
+ {
+ /* 12 is sizeof (mhead) and 4 is EXTRA from GNU malloc.
+ Use the values for range checking, because if range checking is off,
+ the extra bytes won't be missed terribly, but if range checking is on
+ and we used a larger request, a whole extra 4096 bytes would be
+ allocated.
+
+ These number are irrelevant to the new GNU malloc. I suspect it is
+ less sensitive to the size of the request. */
+ int extra = ((((12 + DEFAULT_ROUNDING - 1) & ~(DEFAULT_ROUNDING - 1))
+ + 4 + DEFAULT_ROUNDING - 1)
+ & ~(DEFAULT_ROUNDING - 1));
+ size = 4096 - extra;
+ }
+
+ h->chunkfun = (struct _obstack_chunk * (*)()) chunkfun;
+ h->freefun = freefun;
+ h->chunk_size = size;
+ h->alignment_mask = alignment - 1;
+ h->extra_arg = arg;
+ h->use_extra_arg = 1;
+
+ chunk = h->chunk = CALL_CHUNKFUN (h, h -> chunk_size);
+ if (!chunk)
+ {
+ h->alloc_failed = 1;
+ return 0;
+ }
+ h->alloc_failed = 0;
+ h->next_free = h->object_base = chunk->contents;
+ h->chunk_limit = chunk->limit
+ = (char *) chunk + h->chunk_size;
+ chunk->prev = 0;
+ /* The initial chunk now contains no empty object. */
+ h->maybe_empty_object = 0;
+ return 1;
+}
+
+/* Allocate a new current chunk for the obstack *H
+ on the assumption that LENGTH bytes need to be added
+ to the current object, or a new object of length LENGTH allocated.
+ Copies any partial object from the end of the old chunk
+ to the beginning of the new one. */
+
+void
+_obstack_newchunk (h, length)
+ struct obstack *h;
+ int length;
+{
+ register struct _obstack_chunk* old_chunk = h->chunk;
+ register struct _obstack_chunk* new_chunk;
+ register long new_size;
+ register int obj_size = h->next_free - h->object_base;
+ register int i;
+ int already;
+
+ /* Compute size for new chunk. */
+ new_size = (obj_size + length) + (obj_size >> 3) + 100;
+ if (new_size < h->chunk_size)
+ new_size = h->chunk_size;
+
+ /* Allocate and initialize the new chunk. */
+ new_chunk = CALL_CHUNKFUN (h, new_size);
+ if (!new_chunk)
+ {
+ h->alloc_failed = 1;
+ return;
+ }
+ h->alloc_failed = 0;
+ h->chunk = new_chunk;
+ new_chunk->prev = old_chunk;
+ new_chunk->limit = h->chunk_limit = (char *) new_chunk + new_size;
+
+ /* Move the existing object to the new chunk.
+ Word at a time is fast and is safe if the object
+ is sufficiently aligned. */
+ if (h->alignment_mask + 1 >= DEFAULT_ALIGNMENT)
+ {
+ for (i = obj_size / sizeof (COPYING_UNIT) - 1;
+ i >= 0; i--)
+ ((COPYING_UNIT *)new_chunk->contents)[i]
+ = ((COPYING_UNIT *)h->object_base)[i];
+ /* We used to copy the odd few remaining bytes as one extra COPYING_UNIT,
+ but that can cross a page boundary on a machine
+ which does not do strict alignment for COPYING_UNITS. */
+ already = obj_size / sizeof (COPYING_UNIT) * sizeof (COPYING_UNIT);
+ }
+ else
+ already = 0;
+ /* Copy remaining bytes one by one. */
+ for (i = already; i < obj_size; i++)
+ new_chunk->contents[i] = h->object_base[i];
+
+ /* If the object just copied was the only data in OLD_CHUNK,
+ free that chunk and remove it from the chain.
+ But not if that chunk might contain an empty object. */
+ if (h->object_base == old_chunk->contents && ! h->maybe_empty_object)
+ {
+ new_chunk->prev = old_chunk->prev;
+ CALL_FREEFUN (h, old_chunk);
+ }
+
+ h->object_base = new_chunk->contents;
+ h->next_free = h->object_base + obj_size;
+ /* The new chunk certainly contains no empty object yet. */
+ h->maybe_empty_object = 0;
+}
+
+/* Return nonzero if object OBJ has been allocated from obstack H.
+ This is here for debugging.
+ If you use it in a program, you are probably losing. */
+
+#if defined (__STDC__) && __STDC__
+/* Suppress -Wmissing-prototypes warning. We don't want to declare this in
+ obstack.h because it is just for debugging. */
+int _obstack_allocated_p (struct obstack *h, POINTER obj);
+#endif
+
+int
+_obstack_allocated_p (h, obj)
+ struct obstack *h;
+ POINTER obj;
+{
+ register struct _obstack_chunk* lp; /* below addr of any objects in this chunk */
+ register struct _obstack_chunk* plp; /* point to previous chunk if any */
+
+ lp = (h)->chunk;
+ /* We use >= rather than > since the object cannot be exactly at
+ the beginning of the chunk but might be an empty object exactly
+ at the end of an adjacent chunk. */
+ while (lp != 0 && ((POINTER)lp >= obj || (POINTER)(lp)->limit < obj))
+ {
+ plp = lp->prev;
+ lp = plp;
+ }
+ return lp != 0;
+}
+
+/* Free objects in obstack H, including OBJ and everything allocate
+ more recently than OBJ. If OBJ is zero, free everything in H. */
+
+#undef obstack_free
+
+/* This function has two names with identical definitions.
+ This is the first one, called from non-ANSI code. */
+
+void
+_obstack_free (h, obj)
+ struct obstack *h;
+ POINTER obj;
+{
+ register struct _obstack_chunk* lp; /* below addr of any objects in this chunk */
+ register struct _obstack_chunk* plp; /* point to previous chunk if any */
+
+ lp = h->chunk;
+ /* We use >= because there cannot be an object at the beginning of a chunk.
+ But there can be an empty object at that address
+ at the end of another chunk. */
+ while (lp != 0 && ((POINTER)lp >= obj || (POINTER)(lp)->limit < obj))
+ {
+ plp = lp->prev;
+ CALL_FREEFUN (h, lp);
+ lp = plp;
+ /* If we switch chunks, we can't tell whether the new current
+ chunk contains an empty object, so assume that it may. */
+ h->maybe_empty_object = 1;
+ }
+ if (lp)
+ {
+ h->object_base = h->next_free = (char *)(obj);
+ h->chunk_limit = lp->limit;
+ h->chunk = lp;
+ }
+ else if (obj != 0)
+ /* obj is not in any of the chunks! */
+ abort ();
+}
+
+/* This function is used from ANSI code. */
+
+void
+obstack_free (h, obj)
+ struct obstack *h;
+ POINTER obj;
+{
+ register struct _obstack_chunk* lp; /* below addr of any objects in this chunk */
+ register struct _obstack_chunk* plp; /* point to previous chunk if any */
+
+ lp = h->chunk;
+ /* We use >= because there cannot be an object at the beginning of a chunk.
+ But there can be an empty object at that address
+ at the end of another chunk. */
+ while (lp != 0 && ((POINTER)lp >= obj || (POINTER)(lp)->limit < obj))
+ {
+ plp = lp->prev;
+ CALL_FREEFUN (h, lp);
+ lp = plp;
+ /* If we switch chunks, we can't tell whether the new current
+ chunk contains an empty object, so assume that it may. */
+ h->maybe_empty_object = 1;
+ }
+ if (lp)
+ {
+ h->object_base = h->next_free = (char *)(obj);
+ h->chunk_limit = lp->limit;
+ h->chunk = lp;
+ }
+ else if (obj != 0)
+ /* obj is not in any of the chunks! */
+ abort ();
+}
+
+#if 0
+/* These are now turned off because the applications do not use it
+ and it uses bcopy via obstack_grow, which causes trouble on sysV. */
+
+/* Now define the functional versions of the obstack macros.
+ Define them to simply use the corresponding macros to do the job. */
+
+#if defined (__STDC__) && __STDC__
+/* These function definitions do not work with non-ANSI preprocessors;
+ they won't pass through the macro names in parentheses. */
+
+/* The function names appear in parentheses in order to prevent
+ the macro-definitions of the names from being expanded there. */
+
+POINTER (obstack_base) (obstack)
+ struct obstack *obstack;
+{
+ return obstack_base (obstack);
+}
+
+POINTER (obstack_next_free) (obstack)
+ struct obstack *obstack;
+{
+ return obstack_next_free (obstack);
+}
+
+int (obstack_object_size) (obstack)
+ struct obstack *obstack;
+{
+ return obstack_object_size (obstack);
+}
+
+int (obstack_room) (obstack)
+ struct obstack *obstack;
+{
+ return obstack_room (obstack);
+}
+
+void (obstack_grow) (obstack, pointer, length)
+ struct obstack *obstack;
+ POINTER pointer;
+ int length;
+{
+ obstack_grow (obstack, pointer, length);
+}
+
+void (obstack_grow0) (obstack, pointer, length)
+ struct obstack *obstack;
+ POINTER pointer;
+ int length;
+{
+ obstack_grow0 (obstack, pointer, length);
+}
+
+void (obstack_1grow) (obstack, character)
+ struct obstack *obstack;
+ int character;
+{
+ obstack_1grow (obstack, character);
+}
+
+void (obstack_blank) (obstack, length)
+ struct obstack *obstack;
+ int length;
+{
+ obstack_blank (obstack, length);
+}
+
+void (obstack_1grow_fast) (obstack, character)
+ struct obstack *obstack;
+ int character;
+{
+ obstack_1grow_fast (obstack, character);
+}
+
+void (obstack_blank_fast) (obstack, length)
+ struct obstack *obstack;
+ int length;
+{
+ obstack_blank_fast (obstack, length);
+}
+
+POINTER (obstack_finish) (obstack)
+ struct obstack *obstack;
+{
+ return obstack_finish (obstack);
+}
+
+POINTER (obstack_alloc) (obstack, length)
+ struct obstack *obstack;
+ int length;
+{
+ return obstack_alloc (obstack, length);
+}
+
+POINTER (obstack_copy) (obstack, pointer, length)
+ struct obstack *obstack;
+ POINTER pointer;
+ int length;
+{
+ return obstack_copy (obstack, pointer, length);
+}
+
+POINTER (obstack_copy0) (obstack, pointer, length)
+ struct obstack *obstack;
+ POINTER pointer;
+ int length;
+{
+ return obstack_copy0 (obstack, pointer, length);
+}
+
+#endif /* __STDC__ */
+
+#endif /* 0 */
+
+#endif /* !ELIDE_CODE */
diff --git a/src/obstack.h b/src/obstack.h
new file mode 100644
index 00000000..0dfb9725
--- /dev/null
+++ b/src/obstack.h
@@ -0,0 +1,519 @@
+/* obstack.h - object stack macros
+ Copyright (C) 1988, 89, 90, 91, 92, 93, 94, 95, 96 Free Software Foundation, Inc.
+
+This program is free software; you can redistribute it and/or modify it
+under the terms of the GNU Library General Public License as published by the
+Free Software Foundation; either version 2, or (at your option) any
+later version.
+
+This program 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 Library General Public License for more details.
+
+You should have received a copy of the GNU Library General
+Public Licensealong with this program; if not, write to the Free
+SoftwareFoundation, 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 USA. */
+
+/* Summary:
+
+All the apparent functions defined here are macros. The idea
+is that you would use these pre-tested macros to solve a
+very specific set of problems, and they would run fast.
+Caution: no side-effects in arguments please!! They may be
+evaluated MANY times!!
+
+These macros operate a stack of objects. Each object starts life
+small, and may grow to maturity. (Consider building a word syllable
+by syllable.) An object can move while it is growing. Once it has
+been "finished" it never changes address again. So the "top of the
+stack" is typically an immature growing object, while the rest of the
+stack is of mature, fixed size and fixed address objects.
+
+These routines grab large chunks of memory, using a function you
+supply, called `obstack_chunk_alloc'. On occasion, they free chunks,
+by calling `obstack_chunk_free'. You must define them and declare
+them before using any obstack macros.
+
+Each independent stack is represented by a `struct obstack'.
+Each of the obstack macros expects a pointer to such a structure
+as the first argument.