diff options
author | Sergey Poznyakoff <gray@gnu.org.ua> | 2005-06-04 17:24:04 +0000 |
---|---|---|
committer | Sergey Poznyakoff <gray@gnu.org.ua> | 2005-06-04 17:24:04 +0000 |
commit | 447f6ddce61e287c6b95a403ddbbe827f8c17ce8 (patch) | |
tree | 3d4fbe863ae81dbab8209af48decf04bf6205f9f | |
parent | e462754c736fbf815b20acea417333564f6b9981 (diff) | |
download | mailfromd-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.c | 195 | ||||
-rw-r--r-- | src/dns.c | 194 | ||||
-rw-r--r-- | src/mailfrom.h | 75 | ||||
-rw-r--r-- | src/obstack.c | 498 | ||||
-rw-r--r-- | src/obstack.h | 519 | ||||
-rw-r--r-- | src/snprintf.c | 830 | ||||
-rw-r--r-- | src/snprintf.h | 203 |
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. |