coar        99/04/28 12:20:29

  Modified:    apr/include apr_config.h.in apr_lib.h
               apr/lib  Makefile.in
  Added:       apr/include apr_pools.h hsregex.h
               apr/lib  apr_pools.c
  Log:
        {Whew!}  First pass at sanitising the pool stuff (pools, arrays,
        and tables) for APR.  At least it compiles on Linux, but there's
        lot left to do: make sure it compiles on other platforms (esp.
        Windows), fix the #if'd out BUFF-referencing section, remove
        spurious prototypes from include/apr_pools.h since they're in
        include/apr_lib.h, and general cleanup and sanity-checking.
  
  Revision  Changes    Path
  1.3       +167 -68   apache-apr/apr/include/apr_config.h.in
  
  Index: apr_config.h.in
  ===================================================================
  RCS file: /home/cvs/apache-apr/apr/include/apr_config.h.in,v
  retrieving revision 1.2
  retrieving revision 1.3
  diff -u -r1.2 -r1.3
  --- apr_config.h.in   1999/04/09 05:37:00     1.2
  +++ apr_config.h.in   1999/04/28 19:20:01     1.3
  @@ -1,5 +1,6 @@
   /*
  - * Start..
  + * Include all of the system header files appropriate for this platform,
  + * and make library-wide definitions.
    */
   #ifndef APR_CONFIG_H
   #define APR_CONFIG_H
  @@ -10,7 +11,7 @@
    */
   #ifdef __cplusplus
   extern "C" {
  -#endif
  +#endif /* __cplusplus */
   
   /*
    * List of header files which exist for this platform.
  @@ -71,7 +72,25 @@
    */
   #undef HAVE_MMAP
   
  -#define API_EXPORT(type) type
  +/*
  + * Known problems with system header files that we can fix.
  + */
  +#ifdef ULTRIX
  +/*
  + * We don't want to include Ultrix's sys/resource.h, even if it's
  + * there.
  + */
  +#undef HAVE_SYS_RESOURCE_H
  +/*
  + * Versions of Ultrix prior to 4.3 included a bundled cc with a broken
  + * const.  Don't try to use it; in fact, make sure it's define'd away
  + * before any header files try to use it.
  + */
  +#ifdef __ultrix__
  +#define const
  +#endif /* __ultrix__ */
  +
  +#endif /* ULTRIX */
   
   /*
    * We now know what we have, so make use of it.
  @@ -79,186 +98,204 @@
   
   #ifdef HAVE_SYS_TYPES_H
   #include <sys/types.h>
  -#endif
  +#endif /* HAVE_SYS_TYPES_H */
   
   #ifdef HAVE_CRYPT_H
   #include <crypt.h>
  -#endif
  +#endif /* HAVE_CRYPT_H */
   
   #ifdef HAVE_CTYPE_H
   #include <ctype.h>
  -#endif
  +#endif /* HAVE_CTYPE_H */
   
   #ifdef HAVE_DIR_H
   #include <dir.h>
  -#endif
  +#endif /* HAVE_DIR_H */
   
   #ifdef HAVE_DIRENT_H
   #include <dirent.h>
  -#endif
  +#endif /* HAVE_DIRENT_H */
   
   #ifdef HAVE_ERRNO_H
   #include <errno.h>
  -#endif
  +#endif /* HAVE_ERRNO_H *?
   
   #ifdef HAVE_NET_ERRNO_H
   #include <net/errno.h>
  -#endif
  +#endif /* HAVE_NET_ERRNO_H */
   
   #ifdef HAVE_FCNTL_H
   #include <fcntl.h>
  -#endif
  +#endif /* HAVE_FCNTL_H */
   
   #ifdef HAVE_GRP_H
   #include <grp.h>
  -#endif
  +#endif /* HAVE_GRP_H */
   
   #ifdef HAVE_IO_H
   #include <io.h>
  -#endif
  +#endif /* HAVE_IO_H */
   
   #ifdef HAVE_LIMITS_H
   #include <limits.h>
  -#endif
  +#endif /* HAVE_LIMITS_H */
   
   #ifdef HAVE_MALLOC_H
   #include <malloc.h>
  -#endif
  +#endif /* HAVE_MALLOC_H */
   
   #ifdef HAVE_MATH_H
   #include <math.h>
  -#endif
  +#endif /* HAVE_MATH_H */
   
   #ifdef HAVE_MEMORY_H
   #include <memory.h>
  -#endif
  +#endif /* HAVE_MEMORY_H */
   
   #ifdef HAVE_NETDB_H
   #include <netdb.h>
  -#endif
  +#endif /* HAVE_NETDB_H */
   
   
   #ifdef HAVE_PROCESS_H
   #include <process.h>
  -#endif
  +#endif /* HAVE_PROCESS_H */
   
   #ifdef HAVE_PWD_H
   #include <pwd.h>
  -#endif
  +#endif /* HAVE_PWD_H */
   
   #ifdef HAVE_SETJMP_H
   #include <setjmp.h>
  -#endif
  +#endif /* HAVE_SETJMP_H */
   
   #ifdef HAVE_SIGNAL_H
   #include <signal.h>
  -#endif
  +#endif /* HAVE_SIGNAL_H */
   
   #ifdef HAVE_STDARG_H
   #include <stdarg.h>
  -#endif
  +#endif /* HAVE_STDARG_H */
   
   #ifdef HAVE_STDDEF_H
   #include <stddef.h>
  -#endif
  +#endif /* HAVE_STDDEF_H */
   
   #ifdef HAVE_STDIO_H
   #include <stdio.h>
  -#endif
  +#endif /* HAVE_STDIO_H */
   
   #ifdef HAVE_STDLIB_H
   #include <stdlib.h>
  -#endif
  +#endif /* HAVE_STDLIB_H */
   
   #ifdef HAVE_STRING_H
   #include <string.h>
  -#endif
  +#endif /* HAVE_STRING_H */
   
  +/*
  + * Some sole-platform inclusions.
  + */
   #ifdef TPF
  +
   #ifdef HAVE_TPFEQ_H
   #include <tpfeq.h>
  -#endif
  +#endif /* HAVE_TPFEQ_H */
  +
   #ifdef HAVE_TPFIO_H
   #include <tpfio.h>
  -#endif
  +#endif /* HAVE_TPFIO_H */
  +
   #ifdef HAVE_OSRELDATE_H
   #include <osreldate.h>
  -#endif
  +#endif /* HAVE_OSRELDATE_H */
  +
   #ifdef HAVE_SYSAPI_H
   #include <sysapi.h>
  -#endif
  +#endif /* HAVE_SYSAPI_H */
  +
   #ifdef HAVE_SYSGTIME_H
   #include <sysgtime.h>
  -#endif
  +#endif /* HAVE_SYSGTIME_H */
  +
   #endif /* TPF */
   
   #ifdef HAVE_TIME_H
   #include <time.h>
  -#endif
  +#endif /* HAVE_TIME_H */
   
   #ifdef HAVE_SYS_TIME_H
   #include <sys/time.h>
  -#endif
  +#endif /* HAVE_SYS_TIME_H */
   
   #ifdef HAVE_SYS_TIMES_H
   #include <sys/times.h>
  -#endif
  +#endif /* HAVE_SYS_TIMES_H */
   
   #ifdef HAVE_UNISTD_H
   #include <unistd.h>
  -#endif
  +#endif /* HAVE_UNISTD_H */
   
   #ifdef HAVE_UNIX_H
   #include <unix.h>
  -#endif
  +#endif /* HAVE_UNIX_H */
   
  +/*
  + * Header files to include *except* for a particular platform.
  + */
   #ifndef OS2
  +
   #ifdef HAVE_ARPA_INET_H
   #include <arpa/inet.h>
  -#endif
  -#endif
  +#endif /* HAVE_ARPA_INET_H */
  +
  +#endif /* OS2 */
   
   #ifdef HAVE_NETINET_IN_H
   #include <netinet/in.h>
  -#endif
  +#endif /* HAVE_NETINET_IN_H */
   
   #ifdef HAVE_SYS_FILE_H
   #include <sys/file.h>
  -#endif
  +#endif /* HAVE_SYS_FILE_H */
   
   #ifdef HAVE_SYS_IOCTL_H
   #include <sys/ioctl.h>
  -#endif
  +#endif /* HAVE_SYS_IOCTL_H */
   
   #ifdef HAVE_SYS_MMAN_H
   #include <sys/mman.h>
  -#endif
  +#endif /* HAVE_SYS_MMAN_H */
   
   #ifdef HAVE_SYS_PARAM_H
   #include <sys/param.h>
  -#endif
  +#endif /* HAVE SYS_PARAM_H */
   
   #ifdef HAVE_SYS_RESOURCE_H
   #include <sys/resource.h>
  -#endif
  +#endif /* HAVE_SYS_RESOURCE_H */
   
   #ifdef HAVE_SYS_SELECT_H
   #include <sys/select.h>
  -#endif
  +#endif /* HAVE_SYS_SELECT_H */
   
   #ifdef HAVE_SYS_SOCKET_H
   #include <sys/socket.h>
  -#endif
  +#endif /* HAVE SYS_SOCKET_H */
   
   #ifdef HAVE_SYS_STAT_H
   #include <sys/stat.h>
  -#endif
  +#endif /* HAVE_SYS_STAT_H */
   
  -#ifdef HAVE__SYS_WAIT_H
  +#ifdef HAVE_SYS_WAIT_H
   #include <sys/wait.h>
  -#endif
  +#endif /* HAVE_SYS_WAIT_H */
   
   /*
  + * End of include-file processing.
  + */
  +
  +/*
    * Platform-specific definitions.
    */
   #ifdef _OSD_POSIX
  @@ -269,50 +306,53 @@
   #ifdef UTS21
   #define NEED_HASHBANG_EMUL
   #define NO_USE_SIGACTION
  -#endif
  +#endif /* UTS1 */
   
   #ifdef TPF
   #define NO_USE_SIGACTION
   #define NO_SLACK
  -#endif
  +#endif /* TPF */
   
   #ifdef LYNXOS
   #define NO_USE_SIGACTION
  -#endif
  +#endif /* LYNXOS */
   
   #ifdef __MACHTEN__
   #define NO_USE_SIGACTION
  -#endif
  +#endif /* __MACHTEN__ */
   
   #ifdef NEWOS
   #define NO_USE_SIGACTION
  -#endif
  +#endif /* NEWOS */
   
   #ifdef RISCIX
   #define NO_USE_SIGACTION
  -#endif
  +#endif /* RISCIX */
   
   #ifdef MPE
   #define NO_SLACK
  -#endif
  +#endif /* MPE */
   
   #ifdef AUX3
   #define NO_SLACK
  -#endif
  +#endif /* AUX3 */
   
   #ifdef NEXT
   #define NO_USE_SIGACTION
  -#endif
  +#endif /* NEXT */
  +
   /*
    * IF we're on a Linux platform, AND the kernel is 2.0 or later, AND
    * we have the features.h header file available, include it.
    */
   #ifdef LINUX
  +
   #if (LINUX >= 20) && defined(HAVE_FEATURES_H)
   #include <features.h>
  -#endif
  -#endif
  +#endif /* (LINUX >= 20) && defined(HAVE_FEATURES_H) */
   
  +#endif /* LINUX */
  +
   /*
    * Now things that are dependent upon our derived platform settings.
    */
  @@ -325,11 +365,11 @@
    */
   #define execle  apr_execle
   #define execve(path,argv,envp)  apr_execve(path,argv,envp)
  -#endif
  +#endif /* NEED_HASHBANG_MULTI */
   
   #ifdef CHARSET_EBCDIC
   #include "ebcdic.h"
  -#endif
  +#endif /* CHARSET_EBCDIC */
   
   #ifndef NO_USE_SIGACTION
   /*
  @@ -339,16 +379,17 @@
   
   #if defined(SIG_IGN) && !defined(SIG_ERR)
   #define SIG_ERR ((Sigfunc *)-1)
  -#endif
  +#endif /* defined(SIG_IGN) && !defined(SIG_ERR) */
   
   /*
    * For some strange reason, QNX defines signal to signal. Eliminate it.
    */
   #ifdef signal
   #undef signal
  -#endif
  +#endif /* signal */
   #define signal(s,f)     apr_signal(s,f)
   Sigfunc *signal(int signo, Sigfunc * func);
  +
   #endif /* NO_USE_SIGACTION */
   
   /*
  @@ -365,7 +406,7 @@
    */
   #ifndef LOW_SLACK_LINE
   #define LOW_SLACK_LINE  15
  -#endif
  +#endif /* LOW_SLACK_LINE */
   /* #define HIGH_SLACK_LINE      255 */
   
   /*
  @@ -376,18 +417,76 @@
    */
   #ifdef NO_SLACK
   #define apr_slack(fd,line)   (fd)
  -#else
  +#else /* NO_SLACK */
   int apr_slack(int fd, int line);
   #define APR_SLACK_LOW    1
   #define APR_SLACK_HIGH   2
  +#endif /* NO_SLACK */
  +
  +/* So that we can use inline on some critical functions, and use
  + * GNUC attributes (such as to get -Wall warnings for printf-like
  + * functions).  Only do this in gcc 2.7 or later ... it may work
  + * on earlier stuff, but why chance it.
  + *
  + * We've since discovered that the gcc shipped with NeXT systems
  + * as "cc" is completely broken.  It claims to be __GNUC__ and so
  + * on, but it doesn't implement half of the things that __GNUC__
  + * means.  In particular it's missing inline and the __attribute__
  + * stuff.  So we hack around it.  PR#1613. -djg
  + */
  +#if !defined(__GNUC__) || (__GNUC__ < 2) \
  +    || ((__GNUC__ == 2) && (__GNUC_MINOR__ < 7)) \
  +    || defined(NEXT)
  +#define APR_INLINE
  +#define __attribute__(__x)
  +#define ENUM_BITFIELD(e,n,w)  signed int n : w
  +#else
  +#define APR_INLINE __inline__
  +#define USE_GNU_INLINE
  +#define ENUM_BITFIELD(e,n,w)  e n : w
   #endif
   
  +#ifdef ULTRIX
  +#define apr_fdopen(d,m) fdopen((d), (char *)(m))
  +#else /* ULTRIX */
  +#define apr_fdopen(d,m) fdopen((d), (m))
  +#endif /* ULTRIX */
  +
   /*
  + * Begint he actual APR definitions .
  + */
  +
  +#define API_EXPORT(type) type
  +#define API_EXPORT_NONSTD(type) type
  +
  +/*
  + * A set of flags which indicate places where the server should 
raise(SIGSTOP).
  + * This is useful for debugging, because you can then attach to that process
  + * with gdb and continue.  This is important in cases where one_process
  + * debugging isn't possible.
  + */
  +#define APR_SIGSTOP_DETACH           1
  +#define APR_SIGSTOP_MAKE_CHILD               2
  +#define APR_SIGSTOP_SPAWN_CHILD              4
  +#define APR_SIGSTOP_PIPED_LOG_SPAWN  8
  +#define APR_SIGSTOP_CGI_CHILD                16
  +
  +#ifdef DEBUG_SIGSTOP
  +extern int raise_sigstop_flags;
  +#define RAISE_SIGSTOP(x) \
  + do { \
  +     if (raise_sigstop_flags & APR_SIGSTOP_##x) raise(SIGSTOP);\
  + } while (0)
  +#else
  +#define RAISE_SIGSTOP(x)
  +#endif
  +
  +/*
    * Close up the C++ enclosure.
    */
   #ifdef __cplusplus
   }
  -#endif
  +#endif /* __cplusplus */
   
   /*
    * End..
  
  
  
  1.2       +179 -20   apache-apr/apr/include/apr_lib.h
  
  Index: apr_lib.h
  ===================================================================
  RCS file: /home/cvs/apache-apr/apr/include/apr_lib.h,v
  retrieving revision 1.1
  retrieving revision 1.2
  diff -u -r1.1 -r1.2
  --- apr_lib.h 1999/03/24 18:39:09     1.1
  +++ apr_lib.h 1999/04/28 19:20:01     1.2
  @@ -56,35 +56,85 @@
    * The apr_vsnprintf/apr_snprintf functions are based on, and used with the
    * permission of, the  SIO stdio-replacement strx_* functions by Panos
    * Tsirigotis <[EMAIL PROTECTED]> for xinetd.
  + *
  + * This header file defines the public interfaces for the APR general-purpose
  + * library routines.  No others should need to be #included.
    */
   
   #ifndef APR_LIB_H
   #define APR_LIB_H
   
   #include "apr_config.h"
  +#include "hsregex.h"
   
   #ifdef __cplusplus
   extern "C" {
  -#endif
  +#endif /* __cplusplus */
   
  -API_EXPORT(char *) apr_cpystrn(char *, const char *, size_t);
  -int apr_slack(int, int);
  -int apr_execle(const char *, const char *, ...);
  -int apr_execve(const char *, const char *argv[], const char *envp[]);
  +/*
  + * Define the structures used by the APR general-purpose library.
  + */
  +
  +/*
  + * Memory allocation stuff, like pools, arrays, and tables.  Pools
  + * and tables are opaque structures to applications, but arrays are
  + * published.
  + */
  +typedef struct apr_pool_t apr_pool_t;
  +typedef struct apr_table_t apr_table_t;
  +typedef struct apr_child_info_t apr_child_info_t;
  +typedef void apr_mutex_t;
  +typedef struct apr_array_header_t {
  +    apr_pool_t *pool;
  +    int elt_size;
  +    int nelts;
  +    int nalloc;
  +    char *elts;
  +} apr_array_header_t;
  +
  +/*
  + * Structure used by the variable-formatter routines.
  + */
  +typedef struct apr_vformatter_buff_t {
  +    char *curpos;
  +    char *endpos;
  +} apr_vformatter_buff_t;
  +
  +enum kill_conditions {
  +    kill_never,                      /* process is never sent any signals */
  +    kill_always,             /* process is sent SIGKILL on pool cleanup */
  +    kill_after_timeout,              /* SIGTERM, wait 3 seconds, SIGKILL */
  +    just_wait,                       /* wait forever for the process to 
complete */
  +    kill_only_once           /* send SIGTERM and then wait */
  +};
  +
  +/*
  + * Define the prototypes for the various APR GP routines.
  + */
  +API_EXPORT(char *) apr_cpystrn(char *d, const char *s, size_t l);
  +API_EXPORT(apr_mutex_t *) apr_create_mutex(void *m);
  +API_EXPORT(int) apr_slack(int l, int h);
  +API_EXPORT_NONSTD(int) apr_execle(const char *c, const char *a, ...);
  +API_EXPORT_NONSTD(int) apr_execve(const char *c, const char *argv[],
  +                               const char *envp[]);
   
  -/* small utility macros to make things easier to read */
  +/*
  + * Small utility macros to make things easier to read.  Not usually a
  + * goal, to be sure..
  + */
   
   #ifdef WIN32
   #define apr_killpg(x, y)
  -#else
  +#else /* WIN32 */
   #ifdef NO_KILLPG
   #define apr_killpg(x, y)             (kill (-(x), (y)))
  -#else
  +#else /* NO_KILLPG */
   #define apr_killpg(x, y)             (killpg ((x), (y)))
  -#endif
  +#endif /* NO_KILLPG */
   #endif /* WIN32 */
   
  -/* apr_vformatter() is a generic printf-style formatting routine
  +/*
  + * apr_vformatter() is a generic printf-style formatting routine
    * with some extensions.  The extensions are:
    *
    * %pA       takes a struct in_addr *, and prints it as a.b.c.d
  @@ -133,16 +183,12 @@
    * or until apr_vformatter returns.
    */
   
  -typedef struct apr_vformatter_buff_t {
  -    char *curpos;
  -    char *endpos;
  -} apr_vformatter_buff_t;
  -
   API_EXPORT(int) apr_vformatter(int (*flush_func)(apr_vformatter_buff_t *b),
  -                            apr_vformatter_buff_t *, const char *fmt,
  +                            apr_vformatter_buff_t *c, const char *fmt,
                               va_list ap);
   
  -/* These are snprintf implementations based on apr_vformatter().
  +/*
  + * These are snprintf implementations based on apr_vformatter().
    *
    * Note that various standards and implementations disagree on the return
    * value of snprintf, and side-effects due to %n in the formatting string.
  @@ -163,10 +209,123 @@
                              va_list ap);
   
   /*
  - * Pool stuff.
  + * APR memory structure manipulators (pools, tables, and arrays).
    */
  -typedef struct apr_pool_t apr_pool_t;
  -typedef struct apr_mutex_t apr_mutex_t;
  +API_EXPORT(apr_pool_t *) apr_make_sub_pool(apr_pool_t *p);
  +API_EXPORT(void) apr_clear_pool(apr_pool_t *p);
  +API_EXPORT(void) apr_destroy_pool(apr_pool_t *p);
  +API_EXPORT(long) apr_bytes_in_pool(apr_pool_t *p);
  +API_EXPORT(long) apr_bytes_in_free_blocks(void);
  +API_EXPORT(apr_pool_t *) apr_find_pool(const void *ts);
  +API_EXPORT(int) apr_pool_is_ancestor(apr_pool_t *a, apr_pool_t *b);
  +API_EXPORT(void) apr_pool_join(apr_pool_t *p, apr_pool_t *sub);
  +API_EXPORT(void *) apr_palloc(apr_pool_t *p, int reqsize);
  +API_EXPORT(void *) apr_pcalloc(apr_pool_t *p, int size);
  +API_EXPORT(char *) apr_pstrdup(apr_pool_t *p, const char *s);
  +API_EXPORT(char *) apr_pstrndup(apr_pool_t *p, const char *s, int n);
  +API_EXPORT_NONSTD(char *) apr_pstrcat(apr_pool_t *p, ...);
  +API_EXPORT(char *) apr_pvsprintf(apr_pool_t *p, const char *fmt, va_list ap);
  +API_EXPORT_NONSTD(char *) apr_psprintf(apr_pool_t *p, const char *fmt, ...);
  +API_EXPORT(apr_array_header_t *) apr_make_array(apr_pool_t *p, int nelts,
  +                                             int elt_size);
  +API_EXPORT(void *) apr_push_array(apr_array_header_t *arr);
  +API_EXPORT(void) apr_array_cat(apr_array_header_t *dst,
  +                            const apr_array_header_t *src);
  +API_EXPORT(apr_array_header_t *) apr_copy_array(apr_pool_t *p,
  +                                             const apr_array_header_t *arr);
  +API_EXPORT(apr_array_header_t *)
  +     apr_copy_array_hdr(apr_pool_t *p,
  +                        const apr_array_header_t *arr);
  +API_EXPORT(apr_array_header_t *)
  +     apr_append_arrays(apr_pool_t *p,
  +                       const apr_array_header_t *first,
  +                       const apr_array_header_t *second);
  +API_EXPORT(char *) apr_array_pstrcat(apr_pool_t *p,
  +                                  const apr_array_header_t *arr,
  +                                  const char sep);
  +API_EXPORT(apr_table_t *) apr_make_table(apr_pool_t *p, int nelts);
  +API_EXPORT(apr_table_t *) apr_copy_table(apr_pool_t *p, const apr_table_t 
*t);
  +API_EXPORT(void) apr_clear_table(apr_table_t *t);
  +API_EXPORT(const char *) apr_table_get(const apr_table_t *t, const char 
*key);
  +API_EXPORT(void) apr_table_set(apr_table_t *t, const char *key,
  +                            const char *val);
  +API_EXPORT(void) apr_table_setn(apr_table_t *t, const char *key,
  +                             const char *val);
  +API_EXPORT(void) apr_table_unset(apr_table_t *t, const char *key);
  +API_EXPORT(void) apr_table_merge(apr_table_t *t, const char *key,
  +                              const char *val);
  +API_EXPORT(void) apr_table_mergen(apr_table_t *t, const char *key,
  +                               const char *val);
  +API_EXPORT(void) apr_table_add(apr_table_t *t, const char *key,
  +                            const char *val);
  +API_EXPORT(void) apr_table_addn(apr_table_t *t, const char *key,
  +                             const char *val);
  +API_EXPORT(apr_table_t *) apr_overlay_tables(apr_pool_t *p,
  +                                          const apr_table_t *overlay,
  +                                          const apr_table_t *base);
  +API_EXPORT(void)
  +     apr_table_do(int (*comp) (void *, const char *, const char *),
  +                  void *rec, const apr_table_t *t, ...);
  +API_EXPORT(void) apr_overlap_tables(apr_table_t *a, const apr_table_t *b,
  +                                 unsigned flags);
  +API_EXPORT(void) apr_register_cleanup(apr_pool_t *p, void *data,
  +                                   void (*plain_cleanup) (void *),
  +                                   void (*child_cleanup) (void *));
  +API_EXPORT(void) apr_kill_cleanup(apr_pool_t *p, void *data,
  +                               void (*cleanup) (void *));
  +API_EXPORT(void) apr_run_cleanup(apr_pool_t *p, void *data,
  +                              void (*cleanup) (void *));
  +API_EXPORT(void) apr_cleanup_for_exec(void);
  +API_EXPORT_NONSTD(void) apr_null_cleanup(void *data);
  +API_EXPORT(void) apr_note_cleanups_for_fd(apr_pool_t *p, int fd);
  +API_EXPORT(void) apr_kill_cleanups_for_fd(apr_pool_t *p, int fd);
  +API_EXPORT(int) apr_popenf(apr_pool_t *a, const char *name, int flg, int 
mode);
  +API_EXPORT(int) apr_pclosef(apr_pool_t *a, int fd);
  +API_EXPORT(void) apr_note_cleanups_for_file(apr_pool_t *p, FILE *fp);
  +API_EXPORT(FILE *) apr_pfopen(apr_pool_t *a, const char *name,
  +                           const char *mode);
  +API_EXPORT(FILE *) apr_pfdopen(apr_pool_t *a, int fd, const char *mode);
  +API_EXPORT(int) apr_pfclose(apr_pool_t *a, FILE *fd);
  +API_EXPORT(DIR *) apr_popendir(apr_pool_t *p, const char *name);
  +API_EXPORT(void) apr_pclosedir(apr_pool_t *p, DIR * d);
  +API_EXPORT(void) apr_note_cleanups_for_socket(apr_pool_t *p, int fd);
  +API_EXPORT(void) apr_kill_cleanups_for_socket(apr_pool_t *p, int sock);
  +API_EXPORT(int) apr_psocket(apr_pool_t *p, int domain, int type, int 
protocol);
  +API_EXPORT(int) apr_pclosesocket(apr_pool_t *a, int sock);
  +API_EXPORT(regex_t *) apr_pregcomp(apr_pool_t *p, const char *pattern,
  +                                int cflags);
  +API_EXPORT(void) apr_pregfree(apr_pool_t *p, regex_t *reg);
  +API_EXPORT(void) apr_note_subprocess(apr_pool_t *a, pid_t pid,
  +                                  enum kill_conditions how);
  +API_EXPORT(int)
  +     apr_spawn_child(apr_pool_t *p,
  +                     int (*func) (void *a, apr_child_info_t *c),
  +                     void *data, enum kill_conditions kill_how,
  +                     FILE **pipe_in, FILE **pipe_out,
  +                     FILE **pipe_err);
  +#if 0
  +API_EXPORT(int)
  +     apr_bspawn_child(apr_pool_t *p,
  +                      int (*func) (void *v, apr_child_info_t *c),
  +                      void *data, enum kill_conditions kill_how,
  +                      BUFF **pipe_in, BUFF **pipe_out, BUFF **pipe_err);
  +#endif /* 0 */
  +
  +/*
  + * Routine definitions that only work on Windows.
  + */
  +#ifdef WIN32
  +API_EXPORT(void) apr_note_cleanups_for_h(apr_pool_t *p, HANDLE hDevice);
  +API_EXPORT(int) apr_pcloseh(apr_pool_t *a, HANDLE hDevice);
  +#endif /* WIN32 */
  +
  +#ifdef TPF
  +#define apr_block_alarms() (0)
  +#define apr_unblock_alarms() (0)
  +#else /* TPF */
  +API_EXPORT(void) apr_block_alarms(void);
  +API_EXPORT(void) apr_unblock_alarms(void);
  +#endif /* TPF */
   
   #ifdef __cplusplus
   }
  
  
  
  1.1                  apache-apr/apr/include/apr_pools.h
  
  Index: apr_pools.h
  ===================================================================
  /* ====================================================================
   * Copyright (c) 1995-1999 The Apache Group.  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.
   *
   * 3. All advertising materials mentioning features or use of this
   *    software must display the following acknowledgment:
   *    "This product includes software developed by the Apache Group
   *    for use in the Apache HTTP server project (http://www.apache.org/)."
   *
   * 4. The names "Apache Server" and "Apache Group" must not be used to
   *    endorse or promote products derived from this software without
   *    prior written permission. For written permission, please contact
   *    [EMAIL PROTECTED]
   *
   * 5. Products derived from this software may not be called "Apache"
   *    nor may "Apache" appear in their names without prior written
   *    permission of the Apache Group.
   *
   * 6. Redistributions of any form whatsoever must retain the following
   *    acknowledgment:
   *    "This product includes software developed by the Apache Group
   *    for use in the Apache HTTP server project (http://www.apache.org/)."
   *
   * THIS SOFTWARE IS PROVIDED BY THE APACHE GROUP ``AS IS'' AND ANY
   * EXPRESSED 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 APACHE GROUP OR
   * ITS 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.
   * ====================================================================
   *
   * This software consists of voluntary contributions made by many
   * individuals on behalf of the Apache Group and was originally based
   * on public domain software written at the National Center for
   * Supercomputing Applications, University of Illinois, Urbana-Champaign.
   * For more information on the Apache Group and the Apache HTTP server
   * project, please see <http://www.apache.org/>.
   *
   */
  
  #ifndef APR_POOLS_H
  #define APR_POOLS_H
  
  #ifdef __cplusplus
  extern "C" {
  #endif
  
  /*
   * Resource allocation routines...
   *
   * designed so that we don't have to keep track of EVERYTHING so that
   * it can be explicitly freed later (a fundamentally unsound strategy ---
   * particularly in the presence of die()).
   *
   * Instead, we maintain pools, and allocate items (both memory and I/O
   * handlers) from the pools --- currently there are two, one for per
   * transaction info, and one for config info.  When a transaction is over,
   * we can delete everything in the per-transaction pool without fear, and
   * without thinking too hard about it either.
   *
   * rst
   */
  
  /* Arenas for configuration info and transaction info
   * --- actual layout of the pool structure is private to 
   * alloc.c.  
   */
  
   /* Need declaration of DIR on Win32 */
  #ifdef WIN32
  #include "../os/win32/readdir.h"
  #endif
  #include "apr_lib.h"
  
  struct process_chain {
      pid_t pid;
      enum kill_conditions kill_how;
      struct process_chain *next;
  };
  
  struct apr_pool_t {
      union block_hdr *first;
      union block_hdr *last;
      struct cleanup *cleanups;
      struct process_chain *subprocesses;
      apr_pool_t *sub_pools;
      apr_pool_t *sub_next;
      apr_pool_t *sub_prev;
      apr_pool_t *parent;
      char *free_first_avail;
  #ifdef ALLOC_USE_MALLOC
      void *allocation_list;
  #endif
  #ifdef POOL_DEBUG
      apr_pool_t *joined;
  #endif
  };
  
  struct apr_table_t {
      /* This has to be first to promote backwards compatibility with
       * older modules which cast a apr_table_t * to an apr_array_header_t *...
       * they should use the table_elts() function for most of the
       * cases they do this for.
       */
      apr_array_header_t a;
  #ifdef MAKE_TABLE_PROFILE
      void *creator;
  #endif
  };
  
  /*
   * Tables.  Implemented alist style, for now, though we try to keep
   * it so that imposing a hash table structure on top in the future
   * wouldn't be *too* hard...
   *
   * Note that key comparisons for these are case-insensitive, largely
   * because that's what's appropriate and convenient everywhere they're
   * currently being used...
   */
  
  typedef struct apr_table_entry_t {
      char *key;                /* maybe NULL in future;
                         * check when iterating thru table_elts
                         */
      char *val;
  } apr_table_entry_t;
  
  apr_pool_t *apr_init_alloc(void);             /* Set up everything */
  
  /* used to guarantee to the pool debugging code that the sub pool will not be
   * destroyed before the parent pool
   */
  #ifndef POOL_DEBUG
  #ifdef apr_pool_join
  #undef apr_pool_join
  #endif /* apr_pool_join */
  #define apr_pool_join(a,b)
  #endif /* POOL_DEBUG */
  
  /* Clearing out EVERYTHING in an pool... destroys any sub-pools */
  
  /* Preparing for exec() --- close files, etc., but *don't* flush I/O
   * buffers, *don't* wait for subprocesses, and *don't* free any memory.
   */
  
  /* routines to allocate memory from an pool... */
  
  API_EXPORT_NONSTD(char *) apr_psprintf(apr_pool_t *, const char *fmt, ...)
      __attribute__((format(printf,2,3)));
  
  /* array and alist management... keeping lists of things.
   * Common enough to want common support code ...
   */
  
  /* apr_array_pstrcat generates a new string from the pool containing
   * the concatenated sequence of substrings referenced as elements within
   * the array.  The string will be empty if all substrings are empty or null,
   * or if there are no elements in the array.
   * If sep is non-NUL, it will be inserted between elements as a separator.
   */
  
  /* copy_array copies the *entire* array.  copy_array_hdr just copies
   * the header, and arranges for the elements to be copied if (and only
   * if) the code subsequently does a push or arraycat.
   */
  
  
  
  /* Conceptually, apr_overlap_tables does this:
  
      apr_array_header_t *barr = apr_table_elts(b);
      apr_table_entry_t *belt = (apr_table_entry_t *)barr->elts;
      int i;
  
      for (i = 0; i < barr->nelts; ++i) {
        if (flags & APR_OVERLAP_TABLES_MERGE) {
            apr_table_mergen(a, belt[i].key, belt[i].val);
        }
        else {
            apr_table_setn(a, belt[i].key, belt[i].val);
        }
      }
  
      Except that it is more efficient (less space and cpu-time) especially
      when b has many elements.
  
      Notice the assumptions on the keys and values in b -- they must be
      in an ancestor of a's pool.  In practice b and a are usually from
      the same pool.
  */
  #define APR_OVERLAP_TABLES_SET        (0)
  #define APR_OVERLAP_TABLES_MERGE      (1)
  
  /* XXX: these know about the definition of struct table in alloc.c.  That
   * definition is not here because it is supposed to be private, and by not
   * placing it here we are able to get compile-time diagnostics from modules
   * written which assume that a table is the same as an apr_array_header_t. 
-djg
   */
  #define apr_table_elts(t) ((apr_array_header_t *)(t))
  #define apr_is_empty_table(t) (((t) == NULL)||(((apr_array_header_t 
*)(t))->nelts == 0))
  
  /* routines to remember allocation of other sorts of things...
   * generic interface first.  Note that we want to have two separate
   * cleanup functions in the general case, one for exec() preparation,
   * to keep CGI scripts and the like from inheriting access to things
   * they shouldn't be able to touch, and one for actually cleaning up,
   * when the actual server process wants to get rid of the thing,
   * whatever it is.  
   *
   * kill_cleanup disarms a cleanup, presumably because the resource in
   * question has been closed, freed, or whatever, and it's scarce
   * enough to want to reclaim (e.g., descriptors).  It arranges for the
   * resource not to be cleaned up a second time (it might have been
   * reallocated).  run_cleanup does the same, but runs it first.
   *
   * Cleanups are identified for purposes of finding & running them off by the
   * plain_cleanup and data, which should presumably be unique.
   *
   * NB any code which invokes register_cleanup or kill_cleanup directly
   * is a critical section which should be guarded by block_alarms() and
   * unblock_alarms() below...
   */
  
  /* A "do-nothing" cleanup, for register_cleanup; it's faster to do
   * things this way than to test for NULL. */
  
  /* The time between when a resource is actually allocated, and when
   * its cleanup is registered is a critical section, during which the
   * resource could leak if we got interrupted or timed out.  So, anything
   * which registers cleanups should bracket resource allocation and the
   * cleanup registry with these.  (This is done internally by run_cleanup).
   *
   * NB they are actually implemented in http_main.c, since they are bound
   * up with timeout handling in general...
   */
  
  /* Common cases which want utility support..
   * the note_cleanups_for_foo routines are for 
   */
  
  API_EXPORT(FILE *) apr_pfopen(apr_pool_t *, const char *name, const char 
*fmode);
  API_EXPORT(FILE *) apr_pfdopen(apr_pool_t *, int fd, const char *fmode);
  API_EXPORT(int) apr_popenf(apr_pool_t *, const char *name, int flg, int mode);
  
  API_EXPORT(void) apr_note_cleanups_for_file(apr_pool_t *, FILE *);
  API_EXPORT(void) apr_note_cleanups_for_fd(apr_pool_t *, int);
  API_EXPORT(void) apr_kill_cleanups_for_fd(apr_pool_t *p, int fd);
  API_EXPORT(void) apr_note_cleanups_for_socket(apr_pool_t *, int);
  API_EXPORT(void) apr_kill_cleanups_for_socket(apr_pool_t *p, int sock);
  API_EXPORT(int) apr_psocket(apr_pool_t *p, int, int, int);
  API_EXPORT(int) apr_pclosesocket(apr_pool_t *a, int sock);
  API_EXPORT(regex_t *) apr_pregcomp(apr_pool_t *p, const char *pattern,
                                   int cflags);
  API_EXPORT(void) apr_pregfree(apr_pool_t *p, regex_t * reg);
  
  /* routines to note closes... file descriptors are constrained enough
   * on some systems that we want to support this.
   */
  
  API_EXPORT(int) apr_pfclose(apr_pool_t *, FILE *);
  API_EXPORT(int) apr_pclosef(apr_pool_t *, int fd);
  
  /* routines to deal with directories */
  API_EXPORT(DIR *) apr_popendir(apr_pool_t *p, const char *name);
  API_EXPORT(void) apr_pclosedir(apr_pool_t *p, DIR * d);
  
  /* ... even child processes (which we may want to wait for,
   * or to kill outright, on unexpected termination).
   *
   * apr_spawn_child is a utility routine which handles an awful lot of
   * the rigamarole associated with spawning a child --- it arranges
   * for pipes to the child's stdin and stdout, if desired (if not,
   * set the associated args to NULL).  It takes as args a function
   * to call in the child, and an argument to be passed to the function.
   */
  
  API_EXPORT(void) apr_note_subprocess(apr_pool_t *a, pid_t pid,
                                    enum kill_conditions how);
  
  /* magic numbers --- min free bytes to consider a free pool block useable,
   * and the min amount to allocate if we have to go to malloc() */
  
  #ifndef BLOCK_MINFREE
  #define BLOCK_MINFREE 4096
  #endif
  #ifndef BLOCK_MINALLOC
  #define BLOCK_MINALLOC 8192
  #endif
  
  /* Finally, some accounting */
  
  API_EXPORT(long) apr_bytes_in_pool(apr_pool_t *p);
  API_EXPORT(long) apr_bytes_in_free_blocks(void);
  
  #ifdef __cplusplus
  }
  #endif
  
  #endif        /* !APR_POOLS_H */
  
  
  
  1.1                  apache-apr/apr/include/hsregex.h
  
  Index: hsregex.h
  ===================================================================
  /* DON'T EVEN THINK ABOUT EDITING THIS, go see regex/Makefile,
   * search for mkh */
  #ifndef _REGEX_H_
  #define       _REGEX_H_       /* never again */
  /* ========= begin header generated by ./mkh ========= */
  #ifdef __cplusplus
  extern "C" {
  #endif
  
  /* === regex2.h === */
  #ifndef API_EXPORT
  #ifdef WIN32
  #define API_EXPORT(type)    __declspec(dllexport) type __stdcall
  #else
  #define API_EXPORT(type)    type
  #endif
  #endif
  
  #if defined(RHAPSODY)
  #define ap_private_extern __private_extern__
  #else
  #define ap_private_extern
  #endif
  
  typedef off_t regoff_t;
  typedef struct {
        int re_magic;
        size_t re_nsub;         /* number of parenthesized subexpressions */
        const char *re_endp;    /* end pointer for REG_PEND */
        struct re_guts *re_g;   /* none of your business :-) */
  } regex_t;
  typedef struct {
        regoff_t rm_so;         /* start of match */
        regoff_t rm_eo;         /* end of match */
  } regmatch_t;
  
  
  /* === regcomp.c === */
  API_EXPORT(int) regcomp(regex_t *, const char *, int);
  #define       REG_BASIC       0000
  #define       REG_EXTENDED    0001
  #define       REG_ICASE       0002
  #define       REG_NOSUB       0004
  #define       REG_NEWLINE     0010
  #define       REG_NOSPEC      0020
  #define       REG_PEND        0040
  #define       REG_DUMP        0200
  
  
  /* === regerror.c === */
  #define       REG_NOMATCH      1
  #define       REG_BADPAT       2
  #define       REG_ECOLLATE     3
  #define       REG_ECTYPE       4
  #define       REG_EESCAPE      5
  #define       REG_ESUBREG      6
  #define       REG_EBRACK       7
  #define       REG_EPAREN       8
  #define       REG_EBRACE       9
  #define       REG_BADBR       10
  #define       REG_ERANGE      11
  #define       REG_ESPACE      12
  #define       REG_BADRPT      13
  #define       REG_EMPTY       14
  #define       REG_ASSERT      15
  #define       REG_INVARG      16
  #define       REG_ATOI        255     /* convert name to number (!) */
  #define       REG_ITOA        0400    /* convert number to name (!) */
  API_EXPORT(size_t) regerror(int, const regex_t *, char *, size_t);
  
  
  /* === regexec.c === */
  API_EXPORT(int) regexec(const regex_t *, const char *, size_t, regmatch_t [], 
int);
  #define       REG_NOTBOL      00001
  #define       REG_NOTEOL      00002
  #define       REG_STARTEND    00004
  #define       REG_TRACE       00400   /* tracing of execution */
  #define       REG_LARGE       01000   /* force large representation */
  #define       REG_BACKR       02000   /* force use of backref code */
  
  
  /* === regfree.c === */
  API_EXPORT(void) regfree(regex_t *);
  
  #ifdef __cplusplus
  }
  #endif
  /* ========= end header generated by ./mkh ========= */
  #endif
  
  
  
  1.5       +7 -3      apache-apr/apr/lib/Makefile.in
  
  Index: Makefile.in
  ===================================================================
  RCS file: /home/cvs/apache-apr/apr/lib/Makefile.in,v
  retrieving revision 1.4
  retrieving revision 1.5
  diff -u -r1.4 -r1.5
  --- Makefile.in       1999/04/14 11:06:24     1.4
  +++ Makefile.in       1999/04/28 19:20:15     1.5
  @@ -4,6 +4,7 @@
   #LDFLAGS=$(LDFLAGS1) $(EXTRA_LDFLAGS)
   
   [EMAIL PROTECTED]@
  [EMAIL PROTECTED]@
   [EMAIL PROTECTED]@ @CFLAGS@ @OPTIM@
   [EMAIL PROTECTED]@
   [EMAIL PROTECTED]@ $(LDLIBS)
  @@ -16,6 +17,7 @@
        apr_fnmatch.o \
        apr_execve.o \
        apr_md5.o \
  +     apr_pools.o \
        apr_signal.o \
        apr_slack.o \
        apr_snprintf.o
  @@ -57,9 +59,11 @@
   apr_execve.o: apr_execve.c $(INCDIR)/apr_config.h
   apr_fnmatch.o: apr_fnmatch.c $(INCDIR)/apr_config.h \
    $(INCDIR)/apr_fnmatch.h
  -apr_md5c.o: apr_md5c.c $(INCDIR)/apr_config.h \
  - $(INCDIR)/apr_md5.h $(INCDIR)/apr_lib.h
  +apr_md5.o: apr_md5.c $(INCDIR)/apr_config.h $(INCDIR)/apr_md5.h \
  + $(INCDIR)/apr_lib.h $(INCDIR)/hsregex.h
  +apr_pools.o: apr_pools.c $(INCDIR)/apr_config.h \
  + $(INCDIR)/apr_pools.h $(INCDIR)/apr_lib.h $(INCDIR)/hsregex.h
   apr_signal.o: apr_signal.c $(INCDIR)/apr_config.h
   apr_slack.o: apr_slack.c $(INCDIR)/apr_config.h
   apr_snprintf.o: apr_snprintf.c $(INCDIR)/apr_config.h \
  - $(INCDIR)/apr_lib.h
  + $(INCDIR)/apr_lib.h $(INCDIR)/hsregex.h
  
  
  
  1.1                  apache-apr/apr/lib/apr_pools.c
  
  Index: apr_pools.c
  ===================================================================
  /* ====================================================================
   * Copyright (c) 1995-1999 The Apache Group.  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.
   *
   * 3. All advertising materials mentioning features or use of this
   *    software must display the following acknowledgment:
   *    "This product includes software developed by the Apache Group
   *    for use in the Apache HTTP server project (http://www.apache.org/)."
   *
   * 4. The names "Apache Server" and "Apache Group" must not be used to
   *    endorse or promote products derived from this software without
   *    prior written permission. For written permission, please contact
   *    [EMAIL PROTECTED]
   *
   * 5. Products derived from this software may not be called "Apache"
   *    nor may "Apache" appear in their names without prior written
   *    permission of the Apache Group.
   *
   * 6. Redistributions of any form whatsoever must retain the following
   *    acknowledgment:
   *    "This product includes software developed by the Apache Group
   *    for use in the Apache HTTP server project (http://www.apache.org/)."
   *
   * THIS SOFTWARE IS PROVIDED BY THE APACHE GROUP ``AS IS'' AND ANY
   * EXPRESSED 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 APACHE GROUP OR
   * ITS 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.
   * ====================================================================
   *
   * This software consists of voluntary contributions made by many
   * individuals on behalf of the Apache Group and was originally based
   * on public domain software written at the National Center for
   * Supercomputing Applications, University of Illinois, Urbana-Champaign.
   * For more information on the Apache Group and the Apache HTTP server
   * project, please see <http://www.apache.org/>.
   *
   */
  
  /*
   * Resource allocation code... the code here is responsible for making
   * sure that nothing leaks.
   *
   * rst --- 4/95 --- 6/95
   */
  
  #include "apr_config.h"
  #include "apr_pools.h"
  
  /*
   * Debugging support: Define this to enable code which helps detect re-use
   * of freed memory and other such nonsense.
   *
   * The theory is simple.  The FILL_BYTE (0xa5) is written over all malloc'd
   * memory as we receive it, and is written over everything that we free up
   * during a clear_pool.  We check that blocks on the free list always
   * have the FILL_BYTE in them, and we check during palloc() that the bytes
   * still have FILL_BYTE in them.  If you ever see garbage URLs or whatnot
   * containing lots of 0xa5s then you know something used data that's been
   * freed or uninitialized.
   */
  /* #define ALLOC_DEBUG */
  
  /*
   * Debugging support: If defined all allocations will be done with
   * malloc and free()d appropriately at the end.  This is intended to be
   * used with something like Electric Fence or Purify to help detect
   * memory problems.  Note that if you're using efence then you should also
   * add in ALLOC_DEBUG.  But don't add in ALLOC_DEBUG if you're using Purify
   * because ALLOC_DEBUG would hide all the uninitialized read errors that
   * Purify can diagnose.
   */
  /* #define ALLOC_USE_MALLOC */
  
  /*
   * Pool debugging support:  This is intended to detect cases where the
   * wrong pool is used when assigning data to an object in another pool.
   * In particular, it causes the table_{set,add,merge}n routines to check
   * that their arguments are safe for the table they're being placed in.
   * It currently only works with the unix multiprocess model, but could
   * be extended to others.
   */
  /* #define POOL_DEBUG */
  
  /*
   * Provide diagnostic information about make_table() calls which are
   * possibly too small.  This requires a recent gcc which supports
   * __builtin_return_address().  The error_log output will be a
   * message such as:
   *    table_push: table created by 0x804d874 hit limit of 10
   * Use "l *0x804d874" to find the source that corresponds to.  It
   * indicates that a table allocated by a call at that address has
   * possibly too small an initial table size guess.
   */
  /* #define MAKE_TABLE_PROFILE */
  
  /*
   * Provide some statistics on the cost of allocations.  It requires a
   * bit of an understanding of how alloc.c works.
   */
  /* #define ALLOC_STATS */
  
  #ifdef POOL_DEBUG
  #ifdef ALLOC_USE_MALLOC
  #error "sorry, no support for ALLOC_USE_MALLOC and POOL_DEBUG at the same 
time"
  #endif /* ALLOC_USE_MALLOC */
  
  #ifdef MULTITHREAD
  # error "sorry, no support for MULTITHREAD and POOL_DEBUG at the same time"
  #endif /* MULTITHREAD */
  
  #endif /* POOL_DEBUG */
  
  #ifdef ALLOC_USE_MALLOC
  #undef BLOCK_MINFREE
  #undef BLOCK_MINALLOC
  #define BLOCK_MINFREE 0
  #define BLOCK_MINALLOC        0
  #endif /* ALLOC_USE_MALLOC */
  
  /*****************************************************************
   *
   * Managing free storage blocks...
   */
  
  union align {
      /*
       * Types which are likely to have the longest RELEVANT alignment
       * restrictions...
       */
  
      char *cp;
      void (*f) (void);
      long l;
      FILE *fp;
      double d;
  };
  
  #define CLICK_SZ (sizeof(union align))
  
  union block_hdr {
      union align a;
  
      /* Actual header... */
  
      struct {
        char *endp;
        union block_hdr *next;
        char *first_avail;
  #ifdef POOL_DEBUG
        union block_hdr *global_next;
        apr_pool_t *owning_pool;
  #endif /* POOL_DEBUG */
      } h;
  };
  
  /*
   * Static cells for managing our internal synchronisation.
   */
  static union block_hdr *block_freelist = NULL;
  static apr_mutex_t *alloc_mutex = NULL;
  static apr_mutex_t *spawn_mutex = NULL;
  
  #ifdef POOL_DEBUG
  static char *known_stack_point;
  static int stack_direction;
  static union block_hdr *global_block_list;
  #define FREE_POOL     ((apr_pool_t *)(-1))
  #endif /* POOL_DEBUG */
  
  #ifdef ALLOC_STATS
  static unsigned long long num_free_blocks_calls;
  static unsigned long long num_blocks_freed;
  static unsigned max_blocks_in_one_free;
  static unsigned num_malloc_calls;
  static unsigned num_malloc_bytes;
  #endif /* ALLOC_STATS */
  
  #ifdef ALLOC_DEBUG
  #define FILL_BYTE     ((char)(0xa5))
  #define debug_fill(ptr,size)  ((void)memset((ptr), FILL_BYTE, (size)))
  
  static APR_INLINE void debug_verify_filled(const char *ptr, const char *endp,
                                           const char *error_msg)
  {
      for ( ; ptr < endp; ++ptr) {
        if (*ptr != FILL_BYTE) {
            fputs(error_msg, stderr);
            abort();
            exit(1);
        }
      }
  }
  
  #else /* ALLOC_DEBUG */
  #define debug_fill(a,b)
  #define debug_verify_filled(a,b,c)
  #endif /* ALLOC_DEBUG */
  
  /*
   * Get a completely new block from the system pool. Note that we rely on
   * malloc() to provide aligned memory.
   */
  
  static union block_hdr *malloc_block(int size)
  {
      union block_hdr *blok;
  
  #ifdef ALLOC_DEBUG
      /* make some room at the end which we'll fill and expect to be
       * always filled
       */
      size += CLICK_SZ;
  #endif /* ALLOC_DEBUG */
  
  #ifdef ALLOC_STATS
      ++num_malloc_calls;
      num_malloc_bytes += size + sizeof(union block_hdr);
  #endif /* ALLOC_STATS */
  
      blok = (union block_hdr *) malloc(size + sizeof(union block_hdr));
      if (blok == NULL) {
        fprintf(stderr, "Ouch!  malloc failed in malloc_block()\n");
        exit(1);
      }
      debug_fill(blok, size + sizeof(union block_hdr));
      blok->h.next = NULL;
      blok->h.first_avail = (char *) (blok + 1);
      blok->h.endp = size + blok->h.first_avail;
  
  #ifdef ALLOC_DEBUG
      blok->h.endp -= CLICK_SZ;
  #endif /* ALLOC_DEBUG */
  
  #ifdef POOL_DEBUG
      blok->h.global_next = global_block_list;
      global_block_list = blok;
      blok->h.owning_pool = NULL;
  #endif /* POOL_DEBUG */
  
      return blok;
  }
  
  
  
  #if defined(ALLOC_DEBUG) && !defined(ALLOC_USE_MALLOC)
  static void chk_on_blk_list(union block_hdr *blok, union block_hdr *free_blk)
  {
      debug_verify_filled(blok->h.endp, blok->h.endp + CLICK_SZ,
                        "Ouch!  Someone trounced the padding "
                        "at the end of a block!\n");
      while (free_blk) {
        if (free_blk == blok) {
            fprintf(stderr, "Ouch!  Freeing free block\n");
            abort();
            exit(1);
        }
        free_blk = free_blk->h.next;
      }
  }
  #else /* defined(ALLOC_DEBUG) && !defined(ALLOC_USE_MALLOC) */
  #define chk_on_blk_list(_x, _y)
  #endif /* defined(ALLOC_DEBUG) && !defined(ALLOC_USE_MALLOC) */
  
  /* Free a chain of blocks --- must be called with alarms blocked. */
  
  static void free_blocks(union block_hdr *blok)
  {
  #ifdef ALLOC_USE_MALLOC
      union block_hdr *next;
  
      for ( ; blok; blok = next) {
        next = blok->h.next;
        free(blok);
      }
  #else /* ALLOC_USE_MALLOC */
  
  #ifdef ALLOC_STATS
      unsigned num_blocks;
  #endif /* ALLOC_STATS */
  
      /*
       * First, put new blocks at the head of the free list ---
       * we'll eventually bash the 'next' pointer of the last block
       * in the chain to point to the free blocks we already had.
       */
  
      union block_hdr *old_free_list;
  
      if (blok == NULL) {
        return;                 /* Sanity check --- freeing empty pool? */
      }
  
      (void) apr_acquire_mutex(alloc_mutex);
      old_free_list = block_freelist;
      block_freelist = blok;
  
      /*
       * Next, adjust first_avail pointers of each block --- have to do it
       * sooner or later, and it simplifies the search in new_block to do it
       * now.
       */
  
  #ifdef ALLOC_STATS
      num_blocks = 1;
  #endif /* ALLOC_STATS */
  
      while (blok->h.next != NULL) {
  
  #ifdef ALLOC_STATS
        ++num_blocks;
  #endif /* ALLOC_STATS */
  
        chk_on_blk_list(blok, old_free_list);
        blok->h.first_avail = (char *) (blok + 1);
        debug_fill(blok->h.first_avail, blok->h.endp - blok->h.first_avail);
  #ifdef POOL_DEBUG
        blok->h.owning_pool = FREE_POOL;
  #endif /* POOL_DEBUG */
        blok = blok->h.next;
      }
  
      chk_on_blk_list(blok, old_free_list);
      blok->h.first_avail = (char *) (blok + 1);
      debug_fill(blok->h.first_avail, blok->h.endp - blok->h.first_avail);
  #ifdef POOL_DEBUG
      blok->h.owning_pool = FREE_POOL;
  #endif /* POOL_DEBUG */
  
      /* Finally, reset next pointer to get the old free blocks back */
  
      blok->h.next = old_free_list;
  
  #ifdef ALLOC_STATS
      if (num_blocks > max_blocks_in_one_free) {
        max_blocks_in_one_free = num_blocks;
      }
      ++num_free_blocks_calls;
      num_blocks_freed += num_blocks;
  #endif /* ALLOC_STATS */
  
      (void) apr_release_mutex(alloc_mutex);
  #endif /* ALLOC_USE_MALLOC */
  }
  
  /*
   * Get a new block, from our own free list if possible, from the system
   * if necessary.  Must be called with alarms blocked.
   */
  
  static union block_hdr *new_block(int min_size)
  {
      union block_hdr **lastptr = &block_freelist;
      union block_hdr *blok = block_freelist;
  
      /* First, see if we have anything of the required size
       * on the free list...
       */
  
      while (blok != NULL) {
        if (min_size + BLOCK_MINFREE <= blok->h.endp - blok->h.first_avail) {
            *lastptr = blok->h.next;
            blok->h.next = NULL;
            debug_verify_filled(blok->h.first_avail, blok->h.endp,
                                "Ouch!  Someone trounced a block "
                                "on the free list!\n");
            return blok;
        }
        else {
            lastptr = &blok->h.next;
            blok = blok->h.next;
        }
      }
  
      /* Nope. */
  
      min_size += BLOCK_MINFREE;
      blok = malloc_block((min_size > BLOCK_MINALLOC)
                        ? min_size : BLOCK_MINALLOC);
      return blok;
  }
  
  
  /* Accounting */
  
  static long bytes_in_block_list(union block_hdr *blok)
  {
      long size = 0;
  
      while (blok) {
        size += blok->h.endp - (char *) (blok + 1);
        blok = blok->h.next;
      }
  
      return size;
  }
  
  
  /*****************************************************************
   *
   * Pool internals and management...
   * NB that subprocesses are not handled by the generic cleanup code,
   * basically because we don't want cleanups for multiple subprocesses
   * to result in multiple three-second pauses.
   */
  
  struct process_chain;
  struct cleanup;
  
  static void run_cleanups(struct cleanup *c);
  static void free_proc_chain(struct process_chain *p);
  
  static apr_pool_t *permanent_pool;
  
  /* Each pool structure is allocated in the start of its own first block,
   * so we need to know how many bytes that is (once properly aligned...).
   * This also means that when a pool's sub-pool is destroyed, the storage
   * associated with it is *completely* gone, so we have to make sure it
   * gets taken off the parent's sub-pool list...
   */
  
  #define POOL_HDR_CLICKS (1 + ((sizeof(struct apr_pool_t) - 1) / CLICK_SZ))
  #define POOL_HDR_BYTES (POOL_HDR_CLICKS * CLICK_SZ)
  
  API_EXPORT(apr_pool_t *) apr_make_sub_pool(apr_pool_t *p)
  {
      union block_hdr *blok;
      apr_pool_t *new_pool;
  
      apr_block_alarms();
  
      (void) apr_acquire_mutex(alloc_mutex);
  
      blok = new_block(POOL_HDR_BYTES);
      new_pool = (apr_pool_t *) blok->h.first_avail;
      blok->h.first_avail += POOL_HDR_BYTES;
  #ifdef POOL_DEBUG
      blok->h.owning_pool = new_pool;
  #endif
  
      memset((char *) new_pool, '\0', sizeof(struct apr_pool_t));
      new_pool->free_first_avail = blok->h.first_avail;
      new_pool->first = new_pool->last = blok;
  
      if (p) {
        new_pool->parent = p;
        new_pool->sub_next = p->sub_pools;
        if (new_pool->sub_next) {
            new_pool->sub_next->sub_prev = new_pool;
        }
        p->sub_pools = new_pool;
      }
  
      (void) apr_release_mutex(alloc_mutex);
      apr_unblock_alarms();
  
      return new_pool;
  }
  
  #ifdef POOL_DEBUG
  static void stack_var_init(char *s)
  {
      char t;
  
      if (s < &t) {
        stack_direction = 1; /* stack grows up */
      }
      else {
        stack_direction = -1; /* stack grows down */
      }
  }
  #endif
  
  #ifdef ALLOC_STATS
  static void dump_stats(void)
  {
      fprintf(stderr,
            "alloc_stats: [%d] #free_blocks %llu #blocks %llu max "
            "%u #malloc %u #bytes %u\n",
        (int) getpid(),
        num_free_blocks_calls,
        num_blocks_freed,
        max_blocks_in_one_free,
        num_malloc_calls,
        num_malloc_bytes);
  }
  #endif
  
  apr_pool_t *apr_init_alloc(void)
  {
  #ifdef POOL_DEBUG
      char s;
  
      known_stack_point = &s;
      stack_var_init(&s);
  #endif
      alloc_mutex = apr_create_mutex(NULL);
      spawn_mutex = apr_create_mutex(NULL);
      permanent_pool = apr_make_sub_pool(NULL);
  #ifdef ALLOC_STATS
      atexit(dump_stats);
  #endif
  
      return permanent_pool;
  }
  
  API_EXPORT(void) apr_clear_pool(apr_pool_t *a)
  {
      apr_block_alarms();
  
      (void) apr_acquire_mutex(alloc_mutex);
      while (a->sub_pools) {
        apr_destroy_pool(a->sub_pools);
      }
      (void) apr_release_mutex(alloc_mutex);
      /*
       * Don't hold the mutex during cleanups.
       */
      run_cleanups(a->cleanups);
      a->cleanups = NULL;
      free_proc_chain(a->subprocesses);
      a->subprocesses = NULL;
      free_blocks(a->first->h.next);
      a->first->h.next = NULL;
  
      a->last = a->first;
      a->first->h.first_avail = a->free_first_avail;
      debug_fill(a->first->h.first_avail,
               a->first->h.endp - a->first->h.first_avail);
  
  #ifdef ALLOC_USE_MALLOC
      {
        void *c, *n;
  
        for (c = a->allocation_list; c; c = n) {
            n = *(void **)c;
            free(c);
        }
        a->allocation_list = NULL;
      }
  #endif
  
      apr_unblock_alarms();
  }
  
  API_EXPORT(void) apr_destroy_pool(apr_pool_t *a)
  {
      apr_block_alarms();
      apr_clear_pool(a);
  
      (void) apr_acquire_mutex(alloc_mutex);
      if (a->parent) {
        if (a->parent->sub_pools == a) {
            a->parent->sub_pools = a->sub_next;
        }
        if (a->sub_prev) {
            a->sub_prev->sub_next = a->sub_next;
        }
        if (a->sub_next) {
            a->sub_next->sub_prev = a->sub_prev;
        }
      }
      (void) apr_release_mutex(alloc_mutex);
  
      free_blocks(a->first);
      apr_unblock_alarms();
  }
  
  API_EXPORT(long) apr_bytes_in_pool(apr_pool_t *p)
  {
      return bytes_in_block_list(p->first);
  }
  API_EXPORT(long) apr_bytes_in_free_blocks(void)
  {
      return bytes_in_block_list(block_freelist);
  }
  
  /*****************************************************************
   * POOL_DEBUG support
   */
  #ifdef POOL_DEBUG
  
  /* the unix linker defines this symbol as the last byte + 1 of
   * the executable... so it includes TEXT, BSS, and DATA
   */
  extern char _end;
  
  /* is ptr in the range [lo,hi) */
  #define is_ptr_in_range(ptr, lo, hi) \
      (((unsigned long)(ptr) - (unsigned long)(lo)) \
       < (unsigned long)(hi) - (unsigned long)(lo))
  
  /* Find the pool that ts belongs to, return NULL if it doesn't
   * belong to any pool.
   */
  API_EXPORT(apr_pool_t *) apr_find_pool(const void *ts)
  {
      const char *s = ts;
      union block_hdr **pb;
      union block_hdr *b;
  
      /* short-circuit stuff which is in TEXT, BSS, or DATA */
      if (is_ptr_in_range(s, 0, &_end)) {
        return NULL;
      }
      /* consider stuff on the stack to also be in the NULL pool...
       * XXX: there's cases where we don't want to assume this
       */
      if ((stack_direction == -1 && is_ptr_in_range(s, &ts, known_stack_point))
        || (stack_direction == 1
            && is_ptr_in_range(s, known_stack_point, &ts))) {
        abort();
        return NULL;
      }
      apr_block_alarms();
      /* search the global_block_list */
      for (pb = &global_block_list; *pb; pb = &b->h.global_next) {
        b = *pb;
        if (is_ptr_in_range(s, b, b->h.endp)) {
            if (b->h.owning_pool == FREE_POOL) {
                fprintf(stderr,
                        "Ouch!  find_pool() called on pointer "
                        "in a free block\n");
                abort();
                exit(1);
            }
            if (b != global_block_list) {
                /*
                 * promote b to front of list, this is a hack to speed
                 * up the lookup
                 */
                *pb = b->h.global_next;
                b->h.global_next = global_block_list;
                global_block_list = b;
            }
            apr_unblock_alarms();
            return b->h.owning_pool;
        }
      }
      apr_unblock_alarms();
      return NULL;
  }
  
  /* return TRUE iff a is an ancestor of b
   * NULL is considered an ancestor of all pools
   */
  API_EXPORT(int) apr_pool_is_ancestor(apr_pool_t *a, apr_pool_t *b)
  {
      if (a == NULL) {
        return 1;
      }
      while (a->joined) {
        a = a->joined;
      }
      while (b) {
        if (a == b) {
            return 1;
        }
        b = b->parent;
      }
      return 0;
  }
  
  /*
   * All blocks belonging to sub will be changed to point to p
   * instead.  This is a guarantee by the caller that sub will not
   * be destroyed before p is.
   */
  API_EXPORT(void) apr_pool_join(apr_pool_t *p, apr_pool_t *sub)
  {
      union block_hdr *b;
  
      /* We could handle more general cases... but this is it for now. */
      if (sub->parent != p) {
        fprintf(stderr, "pool_join: p is not parent of sub\n");
        abort();
      }
      apr_block_alarms();
      while (p->joined) {
        p = p->joined;
      }
      sub->joined = p;
      for (b = global_block_list; b; b = b->h.global_next) {
        if (b->h.owning_pool == sub) {
            b->h.owning_pool = p;
        }
      }
      apr_unblock_alarms();
  }
  #endif
  
  /*****************************************************************
   *
   * Allocating stuff...
   */
  
  
  API_EXPORT(void *) apr_palloc(apr_pool_t *a, int reqsize)
  {
  #ifdef ALLOC_USE_MALLOC
      int size = reqsize + CLICK_SZ;
      void *ptr;
  
      apr_block_alarms();
      ptr = malloc(size);
      if (ptr == NULL) {
        fputs("Ouch!  Out of memory!\n", stderr);
        exit(1);
      }
      debug_fill(ptr, size); /* might as well get uninitialized protection */
      *(void **)ptr = a->allocation_list;
      a->allocation_list = ptr;
      apr_unblock_alarms();
      return (char *)ptr + CLICK_SZ;
  #else
  
      /*
       * Round up requested size to an even number of alignment units
       * (core clicks)
       */
  
      int nclicks = 1 + ((reqsize - 1) / CLICK_SZ);
      int size = nclicks * CLICK_SZ;
  
      /* First, see if we have space in the block most recently
       * allocated to this pool
       */
  
      union block_hdr *blok = a->last;
      char *first_avail = blok->h.first_avail;
      char *new_first_avail;
  
      if (reqsize <= 0) {
        return NULL;
      }
  
      new_first_avail = first_avail + size;
  
      if (new_first_avail <= blok->h.endp) {
        debug_verify_filled(first_avail, blok->h.endp,
                            "Ouch!  Someone trounced past the end "
                            "of their allocation!\n");
        blok->h.first_avail = new_first_avail;
        return (void *) first_avail;
      }
  
      /* Nope --- get a new one that's guaranteed to be big enough */
  
      apr_block_alarms();
  
      (void) apr_acquire_mutex(alloc_mutex);
  
      blok = new_block(size);
      a->last->h.next = blok;
      a->last = blok;
  #ifdef POOL_DEBUG
      blok->h.owning_pool = a;
  #endif
  
      (void) apr_release_mutex(alloc_mutex);
  
      apr_unblock_alarms();
  
      first_avail = blok->h.first_avail;
      blok->h.first_avail += size;
  
      return (void *) first_avail;
  #endif
  }
  
  API_EXPORT(void *) apr_pcalloc(apr_pool_t *a, int size)
  {
      void *res = apr_palloc(a, size);
      memset(res, '\0', size);
      return res;
  }
  
  API_EXPORT(char *) apr_pstrdup(apr_pool_t *a, const char *s)
  {
      char *res;
      size_t len;
  
      if (s == NULL) {
        return NULL;
      }
      len = strlen(s) + 1;
      res = apr_palloc(a, len);
      memcpy(res, s, len);
      return res;
  }
  
  API_EXPORT(char *) apr_pstrndup(apr_pool_t *a, const char *s, int n)
  {
      char *res;
  
      if (s == NULL) {
        return NULL;
      }
      res = apr_palloc(a, n + 1);
      memcpy(res, s, n);
      res[n] = '\0';
      return res;
  }
  
  API_EXPORT_NONSTD(char *) apr_pstrcat(apr_pool_t *a, ...)
  {
      char *cp, *argp, *res;
  
      /* Pass one --- find length of required string */
  
      int len = 0;
      va_list adummy;
  
      va_start(adummy, a);
  
      while ((cp = va_arg(adummy, char *)) != NULL) {
        len += strlen(cp);
      }
  
      va_end(adummy);
  
      /* Allocate the required string */
  
      res = (char *) apr_palloc(a, len + 1);
      cp = res;
      *cp = '\0';
  
      /* Pass two --- copy the argument strings into the result space */
  
      va_start(adummy, a);
  
      while ((argp = va_arg(adummy, char *)) != NULL) {
        strcpy(cp, argp);
        cp += strlen(argp);
      }
  
      va_end(adummy);
  
      /* Return the result string */
  
      return res;
  }
  
  /*
   * apr_psprintf is implemented by writing directly into the current
   * block of the pool, starting right at first_avail.  If there's
   * insufficient room, then a new block is allocated and the earlier
   * output is copied over.  The new block isn't linked into the pool
   * until all the output is done.
   *
   * Note that this is completely safe because nothing else can
   * allocate in this pool while apr_psprintf is running.  alarms are
   * blocked, and the only thing outside of alloc.c that's invoked
   * is apr_vformatter -- which was purposefully written to be
   * self-contained with no callouts.
   */
  
  struct psprintf_data {
      apr_vformatter_buff_t vbuff;
  #ifdef ALLOC_USE_MALLOC
      char *base;
  #else
      union block_hdr *blok;
      int got_a_new_block;
  #endif
  };
  
  static int psprintf_flush(apr_vformatter_buff_t *vbuff)
  {
      struct psprintf_data *ps = (struct psprintf_data *)vbuff;
  #ifdef ALLOC_USE_MALLOC
      int size;
      char *ptr;
  
      size = (char *)ps->vbuff.curpos - ps->base;
      ptr = realloc(ps->base, 2*size);
      if (ptr == NULL) {
        fputs("Ouch!  Out of memory!\n", stderr);
        exit(1);
      }
      ps->base = ptr;
      ps->vbuff.curpos = ptr + size;
      ps->vbuff.endpos = ptr + 2*size - 1;
      return 0;
  #else
      union block_hdr *blok;
      union block_hdr *nblok;
      size_t cur_len;
      char *strp;
  
      blok = ps->blok;
      strp = ps->vbuff.curpos;
      cur_len = strp - blok->h.first_avail;
  
      /* must try another blok */
      (void) apr_acquire_mutex(alloc_mutex);
      nblok = new_block(2 * cur_len);
      (void) apr_release_mutex(alloc_mutex);
      memcpy(nblok->h.first_avail, blok->h.first_avail, cur_len);
      ps->vbuff.curpos = nblok->h.first_avail + cur_len;
      /* save a byte for the NUL terminator */
      ps->vbuff.endpos = nblok->h.endp - 1;
  
      /* did we allocate the current blok? if so free it up */
      if (ps->got_a_new_block) {
        debug_fill(blok->h.first_avail, blok->h.endp - blok->h.first_avail);
        (void) apr_acquire_mutex(alloc_mutex);
        blok->h.next = block_freelist;
        block_freelist = blok;
        (void) apr_release_mutex(alloc_mutex);
      }
      ps->blok = nblok;
      ps->got_a_new_block = 1;
      /* note that we've deliberately not linked the new block onto
       * the pool yet... because we may need to flush again later, and
       * we'd have to spend more effort trying to unlink the block.
       */
      return 0;
  #endif
  }
  
  API_EXPORT(char *) apr_pvsprintf(apr_pool_t *p, const char *fmt, va_list ap)
  {
  #ifdef ALLOC_USE_MALLOC
      struct psprintf_data ps;
      void *ptr;
  
      apr_block_alarms();
      ps.base = malloc(512);
      if (ps.base == NULL) {
        fputs("Ouch!  Out of memory!\n", stderr);
        exit(1);
      }
      /* need room at beginning for allocation_list */
      ps.vbuff.curpos = ps.base + CLICK_SZ;
      ps.vbuff.endpos = ps.base + 511;
      apr_vformatter(psprintf_flush, &ps.vbuff, fmt, ap);
      *ps.vbuff.curpos++ = '\0';
      ptr = ps.base;
      /* shrink */
      ptr = realloc(ptr, (char *)ps.vbuff.curpos - (char *)ptr);
      if (ptr == NULL) {
        fputs("Ouch!  Out of memory!\n", stderr);
        exit(1);
      }
      *(void **)ptr = p->allocation_list;
      p->allocation_list = ptr;
      apr_unblock_alarms();
      return (char *)ptr + CLICK_SZ;
  #else
      struct psprintf_data ps;
      char *strp;
      int size;
  
      apr_block_alarms();
      ps.blok = p->last;
      ps.vbuff.curpos = ps.blok->h.first_avail;
      ps.vbuff.endpos = ps.blok->h.endp - 1;    /* save one for NUL */
      ps.got_a_new_block = 0;
  
      apr_vformatter(psprintf_flush, &ps.vbuff, fmt, ap);
  
      strp = ps.vbuff.curpos;
      *strp++ = '\0';
  
      size = strp - ps.blok->h.first_avail;
      size = (1 + ((size - 1) / CLICK_SZ)) * CLICK_SZ;
      strp = ps.blok->h.first_avail;    /* save away result pointer */
      ps.blok->h.first_avail += size;
  
      /* have to link the block in if it's a new one */
      if (ps.got_a_new_block) {
        p->last->h.next = ps.blok;
        p->last = ps.blok;
  #ifdef POOL_DEBUG
        ps.blok->h.owning_pool = p;
  #endif
      }
      apr_unblock_alarms();
  
      return strp;
  #endif
  }
  
  API_EXPORT_NONSTD(char *) apr_psprintf(apr_pool_t *p, const char *fmt, ...)
  {
      va_list ap;
      char *res;
  
      va_start(ap, fmt);
      res = apr_pvsprintf(p, fmt, ap);
      va_end(ap);
      return res;
  }
  
  /*****************************************************************
   *
   * The 'array' functions...
   */
  
  static void make_array_core(apr_array_header_t *res, apr_pool_t *p,
                            int nelts, int elt_size)
  {
      /*
       * Assure sanity if someone asks for
       * array of zero elts.
       */
      if (nelts < 1) {
        nelts = 1;
      }
  
      res->elts = apr_pcalloc(p, nelts * elt_size);
  
      res->pool = p;
      res->elt_size = elt_size;
      res->nelts = 0;           /* No active elements yet... */
      res->nalloc = nelts;      /* ...but this many allocated */
  }
  
  API_EXPORT(apr_array_header_t *) apr_make_array(apr_pool_t *p,
                                                int nelts, int elt_size)
  {
      apr_array_header_t *res;
  
      res = (apr_array_header_t *) apr_palloc(p, sizeof(apr_array_header_t));
      make_array_core(res, p, nelts, elt_size);
      return res;
  }
  
  API_EXPORT(void *) apr_push_array(apr_array_header_t *arr)
  {
      if (arr->nelts == arr->nalloc) {
        int new_size = (arr->nalloc <= 0) ? 1 : arr->nalloc * 2;
        char *new_data;
  
        new_data = apr_pcalloc(arr->pool, arr->elt_size * new_size);
  
        memcpy(new_data, arr->elts, arr->nalloc * arr->elt_size);
        arr->elts = new_data;
        arr->nalloc = new_size;
      }
  
      ++arr->nelts;
      return arr->elts + (arr->elt_size * (arr->nelts - 1));
  }
  
  API_EXPORT(void) apr_array_cat(apr_array_header_t *dst,
                               const apr_array_header_t *src)
  {
      int elt_size = dst->elt_size;
  
      if (dst->nelts + src->nelts > dst->nalloc) {
        int new_size = (dst->nalloc <= 0) ? 1 : dst->nalloc * 2;
        char *new_data;
  
        while (dst->nelts + src->nelts > new_size) {
            new_size *= 2;
        }
  
        new_data = apr_pcalloc(dst->pool, elt_size * new_size);
        memcpy(new_data, dst->elts, dst->nalloc * elt_size);
  
        dst->elts = new_data;
        dst->nalloc = new_size;
      }
  
      memcpy(dst->elts + dst->nelts * elt_size, src->elts,
           elt_size * src->nelts);
      dst->nelts += src->nelts;
  }
  
  API_EXPORT(apr_array_header_t *) apr_copy_array(apr_pool_t *p,
                                                const apr_array_header_t *arr)
  {
      apr_array_header_t *res = apr_make_array(p, arr->nalloc, arr->elt_size);
  
      memcpy(res->elts, arr->elts, arr->elt_size * arr->nelts);
      res->nelts = arr->nelts;
      return res;
  }
  
  /* This cute function copies the array header *only*, but arranges
   * for the data section to be copied on the first push or arraycat.
   * It's useful when the elements of the array being copied are
   * read only, but new stuff *might* get added on the end; we have the
   * overhead of the full copy only where it is really needed.
   */
  
  static APR_INLINE void copy_array_hdr_core(apr_array_header_t *res,
                                           const apr_array_header_t *arr)
  {
      res->elts = arr->elts;
      res->elt_size = arr->elt_size;
      res->nelts = arr->nelts;
      res->nalloc = arr->nelts; /* Force overflow on push */
  }
  
  API_EXPORT(apr_array_header_t *)
      apr_copy_array_hdr(apr_pool_t *p,
                       const apr_array_header_t *arr)
  {
      apr_array_header_t *res;
  
      res = (apr_array_header_t *) apr_palloc(p, sizeof(apr_array_header_t));
      res->pool = p;
      copy_array_hdr_core(res, arr);
      return res;
  }
  
  /* The above is used here to avoid consing multiple new array bodies... */
  
  API_EXPORT(apr_array_header_t *)
      apr_append_arrays(apr_pool_t *p,
                      const apr_array_header_t *first,
                      const apr_array_header_t *second)
  {
      apr_array_header_t *res = apr_copy_array_hdr(p, first);
  
      apr_array_cat(res, second);
      return res;
  }
  
  /* apr_array_pstrcat generates a new string from the pool containing
   * the concatenated sequence of substrings referenced as elements within
   * the array.  The string will be empty if all substrings are empty or null,
   * or if there are no elements in the array.
   * If sep is non-NUL, it will be inserted between elements as a separator.
   */
  API_EXPORT(char *) apr_array_pstrcat(apr_pool_t *p,
                                     const apr_array_header_t *arr,
                                     const char sep)
  {
      char *cp, *res, **strpp;
      int i, len;
  
      if (arr->nelts <= 0 || arr->elts == NULL) {    /* Empty table? */
          return (char *) apr_pcalloc(p, 1);
      }
  
      /* Pass one --- find length of required string */
  
      len = 0;
      for (i = 0, strpp = (char **) arr->elts; ; ++strpp) {
          if (strpp && *strpp != NULL) {
              len += strlen(*strpp);
          }
          if (++i >= arr->nelts) {
              break;
        }
          if (sep) {
              ++len;
        }
      }
  
      /* Allocate the required string */
  
      res = (char *) apr_palloc(p, len + 1);
      cp = res;
  
      /* Pass two --- copy the argument strings into the result space */
  
      for (i = 0, strpp = (char **) arr->elts; ; ++strpp) {
          if (strpp && *strpp != NULL) {
              len = strlen(*strpp);
              memcpy(cp, *strpp, len);
              cp += len;
          }
          if (++i >= arr->nelts) {
              break;
        }
          if (sep) {
              *cp++ = sep;
        }
      }
  
      *cp = '\0';
  
      /* Return the result string */
  
      return res;
  }
  
  
  /*****************************************************************
   *
   * The "table" functions.
   */
  
  /*
   * XXX: if you tweak this you should look at is_empty_table() and table_elts()
   * in alloc.h
   */
  #ifdef MAKE_TABLE_PROFILE
  static apr_table_entry_t *table_push(apr_table_t *t)
  {
      if (t->a.nelts == t->a.nalloc) {
        fprintf(stderr,
                "table_push: table created by %p hit limit of %u\n",
                t->creator, t->a.nalloc);
      }
      return (apr_table_entry_t *) apr_push_array(&t->a);
  }
  #else /* MAKE_TABLE_PROFILE */
  #define table_push(t) ((apr_table_entry_t *) apr_push_array(&(t)->a))
  #endif /* MAKE_TABLE_PROFILE */
  
  
  API_EXPORT(apr_table_t *) apr_make_table(apr_pool_t *p, int nelts)
  {
      apr_table_t *t = apr_palloc(p, sizeof(apr_table_t));
  
      make_array_core(&t->a, p, nelts, sizeof(apr_table_entry_t));
  #ifdef MAKE_TABLE_PROFILE
      t->creator = __builtin_return_address(0);
  #endif
      return t;
  }
  
  API_EXPORT(apr_table_t *) apr_copy_table(apr_pool_t *p, const apr_table_t *t)
  {
      apr_table_t *new = apr_palloc(p, sizeof(apr_table_t));
  
  #ifdef POOL_DEBUG
      /* we don't copy keys and values, so it's necessary that t->a.pool
       * have a life span at least as long as p
       */
      if (!apr_pool_is_ancestor(t->a.pool, p)) {
        fprintf(stderr, "copy_table: t's pool is not an ancestor of p\n");
        abort();
      }
  #endif
      make_array_core(&new->a, p, t->a.nalloc, sizeof(apr_table_entry_t));
      memcpy(new->a.elts, t->a.elts, t->a.nelts * sizeof(apr_table_entry_t));
      new->a.nelts = t->a.nelts;
      return new;
  }
  
  API_EXPORT(void) apr_clear_table(apr_table_t *t)
  {
      t->a.nelts = 0;
  }
  
  API_EXPORT(const char *) apr_table_get(const apr_table_t *t, const char *key)
  {
      apr_table_entry_t *elts = (apr_table_entry_t *) t->a.elts;
      int i;
  
      if (key == NULL) {
        return NULL;
      }
  
      for (i = 0; i < t->a.nelts; ++i) {
        if (!strcasecmp(elts[i].key, key)) {
            return elts[i].val;
        }
      }
  
      return NULL;
  }
  
  API_EXPORT(void) apr_table_set(apr_table_t *t, const char *key,
                               const char *val)
  {
      register int i, j, k;
      apr_table_entry_t *elts = (apr_table_entry_t *) t->a.elts;
      int done = 0;
  
      for (i = 0; i < t->a.nelts; ) {
        if (!strcasecmp(elts[i].key, key)) {
            if (!done) {
                elts[i].val = apr_pstrdup(t->a.pool, val);
                done = 1;
                ++i;
            }
            else {              /* delete an extraneous element */
                for (j = i, k = i + 1; k < t->a.nelts; ++j, ++k) {
                    elts[j].key = elts[k].key;
                    elts[j].val = elts[k].val;
                }
                --t->a.nelts;
            }
        }
        else {
            ++i;
        }
      }
  
      if (!done) {
        elts = (apr_table_entry_t *) table_push(t);
        elts->key = apr_pstrdup(t->a.pool, key);
        elts->val = apr_pstrdup(t->a.pool, val);
      }
  }
  
  API_EXPORT(void) apr_table_setn(apr_table_t *t, const char *key,
                                const char *val)
  {
      register int i, j, k;
      apr_table_entry_t *elts = (apr_table_entry_t *) t->a.elts;
      int done = 0;
  
  #ifdef POOL_DEBUG
      {
        if (!apr_pool_is_ancestor(apr_find_pool(key), t->a.pool)) {
            fprintf(stderr, "table_set: key not in ancestor pool of t\n");
            abort();
        }
        if (!apr_pool_is_ancestor(apr_find_pool(val), t->a.pool)) {
            fprintf(stderr, "table_set: val not in ancestor pool of t\n");
            abort();
        }
      }
  #endif
  
      for (i = 0; i < t->a.nelts; ) {
        if (!strcasecmp(elts[i].key, key)) {
            if (!done) {
                elts[i].val = (char *)val;
                done = 1;
                ++i;
            }
            else {              /* delete an extraneous element */
                for (j = i, k = i + 1; k < t->a.nelts; ++j, ++k) {
                    elts[j].key = elts[k].key;
                    elts[j].val = elts[k].val;
                }
                --t->a.nelts;
            }
        }
        else {
            ++i;
        }
      }
  
      if (!done) {
        elts = (apr_table_entry_t *) table_push(t);
        elts->key = (char *)key;
        elts->val = (char *)val;
      }
  }
  
  API_EXPORT(void) apr_table_unset(apr_table_t *t, const char *key)
  {
      register int i, j, k;
      apr_table_entry_t *elts = (apr_table_entry_t *) t->a.elts;
  
      for (i = 0; i < t->a.nelts; ) {
        if (!strcasecmp(elts[i].key, key)) {
  
            /* found an element to skip over
             * there are any number of ways to remove an element from
             * a contiguous block of memory.  I've chosen one that
             * doesn't do a memcpy/bcopy/array_delete, *shrug*...
             */
            for (j = i, k = i + 1; k < t->a.nelts; ++j, ++k) {
                elts[j].key = elts[k].key;
                elts[j].val = elts[k].val;
            }
            --t->a.nelts;
        }
        else {
            ++i;
        }
      }
  }
  
  API_EXPORT(void) apr_table_merge(apr_table_t *t, const char *key,
                                 const char *val)
  {
      apr_table_entry_t *elts = (apr_table_entry_t *) t->a.elts;
      int i;
  
      for (i = 0; i < t->a.nelts; ++i) {
        if (!strcasecmp(elts[i].key, key)) {
            elts[i].val = apr_pstrcat(t->a.pool, elts[i].val, ", ", val, NULL);
            return;
        }
      }
  
      elts = (apr_table_entry_t *) table_push(t);
      elts->key = apr_pstrdup(t->a.pool, key);
      elts->val = apr_pstrdup(t->a.pool, val);
  }
  
  API_EXPORT(void) apr_table_mergen(apr_table_t *t, const char *key,
                                  const char *val)
  {
      apr_table_entry_t *elts = (apr_table_entry_t *) t->a.elts;
      int i;
  
  #ifdef POOL_DEBUG
      {
        if (!apr_pool_is_ancestor(apr_find_pool(key), t->a.pool)) {
            fprintf(stderr, "table_set: key not in ancestor pool of t\n");
            abort();
        }
        if (!apr_pool_is_ancestor(apr_find_pool(val), t->a.pool)) {
            fprintf(stderr, "table_set: key not in ancestor pool of t\n");
            abort();
        }
      }
  #endif
  
      for (i = 0; i < t->a.nelts; ++i) {
        if (!strcasecmp(elts[i].key, key)) {
            elts[i].val = apr_pstrcat(t->a.pool, elts[i].val, ", ", val, NULL);
            return;
        }
      }
  
      elts = (apr_table_entry_t *) table_push(t);
      elts->key = (char *)key;
      elts->val = (char *)val;
  }
  
  API_EXPORT(void) apr_table_add(apr_table_t *t, const char *key,
                               const char *val)
  {
      apr_table_entry_t *elts = (apr_table_entry_t *) t->a.elts;
  
      elts = (apr_table_entry_t *) table_push(t);
      elts->key = apr_pstrdup(t->a.pool, key);
      elts->val = apr_pstrdup(t->a.pool, val);
  }
  
  API_EXPORT(void) apr_table_addn(apr_table_t *t, const char *key,
                                const char *val)
  {
      apr_table_entry_t *elts = (apr_table_entry_t *) t->a.elts;
  
  #ifdef POOL_DEBUG
      {
        if (!apr_pool_is_ancestor(apr_find_pool(key), t->a.pool)) {
            fprintf(stderr, "table_set: key not in ancestor pool of t\n");
            abort();
        }
        if (!apr_pool_is_ancestor(apr_find_pool(val), t->a.pool)) {
            fprintf(stderr, "table_set: key not in ancestor pool of t\n");
            abort();
        }
      }
  #endif
  
      elts = (apr_table_entry_t *) table_push(t);
      elts->key = (char *)key;
      elts->val = (char *)val;
  }
  
  API_EXPORT(apr_table_t *) apr_overlay_tables(apr_pool_t *p,
                                             const apr_table_t *overlay,
                                             const apr_table_t *base)
  {
      apr_table_t *res;
  
  #ifdef POOL_DEBUG
      /* we don't copy keys and values, so it's necessary that
       * overlay->a.pool and base->a.pool have a life span at least
       * as long as p
       */
      if (!apr_pool_is_ancestor(overlay->a.pool, p)) {
        fprintf(stderr,
                "overlay_tables: overlay's pool is not an ancestor of p\n");
        abort();
      }
      if (!apr_pool_is_ancestor(base->a.pool, p)) {
        fprintf(stderr,
                "overlay_tables: base's pool is not an ancestor of p\n");
        abort();
      }
  #endif
  
      res = apr_palloc(p, sizeof(apr_table_t));
      /* behave like append_arrays */
      res->a.pool = p;
      copy_array_hdr_core(&res->a, &overlay->a);
      apr_array_cat(&res->a, &base->a);
  
      return res;
  }
  
  /* And now for something completely abstract ...
  
   * For each key value given as a vararg:
   *   run the function pointed to as
   *     int comp(void *r, char *key, char *value);
   *   on each valid key-value pair in the table t that matches the vararg key,
   *   or once for every valid key-value pair if the vararg list is empty,
   *   until the function returns false (0) or we finish the table.
   *
   * Note that we restart the traversal for each vararg, which means that
   * duplicate varargs will result in multiple executions of the function
   * for each matching key.  Note also that if the vararg list is empty,
   * only one traversal will be made and will cut short if comp returns 0.
   *
   * Note that the table_get and table_merge functions assume that each key in
   * the table is unique (i.e., no multiple entries with the same key).  This
   * function does not make that assumption, since it (unfortunately) isn't
   * true for some of Apache's tables.
   *
   * Note that rec is simply passed-on to the comp function, so that the
   * caller can pass additional info for the task.
   */
  API_EXPORT(void) apr_table_do(int (*comp) (void *, const char *, const char 
*),
                              void *rec, const apr_table_t *t, ...)
  {
      va_list vp;
      char *argp;
      apr_table_entry_t *elts = (apr_table_entry_t *) t->a.elts;
      int rv, i;
  
      va_start(vp, t);
  
      argp = va_arg(vp, char *);
  
      do {
        for (rv = 1, i = 0; rv && (i < t->a.nelts); ++i) {
            if (elts[i].key && (!argp || !strcasecmp(elts[i].key, argp))) {
                rv = (*comp) (rec, elts[i].key, elts[i].val);
            }
        }
      } while (argp && ((argp = va_arg(vp, char *)) != NULL));
  
      va_end(vp);
  }
  
  /* Curse libc and the fact that it doesn't guarantee a stable sort.  We
   * have to enforce stability ourselves by using the order field.  If it
   * provided a stable sort then we wouldn't even need temporary storage to
   * do the work below. -djg
   *
   * ("stable sort" means that equal keys retain their original relative
   * ordering in the output.)
   */
  typedef struct {
      char *key;
      char *val;
      int order;
  } overlap_key;
  
  static int sort_overlap(const void *va, const void *vb)
  {
      const overlap_key *a = va;
      const overlap_key *b = vb;
      int r;
  
      r = strcasecmp(a->key, b->key);
      if (r) {
        return r;
      }
      return a->order - b->order;
  }
  
  /* prefer to use the stack for temp storage for overlaps smaller than this */
  #ifndef APR_OVERLAP_TABLES_ON_STACK
  #define APR_OVERLAP_TABLES_ON_STACK   (512)
  #endif
  
  API_EXPORT(void) apr_overlap_tables(apr_table_t *a, const apr_table_t *b,
                                    unsigned flags)
  {
      overlap_key cat_keys_buf[APR_OVERLAP_TABLES_ON_STACK];
      overlap_key *cat_keys;
      int nkeys;
      apr_table_entry_t *e;
      apr_table_entry_t *last_e;
      overlap_key *left;
      overlap_key *right;
      overlap_key *last;
  
      nkeys = a->a.nelts + b->a.nelts;
      if (nkeys < APR_OVERLAP_TABLES_ON_STACK) {
        cat_keys = cat_keys_buf;
      }
      else {
        /* XXX: could use scratch free space in a or b's pool instead...
         * which could save an allocation in b's pool.
         */
        cat_keys = apr_palloc(b->a.pool, sizeof(overlap_key) * nkeys);
      }
  
      nkeys = 0;
  
      /* Create a list of the entries from a concatenated with the entries
       * from b.
       */
      e = (apr_table_entry_t *)a->a.elts;
      last_e = e + a->a.nelts;
      while (e < last_e) {
        cat_keys[nkeys].key = e->key;
        cat_keys[nkeys].val = e->val;
        cat_keys[nkeys].order = nkeys;
        ++nkeys;
        ++e;
      }
  
      e = (apr_table_entry_t *)b->a.elts;
      last_e = e + b->a.nelts;
      while (e < last_e) {
        cat_keys[nkeys].key = e->key;
        cat_keys[nkeys].val = e->val;
        cat_keys[nkeys].order = nkeys;
        ++nkeys;
        ++e;
      }
  
      qsort(cat_keys, nkeys, sizeof(overlap_key), sort_overlap);
  
      /* Now iterate over the sorted list and rebuild a.
       * Start by making sure it has enough space.
       */
      a->a.nelts = 0;
      if (a->a.nalloc < nkeys) {
        a->a.elts = apr_palloc(a->a.pool, a->a.elt_size * nkeys * 2);
        a->a.nalloc = nkeys * 2;
      }
  
      /*
       * In both the merge and set cases we retain the invariant:
       *
       * left->key, (left+1)->key, (left+2)->key, ..., (right-1)->key
       * are all equal keys.  (i.e. strcasecmp returns 0)
       *
       * We essentially need to find the maximal
       * right for each key, then we can do a quick merge or set as
       * appropriate.
       */
  
      if (flags & APR_OVERLAP_TABLES_MERGE) {
        left = cat_keys;
        last = left + nkeys;
        while (left < last) {
            right = left + 1;
            if (right == last
                || strcasecmp(left->key, right->key)) {
                apr_table_addn(a, left->key, left->val);
                left = right;
            }
            else {
                char *strp;
                char *value;
                size_t len;
  
                /* Have to merge some headers.  Let's re-use the order field,
                 * since it's handy... we'll store the length of val there.
                 */
                left->order = strlen(left->val);
                len = left->order;
                do {
                    right->order = strlen(right->val);
                    len += 2 + right->order;
                    ++right;
                } while (right < last
                         && !strcasecmp(left->key, right->key));
                /* right points one past the last header to merge */
                value = apr_palloc(a->a.pool, len + 1);
                strp = value;
                for (;;) {
                    memcpy(strp, left->val, left->order);
                    strp += left->order;
                    ++left;
                    if (left == right) {
                        break;
                    }
                    *strp++ = ',';
                    *strp++ = ' ';
                }
                *strp = 0;
                apr_table_addn(a, (left-1)->key, value);
            }
        }
      }
      else {
        left = cat_keys;
        last = left + nkeys;
        while (left < last) {
            right = left + 1;
            while (right < last && !strcasecmp(left->key, right->key)) {
                ++right;
            }
            apr_table_addn(a, (right-1)->key, (right-1)->val);
            left = right;
        }
      }
  }
  
  /*****************************************************************
   *
   * Managing generic cleanups.  
   */
  
  struct cleanup {
      void *data;
      void (*plain_cleanup) (void *);
      void (*child_cleanup) (void *);
      struct cleanup *next;
  };
  
  API_EXPORT(void) apr_register_cleanup(apr_pool_t *p, void *data,
                                      void (*plain_cleanup) (void *),
                                      void (*child_cleanup) (void *))
  {
      struct cleanup *c;
  
      c = (struct cleanup *) apr_palloc(p, sizeof(struct cleanup));
      c->data = data;
      c->plain_cleanup = plain_cleanup;
      c->child_cleanup = child_cleanup;
      c->next = p->cleanups;
      p->cleanups = c;
  }
  
  API_EXPORT(void) apr_kill_cleanup(apr_pool_t *p, void *data,
                                  void (*cleanup) (void *))
  {
      struct cleanup *c = p->cleanups;
      struct cleanup **lastp = &p->cleanups;
  
      while (c) {
        if (c->data == data && c->plain_cleanup == cleanup) {
            *lastp = c->next;
            break;
        }
  
        lastp = &c->next;
        c = c->next;
      }
  }
  
  API_EXPORT(void) apr_run_cleanup(apr_pool_t *p, void *data,
                                 void (*cleanup) (void *))
  {
      apr_block_alarms();               /* Run cleanup only once! */
      (*cleanup) (data);
      apr_kill_cleanup(p, data, cleanup);
      apr_unblock_alarms();
  }
  
  static void run_cleanups(struct cleanup *c)
  {
      while (c) {
        (*c->plain_cleanup) (c->data);
        c = c->next;
      }
  }
  
  static void run_child_cleanups(struct cleanup *c)
  {
      while (c) {
        (*c->child_cleanup) (c->data);
        c = c->next;
      }
  }
  
  static void cleanup_pool_for_exec(apr_pool_t *p)
  {
      run_child_cleanups(p->cleanups);
      p->cleanups = NULL;
  
      for (p = p->sub_pools; p; p = p->sub_next) {
        cleanup_pool_for_exec(p);
      }
  }
  
  API_EXPORT(void) apr_cleanup_for_exec(void)
  {
  #if !defined(WIN32) && !defined(OS2)
      /*
       * Don't need to do anything on NT or OS/2, because I
       * am actually going to spawn the new process - not
       * exec it. All handles that are not inheritable, will
       * be automajically closed. The only problem is with
       * file handles that are open, but there isn't much
       * I can do about that (except if the child decides
       * to go out and close them
       */
      apr_block_alarms();
      cleanup_pool_for_exec(permanent_pool);
      apr_unblock_alarms();
  #endif /* ndef WIN32 */
  }
  
  API_EXPORT_NONSTD(void) apr_null_cleanup(void *data)
  {
      /* do nothing cleanup routine */
  }
  
  /*****************************************************************
   *
   * Files and file descriptors; these are just an application of the
   * generic cleanup interface.
   */
  
  static void fd_cleanup(void *fdv)
  {
      close((int) (long) fdv);
  }
  
  API_EXPORT(void) apr_note_cleanups_for_fd(apr_pool_t *p, int fd)
  {
      apr_register_cleanup(p, (void *) (long) fd, fd_cleanup, fd_cleanup);
  }
  
  API_EXPORT(void) apr_kill_cleanups_for_fd(apr_pool_t *p, int fd)
  {
      apr_kill_cleanup(p, (void *) (long) fd, fd_cleanup);
  }
  
  API_EXPORT(int) apr_popenf(apr_pool_t *a, const char *name, int flg, int mode)
  {
      int fd;
      int save_errno;
  
      apr_block_alarms();
      fd = open(name, flg, mode);
      save_errno = errno;
      if (fd >= 0) {
        fd = apr_slack(fd, APR_SLACK_HIGH);
        apr_note_cleanups_for_fd(a, fd);
      }
      apr_unblock_alarms();
      errno = save_errno;
      return fd;
  }
  
  API_EXPORT(int) apr_pclosef(apr_pool_t *a, int fd)
  {
      int res;
      int save_errno;
  
      apr_block_alarms();
      res = close(fd);
      save_errno = errno;
      apr_kill_cleanup(a, (void *) (long) fd, fd_cleanup);
      apr_unblock_alarms();
      errno = save_errno;
      return res;
  }
  
  #ifdef WIN32
  static void h_cleanup(void *fdv)
  {
      CloseHandle((HANDLE) fdv);
  }
  
  API_EXPORT(void) apr_note_cleanups_for_h(apr_pool_t *p, HANDLE hDevice)
  {
      apr_register_cleanup(p, (void *) hDevice, h_cleanup, h_cleanup);
  }
  
  API_EXPORT(int) apr_pcloseh(apr_pool_t *a, HANDLE hDevice)
  {
      int res=0;
      int save_errno;
  
      apr_block_alarms();
      
      if (!CloseHandle(hDevice)) {
          res = GetLastError();
      }
      
      save_errno = errno;
      apr_kill_cleanup(a, (void *) hDevice, h_cleanup);
      apr_unblock_alarms();
      errno = save_errno;
      return res;
  }
  #endif
  
  /* Note that we have separate plain_ and child_ cleanups for FILE *s,
   * since fclose() would flush I/O buffers, which is extremely undesirable;
   * we just close the descriptor.
   */
  
  static void file_cleanup(void *fpv)
  {
      fclose((FILE *) fpv);
  }
  static void file_child_cleanup(void *fpv)
  {
      close(fileno((FILE *) fpv));
  }
  
  API_EXPORT(void) apr_note_cleanups_for_file(apr_pool_t *p, FILE *fp)
  {
      apr_register_cleanup(p, (void *) fp, file_cleanup, file_child_cleanup);
  }
  
  API_EXPORT(FILE *) apr_pfopen(apr_pool_t *a, const char *name,
                              const char *mode)
  {
      FILE *fd = NULL;
      int baseFlag, desc;
      int modeFlags = 0;
      int saved_errno;
  
  #ifdef WIN32
      modeFlags = _S_IREAD | _S_IWRITE;
  #else /* WIN32 */
      modeFlags = S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH;
  #endif /* WIN32 */
  
      apr_block_alarms();
  
      if (*mode == 'a') {
        /* Work around faulty implementations of fopen */
        baseFlag = (*(mode + 1) == '+') ? O_RDWR : O_WRONLY;
        desc = open(name, baseFlag | O_APPEND | O_CREAT,
                    modeFlags);
        if (desc >= 0) {
            desc = apr_slack(desc, APR_SLACK_LOW);
            fd = apr_fdopen(desc, mode);
        }
      }
      else {
        fd = fopen(name, mode);
      }
      saved_errno = errno;
      if (fd != NULL) {
        apr_note_cleanups_for_file(a, fd);
      }
      apr_unblock_alarms();
      errno = saved_errno;
      return fd;
  }
  
  API_EXPORT(FILE *) apr_pfdopen(apr_pool_t *a, int fd, const char *mode)
  {
      FILE *f;
      int saved_errno;
  
      apr_block_alarms();
      f = apr_fdopen(fd, mode);
      saved_errno = errno;
      if (f != NULL) {
        apr_note_cleanups_for_file(a, f);
      }
      apr_unblock_alarms();
      errno = saved_errno;
      return f;
  }
  
  
  API_EXPORT(int) apr_pfclose(apr_pool_t *a, FILE *fd)
  {
      int res;
  
      apr_block_alarms();
      res = fclose(fd);
      apr_kill_cleanup(a, (void *) fd, file_cleanup);
      apr_unblock_alarms();
      return res;
  }
  
  /*
   * DIR * with cleanup
   */
  
  static void dir_cleanup(void *dv)
  {
      closedir((DIR *) dv);
  }
  
  API_EXPORT(DIR *) apr_popendir(apr_pool_t *p, const char *name)
  {
      DIR *d;
      int save_errno;
  
      apr_block_alarms();
      d = opendir(name);
      if (d == NULL) {
        save_errno = errno;
        apr_unblock_alarms();
        errno = save_errno;
        return NULL;
      }
      apr_register_cleanup(p, (void *) d, dir_cleanup, dir_cleanup);
      apr_unblock_alarms();
      return d;
  }
  
  API_EXPORT(void) apr_pclosedir(apr_pool_t *p, DIR * d)
  {
      apr_block_alarms();
      apr_kill_cleanup(p, (void *) d, dir_cleanup);
      closedir(d);
      apr_unblock_alarms();
  }
  
  /*****************************************************************
   *
   * Files and file descriptors; these are just an application of the
   * generic cleanup interface.
   */
  
  static void socket_cleanup(void *fdv)
  {
      closesocket((int) (long) fdv);
  }
  
  API_EXPORT(void) apr_note_cleanups_for_socket(apr_pool_t *p, int fd)
  {
      apr_register_cleanup(p, (void *) (long) fd, socket_cleanup,
                         socket_cleanup);
  }
  
  API_EXPORT(void) apr_kill_cleanups_for_socket(apr_pool_t *p, int sock)
  {
      apr_kill_cleanup(p, (void *) (long) sock, socket_cleanup);
  }
  
  API_EXPORT(int) apr_psocket(apr_pool_t *p, int domain, int type, int protocol)
  {
      int fd;
  
      apr_block_alarms();
      fd = socket(domain, type, protocol);
      if (fd == -1) {
        int save_errno = errno;
        apr_unblock_alarms();
        errno = save_errno;
        return -1;
      }
      apr_note_cleanups_for_socket(p, fd);
      apr_unblock_alarms();
      return fd;
  }
  
  API_EXPORT(int) apr_pclosesocket(apr_pool_t *a, int sock)
  {
      int res;
      int save_errno;
  
      apr_block_alarms();
      res = closesocket(sock);
  #ifdef WIN32
      errno = WSAGetLastError();
  #endif /* WIN32 */
      save_errno = errno;
      apr_kill_cleanup(a, (void *) (long) sock, socket_cleanup);
      apr_unblock_alarms();
      errno = save_errno;
      return res;
  }
  
  
  /*
   * Here's a pool-based interface to POSIX regex's regcomp().
   * Note that we return regex_t instead of being passed one.
   * The reason is that if you use an already-used regex_t structure,
   * the memory that you've already allocated gets forgotten, and
   * regfree() doesn't clear it. So we don't allow it.
   */
  
  static void regex_cleanup(void *preg)
  {
      regfree((regex_t *) preg);
  }
  
  API_EXPORT(regex_t *) apr_pregcomp(apr_pool_t *p, const char *pattern,
                                   int cflags)
  {
      regex_t *preg = apr_palloc(p, sizeof(regex_t));
  
      if (regcomp(preg, pattern, cflags)) {
        return NULL;
      }
  
      apr_register_cleanup(p, (void *) preg, regex_cleanup, regex_cleanup);
  
      return preg;
  }
  
  
  API_EXPORT(void) apr_pregfree(apr_pool_t *p, regex_t * reg)
  {
      apr_block_alarms();
      regfree(reg);
      apr_kill_cleanup(p, (void *) reg, regex_cleanup);
      apr_unblock_alarms();
  }
  
  /*****************************************************************
   *
   * More grotty system stuff... subprocesses.  Frump.  These don't use
   * the generic cleanup interface because I don't want multiple
   * subprocesses to result in multiple three-second pauses; the
   * subprocesses have to be "freed" all at once.  If someone comes
   * along with another resource they want to allocate which has the
   * same property, we might want to fold support for that into the
   * generic interface, but for now, it's a special case
   */
  
  API_EXPORT(void) apr_note_subprocess(apr_pool_t *a, pid_t pid,
                                     enum kill_conditions how)
  {
      struct process_chain *new =
      (struct process_chain *) apr_palloc(a, sizeof(struct process_chain));
  
      new->pid = pid;
      new->kill_how = how;
      new->next = a->subprocesses;
      a->subprocesses = new;
  }
  
  #ifdef WIN32
  #define os_pipe(fds) _pipe(fds, 512, O_BINARY | O_NOINHERIT)
  #else
  #define os_pipe(fds) pipe(fds)
  #endif /* WIN32 */
  
  /* for apr_fdopen, to get binary mode */
  #if defined (OS2) || defined (WIN32)
  #define BINMODE       "b"
  #else
  #define BINMODE
  #endif
  
  static pid_t spawn_child_core(apr_pool_t *p,
                              int (*func) (void *, apr_child_info_t *),
                              void *data,enum kill_conditions kill_how,
                              int *pipe_in, int *pipe_out, int *pipe_err)
  {
      pid_t pid;
      int in_fds[2];
      int out_fds[2];
      int err_fds[2];
      int save_errno;
  
      if (pipe_in && os_pipe(in_fds) < 0) {
        return 0;
      }
  
      if (pipe_out && os_pipe(out_fds) < 0) {
        save_errno = errno;
        if (pipe_in) {
            close(in_fds[0]);
            close(in_fds[1]);
        }
        errno = save_errno;
        return 0;
      }
  
      if (pipe_err && os_pipe(err_fds) < 0) {
        save_errno = errno;
        if (pipe_in) {
            close(in_fds[0]);
            close(in_fds[1]);
        }
        if (pipe_out) {
            close(out_fds[0]);
            close(out_fds[1]);
        }
        errno = save_errno;
        return 0;
      }
  
  #ifdef WIN32
  
      {
        HANDLE thread_handle;
        int hStdIn, hStdOut, hStdErr;
        int old_priority;
        apr_child_info_t info;
  
        (void) apr_acquire_mutex(spawn_mutex);
        thread_handle = GetCurrentThread();     /* doesn't need to be closed */
        old_priority = GetThreadPriority(thread_handle);
        SetThreadPriority(thread_handle, THREAD_PRIORITY_HIGHEST);
        /* Now do the right thing with your pipes */
        if (pipe_in) {
            hStdIn = dup(fileno(stdin));
            if (dup2(in_fds[0], fileno(stdin))) {
                apr_log_error(APLOG_MARK, APLOG_ERR, NULL,
                              "dup2(stdin) failed");
            }
            close(in_fds[0]);
        }
        if (pipe_out) {
            hStdOut = dup(fileno(stdout));
            close(fileno(stdout));
            if (dup2(out_fds[1], fileno(stdout))) {
                apr_log_error(APLOG_MARK, APLOG_ERR, NULL,
                              "dup2(stdout) failed");
            }
            close(out_fds[1]);
        }
        if (pipe_err) {
            hStdErr = dup(fileno(stderr));
            if (dup2(err_fds[1], fileno(stderr))) {
                apr_log_error(APLOG_MARK, APLOG_ERR, NULL,
                              "dup2(stdin) failed");
            }
            close(err_fds[1]);
        }
  
        info.hPipeInputRead   = GetStdHandle(STD_INPUT_HANDLE);
        info.hPipeOutputWrite = GetStdHandle(STD_OUTPUT_HANDLE);
        info.hPipeErrorWrite  = GetStdHandle(STD_ERROR_HANDLE);
  
        pid = (*func) (data, &info);
          if (pid == -1) {
            pid = 0;   /* map Win32 error code onto Unix default */
        }
  
          if (!pid) {
            save_errno = errno;
            close(in_fds[1]);
            close(out_fds[0]);
            close(err_fds[0]);
        }
  
        /* restore the original stdin, stdout and stderr */
        if (pipe_in) {
            dup2(hStdIn, fileno(stdin));
            close(hStdIn);
          }
        if (pipe_out) {
            dup2(hStdOut, fileno(stdout));
            close(hStdOut);
        }
        if (pipe_err) {
            dup2(hStdErr, fileno(stderr));
            close(hStdErr);
        }
  
          if (pid) {
            apr_note_subprocess(p, pid, kill_how);
            if (pipe_in) {
                *pipe_in = in_fds[1];
            }
            if (pipe_out) {
                *pipe_out = out_fds[0];
            }
            if (pipe_err) {
                *pipe_err = err_fds[0];
            }
        }
        SetThreadPriority(thread_handle, old_priority);
        (void) apr_release_mutex(spawn_mutex);
        /*
         * go on to the end of the function, where you can
         * unblock alarms and return the pid
         */
  
      }
  #elif defined(OS2)
      {
          int save_in=-1, save_out=-1, save_err=-1;
          
          if (pipe_out) {
              save_out = dup(STDOUT_FILENO);
              dup2(out_fds[1], STDOUT_FILENO);
              close(out_fds[1]);
          }
  
          if (pipe_in) {
              save_in = dup(STDIN_FILENO);
              dup2(in_fds[0], STDIN_FILENO);
              close(in_fds[0]);
          }
  
          if (pipe_err) {
              save_err = dup(STDERR_FILENO);
              dup2(err_fds[1], STDERR_FILENO);
              close(err_fds[1]);
          }
      
          pid = func(data, NULL);
      
          if (pid) {
              apr_note_subprocess(p, pid, kill_how);
        }
  
          if (pipe_out) {
              close(STDOUT_FILENO);
              dup2(save_out, STDOUT_FILENO);
              close(save_out);
              *pipe_out = out_fds[0];
          }
  
          if (pipe_in) {
              close(STDIN_FILENO);
              dup2(save_in, STDIN_FILENO);
              close(save_in);
              *pipe_in = in_fds[1];
          }
  
          if (pipe_err) {
              close(STDERR_FILENO);
              dup2(save_err, STDERR_FILENO);
              close(save_err);
              *pipe_err = err_fds[0];
          }
      }
  #elif defined(TPF)
     return (pid = apr_tpf_spawn_child(p, func, data, kill_how, 
                                     pipe_in, pipe_out, pipe_err, out_fds,
                                     in_fds, err_fds));
  #else
  
      if ((pid = fork()) < 0) {
        save_errno = errno;
        if (pipe_in) {
            close(in_fds[0]);
            close(in_fds[1]);
        }
        if (pipe_out) {
            close(out_fds[0]);
            close(out_fds[1]);
        }
        if (pipe_err) {
            close(err_fds[0]);
            close(err_fds[1]);
        }
        errno = save_errno;
        return 0;
      }
  
      if (!pid) {
        /* Child process */
        RAISE_SIGSTOP(SPAWN_CHILD);
  
        if (pipe_out) {
            close(out_fds[0]);
            dup2(out_fds[1], STDOUT_FILENO);
            close(out_fds[1]);
        }
  
        if (pipe_in) {
            close(in_fds[1]);
            dup2(in_fds[0], STDIN_FILENO);
            close(in_fds[0]);
        }
  
        if (pipe_err) {
            close(err_fds[0]);
            dup2(err_fds[1], STDERR_FILENO);
            close(err_fds[1]);
        }
  
        /*
         * HP-UX SIGCHLD fix goes here, if someone will remind me
         * what it is...
         */
        signal(SIGCHLD, SIG_DFL);       /* Was that it? */
  
        func(data, NULL);
        exit(1);        /* Should only get here if the exec in func() failed */
      }
  
      /* Parent process */
  
      apr_note_subprocess(p, pid, kill_how);
  
      if (pipe_out) {
        close(out_fds[1]);
        *pipe_out = out_fds[0];
      }
  
      if (pipe_in) {
        close(in_fds[0]);
        *pipe_in = in_fds[1];
      }
  
      if (pipe_err) {
        close(err_fds[1]);
        *pipe_err = err_fds[0];
      }
  #endif /* WIN32 */
  
      return pid;
  }
  
  
  API_EXPORT(int) apr_spawn_child(apr_pool_t *p,
                                int (*func) (void *v, apr_child_info_t *c),
                                void *data, enum kill_conditions kill_how,
                                FILE **pipe_in, FILE **pipe_out,
                                FILE **pipe_err)
  {
      int fd_in, fd_out, fd_err;
      pid_t pid;
      int save_errno;
  
      apr_block_alarms();
  
      pid = spawn_child_core(p, func, data, kill_how,
                           pipe_in ? &fd_in : NULL,
                           pipe_out ? &fd_out : NULL,
                           pipe_err ? &fd_err : NULL);
  
      if (pid == 0) {
        save_errno = errno;
        apr_unblock_alarms();
        errno = save_errno;
        return 0;
      }
  
      if (pipe_out) {
        *pipe_out = apr_fdopen(fd_out, "r" BINMODE);
        if (*pipe_out) {
            apr_note_cleanups_for_file(p, *pipe_out);
        }
        else {
            close(fd_out);
        }
      }
  
      if (pipe_in) {
        *pipe_in = apr_fdopen(fd_in, "w" BINMODE);
        if (*pipe_in) {
            apr_note_cleanups_for_file(p, *pipe_in);
        }
        else {
            close(fd_in);
        }
      }
  
      if (pipe_err) {
        *pipe_err = apr_fdopen(fd_err, "r" BINMODE);
        if (*pipe_err) {
            apr_note_cleanups_for_file(p, *pipe_err);
        }
        else {
            close(fd_err);
        }
      }
  
      apr_unblock_alarms();
      return pid;
  }
  
  #if 0
  API_EXPORT(int) apr_bspawn_child(apr_pool_t *p,
                                 int (*func) (void *v, apr_child_info_t *c),
                                 void *data, enum kill_conditions kill_how,
                                 BUFF **pipe_in, BUFF **pipe_out,
                                 BUFF **pipe_err)
  {
  #ifdef WIN32
      SECURITY_ATTRIBUTES sa = {0};  
      HANDLE hPipeOutputRead  = NULL;
      HANDLE hPipeOutputWrite = NULL;
      HANDLE hPipeInputRead   = NULL;
      HANDLE hPipeInputWrite  = NULL;
      HANDLE hPipeErrorRead   = NULL;
      HANDLE hPipeErrorWrite  = NULL;
      HANDLE hPipeInputWriteDup = NULL;
      HANDLE hPipeOutputReadDup = NULL;
      HANDLE hPipeErrorReadDup  = NULL;
      HANDLE hCurrentProcess;
      pid_t pid = 0;
      apr_child_info_t info;
  
      apr_block_alarms();
  
      /*
       * First thing to do is to create the pipes that we will use
       * for stdin, stdout, and stderr in the child process.
       */      
      sa.nLength = sizeof(sa);
      sa.bInheritHandle = TRUE;
      sa.lpSecurityDescriptor = NULL;
  
  
      /* Create pipes for standard input/output/error redirection. */
      if (pipe_in && !CreatePipe(&hPipeInputRead, &hPipeInputWrite, &sa, 0)) {
        return 0;
      }
  
      if (pipe_out && !CreatePipe(&hPipeOutputRead, &hPipeOutputWrite, &sa, 0)) 
{
        if (pipe_in) {
            CloseHandle(hPipeInputRead);
            CloseHandle(hPipeInputWrite);
        }
        return 0;
      }
  
      if (pipe_err && !CreatePipe(&hPipeErrorRead, &hPipeErrorWrite, &sa, 0)) {
        if (pipe_in) {
            CloseHandle(hPipeInputRead);
            CloseHandle(hPipeInputWrite);
        }
        if (pipe_out) {
            CloseHandle(hPipeOutputRead);
            CloseHandle(hPipeOutputWrite);
        }
        return 0;
      }
      /*
       * When the pipe handles are created, the security descriptor
       * indicates that the handle can be inherited.  However, we do not
       * want the server side handles to the pipe to be inherited by the
       * child CGI process. If the child CGI does inherit the server
       * side handles, then the child may be left around if the server
       * closes its handles (e.g. if the http connection is aborted),
       * because the child will have a valid copy of handles to both
       * sides of the pipes, and no I/O error will occur.  Microsoft
       * recommends using DuplicateHandle to turn off the inherit bit
       * under NT and Win95.
       */
      hCurrentProcess = GetCurrentProcess();
      if ((pipe_in && !DuplicateHandle(hCurrentProcess, hPipeInputWrite,
                                     hCurrentProcess,
                                     &hPipeInputWriteDup, 0, FALSE,
                                     DUPLICATE_SAME_ACCESS))
        || (pipe_out && !DuplicateHandle(hCurrentProcess, hPipeOutputRead,
                                         hCurrentProcess, &hPipeOutputReadDup,
                                         0, FALSE, DUPLICATE_SAME_ACCESS))
        || (pipe_err && !DuplicateHandle(hCurrentProcess, hPipeErrorRead,
                                         hCurrentProcess, &hPipeErrorReadDup,
                                         0, FALSE, DUPLICATE_SAME_ACCESS))) {
        if (pipe_in) {
            CloseHandle(hPipeInputRead);
            CloseHandle(hPipeInputWrite);
        }
        if (pipe_out) {
            CloseHandle(hPipeOutputRead);
            CloseHandle(hPipeOutputWrite);
        }
        if (pipe_err) {
            CloseHandle(hPipeErrorRead);
            CloseHandle(hPipeErrorWrite);
        }
        return 0;
      }
      else {
        if (pipe_in) {
            CloseHandle(hPipeInputWrite);
            hPipeInputWrite = hPipeInputWriteDup;
        }
        if (pipe_out) {
            CloseHandle(hPipeOutputRead);
            hPipeOutputRead = hPipeOutputReadDup;
        }
        if (pipe_err) {
            CloseHandle(hPipeErrorRead);
            hPipeErrorRead = hPipeErrorReadDup;
        }
      }
  
      /* The script writes stdout to this pipe handle */
      info.hPipeOutputWrite = hPipeOutputWrite;  
  
      /* The script reads stdin from this pipe handle */
      info.hPipeInputRead = hPipeInputRead;
  
      /* The script writes stderr to this pipe handle */
      info.hPipeErrorWrite = hPipeErrorWrite;    
       
      /*
       *  Try to launch the CGI.  Under the covers, this call 
       *  will try to pick up the appropriate interpreter if 
       *  one is needed.
       */
      pid = func(data, &info);
      if (pid == -1) {
          /* Things didn't work, so cleanup */
          pid = 0;   /* map Win32 error code onto Unix default */
          CloseHandle(hPipeOutputRead);
          CloseHandle(hPipeInputWrite);
          CloseHandle(hPipeErrorRead);
      }
      else {
          if (pipe_out) {
              /*
               *  This pipe represents stdout for the script, 
               *  so we read from this pipe.
               */
            /* Create a read buffer */
              *pipe_out = apr_bcreate(p, B_RD);
  
            /* Setup the cleanup routine for the handle */
              apr_note_cleanups_for_h(p, hPipeOutputRead);   
  
            /* Associate the handle with the new buffer */
              apr_bpushh(*pipe_out, hPipeOutputRead);
          }
          
          if (pipe_in) {
              /*
               *  This pipe represents stdin for the script, so we 
               *  write to this pipe.
               */
            /* Create a write buffer */
              *pipe_in = apr_bcreate(p, B_WR);             
  
            /* Setup the cleanup routine for the handle */
              apr_note_cleanups_for_h(p, hPipeInputWrite);
  
            /* Associate the handle with the new buffer */
              apr_bpushh(*pipe_in, hPipeInputWrite);
  
          }
        
          if (pipe_err) {
              /*
               *  This pipe represents stderr for the script, so 
               *  we read from this pipe.
               */
            /* Create a read buffer */
              *pipe_err = apr_bcreate(p, B_RD);
  
            /* Setup the cleanup routine for the handle */
              apr_note_cleanups_for_h(p, hPipeErrorRead);
  
            /* Associate the handle with the new buffer */
              apr_bpushh(*pipe_err, hPipeErrorRead);
          }
      }  
  
  
      /*
       * Now that handles have been inherited, close them to be safe.
       * You don't want to read or write to them accidentally, and we
       * sure don't want to have a handle leak.
       */
      CloseHandle(hPipeOutputWrite);
      CloseHandle(hPipeInputRead);
      CloseHandle(hPipeErrorWrite);
  
  #else
      int fd_in, fd_out, fd_err;
      pid_t pid;
      int save_errno;
  
      apr_block_alarms();
  
      pid = spawn_child_core(p, func, data, kill_how,
                           pipe_in ? &fd_in : NULL,
                           pipe_out ? &fd_out : NULL,
                           pipe_err ? &fd_err : NULL);
  
      if (pid == 0) {
        save_errno = errno;
        apr_unblock_alarms();
        errno = save_errno;
        return 0;
      }
  
      if (pipe_out) {
        *pipe_out = apr_bcreate(p, B_RD);
        apr_note_cleanups_for_fd(p, fd_out);
        apr_bpushfd(*pipe_out, fd_out, fd_out);
      }
  
      if (pipe_in) {
        *pipe_in = apr_bcreate(p, B_WR);
        apr_note_cleanups_for_fd(p, fd_in);
        apr_bpushfd(*pipe_in, fd_in, fd_in);
      }
  
      if (pipe_err) {
        *pipe_err = apr_bcreate(p, B_RD);
        apr_note_cleanups_for_fd(p, fd_err);
        apr_bpushfd(*pipe_err, fd_err, fd_err);
      }
  #endif
  
      apr_unblock_alarms();
      return pid;
  }
  #endif
  
  static void free_proc_chain(struct process_chain *procs)
  {
      /* Dispose of the subprocesses we've spawned off in the course of
       * whatever it was we're cleaning up now.  This may involve killing
       * some of them off...
       */
  
      struct process_chain *p;
      int need_timeout = 0;
      int status;
  
      if (procs == NULL) {
        return;                 /* No work.  Whew! */
      }
  
      /* First, check to see if we need to do the SIGTERM, sleep, SIGKILL
       * dance with any of the processes we're cleaning up.  If we've got
       * any kill-on-sight subprocesses, ditch them now as well, so they
       * don't waste any more cycles doing whatever it is that they shouldn't
       * be doing anymore.
       */
  #ifdef WIN32
      /* Pick up all defunct processes */
      for (p = procs; p; p = p->next) {
        if (GetExitCodeProcess((HANDLE) p->pid, &status)) {
            p->kill_how = kill_never;
        }
      }
  
  
      for (p = procs; p; p = p->next) {
        if (p->kill_how == kill_after_timeout) {
            need_timeout = 1;
        }
        else if (p->kill_how == kill_always) {
            TerminateProcess((HANDLE) p->pid, 1);
        }
      }
      /* Sleep only if we have to... */
  
      if (need_timeout) {
        sleep(3);
      }
  
      /* OK, the scripts we just timed out for have had a chance to clean up
       * --- now, just get rid of them, and also clean up the system accounting
       * goop...
       */
  
      for (p = procs; p; p = p->next) {
        if (p->kill_how == kill_after_timeout) {
            TerminateProcess((HANDLE) p->pid, 1);
        }
      }
  
      for (p = procs; p; p = p->next) {
        CloseHandle((HANDLE) p->pid);
      }
  #else
  #ifndef NEED_WAITPID
      /* Pick up all defunct processes */
      for (p = procs; p; p = p->next) {
        if (waitpid(p->pid, (int *) 0, WNOHANG) > 0) {
            p->kill_how = kill_never;
        }
      }
  #endif
  
      for (p = procs; p; p = p->next) {
        if ((p->kill_how == kill_after_timeout)
            || (p->kill_how == kill_only_once)) {
            /*
             * Subprocess may be dead already.  Only need the timeout if not.
             */
            if (kill(p->pid, SIGTERM) != -1) {
                need_timeout = 1;
            }
        }
        else if (p->kill_how == kill_always) {
            kill(p->pid, SIGKILL);
        }
      }
  
      /* Sleep only if we have to... */
  
      if (need_timeout) {
        sleep(3);
      }
  
      /* OK, the scripts we just timed out for have had a chance to clean up
       * --- now, just get rid of them, and also clean up the system accounting
       * goop...
       */
  
      for (p = procs; p; p = p->next) {
  
        if (p->kill_how == kill_after_timeout) {
            kill(p->pid, SIGKILL);
        }
  
        if (p->kill_how != kill_never) {
            waitpid(p->pid, &status, 0);
        }
      }
  #endif /* WIN32 */
  }
  
  
  

Reply via email to