summaryrefslogtreecommitdiff
path: root/usr/src/cmd/sendmail/libsm
diff options
context:
space:
mode:
Diffstat (limited to 'usr/src/cmd/sendmail/libsm')
-rw-r--r--usr/src/cmd/sendmail/libsm/Makefile83
-rw-r--r--usr/src/cmd/sendmail/libsm/assert.c189
-rw-r--r--usr/src/cmd/sendmail/libsm/cf.c102
-rw-r--r--usr/src/cmd/sendmail/libsm/clock.c642
-rw-r--r--usr/src/cmd/sendmail/libsm/clrerr.c41
-rw-r--r--usr/src/cmd/sendmail/libsm/config.c253
-rw-r--r--usr/src/cmd/sendmail/libsm/debug.c396
-rw-r--r--usr/src/cmd/sendmail/libsm/errstring.c285
-rw-r--r--usr/src/cmd/sendmail/libsm/exc.c671
-rw-r--r--usr/src/cmd/sendmail/libsm/fclose.c151
-rw-r--r--usr/src/cmd/sendmail/libsm/feof.c44
-rw-r--r--usr/src/cmd/sendmail/libsm/ferror.c43
-rw-r--r--usr/src/cmd/sendmail/libsm/fflush.c153
-rw-r--r--usr/src/cmd/sendmail/libsm/fget.c112
-rw-r--r--usr/src/cmd/sendmail/libsm/findfp.c427
-rw-r--r--usr/src/cmd/sendmail/libsm/flags.c65
-rw-r--r--usr/src/cmd/sendmail/libsm/fopen.c376
-rw-r--r--usr/src/cmd/sendmail/libsm/fpos.c155
-rw-r--r--usr/src/cmd/sendmail/libsm/fprintf.c57
-rw-r--r--usr/src/cmd/sendmail/libsm/fpurge.c55
-rw-r--r--usr/src/cmd/sendmail/libsm/fput.c54
-rw-r--r--usr/src/cmd/sendmail/libsm/fread.c102
-rw-r--r--usr/src/cmd/sendmail/libsm/fscanf.c57
-rw-r--r--usr/src/cmd/sendmail/libsm/fseek.c338
-rw-r--r--usr/src/cmd/sendmail/libsm/fvwrite.c281
-rw-r--r--usr/src/cmd/sendmail/libsm/fvwrite.h32
-rw-r--r--usr/src/cmd/sendmail/libsm/fwalk.c63
-rw-r--r--usr/src/cmd/sendmail/libsm/fwrite.c69
-rw-r--r--usr/src/cmd/sendmail/libsm/get.c48
-rw-r--r--usr/src/cmd/sendmail/libsm/glue.h29
-rw-r--r--usr/src/cmd/sendmail/libsm/heap.c822
-rw-r--r--usr/src/cmd/sendmail/libsm/ldap.c1334
-rw-r--r--usr/src/cmd/sendmail/libsm/local.h330
-rw-r--r--usr/src/cmd/sendmail/libsm/makebuf.c156
-rw-r--r--usr/src/cmd/sendmail/libsm/match.c139
-rw-r--r--usr/src/cmd/sendmail/libsm/mbdb.c776
-rw-r--r--usr/src/cmd/sendmail/libsm/niprop.c215
-rw-r--r--usr/src/cmd/sendmail/libsm/path.c17
-rw-r--r--usr/src/cmd/sendmail/libsm/put.c82
-rw-r--r--usr/src/cmd/sendmail/libsm/refill.c301
-rw-r--r--usr/src/cmd/sendmail/libsm/rewind.c46
-rw-r--r--usr/src/cmd/sendmail/libsm/rpool.c526
-rw-r--r--usr/src/cmd/sendmail/libsm/sem.c203
-rw-r--r--usr/src/cmd/sendmail/libsm/setvbuf.c192
-rw-r--r--usr/src/cmd/sendmail/libsm/shm.c143
-rw-r--r--usr/src/cmd/sendmail/libsm/signal.c342
-rw-r--r--usr/src/cmd/sendmail/libsm/sm_os.h8
-rw-r--r--usr/src/cmd/sendmail/libsm/smstdio.c365
-rw-r--r--usr/src/cmd/sendmail/libsm/snprintf.c88
-rw-r--r--usr/src/cmd/sendmail/libsm/sscanf.c104
-rw-r--r--usr/src/cmd/sendmail/libsm/stdio.c521
-rw-r--r--usr/src/cmd/sendmail/libsm/strcasecmp.c108
-rw-r--r--usr/src/cmd/sendmail/libsm/strdup.c168
-rw-r--r--usr/src/cmd/sendmail/libsm/strerror.c62
-rw-r--r--usr/src/cmd/sendmail/libsm/strexit.c129
-rw-r--r--usr/src/cmd/sendmail/libsm/string.c58
-rw-r--r--usr/src/cmd/sendmail/libsm/stringf.c88
-rw-r--r--usr/src/cmd/sendmail/libsm/strio.c492
-rw-r--r--usr/src/cmd/sendmail/libsm/strl.c326
-rw-r--r--usr/src/cmd/sendmail/libsm/strrevcmp.c103
-rw-r--r--usr/src/cmd/sendmail/libsm/strto.c256
-rw-r--r--usr/src/cmd/sendmail/libsm/t-event.c90
-rw-r--r--usr/src/cmd/sendmail/libsm/t-exc.c147
-rw-r--r--usr/src/cmd/sendmail/libsm/t-float.c74
-rw-r--r--usr/src/cmd/sendmail/libsm/t-fopen.c40
-rw-r--r--usr/src/cmd/sendmail/libsm/t-heap.c66
-rw-r--r--usr/src/cmd/sendmail/libsm/t-match.c49
-rw-r--r--usr/src/cmd/sendmail/libsm/t-path.c37
-rw-r--r--usr/src/cmd/sendmail/libsm/t-rpool.c71
-rw-r--r--usr/src/cmd/sendmail/libsm/t-scanf.c61
-rw-r--r--usr/src/cmd/sendmail/libsm/t-sem.c346
-rw-r--r--usr/src/cmd/sendmail/libsm/t-shm.c278
-rw-r--r--usr/src/cmd/sendmail/libsm/t-smstdio.c77
-rw-r--r--usr/src/cmd/sendmail/libsm/t-string.c48
-rw-r--r--usr/src/cmd/sendmail/libsm/t-strio.c35
-rw-r--r--usr/src/cmd/sendmail/libsm/t-strl.c138
-rw-r--r--usr/src/cmd/sendmail/libsm/t-strrevcmp.c55
-rw-r--r--usr/src/cmd/sendmail/libsm/test.c157
-rw-r--r--usr/src/cmd/sendmail/libsm/ungetc.c183
-rw-r--r--usr/src/cmd/sendmail/libsm/vasprintf.c106
-rw-r--r--usr/src/cmd/sendmail/libsm/vfprintf.c1110
-rw-r--r--usr/src/cmd/sendmail/libsm/vfscanf.c877
-rw-r--r--usr/src/cmd/sendmail/libsm/vprintf.c41
-rw-r--r--usr/src/cmd/sendmail/libsm/vsnprintf.c80
-rw-r--r--usr/src/cmd/sendmail/libsm/wbuf.c90
-rw-r--r--usr/src/cmd/sendmail/libsm/wsetup.c86
-rw-r--r--usr/src/cmd/sendmail/libsm/xtrap.c23
87 files changed, 18238 insertions, 0 deletions
diff --git a/usr/src/cmd/sendmail/libsm/Makefile b/usr/src/cmd/sendmail/libsm/Makefile
new file mode 100644
index 0000000000..5feb0f11c1
--- /dev/null
+++ b/usr/src/cmd/sendmail/libsm/Makefile
@@ -0,0 +1,83 @@
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License, Version 1.0 only
+# (the "License"). You may not use this file except in compliance
+# with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+#
+# Copyright 2005 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+#
+# ident "%Z%%M% %I% %E% SMI"
+#
+# cmd/sendmail/libsm/Makefile
+#
+
+include ../../Makefile.cmd
+include ../Makefile.cmd
+
+INCPATH= -I. -I../src -I../include
+CPPFLAGS= $(INCPATH) $(RLS_DEF) $(DBMDEF) $(CPPFLAGS.master)
+
+ARFLAGS= cq
+
+OBJS= assert.o cf.o clock.o clrerr.o config.o debug.o errstring.o exc.o \
+ fclose.o feof.o ferror.o fflush.o fget.o findfp.o flags.o fopen.o \
+ fpos.o fprintf.o fpurge.o fput.o fread.o fscanf.o fseek.o fvwrite.o \
+ fwalk.o fwrite.o get.o heap.o ldap.o makebuf.o match.o mbdb.o niprop.o \
+ path.o put.o refill.o rewind.o rpool.o setvbuf.o sem.o shm.o signal.o \
+ smstdio.o snprintf.o sscanf.o stdio.o strcasecmp.o strdup.o strerror.o \
+ strexit.o string.o stringf.o strio.o strl.o strrevcmp.o strto.o test.o \
+ ungetc.o vasprintf.o vfprintf.o vfscanf.o vprintf.o vsnprintf.o \
+ wbuf.o wsetup.o xtrap.o
+
+SRCS= $(OBJS:%.o=%.c)
+
+TESTS= t-event t-exc t-rpool t-string t-smstdio t-match t-strio t-heap \
+ t-fopen t-strl t-strrevcmp t-path t-float t-scanf t-sem t-shm
+
+libsm= libsm.a
+
+.KEEP_STATE:
+all: $(libsm)
+
+.PARALLEL: $(OBJS)
+
+$(libsm): $(OBJS)
+ $(RM) $@
+ $(AR) $(ARFLAGS) $@ $(OBJS)
+
+clean:
+ $(RM) $(OBJS) $(libsm) $(TESTS) foo t-smstdio.1
+
+depend obj:
+
+install: all
+
+LDLIBS += -lldap
+
+lint: lint_SRCS
+
+test: $(TESTS)
+
+t-%: t-%.c
+ $(LINK.c) $< -o $@ $(libsm) $(LDLIBS)
+ $(POST_PROCESS)
+ ./$@
+
+include ../../Makefile.targ
diff --git a/usr/src/cmd/sendmail/libsm/assert.c b/usr/src/cmd/sendmail/libsm/assert.c
new file mode 100644
index 0000000000..dfced1fcb7
--- /dev/null
+++ b/usr/src/cmd/sendmail/libsm/assert.c
@@ -0,0 +1,189 @@
+/*
+ * Copyright (c) 2000-2001 Sendmail, Inc. and its suppliers.
+ * All rights reserved.
+ *
+ * By using this file, you agree to the terms and conditions set
+ * forth in the LICENSE file which can be found at the top level of
+ * the sendmail distribution.
+ *
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#include <sm/gen.h>
+SM_RCSID("@(#)$Id: assert.c,v 1.25.2.1 2003/12/05 22:44:17 ca Exp $")
+
+/*
+** Abnormal program termination and assertion checking.
+** For documentation, see assert.html.
+*/
+
+#include <signal.h>
+#include <stdlib.h>
+#include <unistd.h>
+
+#include <sm/assert.h>
+#include <sm/exc.h>
+#include <sm/io.h>
+#include <sm/varargs.h>
+
+/*
+** Debug categories that are used to guard expensive assertion checks.
+*/
+
+SM_DEBUG_T SmExpensiveAssert = SM_DEBUG_INITIALIZER("sm_check_assert",
+ "@(#)$Debug: sm_check_assert - check assertions $");
+
+SM_DEBUG_T SmExpensiveRequire = SM_DEBUG_INITIALIZER("sm_check_require",
+ "@(#)$Debug: sm_check_require - check function preconditions $");
+
+SM_DEBUG_T SmExpensiveEnsure = SM_DEBUG_INITIALIZER("sm_check_ensure",
+ "@(#)$Debug: sm_check_ensure - check function postconditions $");
+
+/*
+** Debug category: send self SIGSTOP on fatal error,
+** so that you can run a debugger on the stopped process.
+*/
+
+SM_DEBUG_T SmAbortStop = SM_DEBUG_INITIALIZER("sm_abort_stop",
+ "@(#)$Debug: sm_abort_stop - stop process on fatal error $");
+
+/*
+** SM_ABORT_DEFAULTHANDLER -- Default procedure for abnormal program
+** termination.
+**
+** The goal is to display an error message without disturbing the
+** process state too much, then dump core.
+**
+** Parameters:
+** filename -- filename (can be NULL).
+** lineno -- line number.
+** msg -- message.
+**
+** Returns:
+** doesn't return.
+*/
+
+static void
+sm_abort_defaulthandler __P((
+ const char *filename,
+ int lineno,
+ const char *msg));
+
+static void
+sm_abort_defaulthandler(filename, lineno, msg)
+ const char *filename;
+ int lineno;
+ const char *msg;
+{
+ if (filename != NULL)
+ sm_io_fprintf(smioerr, SM_TIME_DEFAULT, "%s:%d: %s\n", filename,
+ lineno, msg);
+ else
+ sm_io_fprintf(smioerr, SM_TIME_DEFAULT, "%s\n", msg);
+ sm_io_flush(smioerr, SM_TIME_DEFAULT);
+#ifdef SIGSTOP
+ if (sm_debug_active(&SmAbortStop, 1))
+ kill(getpid(), SIGSTOP);
+#endif /* SIGSTOP */
+ abort();
+}
+
+/*
+** This is the action to be taken to cause abnormal program termination.
+*/
+
+static SM_ABORT_HANDLER_T SmAbortHandler = sm_abort_defaulthandler;
+
+/*
+** SM_ABORT_SETHANDLER -- Set handler for SM_ABORT()
+**
+** This allows you to set a handler function for causing abnormal
+** program termination; it is called when a logic bug is detected.
+**
+** Parameters:
+** f -- handler.
+**
+** Returns:
+** none.
+*/
+
+void
+sm_abort_sethandler(f)
+ SM_ABORT_HANDLER_T f;
+{
+ if (f == NULL)
+ SmAbortHandler = sm_abort_defaulthandler;
+ else
+ SmAbortHandler = f;
+}
+
+/*
+** SM_ABORT -- Call it when you have detected a logic bug.
+**
+** Parameters:
+** fmt -- format string.
+** ... -- arguments.
+**
+** Returns:
+** doesn't.
+*/
+
+void SM_DEAD_D
+#if SM_VA_STD
+sm_abort(char *fmt, ...)
+#else /* SM_VA_STD */
+sm_abort(fmt, va_alist)
+ char *fmt;
+ va_dcl
+#endif /* SM_VA_STD */
+{
+ char msg[128];
+ SM_VA_LOCAL_DECL
+
+ SM_VA_START(ap, fmt);
+ sm_vsnprintf(msg, sizeof msg, fmt, ap);
+ SM_VA_END(ap);
+ sm_abort_at(NULL, 0, msg);
+}
+
+/*
+** SM_ABORT_AT -- Initiate abnormal program termination.
+**
+** This is the low level function that is called to initiate abnormal
+** program termination. It prints an error message and terminates the
+** program. It is called by sm_abort and by the assertion macros.
+** If filename != NULL then filename and lineno specify the line of source
+** code at which the bug was detected.
+**
+** Parameters:
+** filename -- filename (can be NULL).
+** lineno -- line number.
+** msg -- message.
+**
+** Returns:
+** doesn't.
+*/
+
+void SM_DEAD_D
+sm_abort_at(filename, lineno, msg)
+ const char *filename;
+ int lineno;
+ const char *msg;
+{
+ SM_TRY
+ (*SmAbortHandler)(filename, lineno, msg);
+ SM_EXCEPT(exc, "*")
+ sm_io_fprintf(smioerr, SM_TIME_DEFAULT,
+ "exception raised by abort handler:\n");
+ sm_exc_print(exc, smioerr);
+ sm_io_flush(smioerr, SM_TIME_DEFAULT);
+ SM_END_TRY
+
+ /*
+ ** SmAbortHandler isn't supposed to return.
+ ** Since it has, let's make sure that the program is terminated.
+ */
+
+ abort();
+}
diff --git a/usr/src/cmd/sendmail/libsm/cf.c b/usr/src/cmd/sendmail/libsm/cf.c
new file mode 100644
index 0000000000..c1bf90d814
--- /dev/null
+++ b/usr/src/cmd/sendmail/libsm/cf.c
@@ -0,0 +1,102 @@
+/*
+ * Copyright (c) 2001 Sendmail, Inc. and its suppliers.
+ * All rights reserved.
+ *
+ * By using this file, you agree to the terms and conditions set
+ * forth in the LICENSE file which can be found at the top level of
+ * the sendmail distribution.
+ *
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#include <sm/gen.h>
+SM_RCSID("@(#)$Id: cf.c,v 1.4 2001/02/01 02:40:21 dmoen Exp $")
+
+#include <ctype.h>
+#include <errno.h>
+
+#include <sm/cf.h>
+#include <sm/io.h>
+#include <sm/string.h>
+#include <sm/heap.h>
+
+/*
+** SM_CF_GETOPT -- look up option values in the sendmail.cf file
+**
+** Open the sendmail.cf file and parse all of the 'O' directives.
+** Each time one of the options named in the option vector optv
+** is found, store a malloced copy of the option value in optv.
+**
+** Parameters:
+** path -- pathname of sendmail.cf file
+** optc -- size of option vector
+** optv -- pointer to option vector
+**
+** Results:
+** 0 on success, or an errno value on failure.
+** An exception is raised on malloc failure.
+*/
+
+int
+sm_cf_getopt(path, optc, optv)
+ char *path;
+ int optc;
+ SM_CF_OPT_T *optv;
+{
+ SM_FILE_T *cfp;
+ char buf[2048];
+ char *p;
+ char *id;
+ char *idend;
+ char *val;
+ int i;
+
+ cfp = sm_io_open(SmFtStdio, SM_TIME_DEFAULT, path, SM_IO_RDONLY, NULL);
+ if (cfp == NULL)
+ return errno;
+
+ while (sm_io_fgets(cfp, SM_TIME_DEFAULT, buf, sizeof(buf)) != NULL)
+ {
+ p = strchr(buf, '\n');
+ if (p != NULL)
+ *p = '\0';
+
+ if (buf[0] != 'O' || buf[1] != ' ')
+ continue;
+
+ id = &buf[2];
+ val = strchr(id, '=');
+ if (val == NULL)
+ val = idend = id + strlen(id);
+ else
+ {
+ idend = val;
+ ++val;
+ while (*val == ' ')
+ ++val;
+ while (idend > id && idend[-1] == ' ')
+ --idend;
+ *idend = '\0';
+ }
+
+ for (i = 0; i < optc; ++i)
+ {
+ if (sm_strcasecmp(optv[i].opt_name, id) == 0)
+ {
+ optv[i].opt_val = sm_strdup_x(val);
+ break;
+ }
+ }
+ }
+ if (sm_io_error(cfp))
+ {
+ int save_errno = errno;
+
+ (void) sm_io_close(cfp, SM_TIME_DEFAULT);
+ errno = save_errno;
+ return errno;
+ }
+ (void) sm_io_close(cfp, SM_TIME_DEFAULT);
+ return 0;
+}
diff --git a/usr/src/cmd/sendmail/libsm/clock.c b/usr/src/cmd/sendmail/libsm/clock.c
new file mode 100644
index 0000000000..0eeb94a196
--- /dev/null
+++ b/usr/src/cmd/sendmail/libsm/clock.c
@@ -0,0 +1,642 @@
+/*
+ * Copyright (c) 1998-2004 Sendmail, Inc. and its suppliers.
+ * All rights reserved.
+ * Copyright (c) 1983, 1995-1997 Eric P. Allman. All rights reserved.
+ * Copyright (c) 1988, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * By using this file, you agree to the terms and conditions set
+ * forth in the LICENSE file which can be found at the top level of
+ * the sendmail distribution.
+ *
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#include <sm/gen.h>
+SM_RCSID("@(#)$Id: clock.c,v 1.46 2004/08/03 19:57:22 ca Exp $")
+#include <unistd.h>
+#include <time.h>
+#include <errno.h>
+#if SM_CONF_SETITIMER
+# include <sys/time.h>
+#endif /* SM_CONF_SETITIMER */
+#include <sm/heap.h>
+#include <sm/debug.h>
+#include <sm/bitops.h>
+#include <sm/clock.h>
+#include "local.h"
+#if _FFR_SLEEP_USE_SELECT > 0
+# include <sys/types.h>
+#endif /* _FFR_SLEEP_USE_SELECT > 0 */
+#if defined(_FFR_MAX_SLEEP_TIME) && _FFR_MAX_SLEEP_TIME > 2
+# include <syslog.h>
+#endif /* defined(_FFR_MAX_SLEEP_TIME) && _FFR_MAX_SLEEP_TIME > 2 */
+
+#ifndef sigmask
+# define sigmask(s) (1 << ((s) - 1))
+#endif /* ! sigmask */
+
+
+/*
+** SM_SETEVENTM -- set an event to happen at a specific time in milliseconds.
+**
+** Events are stored in a sorted list for fast processing.
+** An event only applies to the process that set it.
+** Source is #ifdef'd to work with older OS's that don't have setitimer()
+** (that is, don't have a timer granularity less than 1 second).
+**
+** Parameters:
+** intvl -- interval until next event occurs (milliseconds).
+** func -- function to call on event.
+** arg -- argument to func on event.
+**
+** Returns:
+** On success returns the SM_EVENT entry created.
+** On failure returns NULL.
+**
+** Side Effects:
+** none.
+*/
+
+static SM_EVENT *volatile SmEventQueue; /* head of event queue */
+static SM_EVENT *volatile SmFreeEventList; /* list of free events */
+
+SM_EVENT *
+sm_seteventm(intvl, func, arg)
+ int intvl;
+ void (*func)__P((int));
+ int arg;
+{
+ ENTER_CRITICAL();
+ if (SmFreeEventList == NULL)
+ {
+ SmFreeEventList = (SM_EVENT *) sm_pmalloc_x(sizeof *SmFreeEventList);
+ SmFreeEventList->ev_link = NULL;
+ }
+ LEAVE_CRITICAL();
+
+ return sm_sigsafe_seteventm(intvl, func, arg);
+}
+
+/*
+** NOTE: THIS CAN BE CALLED FROM A SIGNAL HANDLER. DO NOT ADD
+** ANYTHING TO THIS ROUTINE UNLESS YOU KNOW WHAT YOU ARE
+** DOING.
+*/
+
+SM_EVENT *
+sm_sigsafe_seteventm(intvl, func, arg)
+ int intvl;
+ void (*func)__P((int));
+ int arg;
+{
+ register SM_EVENT **evp;
+ register SM_EVENT *ev;
+#if SM_CONF_SETITIMER
+ auto struct timeval now, nowi, ival;
+ auto struct itimerval itime;
+#else /* SM_CONF_SETITIMER */
+ auto time_t now, nowi;
+#endif /* SM_CONF_SETITIMER */
+ int wasblocked;
+
+ /* negative times are not allowed */
+ if (intvl <= 0)
+ return NULL;
+
+ wasblocked = sm_blocksignal(SIGALRM);
+#if SM_CONF_SETITIMER
+ ival.tv_sec = intvl / 1000;
+ ival.tv_usec = (intvl - ival.tv_sec * 1000) * 10;
+ (void) gettimeofday(&now, NULL);
+ nowi = now;
+ timeradd(&now, &ival, &nowi);
+#else /* SM_CONF_SETITIMER */
+ now = time(NULL);
+ nowi = now + (time_t)(intvl / 1000);
+#endif /* SM_CONF_SETITIMER */
+
+ /* search event queue for correct position */
+ for (evp = (SM_EVENT **) (&SmEventQueue);
+ (ev = *evp) != NULL;
+ evp = &ev->ev_link)
+ {
+#if SM_CONF_SETITIMER
+ if (timercmp(&(ev->ev_time), &nowi, >=))
+#else /* SM_CONF_SETITIMER */
+ if (ev->ev_time >= nowi)
+#endif /* SM_CONF_SETITIMER */
+ break;
+ }
+
+ ENTER_CRITICAL();
+ if (SmFreeEventList == NULL)
+ {
+ /*
+ ** This shouldn't happen. If called from sm_seteventm(),
+ ** we have just malloced a SmFreeEventList entry. If
+ ** called from a signal handler, it should have been
+ ** from an existing event which sm_tick() just added to
+ ** SmFreeEventList.
+ */
+
+ LEAVE_CRITICAL();
+ if (wasblocked == 0)
+ (void) sm_releasesignal(SIGALRM);
+ return NULL;
+ }
+ else
+ {
+ ev = SmFreeEventList;
+ SmFreeEventList = ev->ev_link;
+ }
+ LEAVE_CRITICAL();
+
+ /* insert new event */
+ ev->ev_time = nowi;
+ ev->ev_func = func;
+ ev->ev_arg = arg;
+ ev->ev_pid = getpid();
+ ENTER_CRITICAL();
+ ev->ev_link = *evp;
+ *evp = ev;
+ LEAVE_CRITICAL();
+
+ (void) sm_signal(SIGALRM, sm_tick);
+# if SM_CONF_SETITIMER
+ timersub(&SmEventQueue->ev_time, &now, &itime.it_value);
+ itime.it_interval.tv_sec = 0;
+ itime.it_interval.tv_usec = 0;
+ if (itime.it_value.tv_sec < 0)
+ itime.it_value.tv_sec = 0;
+ if (itime.it_value.tv_sec == 0 && itime.it_value.tv_usec == 0)
+ itime.it_value.tv_usec = 1000;
+ (void) setitimer(ITIMER_REAL, &itime, NULL);
+# else /* SM_CONF_SETITIMER */
+ intvl = SmEventQueue->ev_time - now;
+ (void) alarm((unsigned) (intvl < 1 ? 1 : intvl));
+# endif /* SM_CONF_SETITIMER */
+ if (wasblocked == 0)
+ (void) sm_releasesignal(SIGALRM);
+ return ev;
+}
+/*
+** SM_CLREVENT -- remove an event from the event queue.
+**
+** Parameters:
+** ev -- pointer to event to remove.
+**
+** Returns:
+** none.
+**
+** Side Effects:
+** arranges for event ev to not happen.
+*/
+
+void
+sm_clrevent(ev)
+ register SM_EVENT *ev;
+{
+ register SM_EVENT **evp;
+ int wasblocked;
+# if SM_CONF_SETITIMER
+ struct itimerval clr;
+# endif /* SM_CONF_SETITIMER */
+
+ if (ev == NULL)
+ return;
+
+ /* find the parent event */
+ wasblocked = sm_blocksignal(SIGALRM);
+ for (evp = (SM_EVENT **) (&SmEventQueue);
+ *evp != NULL;
+ evp = &(*evp)->ev_link)
+ {
+ if (*evp == ev)
+ break;
+ }
+
+ /* now remove it */
+ if (*evp != NULL)
+ {
+ ENTER_CRITICAL();
+ *evp = ev->ev_link;
+ ev->ev_link = SmFreeEventList;
+ SmFreeEventList = ev;
+ LEAVE_CRITICAL();
+ }
+
+ /* restore clocks and pick up anything spare */
+ if (wasblocked == 0)
+ (void) sm_releasesignal(SIGALRM);
+ if (SmEventQueue != NULL)
+ (void) kill(getpid(), SIGALRM);
+ else
+ {
+ /* nothing left in event queue, no need for an alarm */
+# if SM_CONF_SETITIMER
+ clr.it_interval.tv_sec = 0;
+ clr.it_interval.tv_usec = 0;
+ clr.it_value.tv_sec = 0;
+ clr.it_value.tv_usec = 0;
+ (void) setitimer(ITIMER_REAL, &clr, NULL);
+# else /* SM_CONF_SETITIMER */
+ (void) alarm(0);
+# endif /* SM_CONF_SETITIMER */
+ }
+}
+/*
+** SM_CLEAR_EVENTS -- remove all events from the event queue.
+**
+** Parameters:
+** none.
+**
+** Returns:
+** none.
+*/
+
+void
+sm_clear_events()
+{
+ register SM_EVENT *ev;
+#if SM_CONF_SETITIMER
+ struct itimerval clr;
+#endif /* SM_CONF_SETITIMER */
+ int wasblocked;
+
+ /* nothing will be left in event queue, no need for an alarm */
+#if SM_CONF_SETITIMER
+ clr.it_interval.tv_sec = 0;
+ clr.it_interval.tv_usec = 0;
+ clr.it_value.tv_sec = 0;
+ clr.it_value.tv_usec = 0;
+ (void) setitimer(ITIMER_REAL, &clr, NULL);
+#else /* SM_CONF_SETITIMER */
+ (void) alarm(0);
+#endif /* SM_CONF_SETITIMER */
+
+ if (SmEventQueue == NULL)
+ return;
+
+ wasblocked = sm_blocksignal(SIGALRM);
+
+ /* find the end of the EventQueue */
+ for (ev = SmEventQueue; ev->ev_link != NULL; ev = ev->ev_link)
+ continue;
+
+ ENTER_CRITICAL();
+ ev->ev_link = SmFreeEventList;
+ SmFreeEventList = SmEventQueue;
+ SmEventQueue = NULL;
+ LEAVE_CRITICAL();
+
+ /* restore clocks and pick up anything spare */
+ if (wasblocked == 0)
+ (void) sm_releasesignal(SIGALRM);
+}
+/*
+** SM_TICK -- take a clock tick
+**
+** Called by the alarm clock. This routine runs events as needed.
+** Always called as a signal handler, so we assume that SIGALRM
+** has been blocked.
+**
+** Parameters:
+** One that is ignored; for compatibility with signal handlers.
+**
+** Returns:
+** none.
+**
+** Side Effects:
+** calls the next function in EventQueue.
+**
+** NOTE: THIS CAN BE CALLED FROM A SIGNAL HANDLER. DO NOT ADD
+** ANYTHING TO THIS ROUTINE UNLESS YOU KNOW WHAT YOU ARE
+** DOING.
+*/
+
+/* ARGSUSED */
+SIGFUNC_DECL
+sm_tick(sig)
+ int sig;
+{
+ register SM_EVENT *ev;
+ pid_t mypid;
+ int save_errno = errno;
+#if SM_CONF_SETITIMER
+ struct itimerval clr;
+ struct timeval now;
+#else /* SM_CONF_SETITIMER */
+ register time_t now;
+#endif /* SM_CONF_SETITIMER */
+
+#if SM_CONF_SETITIMER
+ clr.it_interval.tv_sec = 0;
+ clr.it_interval.tv_usec = 0;
+ clr.it_value.tv_sec = 0;
+ clr.it_value.tv_usec = 0;
+ (void) setitimer(ITIMER_REAL, &clr, NULL);
+ gettimeofday(&now, NULL);
+#else /* SM_CONF_SETITIMER */
+ (void) alarm(0);
+ now = time(NULL);
+#endif /* SM_CONF_SETITIMER */
+
+ FIX_SYSV_SIGNAL(sig, sm_tick);
+ errno = save_errno;
+ CHECK_CRITICAL(sig);
+
+ mypid = getpid();
+ while (PendingSignal != 0)
+ {
+ int sigbit = 0;
+ int sig = 0;
+
+ if (bitset(PEND_SIGHUP, PendingSignal))
+ {
+ sigbit = PEND_SIGHUP;
+ sig = SIGHUP;
+ }
+ else if (bitset(PEND_SIGINT, PendingSignal))
+ {
+ sigbit = PEND_SIGINT;
+ sig = SIGINT;
+ }
+ else if (bitset(PEND_SIGTERM, PendingSignal))
+ {
+ sigbit = PEND_SIGTERM;
+ sig = SIGTERM;
+ }
+ else if (bitset(PEND_SIGUSR1, PendingSignal))
+ {
+ sigbit = PEND_SIGUSR1;
+ sig = SIGUSR1;
+ }
+ else
+ {
+ /* If we get here, we are in trouble */
+ abort();
+ }
+ PendingSignal &= ~sigbit;
+ kill(mypid, sig);
+ }
+
+#if SM_CONF_SETITIMER
+ gettimeofday(&now, NULL);
+#else /* SM_CONF_SETITIMER */
+ now = time(NULL);
+#endif /* SM_CONF_SETITIMER */
+ while ((ev = SmEventQueue) != NULL &&
+ (ev->ev_pid != mypid ||
+#if SM_CONF_SETITIMER
+ timercmp(&ev->ev_time, &now, <=)
+#else /* SM_CONF_SETITIMER */
+ ev->ev_time <= now
+#endif /* SM_CONF_SETITIMER */
+ ))
+ {
+ void (*f)__P((int));
+ int arg;
+ pid_t pid;
+
+ /* process the event on the top of the queue */
+ ev = SmEventQueue;
+ SmEventQueue = SmEventQueue->ev_link;
+
+ /* we must be careful in here because ev_func may not return */
+ f = ev->ev_func;
+ arg = ev->ev_arg;
+ pid = ev->ev_pid;
+ ENTER_CRITICAL();
+ ev->ev_link = SmFreeEventList;
+ SmFreeEventList = ev;
+ LEAVE_CRITICAL();
+ if (pid != getpid())
+ continue;
+ if (SmEventQueue != NULL)
+ {
+#if SM_CONF_SETITIMER
+ if (timercmp(&SmEventQueue->ev_time, &now, >))
+ {
+ timersub(&SmEventQueue->ev_time, &now,
+ &clr.it_value);
+ clr.it_interval.tv_sec = 0;
+ clr.it_interval.tv_usec = 0;
+ if (clr.it_value.tv_sec < 0)
+ clr.it_value.tv_sec = 0;
+ if (clr.it_value.tv_sec == 0 &&
+ clr.it_value.tv_usec == 0)
+ clr.it_value.tv_usec = 1000;
+ (void) setitimer(ITIMER_REAL, &clr, NULL);
+ }
+ else
+ {
+ clr.it_interval.tv_sec = 0;
+ clr.it_interval.tv_usec = 0;
+ clr.it_value.tv_sec = 3;
+ clr.it_value.tv_usec = 0;
+ (void) setitimer(ITIMER_REAL, &clr, NULL);
+ }
+#else /* SM_CONF_SETITIMER */
+ if (SmEventQueue->ev_time > now)
+ (void) alarm((unsigned) (SmEventQueue->ev_time
+ - now));
+ else
+ (void) alarm(3);
+#endif /* SM_CONF_SETITIMER */
+ }
+
+ /* call ev_func */
+ errno = save_errno;
+ (*f)(arg);
+#if SM_CONF_SETITIMER
+ clr.it_interval.tv_sec = 0;
+ clr.it_interval.tv_usec = 0;
+ clr.it_value.tv_sec = 0;
+ clr.it_value.tv_usec = 0;
+ (void) setitimer(ITIMER_REAL, &clr, NULL);
+ gettimeofday(&now, NULL);
+#else /* SM_CONF_SETITIMER */
+ (void) alarm(0);
+ now = time(NULL);
+#endif /* SM_CONF_SETITIMER */
+ }
+ if (SmEventQueue != NULL)
+ {
+#if SM_CONF_SETITIMER
+ timersub(&SmEventQueue->ev_time, &now, &clr.it_value);
+ clr.it_interval.tv_sec = 0;
+ clr.it_interval.tv_usec = 0;
+ if (clr.it_value.tv_sec < 0)
+ clr.it_value.tv_sec = 0;
+ if (clr.it_value.tv_sec == 0 && clr.it_value.tv_usec == 0)
+ clr.it_value.tv_usec = 1000;
+ (void) setitimer(ITIMER_REAL, &clr, NULL);
+#else /* SM_CONF_SETITIMER */
+ (void) alarm((unsigned) (SmEventQueue->ev_time - now));
+#endif /* SM_CONF_SETITIMER */
+ }
+ errno = save_errno;
+ return SIGFUNC_RETURN;
+}
+/*
+** SLEEP -- a version of sleep that works with this stuff
+**
+** Because Unix sleep uses the alarm facility, I must reimplement
+** it here.
+**
+** Parameters:
+** intvl -- time to sleep.
+**
+** Returns:
+** zero.
+**
+** Side Effects:
+** waits for intvl time. However, other events can
+** be run during that interval.
+*/
+
+
+# if !HAVE_NANOSLEEP
+static void sm_endsleep __P((int));
+static bool volatile SmSleepDone;
+# endif /* !HAVE_NANOSLEEP */
+
+#ifndef SLEEP_T
+# define SLEEP_T unsigned int
+#endif /* ! SLEEP_T */
+
+SLEEP_T
+sleep(intvl)
+ unsigned int intvl;
+{
+#if HAVE_NANOSLEEP
+ struct timespec rqtp;
+
+ if (intvl == 0)
+ return (SLEEP_T) 0;
+ rqtp.tv_sec = intvl;
+ rqtp.tv_nsec = 0;
+ nanosleep(&rqtp, NULL);
+ return (SLEEP_T) 0;
+#else /* HAVE_NANOSLEEP */
+ int was_held;
+ SM_EVENT *ev;
+#if _FFR_SLEEP_USE_SELECT > 0
+ int r;
+# if _FFR_SLEEP_USE_SELECT > 0
+ struct timeval sm_io_to;
+# endif /* _FFR_SLEEP_USE_SELECT > 0 */
+#endif /* _FFR_SLEEP_USE_SELECT > 0 */
+#if SM_CONF_SETITIMER
+ struct timeval now, begin, diff;
+# if _FFR_SLEEP_USE_SELECT > 0
+ struct timeval slpv;
+# endif /* _FFR_SLEEP_USE_SELECT > 0 */
+#else /* SM_CONF_SETITIMER */
+ time_t begin, now;
+#endif /* SM_CONF_SETITIMER */
+
+ if (intvl == 0)
+ return (SLEEP_T) 0;
+#if defined(_FFR_MAX_SLEEP_TIME) && _FFR_MAX_SLEEP_TIME > 2
+ if (intvl > _FFR_MAX_SLEEP_TIME)
+ {
+ syslog(LOG_ERR, "sleep: interval=%u exceeds max value %d",
+ intvl, _FFR_MAX_SLEEP_TIME);
+# if 0
+ SM_ASSERT(intvl < (unsigned int) INT_MAX);
+# endif /* 0 */
+ intvl = _FFR_MAX_SLEEP_TIME;
+ }
+#endif /* defined(_FFR_MAX_SLEEP_TIME) && _FFR_MAX_SLEEP_TIME > 2 */
+ SmSleepDone = false;
+
+#if SM_CONF_SETITIMER
+# if _FFR_SLEEP_USE_SELECT > 0
+ slpv.tv_sec = intvl;
+ slpv.tv_usec = 0;
+# endif /* _FFR_SLEEP_USE_SELECT > 0 */
+ (void) gettimeofday(&now, NULL);
+ begin = now;
+#else /* SM_CONF_SETITIMER */
+ now = begin = time(NULL);
+#endif /* SM_CONF_SETITIMER */
+
+ ev = sm_setevent((time_t) intvl, sm_endsleep, 0);
+ if (ev == NULL)
+ {
+ /* COMPLAIN */
+#if 0
+ syslog(LOG_ERR, "sleep: sm_setevent(%u) failed", intvl);
+#endif /* 0 */
+ SmSleepDone = true;
+ }
+ was_held = sm_releasesignal(SIGALRM);
+
+ while (!SmSleepDone)
+ {
+#if SM_CONF_SETITIMER
+ (void) gettimeofday(&now, NULL);
+ timersub(&now, &begin, &diff);
+ if (diff.tv_sec < 0 ||
+ (diff.tv_sec == 0 && diff.tv_usec == 0))
+ break;
+# if _FFR_SLEEP_USE_SELECT > 0
+ timersub(&slpv, &diff, &sm_io_to);
+# endif /* _FFR_SLEEP_USE_SELECT > 0 */
+#else /* SM_CONF_SETITIMER */
+ now = time(NULL);
+
+ /*
+ ** Check whether time expired before signal is released.
+ ** Due to the granularity of time() add 1 to be on the
+ ** safe side.
+ */
+
+ if (!(begin + (time_t) intvl + 1 > now))
+ break;
+# if _FFR_SLEEP_USE_SELECT > 0
+ sm_io_to.tv_sec = intvl - (now - begin);
+ if (sm_io_to.tv_sec <= 0)
+ sm_io_to.tv_sec = 1;
+ sm_io_to.tv_usec = 0;
+# endif /* _FFR_SLEEP_USE_SELECT > 0 */
+#endif /* SM_CONF_SETITIMER */
+#if _FFR_SLEEP_USE_SELECT > 0
+ if (intvl <= _FFR_SLEEP_USE_SELECT)
+ {
+ r = select(0, NULL, NULL, NULL, &sm_io_to);
+ if (r == 0)
+ break;
+ }
+ else
+#endif /* _FFR_SLEEP_USE_SELECT > 0 */
+ (void) pause();
+ }
+
+ /* if out of the loop without the event being triggered remove it */
+ if (!SmSleepDone)
+ sm_clrevent(ev);
+ if (was_held > 0)
+ (void) sm_blocksignal(SIGALRM);
+ return (SLEEP_T) 0;
+#endif /* HAVE_NANOSLEEP */
+}
+
+#if !HAVE_NANOSLEEP
+static void
+sm_endsleep(ignore)
+ int ignore;
+{
+ /*
+ ** NOTE: THIS CAN BE CALLED FROM A SIGNAL HANDLER. DO NOT ADD
+ ** ANYTHING TO THIS ROUTINE UNLESS YOU KNOW WHAT YOU ARE
+ ** DOING.
+ */
+
+ SmSleepDone = true;
+}
+#endif /* !HAVE_NANOSLEEP */
+
diff --git a/usr/src/cmd/sendmail/libsm/clrerr.c b/usr/src/cmd/sendmail/libsm/clrerr.c
new file mode 100644
index 0000000000..185cd415ed
--- /dev/null
+++ b/usr/src/cmd/sendmail/libsm/clrerr.c
@@ -0,0 +1,41 @@
+/*
+ * Copyright (c) 2000-2001 Sendmail, Inc. and its suppliers.
+ * All rights reserved.
+ * Copyright (c) 1990, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Chris Torek.
+ *
+ * By using this file, you agree to the terms and conditions set
+ * forth in the LICENSE file which can be found at the top level of
+ * the sendmail distribution.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#include <sm/gen.h>
+SM_RCSID("@(#)$Id: clrerr.c,v 1.11 2001/01/28 00:29:34 ca Exp $")
+#include <sm/io.h>
+#include <sm/assert.h>
+#include "local.h"
+
+/*
+** SM_IO_CLEARERR -- public function to clear a file pointer's error status
+**
+** Parameters:
+** fp -- the file pointer
+**
+** Returns:
+** nothing.
+*/
+#undef sm_io_clearerr
+
+void
+sm_io_clearerr(fp)
+ SM_FILE_T *fp;
+{
+ SM_REQUIRE_ISA(fp, SmFileMagic);
+
+ sm_clearerr(fp);
+}
diff --git a/usr/src/cmd/sendmail/libsm/config.c b/usr/src/cmd/sendmail/libsm/config.c
new file mode 100644
index 0000000000..b6ce966b3b
--- /dev/null
+++ b/usr/src/cmd/sendmail/libsm/config.c
@@ -0,0 +1,253 @@
+/*
+ * Copyright (c) 2000-2003 Sendmail, Inc. and its suppliers.
+ * All rights reserved.
+ *
+ * By using this file, you agree to the terms and conditions set
+ * forth in the LICENSE file which can be found at the top level of
+ * the sendmail distribution.
+ *
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#include <sm/gen.h>
+SM_RCSID("@(#)$Id: config.c,v 1.30 2003/12/10 03:19:07 gshapiro Exp $")
+
+#include <stdlib.h>
+#include <sm/heap.h>
+#include <sm/string.h>
+#include <sm/conf.h>
+
+/*
+** PUTENV -- emulation of putenv() in terms of setenv()
+**
+** Not needed on Posix-compliant systems.
+** This doesn't have full Posix semantics, but it's good enough
+** for sendmail.
+**
+** Parameter:
+** env -- the environment to put.
+**
+** Returns:
+** 0 on success, < 0 on failure.
+*/
+
+#if NEEDPUTENV
+
+# if NEEDPUTENV == 2 /* no setenv(3) call available */
+
+int
+putenv(str)
+ char *str;
+{
+ char **current;
+ int matchlen, envlen = 0;
+ char *tmp;
+ char **newenv;
+ static bool first = true;
+ extern char **environ;
+
+ /*
+ ** find out how much of str to match when searching
+ ** for a string to replace.
+ */
+
+ if ((tmp = strchr(str, '=')) == NULL || tmp == str)
+ matchlen = strlen(str);
+ else
+ matchlen = (int) (tmp - str);
+ ++matchlen;
+
+ /*
+ ** Search for an existing string in the environment and find the
+ ** length of environ. If found, replace and exit.
+ */
+
+ for (current = environ; *current != NULL; current++)
+ {
+ ++envlen;
+
+ if (strncmp(str, *current, matchlen) == 0)
+ {
+ /* found it, now insert the new version */
+ *current = (char *) str;
+ return 0;
+ }
+ }
+
+ /*
+ ** There wasn't already a slot so add space for a new slot.
+ ** If this is our first time through, use malloc(), else realloc().
+ */
+
+ if (first)
+ {
+ newenv = (char **) sm_malloc(sizeof(char *) * (envlen + 2));
+ if (newenv == NULL)
+ return -1;
+
+ first = false;
+ (void) memcpy(newenv, environ, sizeof(char *) * envlen);
+ }
+ else
+ {
+ newenv = (char **) sm_realloc((char *) environ,
+ sizeof(char *) * (envlen + 2));
+ if (newenv == NULL)
+ return -1;
+ }
+
+ /* actually add in the new entry */
+ environ = newenv;
+ environ[envlen] = (char *) str;
+ environ[envlen + 1] = NULL;
+
+ return 0;
+}
+
+# else /* NEEDPUTENV == 2 */
+
+int
+putenv(env)
+ char *env;
+{
+ char *p;
+ int l;
+ char nbuf[100];
+
+ p = strchr(env, '=');
+ if (p == NULL)
+ return 0;
+ l = p - env;
+ if (l > sizeof nbuf - 1)
+ l = sizeof nbuf - 1;
+ memmove(nbuf, env, l);
+ nbuf[l] = '\0';
+ return setenv(nbuf, ++p, 1);
+}
+
+# endif /* NEEDPUTENV == 2 */
+#endif /* NEEDPUTENV */
+/*
+** UNSETENV -- remove a variable from the environment
+**
+** Not needed on newer systems.
+**
+** Parameters:
+** name -- the string name of the environment variable to be
+** deleted from the current environment.
+**
+** Returns:
+** none.
+**
+** Globals:
+** environ -- a pointer to the current environment.
+**
+** Side Effects:
+** Modifies environ.
+*/
+
+#if !HASUNSETENV
+
+void
+unsetenv(name)
+ char *name;
+{
+ extern char **environ;
+ register char **pp;
+ int len = strlen(name);
+
+ for (pp = environ; *pp != NULL; pp++)
+ {
+ if (strncmp(name, *pp, len) == 0 &&
+ ((*pp)[len] == '=' || (*pp)[len] == '\0'))
+ break;
+ }
+
+ for (; *pp != NULL; pp++)
+ *pp = pp[1];
+}
+
+#endif /* !HASUNSETENV */
+
+char *SmCompileOptions[] =
+{
+#if SM_CONF_BROKEN_STRTOD
+ "SM_CONF_BROKEN_STRTOD",
+#endif /* SM_CONF_BROKEN_STRTOD */
+#if SM_CONF_GETOPT
+ "SM_CONF_GETOPT",
+#endif /* SM_CONF_GETOPT */
+#if SM_CONF_LDAP_INITIALIZE
+ "SM_CONF_LDAP_INITIALIZE",
+#endif /* SM_CONF_LDAP_INITIALIZE */
+#if SM_CONF_LDAP_MEMFREE
+ "SM_CONF_LDAP_MEMFREE",
+#endif /* SM_CONF_LDAP_MEMFREE */
+#if SM_CONF_LONGLONG
+ "SM_CONF_LONGLONG",
+#endif /* SM_CONF_LONGLONG */
+#if SM_CONF_MEMCHR
+ "SM_CONF_MEMCHR",
+#endif /* SM_CONF_MEMCHR */
+#if SM_CONF_MSG
+ "SM_CONF_MSG",
+#endif /* SM_CONF_MSG */
+#if SM_CONF_QUAD_T
+ "SM_CONF_QUAD_T",
+#endif /* SM_CONF_QUAD_T */
+#if SM_CONF_SEM
+ "SM_CONF_SEM",
+#endif /* SM_CONF_SEM */
+#if SM_CONF_SETITIMER
+ "SM_CONF_SETITIMER",
+#endif /* SM_CONF_SETITIMER */
+#if SM_CONF_SIGSETJMP
+ "SM_CONF_SIGSETJMP",
+#endif /* SM_CONF_SIGSETJMP */
+#if SM_CONF_SHM
+ "SM_CONF_SHM",
+#endif /* SM_CONF_SHM */
+#if SM_CONF_SHM_DELAY
+ "SM_CONF_SHM_DELAY",
+#endif /* SM_CONF_SHM_DELAY */
+#if SM_CONF_SSIZE_T
+ "SM_CONF_SSIZE_T",
+#endif /* SM_CONF_SSIZE_T */
+#if SM_CONF_STDBOOL_H
+ "SM_CONF_STDBOOL_H",
+#endif /* SM_CONF_STDBOOL_H */
+#if SM_CONF_STDDEF_H
+ "SM_CONF_STDDEF_H",
+#endif /* SM_CONF_STDDEF_H */
+
+#if 0
+/* XXX this is always enabled (for now) */
+#if SM_CONF_STRL
+ "SM_CONF_STRL",
+#endif /* SM_CONF_STRL */
+#endif /* 0 */
+
+#if SM_CONF_SYS_CDEFS_H
+ "SM_CONF_SYS_CDEFS_H",
+#endif /* SM_CONF_SYS_CDEFS_H */
+#if SM_CONF_SYSEXITS_H
+ "SM_CONF_SYSEXITS_H",
+#endif /* SM_CONF_SYSEXITS_H */
+#if SM_CONF_UID_GID
+ "SM_CONF_UID_GID",
+#endif /* SM_CONF_UID_GID */
+#if DO_NOT_USE_STRCPY
+ "DO_NOT_USE_STRCPY",
+#endif /* DO_NOT_USE_STRCPY */
+#if SM_HEAP_CHECK
+ "SM_HEAP_CHECK",
+#endif /* SM_HEAP_CHECK */
+#if defined(SM_OS_NAME) && defined(__STDC__)
+ "SM_OS=sm_os_" SM_OS_NAME,
+#endif /* defined(SM_OS_NAME) && defined(__STDC__) */
+#if SM_VA_STD
+ "SM_VA_STD",
+#endif /* SM_VA_STD */
+ NULL
+};
diff --git a/usr/src/cmd/sendmail/libsm/debug.c b/usr/src/cmd/sendmail/libsm/debug.c
new file mode 100644
index 0000000000..1bc7e3ad2d
--- /dev/null
+++ b/usr/src/cmd/sendmail/libsm/debug.c
@@ -0,0 +1,396 @@
+/*
+ * Copyright (c) 2000, 2001, 2003, 2004 Sendmail, Inc. and its suppliers.
+ * All rights reserved.
+ *
+ * By using this file, you agree to the terms and conditions set
+ * forth in the LICENSE file which can be found at the top level of
+ * the sendmail distribution.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#include <sm/gen.h>
+SM_RCSID("@(#)$Id: debug.c,v 1.30 2004/08/03 20:10:26 ca Exp $")
+
+/*
+** libsm debugging and tracing
+** For documentation, see debug.html.
+*/
+
+#include <ctype.h>
+#include <stdlib.h>
+#include <setjmp.h>
+#include <sm/io.h>
+#include <sm/assert.h>
+#include <sm/conf.h>
+#include <sm/debug.h>
+#include <sm/string.h>
+#include <sm/varargs.h>
+#include <sm/heap.h>
+
+static void sm_debug_reset __P((void));
+static const char *parse_named_setting_x __P((const char *));
+
+/*
+** Abstractions for printing trace messages.
+*/
+
+/*
+** The output file to which trace output is directed.
+** There is a controversy over whether this variable
+** should be process global or thread local.
+** To make the interface more abstract, we've hidden the
+** variable behind access functions.
+*/
+
+static SM_FILE_T *SmDebugOutput = smioout;
+
+/*
+** SM_DEBUG_FILE -- Returns current debug file pointer.
+**
+** Parameters:
+** none.
+**
+** Returns:
+** current debug file pointer.
+*/
+
+SM_FILE_T *
+sm_debug_file()
+{
+ return SmDebugOutput;
+}
+
+/*
+** SM_DEBUG_SETFILE -- Sets debug file pointer.
+**
+** Parameters:
+** fp -- new debug file pointer.
+**
+** Returns:
+** none.
+**
+** Side Effects:
+** Sets SmDebugOutput.
+*/
+
+void
+sm_debug_setfile(fp)
+ SM_FILE_T *fp;
+{
+ SmDebugOutput = fp;
+}
+
+/*
+** SM_DEBUG_CLOSE -- Close debug file pointer.
+**
+** Parameters:
+** none.
+**
+** Returns:
+** none.
+**
+** Side Effects:
+** Closes SmDebugOutput.
+*/
+
+void
+sm_debug_close()
+{
+ if (SmDebugOutput != NULL && SmDebugOutput != smioout)
+ {
+ sm_io_close(SmDebugOutput, SM_TIME_DEFAULT);
+ SmDebugOutput = NULL;
+ }
+}
+
+/*
+** SM_DPRINTF -- printf() for debug output.
+**
+** Parameters:
+** fmt -- format for printf()
+**
+** Returns:
+** none.
+*/
+
+void
+#if SM_VA_STD
+sm_dprintf(char *fmt, ...)
+#else /* SM_VA_STD */
+sm_dprintf(fmt, va_alist)
+ char *fmt;
+ va_dcl
+#endif /* SM_VA_STD */
+{
+ SM_VA_LOCAL_DECL
+
+ if (SmDebugOutput == NULL)
+ return;
+ SM_VA_START(ap, fmt);
+ sm_io_vfprintf(SmDebugOutput, SmDebugOutput->f_timeout, fmt, ap);
+ SM_VA_END(ap);
+}
+
+/*
+** SM_DFLUSH -- Flush debug output.
+**
+** Parameters:
+** none.
+**
+** Returns:
+** none.
+*/
+
+void
+sm_dflush()
+{
+ sm_io_flush(SmDebugOutput, SM_TIME_DEFAULT);
+}
+
+/*
+** This is the internal database of debug settings.
+** The semantics of looking up a setting in the settings database
+** are that the *last* setting specified in a -d option on the sendmail
+** command line that matches a given SM_DEBUG structure is the one that is
+** used. That is necessary to conform to the existing semantics of
+** the sendmail -d option. We store the settings as a linked list in
+** reverse order, so when we do a lookup, we take the *first* entry
+** that matches.
+*/
+
+typedef struct sm_debug_setting SM_DEBUG_SETTING_T;
+struct sm_debug_setting
+{
+ const char *ds_pattern;
+ unsigned int ds_level;
+ SM_DEBUG_SETTING_T *ds_next;
+};
+SM_DEBUG_SETTING_T *SmDebugSettings = NULL;
+
+/*
+** We keep a linked list of SM_DEBUG structures that have been initialized,
+** for use by sm_debug_reset.
+*/
+
+SM_DEBUG_T *SmDebugInitialized = NULL;
+
+const char SmDebugMagic[] = "sm_debug";
+
+/*
+** SM_DEBUG_RESET -- Reset SM_DEBUG structures.
+**
+** Reset all SM_DEBUG structures back to the uninitialized state.
+** This is used by sm_debug_addsetting to ensure that references to
+** SM_DEBUG structures that occur before sendmail processes its -d flags
+** do not cause those structures to be permanently forced to level 0.
+**
+** Parameters:
+** none.
+**
+** Returns:
+** none.
+*/
+
+static void
+sm_debug_reset()
+{
+ SM_DEBUG_T *debug;
+
+ for (debug = SmDebugInitialized;
+ debug != NULL;
+ debug = debug->debug_next)
+ {
+ debug->debug_level = SM_DEBUG_UNKNOWN;
+ }
+ SmDebugInitialized = NULL;
+}
+
+/*
+** SM_DEBUG_ADDSETTING_X -- add an entry to the database of debug settings
+**
+** Parameters:
+** pattern -- a shell-style glob pattern (see sm_match).
+** WARNING: the storage for 'pattern' will be owned by
+** the debug package, so it should either be a string
+** literal or the result of a call to sm_strdup_x.
+** level -- a non-negative integer.
+**
+** Returns:
+** none.
+**
+** Exceptions:
+** F:sm_heap -- out of memory
+*/
+
+void
+sm_debug_addsetting_x(pattern, level)
+ const char *pattern;
+ int level;
+{
+ SM_DEBUG_SETTING_T *s;
+
+ SM_REQUIRE(pattern != NULL);
+ SM_REQUIRE(level >= 0);
+ s = sm_malloc_x(sizeof(SM_DEBUG_SETTING_T));
+ s->ds_pattern = pattern;
+ s->ds_level = (unsigned int) level;
+ s->ds_next = SmDebugSettings;
+ SmDebugSettings = s;
+ sm_debug_reset();
+}
+
+/*
+** PARSE_NAMED_SETTING_X -- process a symbolic debug setting
+**
+** Parameters:
+** s -- Points to a non-empty \0 or , terminated string,
+** of which the initial character is not a digit.
+**
+** Returns:
+** pointer to terminating \0 or , character.
+**
+** Exceptions:
+** F:sm.heap -- out of memory.
+**
+** Side Effects:
+** adds the setting to the database.
+*/
+
+static const char *
+parse_named_setting_x(s)
+ const char *s;
+{
+ const char *pat, *endpat;
+ int level;
+
+ pat = s;
+ while (*s != '\0' && *s != ',' && *s != '.')
+ ++s;
+ endpat = s;
+ if (*s == '.')
+ {
+ ++s;
+ level = 0;
+ while (isascii(*s) && isdigit(*s))
+ {
+ level = level * 10 + (*s - '0');
+ ++s;
+ }
+ if (level < 0)
+ level = 0;
+ }
+ else
+ level = 1;
+
+ sm_debug_addsetting_x(sm_strndup_x(pat, endpat - pat), level);
+
+ /* skip trailing junk */
+ while (*s != '\0' && *s != ',')
+ ++s;
+
+ return s;
+}
+
+/*
+** SM_DEBUG_ADDSETTINGS_X -- process a list of debug options
+**
+** Parameters:
+** s -- a list of debug settings, eg the argument to the
+** sendmail -d option.
+**
+** The syntax of the string s is as follows:
+**
+** <settings> ::= <setting> | <settings> "," <setting>
+** <setting> ::= <categories> | <categories> "." <level>
+** <categories> ::= [a-zA-Z_*?][a-zA-Z0-9_*?]*
+**
+** However, note that we skip over anything we don't
+** understand, rather than report an error.
+**
+** Returns:
+** none.
+**
+** Exceptions:
+** F:sm.heap -- out of memory
+**
+** Side Effects:
+** updates the database of debug settings.
+*/
+
+void
+sm_debug_addsettings_x(s)
+ const char *s;
+{
+ for (;;)
+ {
+ if (*s == '\0')
+ return;
+ if (*s == ',')
+ {
+ ++s;
+ continue;
+ }
+ s = parse_named_setting_x(s);
+ }
+}
+
+/*
+** SM_DEBUG_LOADLEVEL -- Get activation level of the specified debug object.
+**
+** Parameters:
+** debug -- debug object.
+**
+** Returns:
+** Activation level of the specified debug object.
+**
+** Side Effects:
+** Ensures that the debug object is initialized.
+*/
+
+int
+sm_debug_loadlevel(debug)
+ SM_DEBUG_T *debug;
+{
+ if (debug->debug_level == SM_DEBUG_UNKNOWN)
+ {
+ SM_DEBUG_SETTING_T *s;
+
+ for (s = SmDebugSettings; s != NULL; s = s->ds_next)
+ {
+ if (sm_match(debug->debug_name, s->ds_pattern))
+ {
+ debug->debug_level = s->ds_level;
+ goto initialized;
+ }
+ }
+ debug->debug_level = 0;
+ initialized:
+ debug->debug_next = SmDebugInitialized;
+ SmDebugInitialized = debug;
+ }
+ return (int) debug->debug_level;
+}
+
+/*
+** SM_DEBUG_LOADACTIVE -- Activation level reached?
+**
+** Parameters:
+** debug -- debug object.
+** level -- level to check.
+**
+** Returns:
+** true iff the activation level of the specified debug
+** object >= level.
+**
+** Side Effects:
+** Ensures that the debug object is initialized.
+*/
+
+bool
+sm_debug_loadactive(debug, level)
+ SM_DEBUG_T *debug;
+ int level;
+{
+ return sm_debug_loadlevel(debug) >= level;
+}
diff --git a/usr/src/cmd/sendmail/libsm/errstring.c b/usr/src/cmd/sendmail/libsm/errstring.c
new file mode 100644
index 0000000000..ef2fb3749b
--- /dev/null
+++ b/usr/src/cmd/sendmail/libsm/errstring.c
@@ -0,0 +1,285 @@
+/*
+ * Copyright (c) 2001, 2003 Sendmail, Inc. and its suppliers.
+ * All rights reserved.
+ * Copyright (c) 1983, 1995-1997 Eric P. Allman. All rights reserved.
+ * Copyright (c) 1988, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * By using this file, you agree to the terms and conditions set
+ * forth in the LICENSE file which can be found at the top level of
+ * the sendmail distribution.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#include <sm/gen.h>
+SM_RCSID("@(#)$Id: errstring.c,v 1.19 2003/12/10 03:53:05 gshapiro Exp $")
+
+#include <errno.h>
+#include <stdio.h> /* sys_errlist, on some platforms */
+
+#include <sm/io.h> /* sm_snprintf */
+#include <sm/string.h>
+#include <sm/errstring.h>
+
+#if NAMED_BIND
+# include <netdb.h>
+#endif
+
+#if LDAPMAP
+# include <lber.h>
+# include <ldap.h> /* for LDAP error codes */
+#endif /* LDAPMAP */
+
+/*
+** Notice: this file is used by libmilter. Please try to avoid
+** using libsm specific functions.
+*/
+
+/*
+** SM_ERRSTRING -- return string description of error code
+**
+** Parameters:
+** errnum -- the error number to translate
+**
+** Returns:
+** A string description of errnum.
+**
+** Note: this may point to a local (static) buffer.
+*/
+
+const char *
+sm_errstring(errnum)
+ int errnum;
+{
+ char *ret;
+
+
+ switch (errnum)
+ {
+ case EPERM:
+ /* SunOS gives "Not owner" -- this is the POSIX message */
+ return "Operation not permitted";
+
+ /*
+ ** Error messages used internally in sendmail.
+ */
+
+ case E_SM_OPENTIMEOUT:
+ return "Timeout on file open";
+
+ case E_SM_NOSLINK:
+ return "Symbolic links not allowed";
+
+ case E_SM_NOHLINK:
+ return "Hard links not allowed";
+
+ case E_SM_REGONLY:
+ return "Regular files only";
+
+ case E_SM_ISEXEC:
+ return "Executable files not allowed";
+
+ case E_SM_WWDIR:
+ return "World writable directory";
+
+ case E_SM_GWDIR:
+ return "Group writable directory";
+
+ case E_SM_FILECHANGE:
+ return "File changed after open";
+
+ case E_SM_WWFILE:
+ return "World writable file";
+
+ case E_SM_GWFILE:
+ return "Group writable file";
+
+ case E_SM_GRFILE:
+ return "Group readable file";
+
+ case E_SM_WRFILE:
+ return "World readable file";
+
+ /*
+ ** DNS error messages.
+ */
+
+#if NAMED_BIND
+ case HOST_NOT_FOUND + E_DNSBASE:
+ return "Name server: host not found";
+
+ case TRY_AGAIN + E_DNSBASE:
+ return "Name server: host name lookup failure";
+
+ case NO_RECOVERY + E_DNSBASE:
+ return "Name server: non-recoverable error";
+
+ case NO_DATA + E_DNSBASE:
+ return "Name server: no data known";
+#endif /* NAMED_BIND */
+
+ /*
+ ** libsmdb error messages.
+ */
+
+ case SMDBE_MALLOC:
+ return "Memory allocation failed";
+
+ case SMDBE_GDBM_IS_BAD:
+ return "GDBM is not supported";
+
+ case SMDBE_UNSUPPORTED:
+ return "Unsupported action";
+
+ case SMDBE_DUPLICATE:
+ return "Key already exists";
+
+ case SMDBE_BAD_OPEN:
+ return "Database open failed";
+
+ case SMDBE_NOT_FOUND:
+ return "Key not found";
+
+ case SMDBE_UNKNOWN_DB_TYPE:
+ return "Unknown database type";
+
+ case SMDBE_UNSUPPORTED_DB_TYPE:
+ return "Support for database type not compiled into this program";
+
+ case SMDBE_INCOMPLETE:
+ return "DB sync did not finish";
+
+ case SMDBE_KEY_EMPTY:
+ return "Key is empty";
+
+ case SMDBE_KEY_EXIST:
+ return "Key already exists";
+
+ case SMDBE_LOCK_DEADLOCK:
+ return "Locker killed to resolve deadlock";
+
+ case SMDBE_LOCK_NOT_GRANTED:
+ return "Lock unavailable";
+
+ case SMDBE_LOCK_NOT_HELD:
+ return "Lock not held by locker";
+
+ case SMDBE_RUN_RECOVERY:
+ return "Database panic, run recovery";
+
+ case SMDBE_IO_ERROR:
+ return "I/O error";
+
+ case SMDBE_READ_ONLY:
+ return "Database opened read-only";
+
+ case SMDBE_DB_NAME_TOO_LONG:
+ return "Name too long";
+
+ case SMDBE_INVALID_PARAMETER:
+ return "Invalid parameter";
+
+ case SMDBE_ONLY_SUPPORTS_ONE_CURSOR:
+ return "Only one cursor allowed";
+
+ case SMDBE_NOT_A_VALID_CURSOR:
+ return "Invalid cursor";
+
+ case SMDBE_OLD_VERSION:
+ return "Berkeley DB file is an old version, recreate it";
+
+ case SMDBE_VERSION_MISMATCH:
+ return "Berkeley DB version mismatch between include file and library";
+
+#if LDAPMAP
+
+ /*
+ ** LDAP URL error messages.
+ */
+
+ /* OpenLDAP errors */
+# ifdef LDAP_URL_ERR_MEM
+ case E_LDAPURLBASE + LDAP_URL_ERR_MEM:
+ return "LDAP URL can't allocate memory space";
+# endif /* LDAP_URL_ERR_MEM */
+
+# ifdef LDAP_URL_ERR_PARAM
+ case E_LDAPURLBASE + LDAP_URL_ERR_PARAM:
+ return "LDAP URL parameter is bad";
+# endif /* LDAP_URL_ERR_PARAM */
+
+# ifdef LDAP_URL_ERR_BADSCHEME
+ case E_LDAPURLBASE + LDAP_URL_ERR_BADSCHEME:
+ return "LDAP URL doesn't begin with \"ldap[si]://\"";
+# endif /* LDAP_URL_ERR_BADSCHEME */
+
+# ifdef LDAP_URL_ERR_BADENCLOSURE
+ case E_LDAPURLBASE + LDAP_URL_ERR_BADENCLOSURE:
+ return "LDAP URL is missing trailing \">\"";
+# endif /* LDAP_URL_ERR_BADENCLOSURE */
+
+# ifdef LDAP_URL_ERR_BADURL
+ case E_LDAPURLBASE + LDAP_URL_ERR_BADURL:
+ return "LDAP URL is bad";
+# endif /* LDAP_URL_ERR_BADURL */
+
+# ifdef LDAP_URL_ERR_BADHOST
+ case E_LDAPURLBASE + LDAP_URL_ERR_BADHOST:
+ return "LDAP URL host port is bad";
+# endif /* LDAP_URL_ERR_BADHOST */
+
+# ifdef LDAP_URL_ERR_BADATTRS
+ case E_LDAPURLBASE + LDAP_URL_ERR_BADATTRS:
+ return "LDAP URL bad (or missing) attributes";
+# endif /* LDAP_URL_ERR_BADATTRS */
+
+# ifdef LDAP_URL_ERR_BADSCOPE
+ case E_LDAPURLBASE + LDAP_URL_ERR_BADSCOPE:
+ return "LDAP URL scope string is invalid (or missing)";
+# endif /* LDAP_URL_ERR_BADSCOPE */
+
+# ifdef LDAP_URL_ERR_BADFILTER
+ case E_LDAPURLBASE + LDAP_URL_ERR_BADFILTER:
+ return "LDAP URL bad or missing filter";
+# endif /* LDAP_URL_ERR_BADFILTER */
+
+# ifdef LDAP_URL_ERR_BADEXTS
+ case E_LDAPURLBASE + LDAP_URL_ERR_BADEXTS:
+ return "LDAP URL bad or missing extensions";
+# endif /* LDAP_URL_ERR_BADEXTS */
+
+ /* Sun LDAP errors */
+# ifdef LDAP_URL_ERR_NOTLDAP
+ case E_LDAPURLBASE + LDAP_URL_ERR_NOTLDAP:
+ return "LDAP URL doesn't begin with \"ldap://\"";
+# endif /* LDAP_URL_ERR_NOTLDAP */
+
+# ifdef LDAP_URL_ERR_NODN
+ case E_LDAPURLBASE + LDAP_URL_ERR_NODN:
+ return "LDAP URL has no DN (required)";
+# endif /* LDAP_URL_ERR_NODN */
+
+#endif /* LDAPMAP */
+ }
+
+#if LDAPMAP
+
+ /*
+ ** LDAP error messages.
+ */
+
+ if (errnum >= E_LDAPBASE)
+ return ldap_err2string(errnum - E_LDAPBASE);
+#endif /* LDAPMAP */
+
+ ret = strerror(errnum);
+ if (ret == NULL)
+ {
+ static char buf[30];
+
+ (void) sm_snprintf(buf, sizeof buf, "Error %d", errnum);
+ return buf;
+ }
+ return ret;
+}
diff --git a/usr/src/cmd/sendmail/libsm/exc.c b/usr/src/cmd/sendmail/libsm/exc.c
new file mode 100644
index 0000000000..42e4da7924
--- /dev/null
+++ b/usr/src/cmd/sendmail/libsm/exc.c
@@ -0,0 +1,671 @@
+/*
+ * Copyright (c) 2000-2002 Sendmail, Inc. and its suppliers.
+ * All rights reserved.
+ *
+ * By using this file, you agree to the terms and conditions set
+ * forth in the LICENSE file which can be found at the top level of
+ * the sendmail distribution.
+ *
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#include <sm/gen.h>
+SM_RCSID("@(#)$Id: exc.c,v 1.47.2.1 2003/12/05 22:44:17 ca Exp $")
+
+/*
+** exception handling
+** For documentation, see exc.html
+*/
+
+#include <ctype.h>
+#include <string.h>
+
+#include <sm/errstring.h>
+#include <sm/exc.h>
+#include <sm/heap.h>
+#include <sm/string.h>
+#include <sm/varargs.h>
+#include <sm/io.h>
+
+const char SmExcMagic[] = "sm_exc";
+const char SmExcTypeMagic[] = "sm_exc_type";
+
+/*
+** SM_ETYPE_PRINTF -- printf for exception types.
+**
+** Parameters:
+** exc -- exception.
+** stream -- file for output.
+**
+** Returns:
+** none.
+*/
+
+/*
+** A simple formatted print function that can be used as the print function
+** by most exception types. It prints the printcontext string, interpreting
+** occurrences of %0 through %9 as references to the argument vector.
+** If exception argument 3 is an int or long, then %3 will print the
+** argument in decimal, and %o3 or %x3 will print it in octal or hex.
+*/
+
+void
+sm_etype_printf(exc, stream)
+ SM_EXC_T *exc;
+ SM_FILE_T *stream;
+{
+ size_t n = strlen(exc->exc_type->etype_argformat);
+ const char *p, *s;
+ char format;
+
+ for (p = exc->exc_type->etype_printcontext; *p != '\0'; ++p)
+ {
+ if (*p != '%')
+ {
+ (void) sm_io_putc(stream, SM_TIME_DEFAULT, *p);
+ continue;
+ }
+ ++p;
+ if (*p == '\0')
+ {
+ (void) sm_io_putc(stream, SM_TIME_DEFAULT, '%');
+ break;
+ }
+ if (*p == '%')
+ {
+ (void) sm_io_putc(stream, SM_TIME_DEFAULT, '%');
+ continue;
+ }
+ format = '\0';
+ if (isalpha(*p))
+ {
+ format = *p++;
+ if (*p == '\0')
+ {
+ (void) sm_io_putc(stream, SM_TIME_DEFAULT, '%');
+ (void) sm_io_putc(stream, SM_TIME_DEFAULT,
+ format);
+ break;
+ }
+ }
+ if (isdigit(*p))
+ {
+ size_t i = *p - '0';
+ if (i < n)
+ {
+ switch (exc->exc_type->etype_argformat[i])
+ {
+ case 's':
+ case 'r':
+ s = exc->exc_argv[i].v_str;
+ if (s == NULL)
+ s = "(null)";
+ sm_io_fputs(stream, SM_TIME_DEFAULT, s);
+ continue;
+ case 'i':
+ sm_io_fprintf(stream,
+ SM_TIME_DEFAULT,
+ format == 'o' ? "%o"
+ : format == 'x' ? "%x"
+ : "%d",
+ exc->exc_argv[i].v_int);
+ continue;
+ case 'l':
+ sm_io_fprintf(stream,
+ SM_TIME_DEFAULT,
+ format == 'o' ? "%lo"
+ : format == 'x' ? "%lx"
+ : "%ld",
+ exc->exc_argv[i].v_long);
+ continue;
+ case 'e':
+ sm_exc_write(exc->exc_argv[i].v_exc,
+ stream);
+ continue;
+ }
+ }
+ }
+ (void) sm_io_putc(stream, SM_TIME_DEFAULT, '%');
+ if (format)
+ (void) sm_io_putc(stream, SM_TIME_DEFAULT, format);
+ (void) sm_io_putc(stream, SM_TIME_DEFAULT, *p);
+ }
+}
+
+/*
+** Standard exception types.
+*/
+
+/*
+** SM_ETYPE_OS_PRINT -- Print OS related exception.
+**
+** Parameters:
+** exc -- exception.
+** stream -- file for output.
+**
+** Returns:
+** none.
+*/
+
+static void
+sm_etype_os_print __P((
+ SM_EXC_T *exc,
+ SM_FILE_T *stream));
+
+static void
+sm_etype_os_print(exc, stream)
+ SM_EXC_T *exc;
+ SM_FILE_T *stream;
+{
+ int err = exc->exc_argv[0].v_int;
+ char *syscall = exc->exc_argv[1].v_str;
+ char *sysargs = exc->exc_argv[2].v_str;
+
+ if (sysargs)
+ sm_io_fprintf(stream, SM_TIME_DEFAULT, "%s: %s failed: %s",
+ sysargs, syscall, sm_errstring(err));
+ else
+ sm_io_fprintf(stream, SM_TIME_DEFAULT, "%s failed: %s", syscall,
+ sm_errstring(err));
+}
+
+/*
+** SmEtypeOs represents the failure of a Unix system call.
+** The three arguments are:
+** int errno (eg, ENOENT)
+** char *syscall (eg, "open")
+** char *sysargs (eg, NULL or "/etc/mail/sendmail.cf")
+*/
+
+const SM_EXC_TYPE_T SmEtypeOs =
+{
+ SmExcTypeMagic,
+ "E:sm.os",
+ "isr",
+ sm_etype_os_print,
+ NULL,
+};
+
+/*
+** SmEtypeErr is a completely generic error which should only be
+** used in applications and test programs. Libraries should use
+** more specific exception codes.
+*/
+
+const SM_EXC_TYPE_T SmEtypeErr =
+{
+ SmExcTypeMagic,
+ "E:sm.err",
+ "r",
+ sm_etype_printf,
+ "%0",
+};
+
+/*
+** SM_EXC_VNEW_X -- Construct a new exception object.
+**
+** Parameters:
+** etype -- type of exception.
+** ap -- varargs.
+**
+** Returns:
+** pointer to exception object.
+*/
+
+/*
+** This is an auxiliary function called by sm_exc_new_x and sm_exc_raisenew_x.
+**
+** If an exception is raised, then to avoid a storage leak, we must:
+** (a) Free all storage we have allocated.
+** (b) Free all exception arguments in the varargs list.
+** Getting this right is tricky.
+**
+** To see why (b) is required, consider the code fragment
+** SM_EXCEPT(exc, "*")
+** sm_exc_raisenew_x(&MyEtype, exc);
+** SM_END_TRY
+** In the normal case, sm_exc_raisenew_x will allocate and raise a new
+** exception E that owns exc. When E is eventually freed, exc is also freed.
+** In the exceptional case, sm_exc_raisenew_x must free exc before raising
+** an out-of-memory exception so that exc is not leaked.
+*/
+
+SM_EXC_T *
+sm_exc_vnew_x(etype, ap)
+ const SM_EXC_TYPE_T *etype;
+ va_list SM_NONVOLATILE ap;
+{
+ /*
+ ** All variables that are modified in the SM_TRY clause and
+ ** referenced in the SM_EXCEPT clause must be declared volatile.
+ */
+
+ /* NOTE: Type of si, i, and argc *must* match */
+ SM_EXC_T * volatile exc = NULL;
+ int volatile si = 0;
+ SM_VAL_T * volatile argv = NULL;
+ int i, argc;
+
+ SM_REQUIRE_ISA(etype, SmExcTypeMagic);
+ argc = strlen(etype->etype_argformat);
+ SM_TRY
+ {
+ /*
+ ** Step 1. Allocate the exception structure.
+ ** On failure, scan the varargs list and free all
+ ** exception arguments.
+ */
+
+ exc = sm_malloc_x(sizeof(SM_EXC_T));
+ exc->sm_magic = SmExcMagic;
+ exc->exc_refcount = 1;
+ exc->exc_type = etype;
+ exc->exc_argv = NULL;
+
+ /*
+ ** Step 2. Allocate the argument vector.
+ ** On failure, free exc, scan the varargs list and free all
+ ** exception arguments. On success, scan the varargs list,
+ ** and copy the arguments into argv.
+ */
+
+ argv = sm_malloc_x(argc * sizeof(SM_VAL_T));
+ exc->exc_argv = argv;
+ for (i = 0; i < argc; ++i)
+ {
+ switch (etype->etype_argformat[i])
+ {
+ case 'i':
+ argv[i].v_int = SM_VA_ARG(ap, int);
+ break;
+ case 'l':
+ argv[i].v_long = SM_VA_ARG(ap, long);
+ break;
+ case 'e':
+ argv[i].v_exc = SM_VA_ARG(ap, SM_EXC_T*);
+ break;
+ case 's':
+ argv[i].v_str = SM_VA_ARG(ap, char*);
+ break;
+ case 'r':
+ SM_REQUIRE(etype->etype_argformat[i+1] == '\0');
+ argv[i].v_str = SM_VA_ARG(ap, char*);
+ break;
+ default:
+ sm_abort("sm_exc_vnew_x: bad argformat '%c'",
+ etype->etype_argformat[i]);
+ }
+ }
+
+ /*
+ ** Step 3. Scan argv, and allocate space for all
+ ** string arguments. si is the number of elements
+ ** of argv that have been processed so far.
+ ** On failure, free exc, argv, all the exception arguments
+ ** and all of the strings that have been copied.
+ */
+
+ for (si = 0; si < argc; ++si)
+ {
+ switch (etype->etype_argformat[si])
+ {
+ case 's':
+ {
+ char *str = argv[si].v_str;
+ if (str != NULL)
+ argv[si].v_str = sm_strdup_x(str);
+ }
+ break;
+ case 'r':
+ {
+ char *fmt = argv[si].v_str;
+ if (fmt != NULL)
+ argv[si].v_str = sm_vstringf_x(fmt, ap);
+ }
+ break;
+ }
+ }
+ }
+ SM_EXCEPT(e, "*")
+ {
+ if (exc == NULL || argv == NULL)
+ {
+ /*
+ ** Failure in step 1 or step 2.
+ ** Scan ap and free all exception arguments.
+ */
+
+ for (i = 0; i < argc; ++i)
+ {
+ switch (etype->etype_argformat[i])
+ {
+ case 'i':
+ (void) SM_VA_ARG(ap, int);
+ break;
+ case 'l':
+ (void) SM_VA_ARG(ap, long);
+ break;
+ case 'e':
+ sm_exc_free(SM_VA_ARG(ap, SM_EXC_T*));
+ break;
+ case 's':
+ case 'r':
+ (void) SM_VA_ARG(ap, char*);
+ break;
+ }
+ }
+ }
+ else
+ {
+ /*
+ ** Failure in step 3. Scan argv and free
+ ** all exception arguments and all string
+ ** arguments that have been duplicated.
+ ** Then free argv.
+ */
+
+ for (i = 0; i < argc; ++i)
+ {
+ switch (etype->etype_argformat[i])
+ {
+ case 'e':
+ sm_exc_free(argv[i].v_exc);
+ break;
+ case 's':
+ case 'r':
+ if (i < si)
+ sm_free(argv[i].v_str);
+ break;
+ }
+ }
+ sm_free(argv);
+ }
+ sm_free(exc);
+ sm_exc_raise_x(e);
+ }
+ SM_END_TRY
+
+ return exc;
+}
+
+/*
+** SM_EXC_NEW_X -- Construct a new exception object.
+**
+** Parameters:
+** etype -- type of exception.
+** ... -- varargs.
+**
+** Returns:
+** pointer to exception object.
+*/
+
+SM_EXC_T *
+#if SM_VA_STD
+sm_exc_new_x(
+ const SM_EXC_TYPE_T *etype,
+ ...)
+#else /* SM_VA_STD */
+sm_exc_new_x(etype, va_alist)
+ const SM_EXC_TYPE_T *etype;
+ va_dcl
+#endif /* SM_VA_STD */
+{
+ SM_EXC_T *exc;
+ SM_VA_LOCAL_DECL
+
+ SM_VA_START(ap, etype);
+ exc = sm_exc_vnew_x(etype, ap);
+ SM_VA_END(ap);
+ return exc;
+}
+
+/*
+** SM_ADDREF -- Add a reference to an exception object.
+**
+** Parameters:
+** exc -- exception object.
+**
+** Returns:
+** exc itself.
+*/
+
+SM_EXC_T *
+sm_addref(exc)
+ SM_EXC_T *exc;
+{
+ SM_REQUIRE_ISA(exc, SmExcMagic);
+ if (exc->exc_refcount != 0)
+ ++exc->exc_refcount;
+ return exc;
+}
+
+/*
+** SM_EXC_FREE -- Destroy a reference to an exception object.
+**
+** Parameters:
+** exc -- exception object.
+**
+** Returns:
+** none.
+*/
+
+void
+sm_exc_free(exc)
+ SM_EXC_T *exc;
+{
+ if (exc == NULL)
+ return;
+ SM_REQUIRE(exc->sm_magic == SmExcMagic);
+ if (exc->exc_refcount == 0)
+ return;
+ if (--exc->exc_refcount == 0)
+ {
+ int i, c;
+
+ for (i = 0; (c = exc->exc_type->etype_argformat[i]) != '\0';
+ ++i)
+ {
+ switch (c)
+ {
+ case 's':
+ case 'r':
+ sm_free(exc->exc_argv[i].v_str);
+ break;
+ case 'e':
+ sm_exc_free(exc->exc_argv[i].v_exc);
+ break;
+ }
+ }
+ exc->sm_magic = NULL;
+ sm_free(exc->exc_argv);
+ sm_free(exc);
+ }
+}
+
+/*
+** SM_EXC_MATCH -- Match exception category against a glob pattern.
+**
+** Parameters:
+** exc -- exception.
+** pattern -- glob pattern.
+**
+** Returns:
+** true iff match.
+*/
+
+bool
+sm_exc_match(exc, pattern)
+ SM_EXC_T *exc;
+ const char *pattern;
+{
+ if (exc == NULL)
+ return false;
+ SM_REQUIRE(exc->sm_magic == SmExcMagic);
+ return sm_match(exc->exc_type->etype_category, pattern);
+}
+
+/*
+** SM_EXC_WRITE -- Write exception message to a stream (wo trailing newline).
+**
+** Parameters:
+** exc -- exception.
+** stream -- file for output.
+**
+** Returns:
+** none.
+*/
+
+void
+sm_exc_write(exc, stream)
+ SM_EXC_T *exc;
+ SM_FILE_T *stream;
+{
+ SM_REQUIRE_ISA(exc, SmExcMagic);
+ exc->exc_type->etype_print(exc, stream);
+}
+
+/*
+** SM_EXC_PRINT -- Print exception message to a stream (with trailing newline).
+**
+** Parameters:
+** exc -- exception.
+** stream -- file for output.
+**
+** Returns:
+** none.
+*/
+
+void
+sm_exc_print(exc, stream)
+ SM_EXC_T *exc;
+ SM_FILE_T *stream;
+{
+ SM_REQUIRE_ISA(exc, SmExcMagic);
+ exc->exc_type->etype_print(exc, stream);
+ (void) sm_io_putc(stream, SM_TIME_DEFAULT, '\n');
+}
+
+SM_EXC_HANDLER_T *SmExcHandler = NULL;
+static SM_EXC_DEFAULT_HANDLER_T SmExcDefaultHandler = NULL;
+
+/*
+** SM_EXC_NEWTHREAD -- Initialize exception handling for new process/thread.
+**
+** Parameters:
+** h -- default exception handler.
+**
+** Returns:
+** none.
+*/
+
+/*
+** Initialize a new process or a new thread by clearing the
+** exception handler stack and optionally setting a default
+** exception handler function. Call this at the beginning of main,
+** or in a new process after calling fork, or in a new thread.
+**
+** This function is a luxury, not a necessity.
+** If h != NULL then you can get the same effect by
+** wrapping the body of main, or the body of a forked child
+** or a new thread in SM_TRY ... SM_EXCEPT(e,"*") h(e); SM_END_TRY.
+*/
+
+void
+sm_exc_newthread(h)
+ SM_EXC_DEFAULT_HANDLER_T h;
+{
+ SmExcHandler = NULL;
+ SmExcDefaultHandler = h;
+}
+
+/*
+** SM_EXC_RAISE_X -- Raise an exception.
+**
+** Parameters:
+** exc -- exception.
+**
+** Returns:
+** doesn't.
+*/
+
+void SM_DEAD_D
+sm_exc_raise_x(exc)
+ SM_EXC_T *exc;
+{
+ SM_REQUIRE_ISA(exc, SmExcMagic);
+
+ if (SmExcHandler == NULL)
+ {
+ if (SmExcDefaultHandler != NULL)
+ {
+ SM_EXC_DEFAULT_HANDLER_T h;
+
+ /*
+ ** If defined, the default handler is expected
+ ** to terminate the current thread of execution
+ ** using exit() or pthread_exit().
+ ** If it instead returns normally, then we fall
+ ** through to the default case below. If it
+ ** raises an exception, then sm_exc_raise_x is
+ ** re-entered and, because we set SmExcDefaultHandler
+ ** to NULL before invoking h, we will again
+ ** end up in the default case below.
+ */
+
+ h = SmExcDefaultHandler;
+ SmExcDefaultHandler = NULL;
+ (*h)(exc);
+ }
+
+ /*
+ ** No exception handler, so print the error and exit.
+ ** To override this behaviour on a program wide basis,
+ ** call sm_exc_newthread or put an exception handler in main().
+ **
+ ** XXX TODO: map the exception category to an exit code
+ ** XXX from <sysexits.h>.
+ */
+
+ sm_exc_print(exc, smioerr);
+ exit(255);
+ }
+
+ if (SmExcHandler->eh_value == NULL)
+ SmExcHandler->eh_value = exc;
+ else
+ sm_exc_free(exc);
+
+ sm_longjmp_nosig(SmExcHandler->eh_context, 1);
+}
+
+/*
+** SM_EXC_RAISENEW_X -- shorthand for sm_exc_raise_x(sm_exc_new_x(...))
+**
+** Parameters:
+** etype -- type of exception.
+** ap -- varargs.
+**
+** Returns:
+** none.
+*/
+
+void SM_DEAD_D
+#if SM_VA_STD
+sm_exc_raisenew_x(
+ const SM_EXC_TYPE_T *etype,
+ ...)
+#else
+sm_exc_raisenew_x(etype, va_alist)
+ const SM_EXC_TYPE_T *etype;
+ va_dcl
+#endif
+{
+ SM_EXC_T *exc;
+ SM_VA_LOCAL_DECL
+
+ SM_VA_START(ap, etype);
+ exc = sm_exc_vnew_x(etype, ap);
+ SM_VA_END(ap);
+ sm_exc_raise_x(exc);
+}
diff --git a/usr/src/cmd/sendmail/libsm/fclose.c b/usr/src/cmd/sendmail/libsm/fclose.c
new file mode 100644
index 0000000000..d540d99e6f
--- /dev/null
+++ b/usr/src/cmd/sendmail/libsm/fclose.c
@@ -0,0 +1,151 @@
+/*
+ * Copyright (c) 2000-2002, 2004 Sendmail, Inc. and its suppliers.
+ * All rights reserved.
+ * Copyright (c) 1990, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Chris Torek.
+ *
+ * By using this file, you agree to the terms and conditions set
+ * forth in the LICENSE file which can be found at the top level of
+ * the sendmail distribution.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#include <sm/gen.h>
+SM_RCSID("@(#)$Id: fclose.c,v 1.43 2004/08/03 20:17:38 ca Exp $")
+#include <errno.h>
+#include <stdlib.h>
+#include <sys/time.h>
+#include <setjmp.h>
+#include <sm/io.h>
+#include <sm/assert.h>
+#include <sm/heap.h>
+#include <sm/signal.h>
+#include <sm/conf.h>
+#include <sm/clock.h>
+#include "local.h"
+
+static void closealrm __P((int));
+static jmp_buf CloseTimeOut;
+
+/*
+** CLOSEALRM -- handler when timeout activated for sm_io_close()
+**
+** Returns flow of control to where setjmp(CloseTimeOut) was set.
+**
+** Parameters:
+** sig -- unused
+**
+** Returns:
+** does not return
+**
+** Side Effects:
+** returns flow of control to setjmp(CloseTimeOut).
+**
+** NOTE: THIS CAN BE CALLED FROM A SIGNAL HANDLER. DO NOT ADD
+** ANYTHING TO THIS ROUTINE UNLESS YOU KNOW WHAT YOU ARE
+** DOING.
+*/
+
+/* ARGSUSED0 */
+static void
+closealrm(sig)
+ int sig;
+{
+ longjmp(CloseTimeOut, 1);
+}
+
+/*
+** SM_IO_CLOSE -- close a file handle/pointer
+**
+** Parameters:
+** fp -- file pointer to be closed
+** timeout -- maximum time allowed to perform the close (millisecs)
+**
+** Returns:
+** 0 on success
+** -1 on failure and sets errno
+**
+** Side Effects:
+** file pointer 'fp' will no longer be valid.
+*/
+
+int
+sm_io_close(fp, timeout)
+ register SM_FILE_T *fp;
+ int SM_NONVOLATILE timeout;
+{
+ register int SM_NONVOLATILE r;
+ SM_EVENT *evt = NULL;
+
+ if (fp == NULL)
+ {
+ errno = EBADF;
+ return SM_IO_EOF;
+ }
+
+ SM_REQUIRE_ISA(fp, SmFileMagic);
+
+ /* XXX this won't be reached if above macro is active */
+ if (fp->sm_magic == NULL)
+ {
+ /* not open! */
+ errno = EBADF;
+ return SM_IO_EOF;
+ }
+ if (fp->f_close == NULL)
+ {
+ /* no close function! */
+ errno = ENODEV;
+ return SM_IO_EOF;
+ }
+ if (fp->f_dup_cnt > 0)
+ {
+ /* decrement file pointer open count */
+ fp->f_dup_cnt--;
+ return 0;
+ }
+
+ /* Okay, this is where we set the timeout. */
+ if (timeout == SM_TIME_DEFAULT)
+ timeout = fp->f_timeout;
+ if (timeout == SM_TIME_IMMEDIATE)
+ {
+ errno = EAGAIN;
+ return -1;
+ }
+
+ /* No more duplicates of file pointer. Flush buffer and close */
+ r = fp->f_flags & SMWR ? sm_flush(fp, (int *) &timeout) : 0;
+
+ /* sm_flush() has updated to.it_value for the time it's used */
+ if (timeout != SM_TIME_FOREVER)
+ {
+ if (setjmp(CloseTimeOut) != 0)
+ {
+ errno = EAGAIN;
+ return SM_IO_EOF;
+ }
+ evt = sm_seteventm(timeout, closealrm, 0);
+ }
+ if ((*fp->f_close)(fp) < 0)
+ r = SM_IO_EOF;
+
+ /* We're back. So undo our timeout and handler */
+ if (evt != NULL)
+ sm_clrevent(evt);
+ if (fp->f_flags & SMMBF)
+ {
+ sm_free((char *)fp->f_bf.smb_base);
+ fp->f_bf.smb_base = NULL;
+ }
+ if (HASUB(fp))
+ FREEUB(fp);
+ fp->f_flags = 0; /* clear flags */
+ fp->sm_magic = NULL; /* Release this SM_FILE_T for reuse. */
+ fp->f_r = fp->f_w = 0; /* Mess up if reaccessed. */
+ return r;
+}
diff --git a/usr/src/cmd/sendmail/libsm/feof.c b/usr/src/cmd/sendmail/libsm/feof.c
new file mode 100644
index 0000000000..2f1efc37d0
--- /dev/null
+++ b/usr/src/cmd/sendmail/libsm/feof.c
@@ -0,0 +1,44 @@
+/*
+ * Copyright (c) 2000-2001 Sendmail, Inc. and its suppliers.
+ * All rights reserved.
+ * Copyright (c) 1990, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Chris Torek.
+ *
+ * By using this file, you agree to the terms and conditions set
+ * forth in the LICENSE file which can be found at the top level of
+ * the sendmail distribution.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#include <sm/gen.h>
+SM_RCSID("@(#)$Id: feof.c,v 1.11 2001/04/03 01:46:40 ca Exp $")
+#include <sm/io.h>
+#include <sm/assert.h>
+#include "local.h"
+
+/*
+** SM_IO_EOF -- subroutine version of the macro sm_io_eof.
+**
+** Tests if the file for 'fp' has reached the end.
+**
+** Parameters:
+** fp -- file pointer.
+**
+** Returns:
+** 0 (zero) when the file is not at end
+** non-zero when EOF has been found
+*/
+#undef sm_io_eof
+
+int
+sm_io_eof(fp)
+ SM_FILE_T *fp;
+{
+ SM_REQUIRE_ISA(fp, SmFileMagic);
+
+ return sm_eof(fp);
+}
diff --git a/usr/src/cmd/sendmail/libsm/ferror.c b/usr/src/cmd/sendmail/libsm/ferror.c
new file mode 100644
index 0000000000..08ee823810
--- /dev/null
+++ b/usr/src/cmd/sendmail/libsm/ferror.c
@@ -0,0 +1,43 @@
+/*
+ * Copyright (c) 2000-2001 Sendmail, Inc. and its suppliers.
+ * All rights reserved.
+ * Copyright (c) 1990, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Chris Torek.
+ *
+ * By using this file, you agree to the terms and conditions set
+ * forth in the LICENSE file which can be found at the top level of
+ * the sendmail distribution.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#include <sm/gen.h>
+SM_RCSID("@(#)$Id: ferror.c,v 1.11 2001/04/03 01:46:40 ca Exp $")
+#include <sm/io.h>
+#include <sm/assert.h>
+#include "local.h"
+
+/*
+** SM_IO_ERROR -- subroutine version of the macro sm_io_error.
+**
+** Parameters:
+** fp -- file pointer
+**
+** Returns:
+** 0 (zero) when 'fp' is not in an error state
+** non-zero when 'fp' is in an error state
+*/
+
+#undef sm_io_error
+
+int
+sm_io_error(fp)
+ SM_FILE_T *fp;
+{
+ SM_REQUIRE_ISA(fp, SmFileMagic);
+
+ return sm_error(fp);
+}
diff --git a/usr/src/cmd/sendmail/libsm/fflush.c b/usr/src/cmd/sendmail/libsm/fflush.c
new file mode 100644
index 0000000000..4ee32c02f4
--- /dev/null
+++ b/usr/src/cmd/sendmail/libsm/fflush.c
@@ -0,0 +1,153 @@
+/*
+ * Copyright (c) 2000-2001 Sendmail, Inc. and its suppliers.
+ * All rights reserved.
+ * Copyright (c) 1990, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Chris Torek.
+ *
+ * By using this file, you agree to the terms and conditions set
+ * forth in the LICENSE file which can be found at the top level of
+ * the sendmail distribution.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#include <sm/gen.h>
+SM_RCSID("@(#)$Id: fflush.c,v 1.41 2001/05/15 16:55:27 ca Exp $")
+#include <unistd.h>
+#include <errno.h>
+#include <sys/time.h>
+#include <signal.h>
+#include <fcntl.h>
+#include <string.h>
+#include <sm/io.h>
+#include <sm/assert.h>
+#include <sm/setjmp.h>
+#include "local.h"
+#include <sm/conf.h>
+
+/*
+** SM_IO_FLUSH -- flush the buffer for a 'fp' to the "file"
+**
+** Flush a single file. We don't allow this function to flush
+** all open files when fp==NULL any longer.
+**
+** Parameters:
+** fp -- the file pointer buffer to flush
+** timeout -- time to complete the flush
+**
+** Results:
+** Failure: SM_IO_EOF and sets errno
+** Success: 0 (zero)
+*/
+
+int
+sm_io_flush(fp, timeout)
+ register SM_FILE_T *fp;
+ int SM_NONVOLATILE timeout;
+{
+ int fd;
+ struct timeval to;
+
+ SM_REQUIRE_ISA(fp, SmFileMagic);
+
+ if ((fp->f_flags & (SMWR | SMRW)) == 0)
+ {
+ /*
+ ** The file is not opened for writing, so it cannot be flushed
+ ** (writable means SMWR [write] or SMRW [read/write].
+ */
+
+ errno = EBADF;
+ return SM_IO_EOF;
+ }
+
+ SM_CONVERT_TIME(fp, fd, timeout, &to);
+
+ /* Now do the flush */
+ return sm_flush(fp, (int *) &timeout);
+}
+
+/*
+** SM_FLUSH -- perform the actual flush
+**
+** Assumes that 'fp' has been validated before this function called.
+**
+** Parameters:
+** fp -- file pointer to be flushed
+** timeout -- max time allowed for flush (milliseconds)
+**
+** Results:
+** Success: 0 (zero)
+** Failure: SM_IO_EOF and errno set
+**
+** Side Effects:
+** timeout will get updated with the time remaining (if any)
+*/
+
+int
+sm_flush(fp, timeout)
+ register SM_FILE_T *fp;
+ int *timeout;
+{
+ register unsigned char *p;
+ register int n, t;
+ int fd;
+
+ SM_REQUIRE_ISA(fp, SmFileMagic);
+
+ t = fp->f_flags;
+ if ((t & SMWR) == 0)
+ return 0;
+
+ if (t & SMSTR)
+ {
+ *fp->f_p = '\0';
+ return 0;
+ }
+
+ if ((p = fp->f_bf.smb_base) == NULL)
+ return 0;
+
+ n = fp->f_p - p; /* write this much */
+
+ if ((fd = sm_io_getinfo(fp, SM_IO_WHAT_FD, NULL)) == -1)
+ {
+ /* can't get an fd, likely internal 'fake' fp */
+ errno = 0;
+ fd = -1;
+ }
+
+ /*
+ ** Set these immediately to avoid problems with longjmp and to allow
+ ** exchange buffering (via setvbuf) in user write function.
+ */
+
+ fp->f_p = p;
+ fp->f_w = t & (SMLBF|SMNBF) ? 0 : fp->f_bf.smb_size; /* implies SMFBF */
+
+ for (; n > 0; n -= t, p += t)
+ {
+ errno = 0; /* needed to ensure EOF correctly found */
+
+ /* Call the file type's write function */
+ t = (*fp->f_write)(fp, (char *)p, n);
+ if (t <= 0)
+ {
+ if (t == 0 && errno == 0)
+ break; /* EOF found */
+
+ if (IS_IO_ERROR(fd, t, *timeout))
+ {
+ fp->f_flags |= SMERR;
+
+ /* errno set by fp->f_write */
+ return SM_IO_EOF;
+ }
+ SM_IO_WR_TIMEOUT(fp, fd, *timeout);
+ }
+ }
+ return 0;
+}
diff --git a/usr/src/cmd/sendmail/libsm/fget.c b/usr/src/cmd/sendmail/libsm/fget.c
new file mode 100644
index 0000000000..96dbf71cbd
--- /dev/null
+++ b/usr/src/cmd/sendmail/libsm/fget.c
@@ -0,0 +1,112 @@
+/*
+ * Copyright (c) 2000-2001 Sendmail, Inc. and its suppliers.
+ * All rights reserved.
+ * Copyright (c) 1990, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Chris Torek.
+ *
+ * By using this file, you agree to the terms and conditions set
+ * forth in the LICENSE file which can be found at the top level of
+ * the sendmail distribution.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#include <sm/gen.h>
+SM_RCSID("@(#)$Id: fget.c,v 1.22 2001/08/27 18:54:14 gshapiro Exp $")
+#include <stdlib.h>
+#include <string.h>
+#include <sm/io.h>
+#include <sm/assert.h>
+#include "local.h"
+
+/*
+** SM_IO_FGETS -- get a string from a file
+**
+** Read at most n-1 characters from the given file.
+** Stop when a newline has been read, or the count ('n') runs out.
+**
+** Parameters:
+** fp -- the file to read from
+** timeout -- time to complete reading the string in milliseconds
+** buf -- buffer to place read string in
+** n -- size of 'buf'
+**
+** Returns:
+** success: returns value of 'buf'
+** failure: NULL (no characters were read)
+** timeout: NULL and errno set to EAGAIN
+**
+** Side Effects:
+** may move the file pointer
+*/
+
+char *
+sm_io_fgets(fp, timeout, buf, n)
+ register SM_FILE_T *fp;
+ int timeout;
+ char *buf;
+ register int n;
+{
+ register int len;
+ register char *s;
+ register unsigned char *p, *t;
+
+ SM_REQUIRE_ISA(fp, SmFileMagic);
+ if (n <= 0) /* sanity check */
+ return NULL;
+
+ s = buf;
+ n--; /* leave space for NUL */
+ while (n > 0)
+ {
+ /* If the buffer is empty, refill it. */
+ if ((len = fp->f_r) <= 0)
+ {
+
+ /*
+ ** Timeout is only passed if we can't get the data
+ ** from the buffer (which is counted as immediately).
+ */
+
+ if (sm_refill(fp, timeout) != 0)
+ {
+ /* EOF/error: stop with partial or no line */
+ if (s == buf)
+ return NULL;
+ break;
+ }
+ len = fp->f_r;
+ }
+ p = fp->f_p;
+
+ /*
+ ** Scan through at most n bytes of the current buffer,
+ ** looking for '\n'. If found, copy up to and including
+ ** newline, and stop. Otherwise, copy entire chunk
+ ** and loop.
+ */
+
+ if (len > n)
+ len = n;
+ t = (unsigned char *) memchr((void *) p, '\n', len);
+ if (t != NULL)
+ {
+ len = ++t - p;
+ fp->f_r -= len;
+ fp->f_p = t;
+ (void) memcpy((void *) s, (void *) p, len);
+ s[len] = 0;
+ return buf;
+ }
+ fp->f_r -= len;
+ fp->f_p += len;
+ (void) memcpy((void *) s, (void *) p, len);
+ s += len;
+ n -= len;
+ }
+ *s = 0;
+ return buf;
+}
diff --git a/usr/src/cmd/sendmail/libsm/findfp.c b/usr/src/cmd/sendmail/libsm/findfp.c
new file mode 100644
index 0000000000..befb996a89
--- /dev/null
+++ b/usr/src/cmd/sendmail/libsm/findfp.c
@@ -0,0 +1,427 @@
+/*
+ * Copyright (c) 2000-2002 Sendmail, Inc. and its suppliers.
+ * All rights reserved.
+ * Copyright (c) 1990, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Chris Torek.
+ *
+ * By using this file, you agree to the terms and conditions set
+ * forth in the LICENSE file which can be found at the top level of
+ * the sendmail distribution.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#include <sm/gen.h>
+SM_RCSID("@(#)$Id: findfp.c,v 1.66 2002/02/20 02:40:24 ca Exp $")
+#include <stdlib.h>
+#include <unistd.h>
+#include <sys/param.h>
+#include <errno.h>
+#include <string.h>
+#include <syslog.h>
+#include <sm/io.h>
+#include <sm/assert.h>
+#include <sm/heap.h>
+#include <sm/string.h>
+#include <sm/conf.h>
+#include "local.h"
+#include "glue.h"
+
+bool Sm_IO_DidInit; /* IO system has been initialized? */
+
+const char SmFileMagic[] = "sm_file";
+
+/* An open type to map to fopen()-like behavior */
+SM_FILE_T SmFtStdio_def =
+ {SmFileMagic, 0, 0, 0, (SMRW|SMFBF), -1, {0, 0}, 0, 0, 0,
+ sm_stdclose, sm_stdread, sm_stdseek, sm_stdwrite,
+ sm_stdopen, sm_stdsetinfo, sm_stdgetinfo, SM_TIME_FOREVER,
+ SM_TIME_BLOCK, "stdio" };
+
+/* An open type to map to fdopen()-like behavior */
+SM_FILE_T SmFtStdiofd_def =
+ {SmFileMagic, 0, 0, 0, (SMRW|SMFBF), -1, {0, 0}, 0, 0, 0,
+ sm_stdclose, sm_stdread, sm_stdseek, sm_stdwrite,
+ sm_stdfdopen, sm_stdsetinfo, sm_stdgetinfo, SM_TIME_FOREVER,
+ SM_TIME_BLOCK, "stdiofd" };
+
+/* A string file type */
+SM_FILE_T SmFtString_def =
+ {SmFileMagic, 0, 0, 0, (SMRW|SMNBF), -1, {0, 0}, 0, 0, 0,
+ sm_strclose, sm_strread, sm_strseek, sm_strwrite,
+ sm_stropen, sm_strsetinfo, sm_strgetinfo, SM_TIME_FOREVER,
+ SM_TIME_BLOCK, "string" };
+
+#if 0
+/* A file type for syslog communications */
+SM_FILE_T SmFtSyslog_def =
+ {SmFileMagic, 0, 0, 0, (SMRW|SMNBF), -1, {0, 0}, 0, 0, 0,
+ sm_syslogclose, sm_syslogread, sm_syslogseek, sm_syslogwrite,
+ sm_syslogopen, sm_syslogsetinfo, sm_sysloggetinfo, SM_TIME_FOREVER,
+ SM_TIME_BLOCK, "syslog" };
+#endif /* 0 */
+
+#define NDYNAMIC 10 /* add ten more whenever necessary */
+
+#define smio(flags, file, name) \
+ {SmFileMagic, 0, 0, 0, flags, file, {0}, 0, SmIoF+file, 0, \
+ sm_stdclose, sm_stdread, sm_stdseek, sm_stdwrite, \
+ sm_stdopen, sm_stdsetinfo, sm_stdgetinfo, SM_TIME_FOREVER, \
+ SM_TIME_BLOCK, name}
+
+/* sm_magic p r w flags file bf lbfsize cookie ival */
+#define smstd(flags, file, name) \
+ {SmFileMagic, 0, 0, 0, flags, -1, {0}, 0, 0, file, \
+ sm_stdioclose, sm_stdioread, sm_stdioseek, sm_stdiowrite, \
+ sm_stdioopen, sm_stdiosetinfo, sm_stdiogetinfo, SM_TIME_FOREVER,\
+ SM_TIME_BLOCK, name}
+
+/* A file type for interfacing to stdio FILE* streams. */
+SM_FILE_T SmFtRealStdio_def = smstd(SMRW|SMNBF, -1, "realstdio");
+
+ /* the usual - (stdin + stdout + stderr) */
+static SM_FILE_T usual[SM_IO_OPEN_MAX - 3];
+static struct sm_glue smuglue = { 0, SM_IO_OPEN_MAX - 3, usual };
+
+/* List of builtin automagically already open file pointers */
+SM_FILE_T SmIoF[6] =
+{
+ smio(SMRD|SMLBF, SMIOIN_FILENO, "smioin"), /* smioin */
+ smio(SMWR|SMLBF, SMIOOUT_FILENO, "smioout"), /* smioout */
+ smio(SMWR|SMNBF, SMIOERR_FILENO, "smioerr"), /* smioerr */
+ smstd(SMRD|SMNBF, SMIOIN_FILENO, "smiostdin"), /* smiostdin */
+ smstd(SMWR|SMNBF, SMIOOUT_FILENO, "smiostdout"),/* smiostdout */
+ smstd(SMWR|SMNBF, SMIOERR_FILENO, "smiostderr") /* smiostderr */
+};
+
+/* Structure containing list of currently open file pointers */
+struct sm_glue smglue = { &smuglue, 3, SmIoF };
+
+/*
+** SM_MOREGLUE -- adds more space for open file pointers
+**
+** Parameters:
+** n -- number of new spaces for file pointers
+**
+** Returns:
+** Raises an exception if no more memory.
+** Otherwise, returns a pointer to new spaces.
+*/
+
+static struct sm_glue *sm_moreglue_x __P((int));
+static SM_FILE_T empty;
+
+static struct sm_glue *
+sm_moreglue_x(n)
+ register int n;
+{
+ register struct sm_glue *g;
+ register SM_FILE_T *p;
+
+ g = (struct sm_glue *) sm_pmalloc_x(sizeof(*g) + SM_ALIGN_BITS +
+ n * sizeof(SM_FILE_T));
+ p = (SM_FILE_T *) SM_ALIGN(g + 1);
+ g->gl_next = NULL;
+ g->gl_niobs = n;
+ g->gl_iobs = p;
+ while (--n >= 0)
+ *p++ = empty;
+ return g;
+}
+
+/*
+** SM_FP -- allocate and initialize an SM_FILE structure
+**
+** Parameters:
+** t -- file type requested to be opened.
+** flags -- control flags for file type behavior
+** oldfp -- file pointer to reuse if available (optional)
+**
+** Returns:
+** Raises exception on memory exhaustion.
+** Aborts if type is invalid.
+** Otherwise, returns file pointer for requested file type.
+*/
+
+SM_FILE_T *
+sm_fp(t, flags, oldfp)
+ const SM_FILE_T *t;
+ const int flags;
+ SM_FILE_T *oldfp;
+{
+ register SM_FILE_T *fp;
+ register int n;
+ register struct sm_glue *g;
+
+ SM_REQUIRE(t->f_open && t->f_close && (t->f_read || t->f_write));
+
+ if (!Sm_IO_DidInit)
+ sm_init();
+
+ if (oldfp != NULL)
+ {
+ fp = oldfp;
+ goto found; /* for opening reusing an 'fp' */
+ }
+
+ for (g = &smglue;; g = g->gl_next)
+ {
+ for (fp = g->gl_iobs, n = g->gl_niobs; --n >= 0; fp++)
+ if (fp->sm_magic == NULL)
+ goto found;
+ if (g->gl_next == NULL)
+ g->gl_next = sm_moreglue_x(NDYNAMIC);
+ }
+found:
+ fp->sm_magic = SmFileMagic; /* 'fp' now valid and in-use */
+ fp->f_p = NULL; /* no current pointer */
+ fp->f_w = 0; /* nothing to write */
+ fp->f_r = 0; /* nothing to read */
+ fp->f_flags = flags;
+ fp->f_file = -1; /* no file */
+ fp->f_bf.smb_base = NULL; /* no buffer */
+ fp->f_bf.smb_size = 0; /* no buffer size with no buffer */
+ fp->f_lbfsize = 0; /* not line buffered */
+ fp->f_flushfp = NULL; /* no associated flush file */
+
+ fp->f_cookie = fp; /* default: *open* overrides cookie setting */
+ fp->f_close = t->f_close; /* assign close function */
+ fp->f_read = t->f_read; /* assign read function */
+ fp->f_seek = t->f_seek; /* assign seek function */
+ fp->f_write = t->f_write; /* assign write function */
+ fp->f_open = t->f_open; /* assign open function */
+ fp->f_setinfo = t->f_setinfo; /* assign setinfo function */
+ fp->f_getinfo = t->f_getinfo; /* assign getinfo function */
+ fp->f_type = t->f_type; /* file type */
+
+ fp->f_ub.smb_base = NULL; /* no ungetc buffer */
+ fp->f_ub.smb_size = 0; /* no size for no ungetc buffer */
+
+ if (fp->f_timeout == SM_TIME_DEFAULT)
+ fp->f_timeout = SM_TIME_FOREVER;
+ else
+ fp->f_timeout = t->f_timeout; /* traditional behavior */
+ fp->f_timeoutstate = SM_TIME_BLOCK; /* by default */
+
+ return fp;
+}
+
+/*
+** SM_CLEANUP -- cleanup function when exit called.
+**
+** This function is registered via atexit().
+**
+** Parameters:
+** none
+**
+** Returns:
+** nothing.
+**
+** Side Effects:
+** flushes open files before they are forced closed
+*/
+
+void
+sm_cleanup()
+{
+ int timeout = SM_TIME_DEFAULT;
+
+ (void) sm_fwalk(sm_flush, &timeout); /* `cheating' */
+}
+
+/*
+** SM_INIT -- called whenever sm_io's internal variables must be set up.
+**
+** Parameters:
+** none
+**
+** Returns:
+** none
+**
+** Side Effects:
+** Registers sm_cleanup() using atexit().
+*/
+
+void
+sm_init()
+{
+ if (Sm_IO_DidInit) /* paranoia */
+ return;
+
+ /* more paranoia: initialize pointers in a static variable */
+ empty.f_type = NULL;
+ empty.sm_magic = NULL;
+
+ /* make sure we clean up on exit */
+ atexit(sm_cleanup); /* conservative */
+ Sm_IO_DidInit = true;
+}
+
+/*
+** SM_IO_SETINFO -- change info for an open file type (fp)
+**
+** The generic SM_IO_WHAT_VECTORS is auto supplied for all file types.
+** If the request is to set info other than SM_IO_WHAT_VECTORS then
+** the request is passed on to the file type's specific setinfo vector.
+** WARNING: this is working on an active/open file type.
+**
+** Parameters:
+** fp -- file to make the setting on
+** what -- type of information to set
+** valp -- structure to obtain info from
+**
+** Returns:
+** 0 on success
+** -1 on error and sets errno:
+** - when what != SM_IO_WHAT_VECTORS and setinfo vector
+** not set
+** - when vectored setinfo returns -1
+*/
+
+int
+sm_io_setinfo(fp, what, valp)
+ SM_FILE_T *fp;
+ int what;
+ void *valp;
+{
+ SM_FILE_T *v = (SM_FILE_T *) valp;
+
+ SM_REQUIRE_ISA(fp, SmFileMagic);
+ switch (what)
+ {
+ case SM_IO_WHAT_VECTORS:
+
+ /*
+ ** This is the "generic" available for all.
+ ** This allows the function vectors to be replaced
+ ** while the file type is active.
+ */
+
+ fp->f_close = v->f_close;
+ fp->f_read = v->f_read;
+ fp->f_seek = v->f_seek;
+ fp->f_write = v->f_write;
+ fp->f_open = v->f_open;
+ fp->f_setinfo = v->f_setinfo;
+ fp->f_getinfo = v->f_getinfo;
+ sm_free(fp->f_type);
+ fp->f_type = sm_strdup_x(v->f_type);
+ return 0;
+ case SM_IO_WHAT_TIMEOUT:
+ fp->f_timeout = *((int *)valp);
+ return 0;
+ }
+
+ /* Otherwise the vector will check it out */
+ if (fp->f_setinfo == NULL)
+ {
+ errno = EINVAL;
+ return -1;
+ }
+ else
+ return (*fp->f_setinfo)(fp, what, valp);
+}
+
+/*
+** SM_IO_GETINFO -- get information for an active file type (fp)
+**
+** This function supplies for all file types the answers for the
+** three requests SM_IO_WHAT_VECTORS, SM_IO_WHAT_TYPE and
+** SM_IO_WHAT_ISTYPE. Other requests are handled by the getinfo
+** vector if available for the open file type.
+** SM_IO_WHAT_VECTORS returns information for the file pointer vectors.
+** SM_IO_WHAT_TYPE returns the type identifier for the file pointer
+** SM_IO_WHAT_ISTYPE returns >0 if the passed in type matches the
+** file pointer's type.
+** SM_IO_IS_READABLE returns 1 if there is data available for reading,
+** 0 otherwise.
+**
+** Parameters:
+** fp -- file pointer for active file type
+** what -- type of information request
+** valp -- structure to place obtained info into
+**
+** Returns:
+** -1 on error and sets errno:
+** - when valp==NULL and request expects otherwise
+** - when request is not SM_IO_WHAT_VECTORS and not
+** SM_IO_WHAT_TYPE and not SM_IO_WHAT_ISTYPE
+** and getinfo vector is NULL
+** - when getinfo type vector returns -1
+** >=0 on success
+*/
+
+int
+sm_io_getinfo(fp, what, valp)
+ SM_FILE_T *fp;
+ int what;
+ void *valp;
+{
+ SM_FILE_T *v = (SM_FILE_T *) valp;
+
+ SM_REQUIRE_ISA(fp, SmFileMagic);
+
+ switch (what)
+ {
+ case SM_IO_WHAT_VECTORS:
+
+ /* This is the "generic" available for all */
+ v->f_close = fp->f_close;
+ v->f_read = fp->f_read;
+ v->f_seek = fp->f_seek;
+ v->f_write = fp->f_write;
+ v->f_open = fp->f_open;
+ v->f_setinfo = fp->f_setinfo;
+ v->f_getinfo = fp->f_getinfo;
+ v->f_type = fp->f_type;
+ return 0;
+
+ case SM_IO_WHAT_TYPE:
+ if (valp == NULL)
+ {
+ errno = EINVAL;
+ return -1;
+ }
+ valp = sm_strdup_x(fp->f_type);
+ return 0;
+
+ case SM_IO_WHAT_ISTYPE:
+ if (valp == NULL)
+ {
+ errno = EINVAL;
+ return -1;
+ }
+ return strcmp(fp->f_type, valp) == 0;
+
+ case SM_IO_IS_READABLE:
+
+ /* if there is data in the buffer, it must be readable */
+ if (fp->f_r > 0)
+ return 1;
+
+ /* otherwise query the underlying file */
+ break;
+
+ case SM_IO_WHAT_TIMEOUT:
+ *((int *) valp) = fp->f_timeout;
+ return 0;
+
+ case SM_IO_WHAT_FD:
+ if (fp->f_file > -1)
+ return fp->f_file;
+
+ /* try the file type specific getinfo to see if it knows */
+ break;
+ }
+
+ /* Otherwise the vector will check it out */
+ if (fp->f_getinfo == NULL)
+ {
+ errno = EINVAL;
+ return -1;
+ }
+ return (*fp->f_getinfo)(fp, what, valp);
+}
diff --git a/usr/src/cmd/sendmail/libsm/flags.c b/usr/src/cmd/sendmail/libsm/flags.c
new file mode 100644
index 0000000000..8e4ede9546
--- /dev/null
+++ b/usr/src/cmd/sendmail/libsm/flags.c
@@ -0,0 +1,65 @@
+/*
+ * Copyright (c) 2000-2001, 2003 Sendmail, Inc. and its suppliers.
+ * All rights reserved.
+ * Copyright (c) 1990, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Chris Torek.
+ *
+ * By using this file, you agree to the terms and conditions set
+ * forth in the LICENSE file which can be found at the top level of
+ * the sendmail distribution.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#include <sm/gen.h>
+SM_RCSID("@(#)$Id: flags.c,v 1.20.2.1 2003/09/03 18:51:56 ca Exp $")
+#include <sys/types.h>
+#include <sys/file.h>
+#include <errno.h>
+#include <sm/io.h>
+
+/*
+** SM_FLAGS -- translate external (user) flags into internal flags
+**
+** Paramters:
+** flags -- user select flags
+**
+** Returns:
+** Internal flag value matching user selected flags
+*/
+
+int
+sm_flags(flags)
+ register int flags;
+{
+ register int ret;
+
+ switch(SM_IO_MODE(flags))
+ {
+ case SM_IO_RDONLY: /* open for reading */
+ ret = SMRD;
+ break;
+
+ case SM_IO_WRONLY: /* open for writing */
+ ret = SMWR;
+ break;
+
+ case SM_IO_APPEND: /* open for appending */
+ ret = SMWR;
+ break;
+
+ case SM_IO_RDWR: /* open for read and write */
+ ret = SMRW;
+ break;
+
+ default:
+ ret = 0;
+ break;
+ }
+ if (SM_IS_BINARY(flags))
+ ret |= SM_IO_BINARY;
+ return ret;
+}
diff --git a/usr/src/cmd/sendmail/libsm/fopen.c b/usr/src/cmd/sendmail/libsm/fopen.c
new file mode 100644
index 0000000000..cfb3d4baca
--- /dev/null
+++ b/usr/src/cmd/sendmail/libsm/fopen.c
@@ -0,0 +1,376 @@
+/*
+ * Copyright (c) 2000-2002, 2004 Sendmail, Inc. and its suppliers.
+ * All rights reserved.
+ * Copyright (c) 1990, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Chris Torek.
+ *
+ * By using this file, you agree to the terms and conditions set
+ * forth in the LICENSE file which can be found at the top level of
+ * the sendmail distribution.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#include <sm/gen.h>
+SM_RCSID("@(#)$Id: fopen.c,v 1.61 2004/08/03 20:17:38 ca Exp $")
+#include <errno.h>
+#include <setjmp.h>
+#include <sys/time.h>
+#include <sm/heap.h>
+#include <sm/signal.h>
+#include <sm/assert.h>
+#include <sm/io.h>
+#include <sm/clock.h>
+#include "local.h"
+
+static void openalrm __P((int));
+static void reopenalrm __P((int));
+extern int sm_io_fclose __P((SM_FILE_T *));
+
+static jmp_buf OpenTimeOut, ReopenTimeOut;
+
+/*
+** OPENALRM -- handler when timeout activated for sm_io_open()
+**
+** Returns flow of control to where setjmp(OpenTimeOut) was set.
+**
+** Parameters:
+** sig -- unused
+**
+** Returns:
+** does not return
+**
+** Side Effects:
+** returns flow of control to setjmp(OpenTimeOut).
+**
+** NOTE: THIS CAN BE CALLED FROM A SIGNAL HANDLER. DO NOT ADD
+** ANYTHING TO THIS ROUTINE UNLESS YOU KNOW WHAT YOU ARE
+** DOING.
+*/
+
+/* ARGSUSED0 */
+static void
+openalrm(sig)
+ int sig;
+{
+ longjmp(OpenTimeOut, 1);
+}
+/*
+** REOPENALRM -- handler when timeout activated for sm_io_reopen()
+**
+** Returns flow of control to where setjmp(ReopenTimeOut) was set.
+**
+** Parameters:
+** sig -- unused
+**
+** Returns:
+** does not return
+**
+** Side Effects:
+** returns flow of control to setjmp(ReopenTimeOut).
+**
+** NOTE: THIS CAN BE CALLED FROM A SIGNAL HANDLER. DO NOT ADD
+** ANYTHING TO THIS ROUTINE UNLESS YOU KNOW WHAT YOU ARE
+** DOING.
+*/
+
+/* ARGSUSED0 */
+static void
+reopenalrm(sig)
+ int sig;
+{
+ longjmp(ReopenTimeOut, 1);
+}
+
+/*
+** SM_IO_OPEN -- open a file of a specific type
+**
+** Parameters:
+** type -- type of file to open
+** timeout -- time to complete the open
+** info -- info describing what is to be opened (type dependant)
+** flags -- user selected flags
+** rpool -- pointer to rpool to be used for this open
+**
+** Returns:
+** Raises exception on heap exhaustion.
+** Aborts if type is invalid.
+** Returns NULL and sets errno
+** - when the type specific open fails
+** - when open vector errors
+** - when flags not set or invalid
+** Success returns a file pointer to the opened file type.
+*/
+
+SM_FILE_T *
+sm_io_open(type, timeout, info, flags, rpool)
+ const SM_FILE_T *type;
+ int SM_NONVOLATILE timeout; /* this is not the file type timeout */
+ const void *info;
+ int flags;
+ const void *rpool;
+{
+ register SM_FILE_T *fp;
+ int ioflags;
+ SM_EVENT *evt = NULL;
+
+ ioflags = sm_flags(flags);
+
+ if (ioflags == 0)
+ {
+ /* must give some indication/intent */
+ errno = EINVAL;
+ return NULL;
+ }
+
+ if (timeout == SM_TIME_DEFAULT)
+ timeout = SM_TIME_FOREVER;
+ if (timeout == SM_TIME_IMMEDIATE)
+ {
+ errno = EAGAIN;
+ return NULL;
+ }
+
+ fp = sm_fp(type, ioflags, NULL);
+
+ /* Okay, this is where we set the timeout. */
+ if (timeout != SM_TIME_FOREVER)
+ {
+ if (setjmp(OpenTimeOut) != 0)
+ {
+ errno = EAGAIN;
+ return NULL;
+ }
+ evt = sm_seteventm(timeout, openalrm, 0);
+ }
+
+ if ((*fp->f_open)(fp, info, flags, rpool) < 0)
+ {
+ fp->f_flags = 0; /* release */
+ fp->sm_magic = NULL; /* release */
+ return NULL;
+ }
+
+ /* We're back. So undo our timeout and handler */
+ if (evt != NULL)
+ sm_clrevent(evt);
+
+#if SM_RPOOL
+ if (rpool != NULL)
+ sm_rpool_attach_x(rpool, sm_io_fclose, fp);
+#endif /* SM_RPOOL */
+
+ return fp;
+}
+/*
+** SM_IO_DUP -- duplicate a file pointer
+**
+** Parameters:
+** fp -- file pointer to duplicate
+**
+** Returns:
+** Success - the duplicated file pointer
+** Failure - NULL (was an invalid file pointer or too many open)
+**
+** Increments the duplicate counter (dup_cnt) for the open file pointer.
+** The counter counts the number of duplicates. When the duplicate
+** counter is 0 (zero) then the file pointer is the only one left
+** (no duplicates, it is the only one).
+*/
+
+SM_FILE_T *
+sm_io_dup(fp)
+ SM_FILE_T *fp;
+{
+
+ SM_REQUIRE_ISA(fp, SmFileMagic);
+ if (fp->sm_magic != SmFileMagic)
+ {
+ errno = EBADF;
+ return NULL;
+ }
+ if (fp->f_dup_cnt >= INT_MAX - 1)
+ {
+ /* Can't let f_dup_cnt wrap! */
+ errno = EMFILE;
+ return NULL;
+ }
+ fp->f_dup_cnt++;
+ return fp;
+}
+/*
+** SM_IO_REOPEN -- open a new file using the old file pointer
+**
+** Parameters:
+** type -- file type to be opened
+** timeout -- time to complete the reopen
+** info -- infomation about what is to be "re-opened" (type dep.)
+** flags -- user flags to map to internal flags
+** rpool -- rpool file to be associated with
+** fp -- the file pointer to reuse
+**
+** Returns:
+** Raises an exception on heap exhaustion.
+** Aborts if type is invalid.
+** Failure: returns NULL
+** Success: returns "reopened" file pointer
+*/
+
+SM_FILE_T *
+sm_io_reopen(type, timeout, info, flags, rpool, fp)
+ const SM_FILE_T *type;
+ int SM_NONVOLATILE timeout;
+ const void *info;
+ int flags;
+ const void *rpool;
+ SM_FILE_T *fp;
+{
+ int ioflags, ret;
+ SM_FILE_T *fp2;
+ SM_EVENT *evt = NULL;
+
+ if ((ioflags = sm_flags(flags)) == 0)
+ {
+ (void) sm_io_close(fp, timeout);
+ return NULL;
+ }
+
+ if (!Sm_IO_DidInit)
+ sm_init();
+
+ if (timeout == SM_TIME_DEFAULT)
+ timeout = SM_TIME_FOREVER;
+ if (timeout == SM_TIME_IMMEDIATE)
+ {
+ /*
+ ** Filling the buffer will take time and we are wanted to
+ ** return immediately. So...
+ */
+
+ errno = EAGAIN;
+ return NULL;
+ }
+ /* Okay, this is where we set the timeout. */
+ if (timeout != SM_TIME_FOREVER)
+ {
+ if (setjmp(ReopenTimeOut) != 0)
+ {
+ errno = EAGAIN;
+ return NULL;
+ }
+
+ evt = sm_seteventm(timeout, reopenalrm, 0);
+ }
+
+ /*
+ ** There are actually programs that depend on being able to "reopen"
+ ** descriptors that weren't originally open. Keep this from breaking.
+ ** Remember whether the stream was open to begin with, and which file
+ ** descriptor (if any) was associated with it. If it was attached to
+ ** a descriptor, defer closing it; reopen("/dev/stdin", "r", stdin)
+ ** should work. This is unnecessary if it was not a Unix file.
+ */
+
+ if (fp != NULL)
+ {
+ if (fp->sm_magic != SmFileMagic)
+ fp->f_flags = SMFEOF; /* hold on to it */
+ else
+ {
+ /* flush the stream; ANSI doesn't require this. */
+ (void) sm_io_flush(fp, SM_TIME_FOREVER);
+ (void) sm_io_close(fp, SM_TIME_FOREVER);
+ }
+ }
+
+ fp2 = sm_fp(type, ioflags, fp);
+ ret = (*fp2->f_open)(fp2, info, flags, rpool);
+
+ /* We're back. So undo our timeout and handler */
+ if (evt != NULL)
+ sm_clrevent(evt);
+
+ if (ret < 0)
+ {
+ fp2->f_flags = 0; /* release */
+ fp2->sm_magic = NULL; /* release */
+ return NULL;
+ }
+
+ /*
+ ** We're not preserving this logic (below) for sm_io because it is now
+ ** abstracted at least one "layer" away. By closing and reopening
+ ** the 1st fd used should be the just released one (when Unix
+ ** behavior followed). Old comment::
+ ** If reopening something that was open before on a real file, try
+ ** to maintain the descriptor. Various C library routines (perror)
+ ** assume stderr is always fd STDERR_FILENO, even if being reopen'd.
+ */
+
+#if SM_RPOOL
+ if (rpool != NULL)
+ sm_rpool_attach_x(rpool, sm_io_close, fp2);
+#endif /* SM_RPOOL */
+
+ return fp2;
+}
+/*
+** SM_IO_AUTOFLUSH -- link another file to this for auto-flushing
+**
+** When a read occurs on fp, fp2 will be flushed iff there is no
+** data waiting on fp.
+**
+** Parameters:
+** fp -- the file opened for reading.
+** fp2 -- the file opened for writing.
+**
+** Returns:
+** The old flush file pointer.
+*/
+
+SM_FILE_T *
+sm_io_autoflush(fp, fp2)
+ SM_FILE_T *fp;
+ SM_FILE_T *fp2;
+{
+ SM_FILE_T *savefp;
+
+ SM_REQUIRE_ISA(fp, SmFileMagic);
+ if (fp2 != NULL)
+ SM_REQUIRE_ISA(fp2, SmFileMagic);
+
+ savefp = fp->f_flushfp;
+ fp->f_flushfp = fp2;
+ return savefp;
+}
+/*
+** SM_IO_AUTOMODE -- link another file to this for auto-moding
+**
+** When the mode (blocking or non-blocking) changes for fp1 then
+** update fp2's mode at the same time. This is to be used when
+** a system dup() has generated a second file descriptor for
+** another sm_io_open() by file descriptor. The modes have been
+** linked in the system and this formalizes it for sm_io internally.
+**
+** Parameters:
+** fp1 -- the first file
+** fp2 -- the second file
+**
+** Returns:
+** nothing
+*/
+
+void
+sm_io_automode(fp1, fp2)
+ SM_FILE_T *fp1;
+ SM_FILE_T *fp2;
+{
+ SM_REQUIRE_ISA(fp1, SmFileMagic);
+ SM_REQUIRE_ISA(fp2, SmFileMagic);
+
+ fp1->f_modefp = fp2;
+ fp2->f_modefp = fp1;
+}
diff --git a/usr/src/cmd/sendmail/libsm/fpos.c b/usr/src/cmd/sendmail/libsm/fpos.c
new file mode 100644
index 0000000000..b443015626
--- /dev/null
+++ b/usr/src/cmd/sendmail/libsm/fpos.c
@@ -0,0 +1,155 @@
+/*
+ * Copyright (c) 2000-2001, 2004 Sendmail, Inc. and its suppliers.
+ * All rights reserved.
+ * Copyright (c) 1990, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Chris Torek.
+ *
+ * By using this file, you agree to the terms and conditions set
+ * forth in the LICENSE file which can be found at the top level of
+ * the sendmail distribution.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#include <sm/gen.h>
+SM_RCSID("@(#)$Id: fpos.c,v 1.38 2004/08/03 20:17:38 ca Exp $")
+#include <errno.h>
+#include <setjmp.h>
+#include <sys/time.h>
+#include <sm/heap.h>
+#include <sm/signal.h>
+#include <sm/clock.h>
+#include <sm/io.h>
+#include <sm/assert.h>
+#include "local.h"
+
+static void tellalrm __P((int));
+static jmp_buf TellTimeOut;
+
+/*
+** TELLALRM -- handler when timeout activated for sm_io_tell()
+**
+** Returns flow of control to where setjmp(TellTimeOut) was set.
+**
+** Parameters:
+** sig -- unused
+**
+** Returns:
+** does not return
+**
+** Side Effects:
+** returns flow of control to setjmp(TellTimeOut).
+**
+** NOTE: THIS CAN BE CALLED FROM A SIGNAL HANDLER. DO NOT ADD
+** ANYTHING TO THIS ROUTINE UNLESS YOU KNOW WHAT YOU ARE
+** DOING.
+*/
+
+/* ARGSUSED0 */
+static void
+tellalrm(sig)
+ int sig;
+{
+ longjmp(TellTimeOut, 1);
+}
+
+/*
+** SM_IO_TELL -- position the file pointer
+**
+** Paramters:
+** fp -- the file pointer to get repositioned
+** timeout -- time to complete the tell (milliseconds)
+**
+** Returns:
+** Success -- the repositioned location.
+** Failure -- -1 (minus 1) and sets errno
+*/
+
+long
+sm_io_tell(fp, timeout)
+ register SM_FILE_T *fp;
+ int SM_NONVOLATILE timeout;
+{
+ register off_t pos;
+ SM_EVENT *evt = NULL;
+
+ SM_REQUIRE_ISA(fp, SmFileMagic);
+ if (fp->f_seek == NULL)
+ {
+ errno = ESPIPE; /* historic practice */
+ return -1L;
+ }
+
+ if (timeout == SM_TIME_DEFAULT)
+ timeout = fp->f_timeout;
+ if (timeout == SM_TIME_IMMEDIATE)
+ {
+ /*
+ ** Filling the buffer will take time and we are wanted to
+ ** return immediately. So...
+ */
+
+ errno = EAGAIN;
+ return -1L;
+ }
+
+ /*
+ ** Find offset of underlying I/O object, then adjust byte position
+ ** may adjust seek offset on append stream
+ */
+
+ (void) sm_flush(fp, (int *) &timeout);
+
+ /* This is where we start the timeout */
+ if (timeout != SM_TIME_FOREVER)
+ {
+ if (setjmp(TellTimeOut) != 0)
+ {
+ errno = EAGAIN;
+ return -1L;
+ }
+
+ evt = sm_seteventm(timeout, tellalrm, 0);
+ }
+
+ if (fp->f_flags & SMOFF)
+ pos = fp->f_lseekoff;
+ else
+ {
+ /* XXX only set the timeout here? */
+ pos = (*fp->f_seek)(fp, (off_t) 0, SM_IO_SEEK_CUR);
+ if (pos == -1L)
+ goto clean;
+ }
+ if (fp->f_flags & SMRD)
+ {
+ /*
+ ** Reading. Any unread characters (including
+ ** those from ungetc) cause the position to be
+ ** smaller than that in the underlying object.
+ */
+
+ pos -= fp->f_r;
+ if (HASUB(fp))
+ pos -= fp->f_ur;
+ }
+ else if (fp->f_flags & SMWR && fp->f_p != NULL)
+ {
+ /*
+ ** Writing. Any buffered characters cause the
+ ** position to be greater than that in the
+ ** underlying object.
+ */
+
+ pos += fp->f_p - fp->f_bf.smb_base;
+ }
+
+clean:
+ /* We're back. So undo our timeout and handler */
+ if (evt != NULL)
+ sm_clrevent(evt);
+ return pos;
+}
diff --git a/usr/src/cmd/sendmail/libsm/fprintf.c b/usr/src/cmd/sendmail/libsm/fprintf.c
new file mode 100644
index 0000000000..591e09fc09
--- /dev/null
+++ b/usr/src/cmd/sendmail/libsm/fprintf.c
@@ -0,0 +1,57 @@
+/*
+ * Copyright (c) 2000-2001 Sendmail, Inc. and its suppliers.
+ * All rights reserved.
+ * Copyright (c) 1990, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Chris Torek.
+ *
+ * By using this file, you agree to the terms and conditions set
+ * forth in the LICENSE file which can be found at the top level of
+ * the sendmail distribution.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#include <sm/gen.h>
+SM_RCSID("@(#)$Id: fprintf.c,v 1.15 2001/03/02 23:53:41 ca Exp $")
+#include <sm/varargs.h>
+#include <sm/io.h>
+#include <sm/assert.h>
+#include "local.h"
+
+/*
+** SM_IO_FPRINTF -- format and print a string to a file pointer
+**
+** Parameters:
+** fp -- file pointer to be printed to
+** timeout -- time to complete print
+** fmt -- markup format for the string to be printed
+** ... -- additional information for 'fmt'
+**
+** Returns:
+** Failure: returns SM_IO_EOF and sets errno
+** Success: returns the number of characters o/p
+*/
+
+int
+#if SM_VA_STD
+sm_io_fprintf(SM_FILE_T *fp, int timeout, const char *fmt, ...)
+#else /* SM_VA_STD */
+sm_io_fprintf(fp, timeout, fmt, va_alist)
+ SM_FILE_T *fp;
+ int timeout;
+ char *fmt;
+ va_dcl
+#endif /* SM_VA_STD */
+{
+ int ret;
+ SM_VA_LOCAL_DECL
+
+ SM_REQUIRE_ISA(fp, SmFileMagic);
+ SM_VA_START(ap, fmt);
+ ret = sm_io_vfprintf(fp, timeout, fmt, ap);
+ SM_VA_END(ap);
+ return ret;
+}
diff --git a/usr/src/cmd/sendmail/libsm/fpurge.c b/usr/src/cmd/sendmail/libsm/fpurge.c
new file mode 100644
index 0000000000..4484a4d546
--- /dev/null
+++ b/usr/src/cmd/sendmail/libsm/fpurge.c
@@ -0,0 +1,55 @@
+/*
+ * Copyright (c) 2000-2001 Sendmail, Inc. and its suppliers.
+ * All rights reserved.
+ * Copyright (c) 1990, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Chris Torek.
+ *
+ * By using this file, you agree to the terms and conditions set
+ * forth in the LICENSE file which can be found at the top level of
+ * the sendmail distribution.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#include <sm/gen.h>
+SM_RCSID("@(#)$Id: fpurge.c,v 1.18 2001/01/28 00:29:35 ca Exp $")
+#include <stdlib.h>
+#include <errno.h>
+#include <sm/io.h>
+#include <sm/assert.h>
+#include "local.h"
+
+/*
+** SM_IO_PURGE -- purge/empty the buffer without committing buffer content
+**
+** Parameters:
+** fp -- file pointer to purge
+**
+** Returns:
+** Failure: returns SM_IO_EOF and sets errno
+** Success: returns 0 (zero)
+*/
+
+int
+sm_io_purge(fp)
+ register SM_FILE_T *fp;
+{
+ SM_REQUIRE_ISA(fp, SmFileMagic);
+ if (!fp->f_flags)
+ {
+ errno = EBADF;
+ return SM_IO_EOF;
+ }
+
+ if (HASUB(fp))
+ FREEUB(fp);
+ fp->f_p = fp->f_bf.smb_base;
+ fp->f_r = 0;
+
+ /* implies SMFBF */
+ fp->f_w = fp->f_flags & (SMLBF|SMNBF) ? 0 : fp->f_bf.smb_size;
+ return 0;
+}
diff --git a/usr/src/cmd/sendmail/libsm/fput.c b/usr/src/cmd/sendmail/libsm/fput.c
new file mode 100644
index 0000000000..6a787758eb
--- /dev/null
+++ b/usr/src/cmd/sendmail/libsm/fput.c
@@ -0,0 +1,54 @@
+/*
+ * Copyright (c) 2000-2001 Sendmail, Inc. and its suppliers.
+ * All rights reserved.
+ * Copyright (c) 1990, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Chris Torek.
+ *
+ * By using this file, you agree to the terms and conditions set
+ * forth in the LICENSE file which can be found at the top level of
+ * the sendmail distribution.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#include <sm/gen.h>
+SM_RCSID("@(#)$Id: fput.c,v 1.18 2001/01/28 00:29:35 ca Exp $")
+#include <string.h>
+#include <errno.h>
+#include <sm/io.h>
+#include <sm/assert.h>
+#include "local.h"
+#include "fvwrite.h"
+
+/*
+** SM_IO_FPUTS -- add a string to the buffer for the file pointer
+**
+** Parameters:
+** fp -- the file pointer for the buffer to be written to
+** timeout -- time to complete the put-string
+** s -- string to be placed in the buffer
+**
+** Returns:
+** Failure: returns SM_IO_EOF
+** Success: returns 0 (zero)
+*/
+
+int
+sm_io_fputs(fp, timeout, s)
+ SM_FILE_T *fp;
+ int timeout;
+ const char *s;
+{
+ struct sm_uio uio;
+ struct sm_iov iov;
+
+ SM_REQUIRE_ISA(fp, SmFileMagic);
+ iov.iov_base = (void *) s;
+ iov.iov_len = uio.uio_resid = strlen(s);
+ uio.uio_iov = &iov;
+ uio.uio_iovcnt = 1;
+ return sm_fvwrite(fp, timeout, &uio);
+}
diff --git a/usr/src/cmd/sendmail/libsm/fread.c b/usr/src/cmd/sendmail/libsm/fread.c
new file mode 100644
index 0000000000..40cd23cfcd
--- /dev/null
+++ b/usr/src/cmd/sendmail/libsm/fread.c
@@ -0,0 +1,102 @@
+/*
+ * Copyright (c) 2000-2001 Sendmail, Inc. and its suppliers.
+ * All rights reserved.
+ * Copyright (c) 1990, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Chris Torek.
+ *
+ * By using this file, you agree to the terms and conditions set
+ * forth in the LICENSE file which can be found at the top level of
+ * the sendmail distribution.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#include <sm/gen.h>
+SM_RCSID("@(#)$Id: fread.c,v 1.26 2001/02/28 20:54:03 ca Exp $")
+#include <string.h>
+#include <errno.h>
+#include <sm/io.h>
+#include <sm/assert.h>
+#include "local.h"
+
+/*
+** SM_IO_READ -- read data from the file pointer
+**
+** Parameters:
+** fp -- file pointer to read from
+** timeout -- time to complete the read
+** buf -- location to place read data
+** size -- size of each chunk of data
+**
+** Returns:
+** Failure: returns 0 (zero) _and_ sets errno
+** Success: returns the number of whole chunks read.
+**
+** A read returning 0 (zero) is only an indication of error when errno
+** has been set.
+*/
+
+size_t
+sm_io_read(fp, timeout, buf, size)
+ SM_FILE_T *fp;
+ int timeout;
+ void *buf;
+ size_t size;
+{
+ register size_t resid = size;
+ register char *p;
+ register int r;
+
+ SM_REQUIRE_ISA(fp, SmFileMagic);
+
+ if (fp->f_read == NULL)
+ {
+ errno = ENODEV;
+ return 0;
+ }
+
+ /*
+ ** The ANSI standard requires a return value of 0 for a count
+ ** or a size of 0. Peculiarily, it imposes no such requirements
+ ** on fwrite; it only requires read to be broken.
+ */
+
+ if (resid == 0)
+ return 0;
+ if (fp->f_r < 0)
+ fp->f_r = 0;
+ p = buf;
+ while ((int) resid > (r = fp->f_r))
+ {
+ (void) memcpy((void *) p, (void *) fp->f_p, (size_t) r);
+ fp->f_p += r;
+ /* fp->f_r = 0 ... done in sm_refill */
+ p += r;
+ resid -= r;
+ if ((fp->f_flags & SMNOW) != 0 && r > 0)
+ {
+ /*
+ ** Take whatever we have available. Spend no more time
+ ** trying to get all that has been requested.
+ ** This is needed on some file types (such as
+ ** SASL) that would jam when given extra, untimely
+ ** reads.
+ */
+
+ fp->f_r -= r;
+ return size - resid;
+ }
+ if (sm_refill(fp, timeout) != 0)
+ {
+ /* no more input: return partial result */
+ return size - resid;
+ }
+ }
+ (void) memcpy((void *) p, (void *) fp->f_p, resid);
+ fp->f_r -= resid;
+ fp->f_p += resid;
+ return size;
+}
diff --git a/usr/src/cmd/sendmail/libsm/fscanf.c b/usr/src/cmd/sendmail/libsm/fscanf.c
new file mode 100644
index 0000000000..c456740c66
--- /dev/null
+++ b/usr/src/cmd/sendmail/libsm/fscanf.c
@@ -0,0 +1,57 @@
+/*
+ * Copyright (c) 2000-2001 Sendmail, Inc. and its suppliers.
+ * All rights reserved.
+ * Copyright (c) 1990, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Chris Torek.
+ *
+ * By using this file, you agree to the terms and conditions set
+ * forth in the LICENSE file which can be found at the top level of
+ * the sendmail distribution.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#include <sm/gen.h>
+SM_RCSID("@(#)$Id: fscanf.c,v 1.15 2001/03/02 23:53:41 ca Exp $")
+#include <sm/varargs.h>
+#include <sm/assert.h>
+#include <sm/io.h>
+#include "local.h"
+
+/*
+** SM_IO_FSCANF -- convert input data to translated format
+**
+** Parameters:
+** fp -- the file pointer to obtain the data from
+** timeout -- time to complete scan
+** fmt -- the format to translate the data to
+** ... -- memory locations to place the formated data
+**
+** Returns:
+** Failure: returns SM_IO_EOF
+** Success: returns the number of data units translated
+*/
+
+int
+#if SM_VA_STD
+sm_io_fscanf(SM_FILE_T *fp, int timeout, char const *fmt, ...)
+#else /* SM_VA_STD */
+sm_io_fscanf(fp, timeout, fmt, va_alist)
+ SM_FILE_T *fp;
+ int timeout;
+ char *fmt;
+ va_dcl
+#endif /* SM_VA_STD */
+{
+ int ret;
+ SM_VA_LOCAL_DECL
+
+ SM_REQUIRE_ISA(fp, SmFileMagic);
+ SM_VA_START(ap, fmt);
+ ret = sm_vfscanf(fp, timeout, fmt, ap);
+ SM_VA_END(ap);
+ return ret;
+}
diff --git a/usr/src/cmd/sendmail/libsm/fseek.c b/usr/src/cmd/sendmail/libsm/fseek.c
new file mode 100644
index 0000000000..d461aba89b
--- /dev/null
+++ b/usr/src/cmd/sendmail/libsm/fseek.c
@@ -0,0 +1,338 @@
+/*
+ * Copyright (c) 2000-2001, 2004 Sendmail, Inc. and its suppliers.
+ * All rights reserved.
+ * Copyright (c) 1990, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Chris Torek.
+ *
+ * By using this file, you agree to the terms and conditions set
+ * forth in the LICENSE file which can be found at the top level of
+ * the sendmail distribution.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#include <sm/gen.h>
+SM_RCSID("@(#)$Id: fseek.c,v 1.46 2004/08/03 20:17:38 ca Exp $")
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <setjmp.h>
+#include <sys/time.h>
+#include <sm/signal.h>
+#include <sm/io.h>
+#include <sm/assert.h>
+#include <sm/clock.h>
+#include "local.h"
+
+#define POS_ERR (-(off_t)1)
+
+static void seekalrm __P((int));
+static jmp_buf SeekTimeOut;
+
+/*
+** SEEKALRM -- handler when timeout activated for sm_io_seek()
+**
+** Returns flow of control to where setjmp(SeekTimeOut) was set.
+**
+** Parameters:
+** sig -- unused
+**
+** Returns:
+** does not return
+**
+** Side Effects:
+** returns flow of control to setjmp(SeekTimeOut).
+**
+** NOTE: THIS CAN BE CALLED FROM A SIGNAL HANDLER. DO NOT ADD
+** ANYTHING TO THIS ROUTINE UNLESS YOU KNOW WHAT YOU ARE
+** DOING.
+*/
+
+/* ARGSUSED0 */
+static void
+seekalrm(sig)
+ int sig;
+{
+ longjmp(SeekTimeOut, 1);
+}
+
+/*
+** SM_IO_SEEK -- position the file pointer
+**
+** Parameters:
+** fp -- the file pointer to be seek'd
+** timeout -- time to complete seek (milliseconds)
+** offset -- seek offset based on 'whence'
+** whence -- indicates where seek is relative from.
+** One of SM_IO_SEEK_{CUR,SET,END}.
+** Returns:
+** Failure: returns -1 (minus 1) and sets errno
+** Success: returns 0 (zero)
+*/
+
+int
+sm_io_seek(fp, timeout, offset, whence)
+ register SM_FILE_T *fp;
+ int SM_NONVOLATILE timeout;
+ long SM_NONVOLATILE offset;
+ int SM_NONVOLATILE whence;
+{
+ bool havepos;
+ off_t target, curoff;
+ size_t n;
+ struct stat st;
+ int ret;
+ SM_EVENT *evt = NULL;
+ register off_t (*seekfn) __P((SM_FILE_T *, off_t, int));
+
+ SM_REQUIRE_ISA(fp, SmFileMagic);
+
+ /* make sure stdio is set up */
+ if (!Sm_IO_DidInit)
+ sm_init();
+
+ /* Have to be able to seek. */
+ if ((seekfn = fp->f_seek) == NULL)
+ {
+ errno = ESPIPE; /* historic practice */
+ return -1;
+ }
+
+ if (timeout == SM_TIME_DEFAULT)
+ timeout = fp->f_timeout;
+ if (timeout == SM_TIME_IMMEDIATE)
+ {
+ /*
+ ** Filling the buffer will take time and we are wanted to
+ ** return immediately. So...
+ */
+
+ errno = EAGAIN;
+ return -1;
+ }
+
+#define SM_SET_ALARM() \
+ if (timeout != SM_TIME_FOREVER) \
+ { \
+ if (setjmp(SeekTimeOut) != 0) \
+ { \
+ errno = EAGAIN; \
+ return -1; \
+ } \
+ evt = sm_seteventm(timeout, seekalrm, 0); \
+ }
+
+ /*
+ ** Change any SM_IO_SEEK_CUR to SM_IO_SEEK_SET, and check `whence'
+ ** argument. After this, whence is either SM_IO_SEEK_SET or
+ ** SM_IO_SEEK_END.
+ */
+
+ switch (whence)
+ {
+ case SM_IO_SEEK_CUR:
+
+ /*
+ ** In order to seek relative to the current stream offset,
+ ** we have to first find the current stream offset a la
+ ** ftell (see ftell for details).
+ */
+
+ /* may adjust seek offset on append stream */
+ sm_flush(fp, (int *) &timeout);
+ SM_SET_ALARM();
+ if (fp->f_flags & SMOFF)
+ curoff = fp->f_lseekoff;
+ else
+ {
+ curoff = (*seekfn)(fp, (off_t) 0, SM_IO_SEEK_CUR);
+ if (curoff == -1L)
+ {
+ ret = -1;
+ goto clean;
+ }
+ }
+ if (fp->f_flags & SMRD)
+ {
+ curoff -= fp->f_r;
+ if (HASUB(fp))
+ curoff -= fp->f_ur;
+ }
+ else if (fp->f_flags & SMWR && fp->f_p != NULL)
+ curoff += fp->f_p - fp->f_bf.smb_base;
+
+ offset += curoff;
+ whence = SM_IO_SEEK_SET;
+ havepos = true;
+ break;
+
+ case SM_IO_SEEK_SET:
+ case SM_IO_SEEK_END:
+ SM_SET_ALARM();
+ curoff = 0; /* XXX just to keep gcc quiet */
+ havepos = false;
+ break;
+
+ default:
+ errno = EINVAL;
+ return -1;
+ }
+
+ /*
+ ** Can only optimise if:
+ ** reading (and not reading-and-writing);
+ ** not unbuffered; and
+ ** this is a `regular' Unix file (and hence seekfn==sm_stdseek).
+ ** We must check SMNBF first, because it is possible to have SMNBF
+ ** and SMSOPT both set.
+ */
+
+ if (fp->f_bf.smb_base == NULL)
+ sm_makebuf(fp);
+ if (fp->f_flags & (SMWR | SMRW | SMNBF | SMNPT))
+ goto dumb;
+ if ((fp->f_flags & SMOPT) == 0)
+ {
+ if (seekfn != sm_stdseek ||
+ fp->f_file < 0 || fstat(fp->f_file, &st) ||
+ (st.st_mode & S_IFMT) != S_IFREG)
+ {
+ fp->f_flags |= SMNPT;
+ goto dumb;
+ }
+ fp->f_blksize = st.st_blksize;
+ fp->f_flags |= SMOPT;
+ }
+
+ /*
+ ** We are reading; we can try to optimise.
+ ** Figure out where we are going and where we are now.
+ */
+
+ if (whence == SM_IO_SEEK_SET)
+ target = offset;
+ else
+ {
+ if (fstat(fp->f_file, &st))
+ goto dumb;
+ target = st.st_size + offset;
+ }
+
+ if (!havepos)
+ {
+ if (fp->f_flags & SMOFF)
+ curoff = fp->f_lseekoff;
+ else
+ {
+ curoff = (*seekfn)(fp, (off_t) 0, SM_IO_SEEK_CUR);
+ if (curoff == POS_ERR)
+ goto dumb;
+ }
+ curoff -= fp->f_r;
+ if (HASUB(fp))
+ curoff -= fp->f_ur;
+ }
+
+ /*
+ ** Compute the number of bytes in the input buffer (pretending
+ ** that any ungetc() input has been discarded). Adjust current
+ ** offset backwards by this count so that it represents the
+ ** file offset for the first byte in the current input buffer.
+ */
+
+ if (HASUB(fp))
+ {
+ curoff += fp->f_r; /* kill off ungetc */
+ n = fp->f_up - fp->f_bf.smb_base;
+ curoff -= n;
+ n += fp->f_ur;
+ }
+ else
+ {
+ n = fp->f_p - fp->f_bf.smb_base;
+ curoff -= n;
+ n += fp->f_r;
+ }
+
+ /*
+ ** If the target offset is within the current buffer,
+ ** simply adjust the pointers, clear SMFEOF, undo ungetc(),
+ ** and return. (If the buffer was modified, we have to
+ ** skip this; see getln in fget.c.)
+ */
+
+ if (target >= curoff && target < curoff + (off_t) n)
+ {
+ register int o = target - curoff;
+
+ fp->f_p = fp->f_bf.smb_base + o;
+ fp->f_r = n - o;
+ if (HASUB(fp))
+ FREEUB(fp);
+ fp->f_flags &= ~SMFEOF;
+ ret = 0;
+ goto clean;
+ }
+
+ /*
+ ** The place we want to get to is not within the current buffer,
+ ** but we can still be kind to the kernel copyout mechanism.
+ ** By aligning the file offset to a block boundary, we can let
+ ** the kernel use the VM hardware to map pages instead of
+ ** copying bytes laboriously. Using a block boundary also
+ ** ensures that we only read one block, rather than two.
+ */
+
+ curoff = target & ~(fp->f_blksize - 1);
+ if ((*seekfn)(fp, curoff, SM_IO_SEEK_SET) == POS_ERR)
+ goto dumb;
+ fp->f_r = 0;
+ fp->f_p = fp->f_bf.smb_base;
+ if (HASUB(fp))
+ FREEUB(fp);
+ fp->f_flags &= ~SMFEOF;
+ n = target - curoff;
+ if (n)
+ {
+ /* Note: SM_TIME_FOREVER since fn timeout already set */
+ if (sm_refill(fp, SM_TIME_FOREVER) || fp->f_r < (int) n)
+ goto dumb;
+ fp->f_p += n;
+ fp->f_r -= n;
+ }
+
+ ret = 0;
+clean:
+ /* We're back. So undo our timeout and handler */
+ if (evt != NULL)
+ sm_clrevent(evt);
+ return ret;
+dumb:
+ /*
+ ** We get here if we cannot optimise the seek ... just
+ ** do it. Allow the seek function to change fp->f_bf.smb_base.
+ */
+
+ /* Note: SM_TIME_FOREVER since fn timeout already set */
+ ret = SM_TIME_FOREVER;
+ if (sm_flush(fp, &ret) != 0 ||
+ (*seekfn)(fp, (off_t) offset, whence) == POS_ERR)
+ {
+ ret = -1;
+ goto clean;
+ }
+
+ /* success: clear SMFEOF indicator and discard ungetc() data */
+ if (HASUB(fp))
+ FREEUB(fp);
+ fp->f_p = fp->f_bf.smb_base;
+ fp->f_r = 0;
+ fp->f_flags &= ~SMFEOF;
+ ret = 0;
+ goto clean;
+}
diff --git a/usr/src/cmd/sendmail/libsm/fvwrite.c b/usr/src/cmd/sendmail/libsm/fvwrite.c
new file mode 100644
index 0000000000..4cdd022fe0
--- /dev/null
+++ b/usr/src/cmd/sendmail/libsm/fvwrite.c
@@ -0,0 +1,281 @@
+/*
+ * Copyright (c) 2000-2001 Sendmail, Inc. and its suppliers.
+ * All rights reserved.
+ * Copyright (c) 1990, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Chris Torek.
+ *
+ * By using this file, you agree to the terms and conditions set
+ * forth in the LICENSE file which can be found at the top level of
+ * the sendmail distribution.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#include <sm/gen.h>
+SM_RCSID("@(#)$Id: fvwrite.c,v 1.47 2001/08/27 13:02:20 ca Exp $")
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include <errno.h>
+#include <signal.h>
+#include <fcntl.h>
+#include <sm/io.h>
+#include <sm/setjmp.h>
+#include <sm/conf.h>
+#include "local.h"
+#include "fvwrite.h"
+
+/*
+** SM_FVWRITE -- write memory regions and buffer for file pointer
+**
+** Parameters:
+** fp -- the file pointer to write to
+** timeout -- time length for function to return by
+** uio -- the memory regions to write
+**
+** Returns:
+** Failure: returns SM_IO_EOF and sets errno
+** Success: returns 0 (zero)
+**
+** This routine is large and unsightly, but most of the ugliness due
+** to the different kinds of output buffering handled here.
+*/
+
+#define COPY(n) (void)memcpy((void *)fp->f_p, (void *)p, (size_t)(n))
+#define GETIOV(extra_work) \
+ while (len == 0) \
+ { \
+ extra_work; \
+ p = iov->iov_base; \
+ len = iov->iov_len; \
+ iov++; \
+ }
+
+int
+sm_fvwrite(fp, timeout, uio)
+ register SM_FILE_T *fp;
+ int timeout;
+ register struct sm_uio *uio;
+{
+ register size_t len;
+ register char *p;
+ register struct sm_iov *iov;
+ register int w, s;
+ char *nl;
+ int nlknown, nldist;
+ int fd;
+ struct timeval to;
+
+ if (uio->uio_resid == 0)
+ return 0;
+
+ /* make sure we can write */
+ if (cantwrite(fp))
+ {
+ errno = EBADF;
+ return SM_IO_EOF;
+ }
+
+ SM_CONVERT_TIME(fp, fd, timeout, &to);
+
+ iov = uio->uio_iov;
+ p = iov->iov_base;
+ len = iov->iov_len;
+ iov++;
+ if (fp->f_flags & SMNBF)
+ {
+ /* Unbuffered: write up to BUFSIZ bytes at a time. */
+ do
+ {
+ GETIOV(;);
+ errno = 0; /* needed to ensure EOF correctly found */
+ w = (*fp->f_write)(fp, p, SM_MIN(len, SM_IO_BUFSIZ));
+ if (w <= 0)
+ {
+ if (w == 0 && errno == 0)
+ break; /* EOF found */
+ if (IS_IO_ERROR(fd, w, timeout))
+ goto err; /* errno set */
+
+ /* write would block */
+ SM_IO_WR_TIMEOUT(fp, fd, timeout);
+ w = 0;
+ }
+ else
+ {
+ p += w;
+ len -= w;
+ }
+ } while ((uio->uio_resid -= w) != 0);
+ }
+ else if ((fp->f_flags & SMLBF) == 0)
+ {
+ /*
+ ** Not SMLBF (line-buffered). Either SMFBF or SMNOW
+ ** buffered: fill partially full buffer, if any,
+ ** and then flush. If there is no partial buffer, write
+ ** one bf._size byte chunk directly (without copying).
+ **
+ ** String output is a special case: write as many bytes
+ ** as fit, but pretend we wrote everything. This makes
+ ** snprintf() return the number of bytes needed, rather
+ ** than the number used, and avoids its write function
+ ** (so that the write function can be invalid).
+ */
+
+ do
+ {
+ GETIOV(;);
+ if ((((fp->f_flags & (SMALC | SMSTR)) == (SMALC | SMSTR))
+ || ((fp->f_flags & SMNOW) != 0))
+ && (size_t) fp->f_w < len)
+ {
+ size_t blen = fp->f_p - fp->f_bf.smb_base;
+ unsigned char *tbase;
+ int tsize;
+
+ /* Allocate space exponentially. */
+ tsize = fp->f_bf.smb_size;
+ do
+ {
+ tsize = (tsize << 1) + 1;
+ } while ((size_t) tsize < blen + len);
+ tbase = (unsigned char *) sm_realloc(fp->f_bf.smb_base,
+ tsize + 1);
+ if (tbase == NULL)
+ {
+ errno = ENOMEM;
+ goto err; /* errno set */
+ }
+ fp->f_w += tsize - fp->f_bf.smb_size;
+ fp->f_bf.smb_base = tbase;
+ fp->f_bf.smb_size = tsize;
+ fp->f_p = tbase + blen;
+ }
+ w = fp->f_w;
+ errno = 0; /* needed to ensure EOF correctly found */
+ if (fp->f_flags & SMSTR)
+ {
+ if (len < (size_t) w)
+ w = len;
+ COPY(w); /* copy SM_MIN(fp->f_w,len), */
+ fp->f_w -= w;
+ fp->f_p += w;
+ w = len; /* but pretend copied all */
+ }
+ else if (fp->f_p > fp->f_bf.smb_base
+ && len > (size_t) w)
+ {
+ /* fill and flush */
+ COPY(w);
+ fp->f_p += w;
+ if (sm_flush(fp, &timeout))
+ goto err; /* errno set */
+ }
+ else if (len >= (size_t) (w = fp->f_bf.smb_size))
+ {
+ /* write directly */
+ w = (*fp->f_write)(fp, p, w);
+ if (w <= 0)
+ {
+ if (w == 0 && errno == 0)
+ break; /* EOF found */
+ if (IS_IO_ERROR(fd, w, timeout))
+ goto err; /* errno set */
+
+ /* write would block */
+ SM_IO_WR_TIMEOUT(fp, fd, timeout);
+ w = 0;
+ }
+ }
+ else
+ {
+ /* fill and done */
+ w = len;
+ COPY(w);
+ fp->f_w -= w;
+ fp->f_p += w;
+ }
+ p += w;
+ len -= w;
+ } while ((uio->uio_resid -= w) != 0);
+
+ if ((fp->f_flags & SMNOW) != 0 && sm_flush(fp, &timeout))
+ goto err; /* errno set */
+ }
+ else
+ {
+ /*
+ ** Line buffered: like fully buffered, but we
+ ** must check for newlines. Compute the distance
+ ** to the first newline (including the newline),
+ ** or `infinity' if there is none, then pretend
+ ** that the amount to write is SM_MIN(len,nldist).
+ */
+
+ nlknown = 0;
+ nldist = 0; /* XXX just to keep gcc happy */
+ do
+ {
+ GETIOV(nlknown = 0);
+ if (!nlknown)
+ {
+ nl = memchr((void *)p, '\n', len);
+ nldist = nl != NULL ? nl + 1 - p : len + 1;
+ nlknown = 1;
+ }
+ s = SM_MIN(len, ((size_t) nldist));
+ w = fp->f_w + fp->f_bf.smb_size;
+ errno = 0; /* needed to ensure EOF correctly found */
+ if (fp->f_p > fp->f_bf.smb_base && s > w)
+ {
+ COPY(w);
+ /* fp->f_w -= w; */
+ fp->f_p += w;
+ if (sm_flush(fp, &timeout))
+ goto err; /* errno set */
+ }
+ else if (s >= (w = fp->f_bf.smb_size))
+ {
+ w = (*fp->f_write)(fp, p, w);
+ if (w <= 0)
+ {
+ if (w == 0 && errno == 0)
+ break; /* EOF found */
+ if (IS_IO_ERROR(fd, w, timeout))
+ goto err; /* errno set */
+
+ /* write would block */
+ SM_IO_WR_TIMEOUT(fp, fd, timeout);
+ w = 0;
+ }
+ }
+ else
+ {
+ w = s;
+ COPY(w);
+ fp->f_w -= w;
+ fp->f_p += w;
+ }
+ if ((nldist -= w) == 0)
+ {
+ /* copied the newline: flush and forget */
+ if (sm_flush(fp, &timeout))
+ goto err; /* errno set */
+ nlknown = 0;
+ }
+ p += w;
+ len -= w;
+ } while ((uio->uio_resid -= w) != 0);
+ }
+
+ return 0;
+
+err:
+ /* errno set before goto places us here */
+ fp->f_flags |= SMERR;
+ return SM_IO_EOF;
+}
diff --git a/usr/src/cmd/sendmail/libsm/fvwrite.h b/usr/src/cmd/sendmail/libsm/fvwrite.h
new file mode 100644
index 0000000000..f7344e5535
--- /dev/null
+++ b/usr/src/cmd/sendmail/libsm/fvwrite.h
@@ -0,0 +1,32 @@
+/*
+ * Copyright (c) 2000-2001 Sendmail, Inc. and its suppliers.
+ * All rights reserved.
+ * Copyright (c) 1990, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Chris Torek.
+ *
+ * By using this file, you agree to the terms and conditions set
+ * forth in the LICENSE file which can be found at the top level of
+ * the sendmail distribution.
+ *
+ * $Id: fvwrite.h,v 1.7 2001/03/02 00:18:19 ca Exp $
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+/* I/O descriptors for sm_fvwrite() */
+struct sm_iov
+{
+ void *iov_base;
+ size_t iov_len;
+};
+struct sm_uio
+{
+ struct sm_iov *uio_iov;
+ int uio_iovcnt;
+ int uio_resid;
+};
+
+extern int sm_fvwrite __P((SM_FILE_T *, int, struct sm_uio *));
diff --git a/usr/src/cmd/sendmail/libsm/fwalk.c b/usr/src/cmd/sendmail/libsm/fwalk.c
new file mode 100644
index 0000000000..2c0522e989
--- /dev/null
+++ b/usr/src/cmd/sendmail/libsm/fwalk.c
@@ -0,0 +1,63 @@
+/*
+ * Copyright (c) 2000-2001 Sendmail, Inc. and its suppliers.
+ * All rights reserved.
+ * Copyright (c) 1990, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Chris Torek.
+ *
+ * By using this file, you agree to the terms and conditions set
+ * forth in the LICENSE file which can be found at the top level of
+ * the sendmail distribution.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#include <sm/gen.h>
+SM_RCSID("@(#)$Id: fwalk.c,v 1.19 2001/03/02 03:22:18 ca Exp $")
+#include <errno.h>
+#include <sm/io.h>
+#include "local.h"
+#include "glue.h"
+
+/*
+** SM_FWALK -- apply a function to all found-open file pointers
+**
+** Parameters:
+** function -- a function vector to be applied
+** timeout -- time to complete actions (milliseconds)
+**
+** Returns:
+** The (binary) OR'd result of each function call
+*/
+
+int
+sm_fwalk(function, timeout)
+ int (*function) __P((SM_FILE_T *, int *));
+ int *timeout;
+{
+ register SM_FILE_T *fp;
+ register int n, ret;
+ register struct sm_glue *g;
+ int fptimeout;
+
+ ret = 0;
+ for (g = &smglue; g != NULL; g = g->gl_next)
+ {
+ for (fp = g->gl_iobs, n = g->gl_niobs; --n >= 0; fp++)
+ {
+ if (fp->f_flags != 0)
+ {
+ if (*timeout == SM_TIME_DEFAULT)
+ fptimeout = fp->f_timeout;
+ else
+ fptimeout = *timeout;
+ if (fptimeout == SM_TIME_IMMEDIATE)
+ continue; /* skip it */
+ ret |= (*function)(fp, &fptimeout);
+ }
+ }
+ }
+ return ret;
+}
diff --git a/usr/src/cmd/sendmail/libsm/fwrite.c b/usr/src/cmd/sendmail/libsm/fwrite.c
new file mode 100644
index 0000000000..f99eab1bb2
--- /dev/null
+++ b/usr/src/cmd/sendmail/libsm/fwrite.c
@@ -0,0 +1,69 @@
+/*
+ * Copyright (c) 2000-2001 Sendmail, Inc. and its suppliers.
+ * All rights reserved.
+ * Copyright (c) 1990, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Chris Torek.
+ *
+ * By using this file, you agree to the terms and conditions set
+ * forth in the LICENSE file which can be found at the top level of
+ * the sendmail distribution.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#include <sm/gen.h>
+SM_RCSID("@(#)$Id: fwrite.c,v 1.22 2001/04/03 01:46:40 ca Exp $")
+#include <errno.h>
+#include <sm/io.h>
+#include <sm/assert.h>
+#include "local.h"
+#include "fvwrite.h"
+
+/*
+** SM_IO_WRITE -- write to a file pointer
+**
+** Parameters:
+** fp -- file pointer writing to
+** timeout -- time to complete the write
+** buf -- location of data to be written
+** size -- number of bytes to be written
+**
+** Result:
+** Failure: returns 0 _and_ sets errno
+** Success: returns >=0 with errno unchanged, where the
+** number returned is the number of bytes written.
+*/
+
+size_t
+sm_io_write(fp, timeout, buf, size)
+ SM_FILE_T *fp;
+ int timeout;
+ const void *buf;
+ size_t size;
+{
+ struct sm_uio uio;
+ struct sm_iov iov;
+
+ SM_REQUIRE_ISA(fp, SmFileMagic);
+
+ if (fp->f_write == NULL)
+ {
+ errno = ENODEV;
+ return 0;
+ }
+
+ iov.iov_base = (void *) buf;
+ uio.uio_resid = iov.iov_len = size;
+ uio.uio_iov = &iov;
+ uio.uio_iovcnt = 1;
+
+ /* The usual case is success (sm_fvwrite returns 0) */
+ if (sm_fvwrite(fp, timeout, &uio) == 0)
+ return size;
+
+ /* else return number of bytes actually written */
+ return size - uio.uio_resid;
+}
diff --git a/usr/src/cmd/sendmail/libsm/get.c b/usr/src/cmd/sendmail/libsm/get.c
new file mode 100644
index 0000000000..26226309f9
--- /dev/null
+++ b/usr/src/cmd/sendmail/libsm/get.c
@@ -0,0 +1,48 @@
+/*
+ * Copyright (c) 2000-2001 Sendmail, Inc. and its suppliers.
+ * All rights reserved.
+ * Copyright (c) 1990, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Chris Torek.
+ *
+ * By using this file, you agree to the terms and conditions set
+ * forth in the LICENSE file which can be found at the top level of
+ * the sendmail distribution.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#include <sm/gen.h>
+SM_RCSID("@(#)$Id: get.c,v 1.16 2001/01/28 00:29:35 ca Exp $")
+#include <sm/io.h>
+#include <sm/assert.h>
+#include "local.h"
+
+/*
+** SM_IO_GETC -- get a character from a file
+**
+** Parameters:
+** fp -- the file to get the character from
+** timeout -- time to complete getc
+**
+** Returns:
+** Success: the value of the character read.
+** Failure: SM_IO_EOF
+**
+** This is a function version of the macro (in <sm/io.h>).
+** It is guarded with locks (which are currently not functional)
+** for multi-threaded programs.
+*/
+
+#undef sm_io_getc
+
+int
+sm_io_getc(fp, timeout)
+ register SM_FILE_T *fp;
+ int timeout;
+{
+ SM_REQUIRE_ISA(fp, SmFileMagic);
+ return sm_getc(fp, timeout);
+}
diff --git a/usr/src/cmd/sendmail/libsm/glue.h b/usr/src/cmd/sendmail/libsm/glue.h
new file mode 100644
index 0000000000..6993bb4292
--- /dev/null
+++ b/usr/src/cmd/sendmail/libsm/glue.h
@@ -0,0 +1,29 @@
+/*
+ * Copyright (c) 2000-2001 Sendmail, Inc. and its suppliers.
+ * All rights reserved.
+ * Copyright (c) 1990, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Chris Torek.
+ *
+ * By using this file, you agree to the terms and conditions set
+ * forth in the LICENSE file which can be found at the top level of
+ * the sendmail distribution.
+ *
+ * $Id: glue.h,v 1.6 2001/01/22 23:09:49 ca Exp $
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+/*
+** The first few FILEs are statically allocated; others are dynamically
+** allocated and linked in via this glue structure.
+*/
+
+extern struct sm_glue
+{
+ struct sm_glue *gl_next;
+ int gl_niobs;
+ SM_FILE_T *gl_iobs;
+} smglue;
diff --git a/usr/src/cmd/sendmail/libsm/heap.c b/usr/src/cmd/sendmail/libsm/heap.c
new file mode 100644
index 0000000000..03acf34555
--- /dev/null
+++ b/usr/src/cmd/sendmail/libsm/heap.c
@@ -0,0 +1,822 @@
+/*
+ * Copyright (c) 2000-2001, 2004 Sendmail, Inc. and its suppliers.
+ * All rights reserved.
+ *
+ * By using this file, you agree to the terms and conditions set
+ * forth in the LICENSE file which can be found at the top level of
+ * the sendmail distribution.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#include <sm/gen.h>
+SM_RCSID("@(#)$Id: heap.c,v 1.51 2004/08/03 20:32:00 ca Exp $")
+
+/*
+** debugging memory allocation package
+** See heap.html for documentation.
+*/
+
+#include <string.h>
+
+#include <sm/assert.h>
+#include <sm/debug.h>
+#include <sm/exc.h>
+#include <sm/heap.h>
+#include <sm/io.h>
+#include <sm/signal.h>
+#include <sm/xtrap.h>
+
+/* undef all macro versions of the "functions" so they can be specified here */
+#undef sm_malloc
+#undef sm_malloc_x
+#undef sm_malloc_tagged
+#undef sm_malloc_tagged_x
+#undef sm_free
+#undef sm_free_tagged
+#undef sm_realloc
+#if SM_HEAP_CHECK
+# undef sm_heap_register
+# undef sm_heap_checkptr
+# undef sm_heap_report
+#endif /* SM_HEAP_CHECK */
+
+#if SM_HEAP_CHECK
+SM_DEBUG_T SmHeapCheck = SM_DEBUG_INITIALIZER("sm_check_heap",
+ "@(#)$Debug: sm_check_heap - check sm_malloc, sm_realloc, sm_free calls $");
+# define HEAP_CHECK sm_debug_active(&SmHeapCheck, 1)
+static int ptrhash __P((void *p));
+#endif /* SM_HEAP_CHECK */
+
+const SM_EXC_TYPE_T SmHeapOutOfMemoryType =
+{
+ SmExcTypeMagic,
+ "F:sm.heap",
+ "",
+ sm_etype_printf,
+ "out of memory",
+};
+
+SM_EXC_T SmHeapOutOfMemory = SM_EXC_INITIALIZER(&SmHeapOutOfMemoryType, NULL);
+
+
+/*
+** The behaviour of malloc with size==0 is platform dependent (it
+** says so in the C standard): it can return NULL or non-NULL. We
+** don't want sm_malloc_x(0) to raise an exception on some platforms
+** but not others, so this case requires special handling. We've got
+** two choices: "size = 1" or "return NULL". We use the former in the
+** following.
+** If we had something like autoconf we could figure out the
+** behaviour of the platform and either use this hack or just
+** use size.
+*/
+
+#define MALLOC_SIZE(size) ((size) == 0 ? 1 : (size))
+
+/*
+** SM_MALLOC_X -- wrapper around malloc(), raises an exception on error.
+**
+** Parameters:
+** size -- size of requested memory.
+**
+** Returns:
+** Pointer to memory region.
+**
+** Note:
+** sm_malloc_x only gets called from source files in which heap
+** debugging is disabled at compile time. Otherwise, a call to
+** sm_malloc_x is macro expanded to a call to sm_malloc_tagged_x.
+**
+** Exceptions:
+** F:sm_heap -- out of memory
+*/
+
+void *
+sm_malloc_x(size)
+ size_t size;
+{
+ void *ptr;
+
+ ENTER_CRITICAL();
+ ptr = malloc(MALLOC_SIZE(size));
+ LEAVE_CRITICAL();
+ if (ptr == NULL)
+ sm_exc_raise_x(&SmHeapOutOfMemory);
+ return ptr;
+}
+
+#if !SM_HEAP_CHECK
+
+/*
+** SM_MALLOC -- wrapper around malloc()
+**
+** Parameters:
+** size -- size of requested memory.
+**
+** Returns:
+** Pointer to memory region.
+*/
+
+void *
+sm_malloc(size)
+ size_t size;
+{
+ void *ptr;
+
+ ENTER_CRITICAL();
+ ptr = malloc(MALLOC_SIZE(size));
+ LEAVE_CRITICAL();
+ return ptr;
+}
+
+/*
+** SM_REALLOC -- wrapper for realloc()
+**
+** Parameters:
+** ptr -- pointer to old memory area.
+** size -- size of requested memory.
+**
+** Returns:
+** Pointer to new memory area, NULL on failure.
+*/
+
+void *
+sm_realloc(ptr, size)
+ void *ptr;
+ size_t size;
+{
+ void *newptr;
+
+ ENTER_CRITICAL();
+ newptr = realloc(ptr, MALLOC_SIZE(size));
+ LEAVE_CRITICAL();
+ return newptr;
+}
+
+/*
+** SM_REALLOC_X -- wrapper for realloc()
+**
+** Parameters:
+** ptr -- pointer to old memory area.
+** size -- size of requested memory.
+**
+** Returns:
+** Pointer to new memory area.
+**
+** Exceptions:
+** F:sm_heap -- out of memory
+*/
+
+void *
+sm_realloc_x(ptr, size)
+ void *ptr;
+ size_t size;
+{
+ void *newptr;
+
+ ENTER_CRITICAL();
+ newptr = realloc(ptr, MALLOC_SIZE(size));
+ LEAVE_CRITICAL();
+ if (newptr == NULL)
+ sm_exc_raise_x(&SmHeapOutOfMemory);
+ return newptr;
+}
+/*
+** SM_FREE -- wrapper around free()
+**
+** Parameters:
+** ptr -- pointer to memory region.
+**
+** Returns:
+** none.
+*/
+
+void
+sm_free(ptr)
+ void *ptr;
+{
+ if (ptr == NULL)
+ return;
+ ENTER_CRITICAL();
+ free(ptr);
+ LEAVE_CRITICAL();
+ return;
+}
+
+#else /* !SM_HEAP_CHECK */
+
+/*
+** Each allocated block is assigned a "group number".
+** By default, all blocks are assigned to group #1.
+** By convention, group #0 is for memory that is never freed.
+** You can use group numbers any way you want, in order to help make
+** sense of sm_heap_report output.
+*/
+
+int SmHeapGroup = 1;
+int SmHeapMaxGroup = 1;
+
+/*
+** Total number of bytes allocated.
+** This is only maintained if the sm_check_heap debug category is active.
+*/
+
+size_t SmHeapTotal = 0;
+
+/*
+** High water mark: the most that SmHeapTotal has ever been.
+*/
+
+size_t SmHeapMaxTotal = 0;
+
+/*
+** Maximum number of bytes that may be allocated at any one time.
+** 0 means no limit.
+** This is only honoured if sm_check_heap is active.
+*/
+
+SM_DEBUG_T SmHeapLimit = SM_DEBUG_INITIALIZER("sm_heap_limit",
+ "@(#)$Debug: sm_heap_limit - max # of bytes permitted in heap $");
+
+/*
+** This is the data structure that keeps track of all currently
+** allocated blocks of memory known to the heap package.
+*/
+
+typedef struct sm_heap_item SM_HEAP_ITEM_T;
+struct sm_heap_item
+{
+ void *hi_ptr;
+ size_t hi_size;
+ char *hi_tag;
+ int hi_num;
+ int hi_group;
+ SM_HEAP_ITEM_T *hi_next;
+};
+
+#define SM_HEAP_TABLE_SIZE 256
+static SM_HEAP_ITEM_T *SmHeapTable[SM_HEAP_TABLE_SIZE];
+
+/*
+** This is a randomly generated table
+** which contains exactly one occurrence
+** of each of the numbers between 0 and 255.
+** It is used by ptrhash.
+*/
+
+static unsigned char hashtab[SM_HEAP_TABLE_SIZE] =
+{
+ 161, 71, 77,187, 15,229, 9,176,221,119,239, 21, 85,138,203, 86,
+ 102, 65, 80,199,235, 32,140, 96,224, 78,126,127,144, 0, 11,179,
+ 64, 30,120, 23,225,226, 33, 50,205,167,130,240,174, 99,206, 73,
+ 231,210,189,162, 48, 93,246, 54,213,141,135, 39, 41,192,236,193,
+ 157, 88, 95,104,188, 63,133,177,234,110,158,214,238,131,233, 91,
+ 125, 82, 94, 79, 66, 92,151, 45,252, 98, 26,183, 7,191,171,106,
+ 145,154,251,100,113, 5, 74, 62, 76,124, 14,217,200, 75,115,190,
+ 103, 28,198,196,169,219, 37,118,150, 18,152,175, 49,136, 6,142,
+ 89, 19,243,254, 47,137, 24,166,180, 10, 40,186,202, 46,184, 67,
+ 148,108,181, 81, 25,241, 13,139, 58, 38, 84,253,201, 12,116, 17,
+ 195, 22,112, 69,255, 43,147,222,111, 56,194,216,149,244, 42,173,
+ 232,220,249,105,207, 51,197,242, 72,211,208, 59,122,230,237,170,
+ 165, 44, 68,123,129,245,143,101, 8,209,215,247,185, 57,218, 53,
+ 114,121, 3,128, 4,204,212,146, 2,155, 83,250, 87, 29, 31,159,
+ 60, 27,107,156,227,182, 1, 61, 36,160,109, 97, 90, 20,168,132,
+ 223,248, 70,164, 55,172, 34, 52,163,117, 35,153,134, 16,178,228
+};
+
+/*
+** PTRHASH -- hash a pointer value
+**
+** Parameters:
+** p -- pointer.
+**
+** Returns:
+** hash value.
+**
+** ptrhash hashes a pointer value to a uniformly distributed random
+** number between 0 and 255.
+**
+** This hash algorithm is based on Peter K. Pearson,
+** "Fast Hashing of Variable-Length Text Strings",
+** in Communications of the ACM, June 1990, vol 33 no 6.
+*/
+
+static int
+ptrhash(p)
+ void *p;
+{
+ int h;
+
+ if (sizeof(void*) == 4 && sizeof(unsigned long) == 4)
+ {
+ unsigned long n = (unsigned long)p;
+
+ h = hashtab[n & 0xFF];
+ h = hashtab[h ^ ((n >> 8) & 0xFF)];
+ h = hashtab[h ^ ((n >> 16) & 0xFF)];
+ h = hashtab[h ^ ((n >> 24) & 0xFF)];
+ }
+# if 0
+ else if (sizeof(void*) == 8 && sizeof(unsigned long) == 8)
+ {
+ unsigned long n = (unsigned long)p;
+
+ h = hashtab[n & 0xFF];
+ h = hashtab[h ^ ((n >> 8) & 0xFF)];
+ h = hashtab[h ^ ((n >> 16) & 0xFF)];
+ h = hashtab[h ^ ((n >> 24) & 0xFF)];
+ h = hashtab[h ^ ((n >> 32) & 0xFF)];
+ h = hashtab[h ^ ((n >> 40) & 0xFF)];
+ h = hashtab[h ^ ((n >> 48) & 0xFF)];
+ h = hashtab[h ^ ((n >> 56) & 0xFF)];
+ }
+# endif /* 0 */
+ else
+ {
+ unsigned char *cp = (unsigned char *)&p;
+ int i;
+
+ h = 0;
+ for (i = 0; i < sizeof(void*); ++i)
+ h = hashtab[h ^ cp[i]];
+ }
+ return h;
+}
+
+/*
+** SM_MALLOC_TAGGED -- wrapper around malloc(), debugging version.
+**
+** Parameters:
+** size -- size of requested memory.
+** tag -- tag for debugging.
+** num -- additional value for debugging.
+** group -- heap group for debugging.
+**
+** Returns:
+** Pointer to memory region.
+*/
+
+void *
+sm_malloc_tagged(size, tag, num, group)
+ size_t size;
+ char *tag;
+ int num;
+ int group;
+{
+ void *ptr;
+
+ if (!HEAP_CHECK)
+ {
+ ENTER_CRITICAL();
+ ptr = malloc(MALLOC_SIZE(size));
+ LEAVE_CRITICAL();
+ return ptr;
+ }
+
+ if (sm_xtrap_check())
+ return NULL;
+ if (sm_debug_active(&SmHeapLimit, 1)
+ && sm_debug_level(&SmHeapLimit) < SmHeapTotal + size)
+ return NULL;
+ ENTER_CRITICAL();
+ ptr = malloc(MALLOC_SIZE(size));
+ LEAVE_CRITICAL();
+ if (ptr != NULL && !sm_heap_register(ptr, size, tag, num, group))
+ {
+ ENTER_CRITICAL();
+ free(ptr);
+ LEAVE_CRITICAL();
+ ptr = NULL;
+ }
+ SmHeapTotal += size;
+ if (SmHeapTotal > SmHeapMaxTotal)
+ SmHeapMaxTotal = SmHeapTotal;
+ return ptr;
+}
+
+/*
+** SM_MALLOC_TAGGED_X -- wrapper around malloc(), debugging version.
+**
+** Parameters:
+** size -- size of requested memory.
+** tag -- tag for debugging.
+** num -- additional value for debugging.
+** group -- heap group for debugging.
+**
+** Returns:
+** Pointer to memory region.
+**
+** Exceptions:
+** F:sm_heap -- out of memory
+*/
+
+void *
+sm_malloc_tagged_x(size, tag, num, group)
+ size_t size;
+ char *tag;
+ int num;
+ int group;
+{
+ void *ptr;
+
+ if (!HEAP_CHECK)
+ {
+ ENTER_CRITICAL();
+ ptr = malloc(MALLOC_SIZE(size));
+ LEAVE_CRITICAL();
+ if (ptr == NULL)
+ sm_exc_raise_x(&SmHeapOutOfMemory);
+ return ptr;
+ }
+
+ sm_xtrap_raise_x(&SmHeapOutOfMemory);
+ if (sm_debug_active(&SmHeapLimit, 1)
+ && sm_debug_level(&SmHeapLimit) < SmHeapTotal + size)
+ {
+ sm_exc_raise_x(&SmHeapOutOfMemory);
+ }
+ ENTER_CRITICAL();
+ ptr = malloc(MALLOC_SIZE(size));
+ LEAVE_CRITICAL();
+ if (ptr != NULL && !sm_heap_register(ptr, size, tag, num, group))
+ {
+ ENTER_CRITICAL();
+ free(ptr);
+ LEAVE_CRITICAL();
+ ptr = NULL;
+ }
+ if (ptr == NULL)
+ sm_exc_raise_x(&SmHeapOutOfMemory);
+ SmHeapTotal += size;
+ if (SmHeapTotal > SmHeapMaxTotal)
+ SmHeapMaxTotal = SmHeapTotal;
+ return ptr;
+}
+
+/*
+** SM_HEAP_REGISTER -- register a pointer into the heap for debugging.
+**
+** Parameters:
+** ptr -- pointer to register.
+** size -- size of requested memory.
+** tag -- tag for debugging.
+** num -- additional value for debugging.
+** group -- heap group for debugging.
+**
+** Returns:
+** true iff successfully registered (not yet in table).
+*/
+
+bool
+sm_heap_register(ptr, size, tag, num, group)
+ void *ptr;
+ size_t size;
+ char *tag;
+ int num;
+ int group;
+{
+ int i;
+ SM_HEAP_ITEM_T *hi;
+
+ if (!HEAP_CHECK)
+ return true;
+ SM_REQUIRE(ptr != NULL);
+ i = ptrhash(ptr);
+# if SM_CHECK_REQUIRE
+
+ /*
+ ** We require that ptr is not already in SmHeapTable.
+ */
+
+ for (hi = SmHeapTable[i]; hi != NULL; hi = hi->hi_next)
+ {
+ if (hi->hi_ptr == ptr)
+ sm_abort("sm_heap_register: ptr %p is already registered (%s:%d)",
+ ptr, hi->hi_tag, hi->hi_num);
+ }
+# endif /* SM_CHECK_REQUIRE */
+ ENTER_CRITICAL();
+ hi = (SM_HEAP_ITEM_T *) malloc(sizeof(SM_HEAP_ITEM_T));
+ LEAVE_CRITICAL();
+ if (hi == NULL)
+ return false;
+ hi->hi_ptr = ptr;
+ hi->hi_size = size;
+ hi->hi_tag = tag;
+ hi->hi_num = num;
+ hi->hi_group = group;
+ hi->hi_next = SmHeapTable[i];
+ SmHeapTable[i] = hi;
+ return true;
+}
+/*
+** SM_REALLOC -- wrapper for realloc(), debugging version.
+**
+** Parameters:
+** ptr -- pointer to old memory area.
+** size -- size of requested memory.
+**
+** Returns:
+** Pointer to new memory area, NULL on failure.
+*/
+
+void *
+sm_realloc(ptr, size)
+ void *ptr;
+ size_t size;
+{
+ void *newptr;
+ SM_HEAP_ITEM_T *hi, **hp;
+
+ if (!HEAP_CHECK)
+ {
+ ENTER_CRITICAL();
+ newptr = realloc(ptr, MALLOC_SIZE(size));
+ LEAVE_CRITICAL();
+ return newptr;
+ }
+
+ if (ptr == NULL)
+ return sm_malloc_tagged(size, "realloc", 0, SmHeapGroup);
+
+ for (hp = &SmHeapTable[ptrhash(ptr)]; *hp != NULL; hp = &(**hp).hi_next)
+ {
+ if ((**hp).hi_ptr == ptr)
+ {
+ if (sm_xtrap_check())
+ return NULL;
+ hi = *hp;
+ if (sm_debug_active(&SmHeapLimit, 1)
+ && sm_debug_level(&SmHeapLimit)
+ < SmHeapTotal - hi->hi_size + size)
+ {
+ return NULL;
+ }
+ ENTER_CRITICAL();
+ newptr = realloc(ptr, MALLOC_SIZE(size));
+ LEAVE_CRITICAL();
+ if (newptr == NULL)
+ return NULL;
+ SmHeapTotal = SmHeapTotal - hi->hi_size + size;
+ if (SmHeapTotal > SmHeapMaxTotal)
+ SmHeapMaxTotal = SmHeapTotal;
+ *hp = hi->hi_next;
+ hi->hi_ptr = newptr;
+ hi->hi_size = size;
+ hp = &SmHeapTable[ptrhash(newptr)];
+ hi->hi_next = *hp;
+ *hp = hi;
+ return newptr;
+ }
+ }
+ sm_abort("sm_realloc: bad argument (%p)", ptr);
+ /* NOTREACHED */
+ return NULL; /* keep Irix compiler happy */
+}
+
+/*
+** SM_REALLOC_X -- wrapper for realloc(), debugging version.
+**
+** Parameters:
+** ptr -- pointer to old memory area.
+** size -- size of requested memory.
+**
+** Returns:
+** Pointer to new memory area.
+**
+** Exceptions:
+** F:sm_heap -- out of memory
+*/
+
+void *
+sm_realloc_x(ptr, size)
+ void *ptr;
+ size_t size;
+{
+ void *newptr;
+ SM_HEAP_ITEM_T *hi, **hp;
+
+ if (!HEAP_CHECK)
+ {
+ ENTER_CRITICAL();
+ newptr = realloc(ptr, MALLOC_SIZE(size));
+ LEAVE_CRITICAL();
+ if (newptr == NULL)
+ sm_exc_raise_x(&SmHeapOutOfMemory);
+ return newptr;
+ }
+
+ if (ptr == NULL)
+ return sm_malloc_tagged_x(size, "realloc", 0, SmHeapGroup);
+
+ for (hp = &SmHeapTable[ptrhash(ptr)]; *hp != NULL; hp = &(**hp).hi_next)
+ {
+ if ((**hp).hi_ptr == ptr)
+ {
+ sm_xtrap_raise_x(&SmHeapOutOfMemory);
+ hi = *hp;
+ if (sm_debug_active(&SmHeapLimit, 1)
+ && sm_debug_level(&SmHeapLimit)
+ < SmHeapTotal - hi->hi_size + size)
+ {
+ sm_exc_raise_x(&SmHeapOutOfMemory);
+ }
+ ENTER_CRITICAL();
+ newptr = realloc(ptr, MALLOC_SIZE(size));
+ LEAVE_CRITICAL();
+ if (newptr == NULL)
+ sm_exc_raise_x(&SmHeapOutOfMemory);
+ SmHeapTotal = SmHeapTotal - hi->hi_size + size;
+ if (SmHeapTotal > SmHeapMaxTotal)
+ SmHeapMaxTotal = SmHeapTotal;
+ *hp = hi->hi_next;
+ hi->hi_ptr = newptr;
+ hi->hi_size = size;
+ hp = &SmHeapTable[ptrhash(newptr)];
+ hi->hi_next = *hp;
+ *hp = hi;
+ return newptr;
+ }
+ }
+ sm_abort("sm_realloc_x: bad argument (%p)", ptr);
+ /* NOTREACHED */
+ return NULL; /* keep Irix compiler happy */
+}
+
+/*
+** SM_FREE_TAGGED -- wrapper around free(), debugging version.
+**
+** Parameters:
+** ptr -- pointer to memory region.
+** tag -- tag for debugging.
+** num -- additional value for debugging.
+**
+** Returns:
+** none.
+*/
+
+void
+sm_free_tagged(ptr, tag, num)
+ void *ptr;
+ char *tag;
+ int num;
+{
+ SM_HEAP_ITEM_T **hp;
+
+ if (ptr == NULL)
+ return;
+ if (!HEAP_CHECK)
+ {
+ ENTER_CRITICAL();
+ free(ptr);
+ LEAVE_CRITICAL();
+ return;
+ }
+ for (hp = &SmHeapTable[ptrhash(ptr)]; *hp != NULL; hp = &(**hp).hi_next)
+ {
+ if ((**hp).hi_ptr == ptr)
+ {
+ SM_HEAP_ITEM_T *hi = *hp;
+
+ *hp = hi->hi_next;
+
+ /*
+ ** Fill the block with zeros before freeing.
+ ** This is intended to catch problems with
+ ** dangling pointers. The block is filled with
+ ** zeros, not with some non-zero value, because
+ ** it is common practice in some C code to store
+ ** a zero in a structure member before freeing the
+ ** structure, as a defense against dangling pointers.
+ */
+
+ (void) memset(ptr, 0, hi->hi_size);
+ SmHeapTotal -= hi->hi_size;
+ ENTER_CRITICAL();
+ free(ptr);
+ free(hi);
+ LEAVE_CRITICAL();
+ return;
+ }
+ }
+ sm_abort("sm_free: bad argument (%p) (%s:%d)", ptr, tag, num);
+}
+
+/*
+** SM_HEAP_CHECKPTR_TAGGED -- check whether ptr is a valid argument to sm_free
+**
+** Parameters:
+** ptr -- pointer to memory region.
+** tag -- tag for debugging.
+** num -- additional value for debugging.
+**
+** Returns:
+** none.
+**
+** Side Effects:
+** aborts if check fails.
+*/
+
+void
+sm_heap_checkptr_tagged(ptr, tag, num)
+ void *ptr;
+ char *tag;
+ int num;
+{
+ SM_HEAP_ITEM_T *hp;
+
+ if (!HEAP_CHECK)
+ return;
+ if (ptr == NULL)
+ return;
+ for (hp = SmHeapTable[ptrhash(ptr)]; hp != NULL; hp = hp->hi_next)
+ {
+ if (hp->hi_ptr == ptr)
+ return;
+ }
+ sm_abort("sm_heap_checkptr(%p): bad ptr (%s:%d)", ptr, tag, num);
+}
+
+/*
+** SM_HEAP_REPORT -- output "map" of used heap.
+**
+** Parameters:
+** stream -- the file pointer to write to.
+** verbosity -- how much info?
+**
+** Returns:
+** none.
+*/
+
+void
+sm_heap_report(stream, verbosity)
+ SM_FILE_T *stream;
+ int verbosity;
+{
+ int i;
+ unsigned long group0total, group1total, otherstotal, grandtotal;
+
+ if (!HEAP_CHECK || verbosity <= 0)
+ return;
+ group0total = group1total = otherstotal = grandtotal = 0;
+ for (i = 0; i < sizeof(SmHeapTable) / sizeof(SmHeapTable[0]); ++i)
+ {
+ SM_HEAP_ITEM_T *hi = SmHeapTable[i];
+
+ while (hi != NULL)
+ {
+ if (verbosity > 2
+ || (verbosity > 1 && hi->hi_group != 0))
+ {
+ sm_io_fprintf(stream, SM_TIME_DEFAULT,
+ "%4d %*lx %7lu bytes",
+ hi->hi_group,
+ (int) sizeof(void *) * 2,
+ (long)hi->hi_ptr,
+ (unsigned long)hi->hi_size);
+ if (hi->hi_tag != NULL)
+ {
+ sm_io_fprintf(stream, SM_TIME_DEFAULT,
+ " %s",
+ hi->hi_tag);
+ if (hi->hi_num)
+ {
+ sm_io_fprintf(stream,
+ SM_TIME_DEFAULT,
+ ":%d",
+ hi->hi_num);
+ }
+ }
+ sm_io_fprintf(stream, SM_TIME_DEFAULT, "\n");
+ }
+ switch (hi->hi_group)
+ {
+ case 0:
+ group0total += hi->hi_size;
+ break;
+ case 1:
+ group1total += hi->hi_size;
+ break;
+ default:
+ otherstotal += hi->hi_size;
+ break;
+ }
+ grandtotal += hi->hi_size;
+ hi = hi->hi_next;
+ }
+ }
+ sm_io_fprintf(stream, SM_TIME_DEFAULT,
+ "heap max=%lu, total=%lu, ",
+ (unsigned long) SmHeapMaxTotal, grandtotal);
+ sm_io_fprintf(stream, SM_TIME_DEFAULT,
+ "group 0=%lu, group 1=%lu, others=%lu\n",
+ group0total, group1total, otherstotal);
+ if (grandtotal != SmHeapTotal)
+ {
+ sm_io_fprintf(stream, SM_TIME_DEFAULT,
+ "BUG => SmHeapTotal: got %lu, expected %lu\n",
+ (unsigned long) SmHeapTotal, grandtotal);
+ }
+}
+#endif /* !SM_HEAP_CHECK */
diff --git a/usr/src/cmd/sendmail/libsm/ldap.c b/usr/src/cmd/sendmail/libsm/ldap.c
new file mode 100644
index 0000000000..f438b91823
--- /dev/null
+++ b/usr/src/cmd/sendmail/libsm/ldap.c
@@ -0,0 +1,1334 @@
+/*
+ * Copyright (c) 2001-2005 Sendmail, Inc. and its suppliers.
+ * All rights reserved.
+ *
+ * By using this file, you agree to the terms and conditions set
+ * forth in the LICENSE file which can be found at the top level of
+ * the sendmail distribution.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#include <sm/gen.h>
+SM_RCSID("@(#)$Id: ldap.c,v 1.62 2005/02/24 00:30:01 ca Exp $")
+
+#if LDAPMAP
+# include <sys/types.h>
+# include <errno.h>
+# include <setjmp.h>
+# include <stdlib.h>
+# include <unistd.h>
+
+# include <sm/bitops.h>
+# include <sm/clock.h>
+# include <sm/conf.h>
+# include <sm/debug.h>
+# include <sm/errstring.h>
+# include <sm/ldap.h>
+# include <sm/string.h>
+# ifdef EX_OK
+# undef EX_OK /* for SVr4.2 SMP */
+# endif /* EX_OK */
+# include <sm/sysexits.h>
+
+SM_DEBUG_T SmLDAPTrace = SM_DEBUG_INITIALIZER("sm_trace_ldap",
+ "@(#)$Debug: sm_trace_ldap - trace LDAP operations $");
+
+static void ldaptimeout __P((int));
+static bool sm_ldap_has_objectclass __P((SM_LDAP_STRUCT *, LDAPMessage *, char *));
+static SM_LDAP_RECURSE_ENTRY *sm_ldap_add_recurse __P((SM_LDAP_RECURSE_LIST **, char *, int, SM_RPOOL_T *));
+
+/*
+** SM_LDAP_CLEAR -- set default values for SM_LDAP_STRUCT
+**
+** Parameters:
+** lmap -- pointer to SM_LDAP_STRUCT to clear
+**
+** Returns:
+** None.
+**
+*/
+
+void
+sm_ldap_clear(lmap)
+ SM_LDAP_STRUCT *lmap;
+{
+ if (lmap == NULL)
+ return;
+
+ lmap->ldap_host = NULL;
+ lmap->ldap_port = LDAP_PORT;
+ lmap->ldap_uri = NULL;
+ lmap->ldap_version = 0;
+ lmap->ldap_deref = LDAP_DEREF_NEVER;
+ lmap->ldap_timelimit = LDAP_NO_LIMIT;
+ lmap->ldap_sizelimit = LDAP_NO_LIMIT;
+# ifdef LDAP_REFERRALS
+ lmap->ldap_options = LDAP_OPT_REFERRALS;
+# else /* LDAP_REFERRALS */
+ lmap->ldap_options = 0;
+# endif /* LDAP_REFERRALS */
+ lmap->ldap_attrsep = '\0';
+ lmap->ldap_binddn = NULL;
+ lmap->ldap_secret = NULL;
+ lmap->ldap_method = LDAP_AUTH_SIMPLE;
+ lmap->ldap_base = NULL;
+ lmap->ldap_scope = LDAP_SCOPE_SUBTREE;
+ lmap->ldap_attrsonly = LDAPMAP_FALSE;
+ lmap->ldap_timeout.tv_sec = 0;
+ lmap->ldap_timeout.tv_usec = 0;
+ lmap->ldap_ld = NULL;
+ lmap->ldap_filter = NULL;
+ lmap->ldap_attr[0] = NULL;
+ lmap->ldap_attr_type[0] = SM_LDAP_ATTR_NONE;
+ lmap->ldap_attr_needobjclass[0] = NULL;
+ lmap->ldap_res = NULL;
+ lmap->ldap_next = NULL;
+ lmap->ldap_pid = 0;
+}
+
+/*
+** SM_LDAP_START -- actually connect to an LDAP server
+**
+** Parameters:
+** name -- name of map for debug output.
+** lmap -- the LDAP map being opened.
+**
+** Returns:
+** true if connection is successful, false otherwise.
+**
+** Side Effects:
+** Populates lmap->ldap_ld.
+*/
+
+static jmp_buf LDAPTimeout;
+
+#define SM_LDAP_SETTIMEOUT(to) \
+do \
+{ \
+ if (to != 0) \
+ { \
+ if (setjmp(LDAPTimeout) != 0) \
+ { \
+ errno = ETIMEDOUT; \
+ return false; \
+ } \
+ ev = sm_setevent(to, ldaptimeout, 0); \
+ } \
+} while (0)
+
+#define SM_LDAP_CLEARTIMEOUT() \
+do \
+{ \
+ if (ev != NULL) \
+ sm_clrevent(ev); \
+} while (0)
+
+bool
+sm_ldap_start(name, lmap)
+ char *name;
+ SM_LDAP_STRUCT *lmap;
+{
+ int bind_result;
+ int save_errno = 0;
+ char *id;
+ SM_EVENT *ev = NULL;
+ LDAP *ld = NULL;
+
+ if (sm_debug_active(&SmLDAPTrace, 2))
+ sm_dprintf("ldapmap_start(%s)\n", name == NULL ? "" : name);
+
+ if (lmap->ldap_host != NULL)
+ id = lmap->ldap_host;
+ else if (lmap->ldap_uri != NULL)
+ id = lmap->ldap_uri;
+ else
+ id = "localhost";
+
+ if (sm_debug_active(&SmLDAPTrace, 9))
+ {
+ /* Don't print a port number for LDAP URIs */
+ if (lmap->ldap_uri != NULL)
+ sm_dprintf("ldapmap_start(%s)\n", id);
+ else
+ sm_dprintf("ldapmap_start(%s, %d)\n", id,
+ lmap->ldap_port);
+ }
+
+ if (lmap->ldap_uri != NULL)
+ {
+#if SM_CONF_LDAP_INITIALIZE
+ /* LDAP server supports URIs so use them directly */
+ save_errno = ldap_initialize(&ld, lmap->ldap_uri);
+#else /* SM_CONF_LDAP_INITIALIZE */
+ int err;
+ LDAPURLDesc *ludp = NULL;
+
+ /* Blast apart URL and use the ldap_init/ldap_open below */
+ err = ldap_url_parse(lmap->ldap_uri, &ludp);
+ if (err != 0)
+ {
+ errno = err + E_LDAPURLBASE;
+ return false;
+ }
+ lmap->ldap_host = sm_strdup_x(ludp->lud_host);
+ if (lmap->ldap_host == NULL)
+ {
+ save_errno = errno;
+ ldap_free_urldesc(ludp);
+ errno = save_errno;
+ return false;
+ }
+ lmap->ldap_port = ludp->lud_port;
+ ldap_free_urldesc(ludp);
+#endif /* SM_CONF_LDAP_INITIALIZE */
+ }
+
+ if (ld == NULL)
+ {
+# if USE_LDAP_INIT
+ ld = ldap_init(lmap->ldap_host, lmap->ldap_port);
+ save_errno = errno;
+# else /* USE_LDAP_INIT */
+ /*
+ ** If using ldap_open(), the actual connection to the server
+ ** happens now so we need the timeout here. For ldap_init(),
+ ** the connection happens at bind time.
+ */
+
+ SM_LDAP_SETTIMEOUT(lmap->ldap_timeout.tv_sec);
+ ld = ldap_open(lmap->ldap_host, lmap->ldap_port);
+ save_errno = errno;
+
+ /* clear the event if it has not sprung */
+ SM_LDAP_CLEARTIMEOUT();
+# endif /* USE_LDAP_INIT */
+ }
+
+ errno = save_errno;
+ if (ld == NULL)
+ return false;
+
+ sm_ldap_setopts(ld, lmap);
+
+# if USE_LDAP_INIT
+ /*
+ ** If using ldap_init(), the actual connection to the server
+ ** happens at ldap_bind_s() so we need the timeout here.
+ */
+
+ SM_LDAP_SETTIMEOUT(lmap->ldap_timeout.tv_sec);
+# endif /* USE_LDAP_INIT */
+
+# ifdef LDAP_AUTH_KRBV4
+ if (lmap->ldap_method == LDAP_AUTH_KRBV4 &&
+ lmap->ldap_secret != NULL)
+ {
+ /*
+ ** Need to put ticket in environment here instead of
+ ** during parseargs as there may be different tickets
+ ** for different LDAP connections.
+ */
+
+ (void) putenv(lmap->ldap_secret);
+ }
+# endif /* LDAP_AUTH_KRBV4 */
+
+ bind_result = ldap_bind_s(ld, lmap->ldap_binddn,
+ lmap->ldap_secret, lmap->ldap_method);
+
+# if USE_LDAP_INIT
+ /* clear the event if it has not sprung */
+ SM_LDAP_CLEARTIMEOUT();
+# endif /* USE_LDAP_INIT */
+
+ if (bind_result != LDAP_SUCCESS)
+ {
+ errno = bind_result + E_LDAPBASE;
+ return false;
+ }
+
+ /* Save PID to make sure only this PID closes the LDAP connection */
+ lmap->ldap_pid = getpid();
+ lmap->ldap_ld = ld;
+ return true;
+}
+
+/* ARGSUSED */
+static void
+ldaptimeout(unused)
+ int unused;
+{
+ /*
+ ** NOTE: THIS CAN BE CALLED FROM A SIGNAL HANDLER. DO NOT ADD
+ ** ANYTHING TO THIS ROUTINE UNLESS YOU KNOW WHAT YOU ARE
+ ** DOING.
+ */
+
+ errno = ETIMEDOUT;
+ longjmp(LDAPTimeout, 1);
+}
+
+/*
+** SM_LDAP_SEARCH -- initiate LDAP search
+**
+** Initiate an LDAP search, return the msgid.
+** The calling function must collect the results.
+**
+** Parameters:
+** lmap -- LDAP map information
+** key -- key to substitute in LDAP filter
+**
+** Returns:
+** -1 on failure, msgid on success
+**
+*/
+
+int
+sm_ldap_search(lmap, key)
+ SM_LDAP_STRUCT *lmap;
+ char *key;
+{
+ int msgid;
+ char *fp, *p, *q;
+ char filter[LDAPMAP_MAX_FILTER + 1];
+
+ /* substitute key into filter, perhaps multiple times */
+ memset(filter, '\0', sizeof filter);
+ fp = filter;
+ p = lmap->ldap_filter;
+ while ((q = strchr(p, '%')) != NULL)
+ {
+ if (q[1] == 's')
+ {
+ (void) sm_snprintf(fp, SPACELEFT(filter, fp),
+ "%.*s%s", (int) (q - p), p, key);
+ fp += strlen(fp);
+ p = q + 2;
+ }
+ else if (q[1] == '0')
+ {
+ char *k = key;
+
+ (void) sm_snprintf(fp, SPACELEFT(filter, fp),
+ "%.*s", (int) (q - p), p);
+ fp += strlen(fp);
+ p = q + 2;
+
+ /* Properly escape LDAP special characters */
+ while (SPACELEFT(filter, fp) > 0 &&
+ *k != '\0')
+ {
+ if (*k == '*' || *k == '(' ||
+ *k == ')' || *k == '\\')
+ {
+ (void) sm_strlcat(fp,
+ (*k == '*' ? "\\2A" :
+ (*k == '(' ? "\\28" :
+ (*k == ')' ? "\\29" :
+ (*k == '\\' ? "\\5C" :
+ "\00")))),
+ SPACELEFT(filter, fp));
+ fp += strlen(fp);
+ k++;
+ }
+ else
+ *fp++ = *k++;
+ }
+ }
+ else
+ {
+ (void) sm_snprintf(fp, SPACELEFT(filter, fp),
+ "%.*s", (int) (q - p + 1), p);
+ p = q + (q[1] == '%' ? 2 : 1);
+ fp += strlen(fp);
+ }
+ }
+ (void) sm_strlcpy(fp, p, SPACELEFT(filter, fp));
+ if (sm_debug_active(&SmLDAPTrace, 20))
+ sm_dprintf("ldap search filter=%s\n", filter);
+
+ lmap->ldap_res = NULL;
+ msgid = ldap_search(lmap->ldap_ld, lmap->ldap_base,
+ lmap->ldap_scope, filter,
+ (lmap->ldap_attr[0] == NULL ? NULL :
+ lmap->ldap_attr),
+ lmap->ldap_attrsonly);
+ return msgid;
+}
+
+/*
+** SM_LDAP_HAS_OBJECTCLASS -- determine if an LDAP entry is part of a
+** particular objectClass
+**
+** Parameters:
+** lmap -- pointer to SM_LDAP_STRUCT in use
+** entry -- current LDAP entry struct
+** ocvalue -- particular objectclass in question.
+** may be of form (fee|foo|fum) meaning
+** any entry can be part of either fee,
+** foo or fum objectclass
+**
+** Returns:
+** true if item has that objectClass
+*/
+
+static bool
+sm_ldap_has_objectclass(lmap, entry, ocvalue)
+ SM_LDAP_STRUCT *lmap;
+ LDAPMessage *entry;
+ char *ocvalue;
+{
+ char **vals = NULL;
+ int i;
+
+ if (ocvalue == NULL)
+ return false;
+
+ vals = ldap_get_values(lmap->ldap_ld, entry, "objectClass");
+ if (vals == NULL)
+ return false;
+
+ for (i = 0; vals[i] != NULL; i++)
+ {
+ char *p;
+ char *q;
+
+ p = q = ocvalue;
+ while (*p != '\0')
+ {
+ while (*p != '\0' && *p != '|')
+ p++;
+
+ if ((p - q) == strlen(vals[i]) &&
+ sm_strncasecmp(vals[i], q, p - q) == 0)
+ {
+ ldap_value_free(vals);
+ return true;
+ }
+
+ while (*p == '|')
+ p++;
+ q = p;
+ }
+ }
+
+ ldap_value_free(vals);
+ return false;
+}
+
+/*
+** SM_LDAP_RESULTS -- return results from an LDAP lookup in result
+**
+** Parameters:
+** lmap -- pointer to SM_LDAP_STRUCT in use
+** msgid -- msgid returned by sm_ldap_search()
+** flags -- flags for the lookup
+** delim -- delimiter for result concatenation
+** rpool -- memory pool for storage
+** result -- return string
+** recurse -- recursion list
+**
+** Returns:
+** status (sysexit)
+*/
+
+# define SM_LDAP_ERROR_CLEANUP() \
+{ \
+ if (lmap->ldap_res != NULL) \
+ { \
+ ldap_msgfree(lmap->ldap_res); \
+ lmap->ldap_res = NULL; \
+ } \
+ (void) ldap_abandon(lmap->ldap_ld, msgid); \
+}
+
+static SM_LDAP_RECURSE_ENTRY *
+sm_ldap_add_recurse(top, item, type, rpool)
+ SM_LDAP_RECURSE_LIST **top;
+ char *item;
+ int type;
+ SM_RPOOL_T *rpool;
+{
+ int n;
+ int m;
+ int p;
+ int insertat;
+ int moveb;
+ int oldsizeb;
+ int rc;
+ SM_LDAP_RECURSE_ENTRY *newe;
+ SM_LDAP_RECURSE_ENTRY **olddata;
+
+ /*
+ ** This code will maintain a list of
+ ** SM_LDAP_RECURSE_ENTRY structures
+ ** in ascending order.
+ */
+
+ if (*top == NULL)
+ {
+ /* Allocate an initial SM_LDAP_RECURSE_LIST struct */
+ *top = sm_rpool_malloc_x(rpool, sizeof **top);
+ (*top)->lr_cnt = 0;
+ (*top)->lr_size = 0;
+ (*top)->lr_data = NULL;
+ }
+
+ if ((*top)->lr_cnt >= (*top)->lr_size)
+ {
+ /* Grow the list of SM_LDAP_RECURSE_ENTRY ptrs */
+ olddata = (*top)->lr_data;
+ if ((*top)->lr_size == 0)
+ {
+ oldsizeb = 0;
+ (*top)->lr_size = 256;
+ }
+ else
+ {
+ oldsizeb = (*top)->lr_size * sizeof *((*top)->lr_data);
+ (*top)->lr_size *= 2;
+ }
+ (*top)->lr_data = sm_rpool_malloc_x(rpool,
+ (*top)->lr_size * sizeof *((*top)->lr_data));
+ if (oldsizeb > 0)
+ memcpy((*top)->lr_data, olddata, oldsizeb);
+ }
+
+ /*
+ ** Binary search/insert item:type into list.
+ ** Return current entry pointer if already exists.
+ */
+
+ n = 0;
+ m = (*top)->lr_cnt - 1;
+ if (m < 0)
+ insertat = 0;
+ else
+ insertat = -1;
+
+ while (insertat == -1)
+ {
+ p = (m + n) / 2;
+
+ rc = sm_strcasecmp(item, (*top)->lr_data[p]->lr_search);
+ if (rc == 0)
+ rc = type - (*top)->lr_data[p]->lr_type;
+
+ if (rc < 0)
+ m = p - 1;
+ else if (rc > 0)
+ n = p + 1;
+ else
+ return (*top)->lr_data[p];
+
+ if (m == -1)
+ insertat = 0;
+ else if (n >= (*top)->lr_cnt)
+ insertat = (*top)->lr_cnt;
+ else if (m < n)
+ insertat = m + 1;
+ }
+
+ /*
+ ** Not found in list, make room
+ ** at insert point and add it.
+ */
+
+ newe = sm_rpool_malloc_x(rpool, sizeof *newe);
+ if (newe != NULL)
+ {
+ moveb = ((*top)->lr_cnt - insertat) * sizeof *((*top)->lr_data);
+ if (moveb > 0)
+ memmove(&((*top)->lr_data[insertat + 1]),
+ &((*top)->lr_data[insertat]),
+ moveb);
+
+ newe->lr_search = sm_rpool_strdup_x(rpool, item);
+ newe->lr_type = type;
+ newe->lr_ludp = NULL;
+ newe->lr_attrs = NULL;
+ newe->lr_done = false;
+
+ ((*top)->lr_data)[insertat] = newe;
+ (*top)->lr_cnt++;
+ }
+ return newe;
+}
+
+int
+sm_ldap_results(lmap, msgid, flags, delim, rpool, result,
+ resultln, resultsz, recurse)
+ SM_LDAP_STRUCT *lmap;
+ int msgid;
+ int flags;
+ int delim;
+ SM_RPOOL_T *rpool;
+ char **result;
+ int *resultln;
+ int *resultsz;
+ SM_LDAP_RECURSE_LIST *recurse;
+{
+ bool toplevel;
+ int i;
+ int statp;
+ int vsize;
+ int ret;
+ int save_errno;
+ char *p;
+ SM_LDAP_RECURSE_ENTRY *rl;
+
+ /* Are we the top top level of the search? */
+ toplevel = (recurse == NULL);
+
+ /* Get results */
+ statp = EX_NOTFOUND;
+ while ((ret = ldap_result(lmap->ldap_ld, msgid, 0,
+ (lmap->ldap_timeout.tv_sec == 0 ? NULL :
+ &(lmap->ldap_timeout)),
+ &(lmap->ldap_res))) == LDAP_RES_SEARCH_ENTRY)
+ {
+ LDAPMessage *entry;
+
+ /* If we don't want multiple values and we have one, break */
+ if ((char) delim == '\0' &&
+ !bitset(SM_LDAP_SINGLEMATCH, flags) &&
+ *result != NULL)
+ break;
+
+ /* Cycle through all entries */
+ for (entry = ldap_first_entry(lmap->ldap_ld, lmap->ldap_res);
+ entry != NULL;
+ entry = ldap_next_entry(lmap->ldap_ld, lmap->ldap_res))
+ {
+ BerElement *ber;
+ char *attr;
+ char **vals = NULL;
+ char *dn;
+
+ /*
+ ** If matching only and found an entry,
+ ** no need to spin through attributes
+ */
+
+ if (bitset(SM_LDAP_MATCHONLY, flags))
+ {
+ statp = EX_OK;
+ continue;
+ }
+
+ /* record completed DN's to prevent loops */
+ dn = ldap_get_dn(lmap->ldap_ld, entry);
+ if (dn == NULL)
+ {
+ save_errno = sm_ldap_geterrno(lmap->ldap_ld);
+ save_errno += E_LDAPBASE;
+ SM_LDAP_ERROR_CLEANUP();
+ errno = save_errno;
+ return EX_TEMPFAIL;
+ }
+
+ rl = sm_ldap_add_recurse(&recurse, dn,
+ SM_LDAP_ATTR_DN,
+ rpool);
+
+ if (rl == NULL)
+ {
+ ldap_memfree(dn);
+ SM_LDAP_ERROR_CLEANUP();
+ errno = ENOMEM;
+ return EX_OSERR;
+ }
+ else if (rl->lr_done)
+ {
+ /* already on list, skip it */
+ ldap_memfree(dn);
+ continue;
+ }
+ ldap_memfree(dn);
+
+# if !defined(LDAP_VERSION_MAX) && !defined(LDAP_OPT_SIZELIMIT)
+ /*
+ ** Reset value to prevent lingering
+ ** LDAP_DECODING_ERROR due to
+ ** OpenLDAP 1.X's hack (see below)
+ */
+
+ lmap->ldap_ld->ld_errno = LDAP_SUCCESS;
+# endif /* !defined(LDAP_VERSION_MAX) !defined(LDAP_OPT_SIZELIMIT) */
+
+ for (attr = ldap_first_attribute(lmap->ldap_ld, entry,
+ &ber);
+ attr != NULL;
+ attr = ldap_next_attribute(lmap->ldap_ld, entry,
+ ber))
+ {
+ char *tmp, *vp_tmp;
+ int type;
+ char *needobjclass = NULL;
+
+ type = SM_LDAP_ATTR_NONE;
+ for (i = 0; lmap->ldap_attr[i] != NULL; i++)
+ {
+ if (sm_strcasecmp(lmap->ldap_attr[i],
+ attr) == 0)
+ {
+ type = lmap->ldap_attr_type[i];
+ needobjclass = lmap->ldap_attr_needobjclass[i];
+ break;
+ }
+ }
+
+ if (bitset(SM_LDAP_USE_ALLATTR, flags) &&
+ type == SM_LDAP_ATTR_NONE)
+ {
+ /* URL lookups specify attrs to use */
+ type = SM_LDAP_ATTR_NORMAL;
+ needobjclass = NULL;
+ }
+
+ if (type == SM_LDAP_ATTR_NONE)
+ {
+ /* attribute not requested */
+ ldap_memfree(attr);
+ SM_LDAP_ERROR_CLEANUP();
+ errno = EFAULT;
+ return EX_SOFTWARE;
+ }
+
+ /*
+ ** For recursion on a particular attribute,
+ ** we may need to see if this entry is
+ ** part of a particular objectclass.
+ ** Also, ignore objectClass attribute.
+ ** Otherwise we just ignore this attribute.
+ */
+
+ if (type == SM_LDAP_ATTR_OBJCLASS ||
+ (needobjclass != NULL &&
+ !sm_ldap_has_objectclass(lmap, entry,
+ needobjclass)))
+ {
+ ldap_memfree(attr);
+ continue;
+ }
+
+ if (lmap->ldap_attrsonly == LDAPMAP_FALSE)
+ {
+ vals = ldap_get_values(lmap->ldap_ld,
+ entry,
+ attr);
+ if (vals == NULL)
+ {
+ save_errno = sm_ldap_geterrno(lmap->ldap_ld);
+ if (save_errno == LDAP_SUCCESS)
+ {
+ ldap_memfree(attr);
+ continue;
+ }
+
+ /* Must be an error */
+ save_errno += E_LDAPBASE;
+ ldap_memfree(attr);
+ SM_LDAP_ERROR_CLEANUP();
+ errno = save_errno;
+ return EX_TEMPFAIL;
+ }
+ }
+
+ statp = EX_OK;
+
+# if !defined(LDAP_VERSION_MAX) && !defined(LDAP_OPT_SIZELIMIT)
+ /*
+ ** Reset value to prevent lingering
+ ** LDAP_DECODING_ERROR due to
+ ** OpenLDAP 1.X's hack (see below)
+ */
+
+ lmap->ldap_ld->ld_errno = LDAP_SUCCESS;
+# endif /* !defined(LDAP_VERSION_MAX) !defined(LDAP_OPT_SIZELIMIT) */
+
+ /*
+ ** If matching only,
+ ** no need to spin through entries
+ */
+
+ if (bitset(SM_LDAP_MATCHONLY, flags))
+ {
+ if (lmap->ldap_attrsonly == LDAPMAP_FALSE)
+ ldap_value_free(vals);
+ ldap_memfree(attr);
+ continue;
+ }
+
+ /*
+ ** If we don't want multiple values,
+ ** return first found.
+ */
+
+ if ((char) delim == '\0')
+ {
+ if (*result != NULL)
+ {
+ /* already have a value */
+ if (bitset(SM_LDAP_SINGLEMATCH,
+ flags))
+ {
+ /* only wanted one match */
+ SM_LDAP_ERROR_CLEANUP();
+ errno = ENOENT;
+ return EX_NOTFOUND;
+ }
+ break;
+ }
+
+ if (lmap->ldap_attrsonly == LDAPMAP_TRUE)
+ {
+ *result = sm_rpool_strdup_x(rpool,
+ attr);
+ ldap_memfree(attr);
+ break;
+ }
+
+ if (vals[0] == NULL)
+ {
+ ldap_value_free(vals);
+ ldap_memfree(attr);
+ continue;
+ }
+
+ vsize = strlen(vals[0]) + 1;
+ if (lmap->ldap_attrsep != '\0')
+ vsize += strlen(attr) + 1;
+ *result = sm_rpool_malloc_x(rpool,
+ vsize);
+ if (lmap->ldap_attrsep != '\0')
+ sm_snprintf(*result, vsize,
+ "%s%c%s",
+ attr,
+ lmap->ldap_attrsep,
+ vals[0]);
+ else
+ sm_strlcpy(*result, vals[0],
+ vsize);
+ ldap_value_free(vals);
+ ldap_memfree(attr);
+ break;
+ }
+
+ /* attributes only */
+ if (lmap->ldap_attrsonly == LDAPMAP_TRUE)
+ {
+ if (*result == NULL)
+ *result = sm_rpool_strdup_x(rpool,
+ attr);
+ else
+ {
+ if (bitset(SM_LDAP_SINGLEMATCH,
+ flags) &&
+ *result != NULL)
+ {
+ /* only wanted one match */
+ SM_LDAP_ERROR_CLEANUP();
+ errno = ENOENT;
+ return EX_NOTFOUND;
+ }
+
+ vsize = strlen(*result) +
+ strlen(attr) + 2;
+ tmp = sm_rpool_malloc_x(rpool,
+ vsize);
+ (void) sm_snprintf(tmp,
+ vsize, "%s%c%s",
+ *result, (char) delim,
+ attr);
+ *result = tmp;
+ }
+ ldap_memfree(attr);
+ continue;
+ }
+
+ /*
+ ** If there is more than one, munge then
+ ** into a map_coldelim separated string.
+ ** If we are recursing we may have an entry
+ ** with no 'normal' values to put in the
+ ** string.
+ ** This is not an error.
+ */
+
+ if (type == SM_LDAP_ATTR_NORMAL &&
+ bitset(SM_LDAP_SINGLEMATCH, flags) &&
+ *result != NULL)
+ {
+ /* only wanted one match */
+ SM_LDAP_ERROR_CLEANUP();
+ errno = ENOENT;
+ return EX_NOTFOUND;
+ }
+
+ vsize = 0;
+ for (i = 0; vals[i] != NULL; i++)
+ {
+ if (type == SM_LDAP_ATTR_DN ||
+ type == SM_LDAP_ATTR_FILTER ||
+ type == SM_LDAP_ATTR_URL)
+ {
+ /* add to recursion */
+ if (sm_ldap_add_recurse(&recurse,
+ vals[i],
+ type,
+ rpool) == NULL)
+ {
+ SM_LDAP_ERROR_CLEANUP();
+ errno = ENOMEM;
+ return EX_OSERR;
+ }
+ continue;
+ }
+
+ vsize += strlen(vals[i]) + 1;
+ if (lmap->ldap_attrsep != '\0')
+ vsize += strlen(attr) + 1;
+ }
+
+ /*
+ ** Create/Append to string any normal
+ ** attribute values. Otherwise, just free
+ ** memory and move on to the next
+ ** attribute in this entry.
+ */
+
+ if (type == SM_LDAP_ATTR_NORMAL && vsize > 0)
+ {
+ char *pe;
+
+ /* Grow result string if needed */
+ if ((*resultln + vsize) >= *resultsz)
+ {
+ while ((*resultln + vsize) >= *resultsz)
+ {
+ if (*resultsz == 0)
+ *resultsz = 1024;
+ else
+ *resultsz *= 2;
+ }
+
+ vp_tmp = sm_rpool_malloc_x(rpool, *resultsz);
+ *vp_tmp = '\0';
+
+ if (*result != NULL)
+ sm_strlcpy(vp_tmp,
+ *result,
+ *resultsz);
+ *result = vp_tmp;
+ }
+
+ p = *result + *resultln;
+ pe = *result + *resultsz;
+
+ for (i = 0; vals[i] != NULL; i++)
+ {
+ if (*resultln > 0 &&
+ p < pe)
+ *p++ = (char) delim;
+
+ if (lmap->ldap_attrsep != '\0')
+ {
+ p += sm_strlcpy(p, attr,
+ pe - p);
+ if (p < pe)
+ *p++ = lmap->ldap_attrsep;
+ }
+
+ p += sm_strlcpy(p, vals[i],
+ pe - p);
+ *resultln = p - (*result);
+ if (p >= pe)
+ {
+ /* Internal error: buffer too small for LDAP values */
+ SM_LDAP_ERROR_CLEANUP();
+ errno = ENOMEM;
+ return EX_OSERR;
+ }
+ }
+ }
+
+ ldap_value_free(vals);
+ ldap_memfree(attr);
+ }
+ save_errno = sm_ldap_geterrno(lmap->ldap_ld);
+
+ /*
+ ** We check save_errno != LDAP_DECODING_ERROR since
+ ** OpenLDAP 1.X has a very ugly *undocumented*
+ ** hack of returning this error code from
+ ** ldap_next_attribute() if the library freed the
+ ** ber attribute. See:
+ ** http://www.openldap.org/lists/openldap-devel/9901/msg00064.html
+ */
+
+ if (save_errno != LDAP_SUCCESS &&
+ save_errno != LDAP_DECODING_ERROR)
+ {
+ /* Must be an error */
+ save_errno += E_LDAPBASE;
+ SM_LDAP_ERROR_CLEANUP();
+ errno = save_errno;
+ return EX_TEMPFAIL;
+ }
+
+ /* mark this DN as done */
+ rl->lr_done = true;
+ if (rl->lr_ludp != NULL)
+ {
+ ldap_free_urldesc(rl->lr_ludp);
+ rl->lr_ludp = NULL;
+ }
+ if (rl->lr_attrs != NULL)
+ {
+ free(rl->lr_attrs);
+ rl->lr_attrs = NULL;
+ }
+
+ /* We don't want multiple values and we have one */
+ if ((char) delim == '\0' &&
+ !bitset(SM_LDAP_SINGLEMATCH, flags) &&
+ *result != NULL)
+ break;
+ }
+ save_errno = sm_ldap_geterrno(lmap->ldap_ld);
+ if (save_errno != LDAP_SUCCESS &&
+ save_errno != LDAP_DECODING_ERROR)
+ {
+ /* Must be an error */
+ save_errno += E_LDAPBASE;
+ SM_LDAP_ERROR_CLEANUP();
+ errno = save_errno;
+ return EX_TEMPFAIL;
+ }
+ ldap_msgfree(lmap->ldap_res);
+ lmap->ldap_res = NULL;
+ }
+
+ if (ret == 0)
+ save_errno = ETIMEDOUT;
+ else
+ save_errno = sm_ldap_geterrno(lmap->ldap_ld);
+ if (save_errno != LDAP_SUCCESS)
+ {
+ statp = EX_TEMPFAIL;
+ if (ret != 0)
+ {
+ switch (save_errno)
+ {
+#ifdef LDAP_SERVER_DOWN
+ case LDAP_SERVER_DOWN:
+#endif /* LDAP_SERVER_DOWN */
+ case LDAP_TIMEOUT:
+ case LDAP_UNAVAILABLE:
+
+ /*
+ ** server disappeared,
+ ** try reopen on next search
+ */
+
+ statp = EX_RESTART;
+ break;
+ }
+ save_errno += E_LDAPBASE;
+ }
+ SM_LDAP_ERROR_CLEANUP();
+ errno = save_errno;
+ return statp;
+ }
+
+ if (lmap->ldap_res != NULL)
+ {
+ ldap_msgfree(lmap->ldap_res);
+ lmap->ldap_res = NULL;
+ }
+
+ if (toplevel)
+ {
+ int rlidx;
+
+ /*
+ ** Spin through the built-up recurse list at the top
+ ** of the recursion. Since new items are added at the
+ ** end of the shared list, we actually only ever get
+ ** one level of recursion before things pop back to the
+ ** top. Any items added to the list during that recursion
+ ** will be expanded by the top level.
+ */
+
+ for (rlidx = 0; recurse != NULL && rlidx < recurse->lr_cnt; rlidx++)
+ {
+ int newflags;
+ int sid;
+ int status;
+
+ rl = recurse->lr_data[rlidx];
+
+ newflags = flags;
+ if (rl->lr_done)
+ {
+ /* already expanded */
+ continue;
+ }
+
+ if (rl->lr_type == SM_LDAP_ATTR_DN)
+ {
+ /* do DN search */
+ sid = ldap_search(lmap->ldap_ld,
+ rl->lr_search,
+ lmap->ldap_scope,
+ "(objectClass=*)",
+ (lmap->ldap_attr[0] == NULL ?
+ NULL : lmap->ldap_attr),
+ lmap->ldap_attrsonly);
+ }
+ else if (rl->lr_type == SM_LDAP_ATTR_FILTER)
+ {
+ /* do new search */
+ sid = ldap_search(lmap->ldap_ld,
+ lmap->ldap_base,
+ lmap->ldap_scope,
+ rl->lr_search,
+ (lmap->ldap_attr[0] == NULL ?
+ NULL : lmap->ldap_attr),
+ lmap->ldap_attrsonly);
+ }
+ else if (rl->lr_type == SM_LDAP_ATTR_URL)
+ {
+ /* Parse URL */
+ sid = ldap_url_parse(rl->lr_search,
+ &rl->lr_ludp);
+
+ if (sid != 0)
+ {
+ errno = sid + E_LDAPURLBASE;
+ return EX_TEMPFAIL;
+ }
+
+ /* We need to add objectClass */
+ if (rl->lr_ludp->lud_attrs != NULL)
+ {
+ int attrnum = 0;
+
+ while (rl->lr_ludp->lud_attrs[attrnum] != NULL)
+ {
+ if (strcasecmp(rl->lr_ludp->lud_attrs[attrnum],
+ "objectClass") == 0)
+ {
+ /* already requested */
+ attrnum = -1;
+ break;
+ }
+ attrnum++;
+ }
+
+ if (attrnum >= 0)
+ {
+ int i;
+
+ rl->lr_attrs = (char **)malloc(sizeof(char *) * (attrnum + 2));
+ if (rl->lr_attrs == NULL)
+ {
+ save_errno = errno;
+ ldap_free_urldesc(rl->lr_ludp);
+ errno = save_errno;
+ return EX_TEMPFAIL;
+ }
+ for (i = 0 ; i < attrnum; i++)
+ {
+ rl->lr_attrs[i] = rl->lr_ludp->lud_attrs[i];
+ }
+ rl->lr_attrs[i++] = "objectClass";
+ rl->lr_attrs[i++] = NULL;
+ }
+ }
+
+ /*
+ ** Use the existing connection
+ ** for this search. It really
+ ** should use lud_scheme://lud_host:lud_port/
+ ** instead but that would require
+ ** opening a new connection.
+ ** This should be fixed ASAP.
+ */
+
+ sid = ldap_search(lmap->ldap_ld,
+ rl->lr_ludp->lud_dn,
+ rl->lr_ludp->lud_scope,
+ rl->lr_ludp->lud_filter,
+ rl->lr_attrs,
+ lmap->ldap_attrsonly);
+
+ /* Use the attributes specified by URL */
+ newflags |= SM_LDAP_USE_ALLATTR;
+ }
+ else
+ {
+ /* unknown or illegal attribute type */
+ errno = EFAULT;
+ return EX_SOFTWARE;
+ }
+
+ /* Collect results */
+ if (sid == -1)
+ {
+ save_errno = sm_ldap_geterrno(lmap->ldap_ld);
+ statp = EX_TEMPFAIL;
+ switch (save_errno)
+ {
+#ifdef LDAP_SERVER_DOWN
+ case LDAP_SERVER_DOWN:
+#endif /* LDAP_SERVER_DOWN */
+ case LDAP_TIMEOUT:
+ case LDAP_UNAVAILABLE:
+
+ /*
+ ** server disappeared,
+ ** try reopen on next search
+ */
+
+ statp = EX_RESTART;
+ break;
+ }
+ errno = save_errno + E_LDAPBASE;
+ return statp;
+ }
+
+ status = sm_ldap_results(lmap, sid, newflags, delim,
+ rpool, result, resultln,
+ resultsz, recurse);
+ save_errno = errno;
+ if (status != EX_OK && status != EX_NOTFOUND)
+ {
+ errno = save_errno;
+ return status;
+ }
+
+ /* Mark as done */
+ rl->lr_done = true;
+ if (rl->lr_ludp != NULL)
+ {
+ ldap_free_urldesc(rl->lr_ludp);
+ rl->lr_ludp = NULL;
+ }
+ if (rl->lr_attrs != NULL)
+ {
+ free(rl->lr_attrs);
+ rl->lr_attrs = NULL;
+ }
+
+ /* Reset rlidx as new items may have been added */
+ rlidx = -1;
+ }
+ }
+ return statp;
+}
+
+/*
+** SM_LDAP_CLOSE -- close LDAP connection
+**
+** Parameters:
+** lmap -- LDAP map information
+**
+** Returns:
+** None.
+**
+*/
+
+void
+sm_ldap_close(lmap)
+ SM_LDAP_STRUCT *lmap;
+{
+ if (lmap->ldap_ld == NULL)
+ return;
+
+ if (lmap->ldap_pid == getpid())
+ ldap_unbind(lmap->ldap_ld);
+ lmap->ldap_ld = NULL;
+ lmap->ldap_pid = 0;
+}
+
+/*
+** SM_LDAP_SETOPTS -- set LDAP options
+**
+** Parameters:
+** ld -- LDAP session handle
+** lmap -- LDAP map information
+**
+** Returns:
+** None.
+**
+*/
+
+void
+sm_ldap_setopts(ld, lmap)
+ LDAP *ld;
+ SM_LDAP_STRUCT *lmap;
+{
+# if USE_LDAP_SET_OPTION
+ if (lmap->ldap_version != 0)
+ {
+ ldap_set_option(ld, LDAP_OPT_PROTOCOL_VERSION,
+ &lmap->ldap_version);
+ }
+ ldap_set_option(ld, LDAP_OPT_DEREF, &lmap->ldap_deref);
+ if (bitset(LDAP_OPT_REFERRALS, lmap->ldap_options))
+ ldap_set_option(ld, LDAP_OPT_REFERRALS, LDAP_OPT_ON);
+ else
+ ldap_set_option(ld, LDAP_OPT_REFERRALS, LDAP_OPT_OFF);
+ ldap_set_option(ld, LDAP_OPT_SIZELIMIT, &lmap->ldap_sizelimit);
+ ldap_set_option(ld, LDAP_OPT_TIMELIMIT, &lmap->ldap_timelimit);
+# ifdef LDAP_OPT_RESTART
+ ldap_set_option(ld, LDAP_OPT_RESTART, LDAP_OPT_ON);
+# endif /* LDAP_OPT_RESTART */
+# else /* USE_LDAP_SET_OPTION */
+ /* From here on in we can use ldap internal timelimits */
+ ld->ld_deref = lmap->ldap_deref;
+ ld->ld_options = lmap->ldap_options;
+ ld->ld_sizelimit = lmap->ldap_sizelimit;
+ ld->ld_timelimit = lmap->ldap_timelimit;
+# endif /* USE_LDAP_SET_OPTION */
+}
+
+/*
+** SM_LDAP_GETERRNO -- get ldap errno value
+**
+** Parameters:
+** ld -- LDAP session handle
+**
+** Returns:
+** LDAP errno.
+**
+*/
+
+int
+sm_ldap_geterrno(ld)
+ LDAP *ld;
+{
+ int err = LDAP_SUCCESS;
+
+# if defined(LDAP_VERSION_MAX) && LDAP_VERSION_MAX >= 3
+ (void) ldap_get_option(ld, LDAP_OPT_ERROR_NUMBER, &err);
+# else /* defined(LDAP_VERSION_MAX) && LDAP_VERSION_MAX >= 3 */
+# ifdef LDAP_OPT_SIZELIMIT
+ err = ldap_get_lderrno(ld, NULL, NULL);
+# else /* LDAP_OPT_SIZELIMIT */
+ err = ld->ld_errno;
+
+ /*
+ ** Reset value to prevent lingering LDAP_DECODING_ERROR due to
+ ** OpenLDAP 1.X's hack (see above)
+ */
+
+ ld->ld_errno = LDAP_SUCCESS;
+# endif /* LDAP_OPT_SIZELIMIT */
+# endif /* defined(LDAP_VERSION_MAX) && LDAP_VERSION_MAX >= 3 */
+ return err;
+}
+# endif /* LDAPMAP */
diff --git a/usr/src/cmd/sendmail/libsm/local.h b/usr/src/cmd/sendmail/libsm/local.h
new file mode 100644
index 0000000000..a52c786329
--- /dev/null
+++ b/usr/src/cmd/sendmail/libsm/local.h
@@ -0,0 +1,330 @@
+/*
+ * Copyright (c) 2000-2002, 2004 Sendmail, Inc. and its suppliers.
+ * All rights reserved.
+ * Copyright (c) 1990, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Chris Torek.
+ *
+ * By using this file, you agree to the terms and conditions set
+ * forth in the LICENSE file which can be found at the top level of
+ * the sendmail distribution.
+ *
+ * $Id: local.h,v 1.51.2.2 2004/01/09 18:32:44 ca Exp $
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+/*
+** Information local to this implementation of stdio,
+** in particular, macros and private variables.
+*/
+
+#include <sys/time.h>
+#if !SM_CONF_MEMCHR
+# include <memory.h>
+#endif /* !SM_CONF_MEMCHR */
+#include <sm/heap.h>
+
+int sm_flush __P((SM_FILE_T *, int *));
+SM_FILE_T *smfp __P((void));
+int sm_refill __P((SM_FILE_T *, int));
+void sm_init __P((void));
+void sm_cleanup __P((void));
+void sm_makebuf __P((SM_FILE_T *));
+int sm_whatbuf __P((SM_FILE_T *, size_t *, int *));
+int sm_fwalk __P((int (*)(SM_FILE_T *, int *), int *));
+int sm_wsetup __P((SM_FILE_T *));
+int sm_flags __P((int));
+SM_FILE_T *sm_fp __P((const SM_FILE_T *, const int, SM_FILE_T *));
+int sm_vprintf __P((int, char const *, va_list));
+
+/* std io functions */
+ssize_t sm_stdread __P((SM_FILE_T *, char *, size_t));
+ssize_t sm_stdwrite __P((SM_FILE_T *, char const *, size_t));
+off_t sm_stdseek __P((SM_FILE_T *, off_t, int));
+int sm_stdclose __P((SM_FILE_T *));
+int sm_stdopen __P((SM_FILE_T *, const void *, int, const void *));
+int sm_stdfdopen __P((SM_FILE_T *, const void *, int, const void *));
+int sm_stdsetinfo __P((SM_FILE_T *, int , void *));
+int sm_stdgetinfo __P((SM_FILE_T *, int , void *));
+
+/* stdio io functions */
+ssize_t sm_stdioread __P((SM_FILE_T *, char *, size_t));
+ssize_t sm_stdiowrite __P((SM_FILE_T *, char const *, size_t));
+off_t sm_stdioseek __P((SM_FILE_T *, off_t, int));
+int sm_stdioclose __P((SM_FILE_T *));
+int sm_stdioopen __P((SM_FILE_T *, const void *, int, const void *));
+int sm_stdiosetinfo __P((SM_FILE_T *, int , void *));
+int sm_stdiogetinfo __P((SM_FILE_T *, int , void *));
+
+/* string io functions */
+ssize_t sm_strread __P((SM_FILE_T *, char *, size_t));
+ssize_t sm_strwrite __P((SM_FILE_T *, char const *, size_t));
+off_t sm_strseek __P((SM_FILE_T *, off_t, int));
+int sm_strclose __P((SM_FILE_T *));
+int sm_stropen __P((SM_FILE_T *, const void *, int, const void *));
+int sm_strsetinfo __P((SM_FILE_T *, int , void *));
+int sm_strgetinfo __P((SM_FILE_T *, int , void *));
+
+/* syslog io functions */
+ssize_t sm_syslogread __P((SM_FILE_T *, char *, size_t));
+ssize_t sm_syslogwrite __P((SM_FILE_T *, char const *, size_t));
+off_t sm_syslogseek __P((SM_FILE_T *, off_t, int));
+int sm_syslogclose __P((SM_FILE_T *));
+int sm_syslogopen __P((SM_FILE_T *, const void *, int, const void *));
+int sm_syslogsetinfo __P((SM_FILE_T *, int , void *));
+int sm_sysloggetinfo __P((SM_FILE_T *, int , void *));
+
+/* should be defined in sys/time.h */
+#ifndef timersub
+# define timersub(tvp, uvp, vvp) \
+ do \
+ { \
+ (vvp)->tv_sec = (tvp)->tv_sec - (uvp)->tv_sec; \
+ (vvp)->tv_usec = (tvp)->tv_usec - (uvp)->tv_usec; \
+ if ((vvp)->tv_usec < 0) \
+ { \
+ (vvp)->tv_sec--; \
+ (vvp)->tv_usec += 1000000; \
+ } \
+ } while (0)
+#endif /* !timersub */
+
+#ifndef timeradd
+# define timeradd(tvp, uvp, vvp) \
+ do \
+ { \
+ (vvp)->tv_sec = (tvp)->tv_sec + (uvp)->tv_sec; \
+ (vvp)->tv_usec = (tvp)->tv_usec + (uvp)->tv_usec; \
+ if ((vvp)->tv_usec >= 1000000) \
+ { \
+ (vvp)->tv_sec++; \
+ (vvp)->tv_usec -= 1000000; \
+ } \
+ } while (0)
+#endif /* !timeradd */
+
+#ifndef timercmp
+# define timercmp(tvp, uvp, cmp) \
+ (((tvp)->tv_sec == (uvp)->tv_sec) ? \
+ ((tvp)->tv_usec cmp (uvp)->tv_usec) : \
+ ((tvp)->tv_sec cmp (uvp)->tv_sec))
+#endif /* !timercmp */
+
+extern bool Sm_IO_DidInit;
+
+/* Return true iff the given SM_FILE_T cannot be written now. */
+#define cantwrite(fp) \
+ ((((fp)->f_flags & SMWR) == 0 || (fp)->f_bf.smb_base == NULL) && \
+ sm_wsetup(fp))
+
+/*
+** Test whether the given stdio file has an active ungetc buffer;
+** release such a buffer, without restoring ordinary unread data.
+*/
+
+#define HASUB(fp) ((fp)->f_ub.smb_base != NULL)
+#define FREEUB(fp) \
+{ \
+ if ((fp)->f_ub.smb_base != (fp)->f_ubuf) \
+ sm_free((char *)(fp)->f_ub.smb_base); \
+ (fp)->f_ub.smb_base = NULL; \
+}
+
+extern const char SmFileMagic[];
+
+#define SM_ALIGN(p) (((unsigned long)(p) + SM_ALIGN_BITS) & ~SM_ALIGN_BITS)
+
+#define sm_io_flockfile(fp) ((void) 0)
+#define sm_io_funlockfile(fp) ((void) 0)
+
+#ifndef FDSET_CAST
+# define FDSET_CAST /* empty cast for fd_set arg to select */
+#endif
+
+/*
+** SM_CONVERT_TIME -- convert the API timeout flag for select() usage.
+**
+** This takes a 'fp' (a file type pointer) and obtains the "raw"
+** file descriptor (fd) if possible. The 'fd' is needed to possibly
+** switch the mode of the file (blocking/non-blocking) to match
+** the type of timeout. If timeout is SM_TIME_FOREVER then the
+** timeout using select won't be needed and the file is best placed
+** in blocking mode. If there is to be a finite timeout then the file
+** is best placed in non-blocking mode. Then, if not enough can be
+** written, select() can be used to test when something can be written
+** yet still timeout if the wait is too long.
+** If the mode is already in the correct state we don't change it.
+** Iff (yes "iff") the 'fd' is "-1" in value then the mode change
+** will not happen. This situation arises when a late-binding-to-disk
+** file type is in use. An example of this is the sendmail buffered
+** file type (in sendmail/bf.c).
+**
+** Parameters
+** fp -- the file pointer the timeout is for
+** fd -- to become the file descriptor value from 'fp'
+** val -- the timeout value to be converted
+** time -- a struct timeval holding the converted value
+**
+** Returns
+** nothing, this is flow-through code
+**
+** Side Effects:
+** May or may not change the mode of a currently open file.
+** The file mode may be changed to O_NONBLOCK or ~O_NONBLOCK
+** (meaning block). This is done to best match the type of
+** timeout and for (possible) use with select().
+*/
+
+# define SM_CONVERT_TIME(fp, fd, val, time) { \
+ if (((fd) = sm_io_getinfo(fp, SM_IO_WHAT_FD, NULL)) == -1) \
+ { \
+ /* can't get an fd, likely internal 'fake' fp */ \
+ errno = 0; \
+ } \
+ if ((val) == SM_TIME_DEFAULT) \
+ (val) = (fp)->f_timeout; \
+ if ((val) == SM_TIME_IMMEDIATE || (val) == SM_TIME_FOREVER) \
+ { \
+ (time)->tv_sec = 0; \
+ (time)->tv_usec = 0; \
+ } \
+ else \
+ { \
+ (time)->tv_sec = (val) / 1000; \
+ (time)->tv_usec = ((val) - ((time)->tv_sec * 1000)) * 10; \
+ } \
+ if ((val) == SM_TIME_FOREVER) \
+ { \
+ if ((fp)->f_timeoutstate == SM_TIME_NONBLOCK && (fd) != -1) \
+ { \
+ int ret; \
+ ret = fcntl((fd), F_GETFL, 0); \
+ if (ret == -1 || fcntl((fd), F_SETFL, \
+ ret & ~O_NONBLOCK) == -1) \
+ { \
+ /* errno should be set */ \
+ return SM_IO_EOF; \
+ } \
+ (fp)->f_timeoutstate = SM_TIME_BLOCK; \
+ if ((fp)->f_modefp != NULL) \
+ (fp)->f_modefp->f_timeoutstate = SM_TIME_BLOCK; \
+ } \
+ } \
+ else { \
+ if ((fp)->f_timeoutstate == SM_TIME_BLOCK && (fd) != -1) \
+ { \
+ int ret; \
+ ret = fcntl((fd), F_GETFL, 0); \
+ if (ret == -1 || fcntl((fd), F_SETFL, \
+ ret | O_NONBLOCK) == -1) \
+ { \
+ /* errno should be set */ \
+ return SM_IO_EOF; \
+ } \
+ (fp)->f_timeoutstate = SM_TIME_NONBLOCK; \
+ if ((fp)->f_modefp != NULL) \
+ (fp)->f_modefp->f_timeoutstate = SM_TIME_NONBLOCK; \
+ } \
+ } \
+}
+
+/*
+** SM_IO_WR_TIMEOUT -- setup the timeout for the write
+**
+** This #define uses a select() to wait for the 'fd' to become writable.
+** The select() can be active for up to 'to' time. The select may not
+** use all of the the 'to' time. Hence, the amount of "wall-clock" time is
+** measured to decide how much to subtract from 'to' to update it. On some
+** BSD-based/like systems the timeout for a select is updated for the
+** amount of time used. On many/most systems this does not happen. Therefore
+** the updating of 'to' must be done ourselves; a copy of 'to' is passed
+** since a BSD-like system will have updated it and we don't want to
+** double the time used!
+** Note: if a valid 'fd' doesn't exist yet, don't use this (e.g. the
+** sendmail buffered file type in sendmail/bf.c; see fvwrite.c).
+**
+** Parameters
+** fd -- a file descriptor for doing select() with
+** timeout -- the original user set value.
+**
+** Returns
+** nothing, this is flow through code
+**
+** Side Effects:
+** adjusts 'timeout' for time used
+*/
+
+#define SM_IO_WR_TIMEOUT(fp, fd, to) { \
+ struct timeval sm_io_to_before, sm_io_to_after, sm_io_to_diff; \
+ struct timeval sm_io_to; \
+ int sm_io_to_sel; \
+ fd_set sm_io_to_mask, sm_io_x_mask; \
+ errno = 0; \
+ if ((to) == SM_TIME_DEFAULT) \
+ (to) = (fp)->f_timeout; \
+ if ((to) == SM_TIME_IMMEDIATE) \
+ { \
+ errno = EAGAIN; \
+ return SM_IO_EOF; \
+ } \
+ else if ((to) == SM_TIME_FOREVER) \
+ { \
+ errno = EINVAL; \
+ return SM_IO_EOF; \
+ } \
+ else \
+ { \
+ sm_io_to.tv_sec = (to) / 1000; \
+ sm_io_to.tv_usec = ((to) - (sm_io_to.tv_sec * 1000)) * 10; \
+ } \
+ if (FD_SETSIZE > 0 && (fd) >= FD_SETSIZE) \
+ { \
+ errno = EINVAL; \
+ return SM_IO_EOF; \
+ } \
+ FD_ZERO(&sm_io_to_mask); \
+ FD_SET((fd), &sm_io_to_mask); \
+ FD_ZERO(&sm_io_x_mask); \
+ FD_SET((fd), &sm_io_x_mask); \
+ if (gettimeofday(&sm_io_to_before, NULL) < 0) \
+ return SM_IO_EOF; \
+ sm_io_to_sel = select((fd) + 1, NULL, &sm_io_to_mask, &sm_io_x_mask, \
+ &sm_io_to); \
+ if (sm_io_to_sel < 0) \
+ { \
+ /* something went wrong, errno set */ \
+ return SM_IO_EOF; \
+ } \
+ else if (sm_io_to_sel == 0) \
+ { \
+ /* timeout */ \
+ errno = EAGAIN; \
+ return SM_IO_EOF; \
+ } \
+ /* else loop again */ \
+ if (gettimeofday(&sm_io_to_after, NULL) < 0) \
+ return SM_IO_EOF; \
+ timersub(&sm_io_to_before, &sm_io_to_after, &sm_io_to_diff); \
+ timersub(&sm_io_to, &sm_io_to_diff, &sm_io_to); \
+ (to) -= (sm_io_to.tv_sec * 1000); \
+ (to) -= (sm_io_to.tv_usec / 10); \
+ if ((to) < 0) \
+ (to) = 0; \
+}
+
+/*
+** If there is no 'fd' just error (we can't timeout). If the timeout
+** is SM_TIME_FOREVER then there is no need to do a timeout with
+** select since this will be a real error. If the error is not
+** EAGAIN/EWOULDBLOCK (from a nonblocking) then it's a real error.
+** Specify the condition here as macro so it can be used in several places.
+*/
+
+#define IS_IO_ERROR(fd, ret, to) \
+ ((fd) < 0 || \
+ ((ret) < 0 && errno != EAGAIN && errno != EWOULDBLOCK) || \
+ (to) == SM_TIME_FOREVER)
+
diff --git a/usr/src/cmd/sendmail/libsm/makebuf.c b/usr/src/cmd/sendmail/libsm/makebuf.c
new file mode 100644
index 0000000000..09ca853cc8
--- /dev/null
+++ b/usr/src/cmd/sendmail/libsm/makebuf.c
@@ -0,0 +1,156 @@
+/*
+ * Copyright (c) 2000-2001 Sendmail, Inc. and its suppliers.
+ * All rights reserved.
+ * Copyright (c) 1990, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Chris Torek.
+ *
+ * By using this file, you agree to the terms and conditions set
+ * forth in the LICENSE file which can be found at the top level of
+ * the sendmail distribution.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#include <sm/gen.h>
+SM_RCSID("@(#)$Id: makebuf.c,v 1.26 2001/10/31 16:04:08 ca Exp $")
+#include <stdlib.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sm/io.h>
+#include <sm/heap.h>
+#include <sm/conf.h>
+#include "local.h"
+
+/*
+** SM_MAKEBUF -- make a buffer for the file
+**
+** Parameters:
+** fp -- the file to be buffered
+**
+** Returns:
+** nothing
+**
+** Allocate a file buffer, or switch to unbuffered I/O.
+** By default tty devices default to line buffered.
+*/
+
+void
+sm_makebuf(fp)
+ register SM_FILE_T *fp;
+{
+ register void *p;
+ register int flags;
+ size_t size;
+ int couldbetty;
+
+ if (fp->f_flags & SMNBF)
+ {
+ fp->f_bf.smb_base = fp->f_p = fp->f_nbuf;
+ fp->f_bf.smb_size = 1;
+ return;
+ }
+ flags = sm_whatbuf(fp, &size, &couldbetty);
+ if ((p = sm_malloc(size)) == NULL)
+ {
+ fp->f_flags |= SMNBF;
+ fp->f_bf.smb_base = fp->f_p = fp->f_nbuf;
+ fp->f_bf.smb_size = 1;
+ return;
+ }
+ if (!Sm_IO_DidInit)
+ sm_init();
+ flags |= SMMBF;
+ fp->f_bf.smb_base = fp->f_p = p;
+ fp->f_bf.smb_size = size;
+ if (couldbetty && isatty(fp->f_file))
+ flags |= SMLBF;
+ fp->f_flags |= flags;
+}
+
+/*
+** SM_WHATBUF -- determine proper buffer for a file (internal)
+**
+** Plus it fills in 'bufsize' for recommended buffer size and
+** fills in flag to indicate if 'fp' could be a tty (nothing
+** to do with "betty" :-) ).
+**
+** Parameters:
+** fp -- file pointer to be buffered
+** bufsize -- new buffer size (a return)
+** couldbetty -- could be a tty (returns)
+**
+** Returns:
+** Success:
+** on error:
+** SMNPT -- not seek opimized
+** SMOPT -- seek opimized
+*/
+
+int
+sm_whatbuf(fp, bufsize, couldbetty)
+ register SM_FILE_T *fp;
+ size_t *bufsize;
+ int *couldbetty;
+{
+ struct stat st;
+
+ if (fp->f_file < 0 || fstat(fp->f_file, &st) < 0)
+ {
+ *couldbetty = 0;
+ *bufsize = SM_IO_BUFSIZ;
+ return SMNPT;
+ }
+
+ /* could be a tty iff it is a character device */
+ *couldbetty = S_ISCHR(st.st_mode);
+ if (st.st_blksize == 0)
+ {
+ *bufsize = SM_IO_BUFSIZ;
+ return SMNPT;
+ }
+
+#if SM_IO_MAX_BUF_FILE > 0
+ if (S_ISREG(st.st_mode) && st.st_blksize > SM_IO_MAX_BUF_FILE)
+ st.st_blksize = SM_IO_MAX_BUF_FILE;
+#endif /* SM_IO_MAX_BUF_FILE > 0 */
+
+#if SM_IO_MAX_BUF > 0 || SM_IO_MIN_BUF > 0
+ if (!S_ISREG(st.st_mode))
+ {
+# if SM_IO_MAX_BUF > 0
+ if (st.st_blksize > SM_IO_MAX_BUF)
+ st.st_blksize = SM_IO_MAX_BUF;
+# if SM_IO_MIN_BUF > 0
+ else
+# endif /* SM_IO_MIN_BUF > 0 */
+# endif /* SM_IO_MAX_BUF > 0 */
+# if SM_IO_MIN_BUF > 0
+ if (st.st_blksize < SM_IO_MIN_BUF)
+ st.st_blksize = SM_IO_MIN_BUF;
+# endif /* SM_IO_MIN_BUF > 0 */
+ }
+#endif /* SM_IO_MAX_BUF > 0 || SM_IO_MIN_BUF > 0 */
+
+ /*
+ ** Optimise fseek() only if it is a regular file. (The test for
+ ** sm_std_seek is mainly paranoia.) It is safe to set _blksize
+ ** unconditionally; it will only be used if SMOPT is also set.
+ */
+
+ if ((fp->f_flags & SMSTR) == 0)
+ {
+ *bufsize = st.st_blksize;
+ fp->f_blksize = st.st_blksize;
+ }
+ else
+ *bufsize = SM_IO_BUFSIZ;
+ if ((st.st_mode & S_IFMT) == S_IFREG &&
+ fp->f_seek == sm_stdseek)
+ return SMOPT;
+ else
+ return SMNPT;
+}
diff --git a/usr/src/cmd/sendmail/libsm/match.c b/usr/src/cmd/sendmail/libsm/match.c
new file mode 100644
index 0000000000..944f30bc64
--- /dev/null
+++ b/usr/src/cmd/sendmail/libsm/match.c
@@ -0,0 +1,139 @@
+/*
+ * Copyright (c) 2000-2001 Sendmail, Inc. and its suppliers.
+ * All rights reserved.
+ *
+ * By using this file, you agree to the terms and conditions set
+ * forth in the LICENSE file which can be found at the top level of
+ * the sendmail distribution.
+ *
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#include <sm/gen.h>
+SM_RCSID("@(#)$Id: match.c,v 1.8 2001/03/02 19:57:08 ca Exp $")
+
+#include <sm/string.h>
+
+/*
+** SM_MATCH -- Match a character string against a glob pattern.
+**
+** Parameters:
+** str -- string.
+** par -- pattern to find in str.
+**
+** Returns:
+** true on match, false on non-match.
+**
+** A pattern consists of normal characters, which match themselves,
+** and meta-sequences. A * matches any sequence of characters.
+** A ? matches any single character. A [ introduces a character class.
+** A ] marks the end of a character class; if the ] is missing then
+** the [ matches itself rather than introducing a character class.
+** A character class matches any of the characters between the brackets.
+** The range of characters from X to Y inclusive is written X-Y.
+** If the first character after the [ is ! then the character class is
+** complemented.
+**
+** To include a ] in a character class, make it the first character
+** listed (after the !, if any). To include a -, make it the first
+** character listed (after the !, if any) or the last character.
+** It is impossible for a ] to be the final character in a range.
+** For glob patterns that literally match "*", "?" or "[",
+** use [*], [?] or [[].
+*/
+
+bool
+sm_match(str, pat)
+ const char *str;
+ const char *pat;
+{
+ bool ccnot, ccmatch, ccfirst;
+ const char *ccstart;
+ char c, c2;
+
+ for (;;)
+ {
+ switch (*pat)
+ {
+ case '\0':
+ return *str == '\0';
+ case '?':
+ if (*str == '\0')
+ return false;
+ ++pat;
+ ++str;
+ continue;
+ case '*':
+ ++pat;
+ if (*pat == '\0')
+ {
+ /* optimize case of trailing '*' */
+ return true;
+ }
+ for (;;)
+ {
+ if (sm_match(pat, str))
+ return true;
+ if (*str == '\0')
+ return false;
+ ++str;
+ }
+ /* NOTREACHED */
+ case '[':
+ ccstart = pat++;
+ ccnot = false;
+ if (*pat == '!')
+ {
+ ccnot = true;
+ ++pat;
+ }
+ ccmatch = false;
+ ccfirst = true;
+ for (;;)
+ {
+ if (*pat == '\0')
+ {
+ pat = ccstart;
+ goto defl;
+ }
+ if (*pat == ']' && !ccfirst)
+ break;
+ c = *pat++;
+ ccfirst = false;
+ if (*pat == '-' && pat[1] != ']')
+ {
+ ++pat;
+ if (*pat == '\0')
+ {
+ pat = ccstart;
+ goto defl;
+ }
+ c2 = *pat++;
+ if (*str >= c && *str <= c2)
+ ccmatch = true;
+ }
+ else
+ {
+ if (*str == c)
+ ccmatch = true;
+ }
+ }
+ if (ccmatch ^ ccnot)
+ {
+ ++pat;
+ ++str;
+ }
+ else
+ return false;
+ continue;
+ default:
+ defl:
+ if (*pat != *str)
+ return false;
+ ++pat;
+ ++str;
+ continue;
+ }
+ }
+}
diff --git a/usr/src/cmd/sendmail/libsm/mbdb.c b/usr/src/cmd/sendmail/libsm/mbdb.c
new file mode 100644
index 0000000000..d1a66d5894
--- /dev/null
+++ b/usr/src/cmd/sendmail/libsm/mbdb.c
@@ -0,0 +1,776 @@
+/*
+ * Copyright (c) 2001-2002 Sendmail, Inc. and its suppliers.
+ * All rights reserved.
+ *
+ * By using this file, you agree to the terms and conditions set
+ * forth in the LICENSE file which can be found at the top level of
+ * the sendmail distribution.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#include <sm/gen.h>
+SM_RCSID("@(#)$Id: mbdb.c,v 1.40 2003/12/10 03:19:07 gshapiro Exp $")
+
+#include <sys/param.h>
+
+#include <ctype.h>
+#include <errno.h>
+#include <pwd.h>
+#include <stdlib.h>
+#include <setjmp.h>
+#include <unistd.h>
+
+#include <sm/limits.h>
+#include <sm/conf.h>
+#include <sm/assert.h>
+#include <sm/bitops.h>
+#include <sm/errstring.h>
+#include <sm/heap.h>
+#include <sm/mbdb.h>
+#include <sm/string.h>
+# ifdef EX_OK
+# undef EX_OK /* for SVr4.2 SMP */
+# endif /* EX_OK */
+#include <sm/sysexits.h>
+
+#if LDAPMAP
+# if _LDAP_EXAMPLE_
+# include <sm/ldap.h>
+# endif /* _LDAP_EXAMPLE_ */
+#endif /* LDAPMAP */
+
+typedef struct
+{
+ char *mbdb_typename;
+ int (*mbdb_initialize) __P((char *));
+ int (*mbdb_lookup) __P((char *name, SM_MBDB_T *user));
+ void (*mbdb_terminate) __P((void));
+} SM_MBDB_TYPE_T;
+
+static int mbdb_pw_initialize __P((char *));
+static int mbdb_pw_lookup __P((char *name, SM_MBDB_T *user));
+static void mbdb_pw_terminate __P((void));
+
+#if LDAPMAP
+# if _LDAP_EXAMPLE_
+static struct sm_ldap_struct LDAPLMAP;
+static int mbdb_ldap_initialize __P((char *));
+static int mbdb_ldap_lookup __P((char *name, SM_MBDB_T *user));
+static void mbdb_ldap_terminate __P((void));
+# endif /* _LDAP_EXAMPLE_ */
+#endif /* LDAPMAP */
+
+static SM_MBDB_TYPE_T SmMbdbTypes[] =
+{
+ { "pw", mbdb_pw_initialize, mbdb_pw_lookup, mbdb_pw_terminate },
+#if LDAPMAP
+# if _LDAP_EXAMPLE_
+ { "ldap", mbdb_ldap_initialize, mbdb_ldap_lookup, mbdb_ldap_terminate },
+# endif /* _LDAP_EXAMPLE_ */
+#endif /* LDAPMAP */
+ { NULL, NULL, NULL, NULL }
+};
+
+static SM_MBDB_TYPE_T *SmMbdbType = &SmMbdbTypes[0];
+
+/*
+** SM_MBDB_INITIALIZE -- specify which mailbox database to use
+**
+** If this function is not called, then the "pw" implementation
+** is used by default; this implementation uses getpwnam().
+**
+** Parameters:
+** mbdb -- Which mailbox database to use.
+** The argument has the form "name" or "name.arg".
+** "pw" means use getpwnam().
+**
+** Results:
+** EX_OK on success, or an EX_* code on failure.
+*/
+
+int
+sm_mbdb_initialize(mbdb)
+ char *mbdb;
+{
+ size_t namelen;
+ int err;
+ char *name;
+ char *arg;
+ SM_MBDB_TYPE_T *t;
+
+ SM_REQUIRE(mbdb != NULL);
+
+ name = mbdb;
+ arg = strchr(mbdb, '.');
+ if (arg == NULL)
+ namelen = strlen(name);
+ else
+ {
+ namelen = arg - name;
+ ++arg;
+ }
+
+ for (t = SmMbdbTypes; t->mbdb_typename != NULL; ++t)
+ {
+ if (strlen(t->mbdb_typename) == namelen &&
+ strncmp(name, t->mbdb_typename, namelen) == 0)
+ {
+ err = EX_OK;
+ if (t->mbdb_initialize != NULL)
+ err = t->mbdb_initialize(arg);
+ if (err == EX_OK)
+ SmMbdbType = t;
+ return err;
+ }
+ }
+ return EX_UNAVAILABLE;
+}
+
+/*
+** SM_MBDB_TERMINATE -- terminate connection to the mailbox database
+**
+** Because this function closes any cached file descriptors that
+** are being held open for the connection to the mailbox database,
+** it should be called for security reasons prior to dropping privileges
+** and execing another process.
+**
+** Parameters:
+** none.
+**
+** Results:
+** none.
+*/
+
+void
+sm_mbdb_terminate()
+{
+ if (SmMbdbType->mbdb_terminate != NULL)
+ SmMbdbType->mbdb_terminate();
+}
+
+/*
+** SM_MBDB_LOOKUP -- look up a local mail recipient, given name
+**
+** Parameters:
+** name -- name of local mail recipient
+** user -- pointer to structure to fill in on success
+**
+** Results:
+** On success, fill in *user and return EX_OK.
+** If the user does not exist, return EX_NOUSER.
+** If a temporary failure (eg, a network failure) occurred,
+** return EX_TEMPFAIL. Otherwise return EX_OSERR.
+*/
+
+int
+sm_mbdb_lookup(name, user)
+ char *name;
+ SM_MBDB_T *user;
+{
+ int ret = EX_NOUSER;
+
+ if (SmMbdbType->mbdb_lookup != NULL)
+ ret = SmMbdbType->mbdb_lookup(name, user);
+ return ret;
+}
+
+/*
+** SM_MBDB_FROMPW -- copy from struct pw to SM_MBDB_T
+**
+** Parameters:
+** user -- destination user information structure
+** pw -- source passwd structure
+**
+** Results:
+** none.
+*/
+
+void
+sm_mbdb_frompw(user, pw)
+ SM_MBDB_T *user;
+ struct passwd *pw;
+{
+ SM_REQUIRE(user != NULL);
+ (void) sm_strlcpy(user->mbdb_name, pw->pw_name,
+ sizeof(user->mbdb_name));
+ user->mbdb_uid = pw->pw_uid;
+ user->mbdb_gid = pw->pw_gid;
+ sm_pwfullname(pw->pw_gecos, pw->pw_name, user->mbdb_fullname,
+ sizeof(user->mbdb_fullname));
+ (void) sm_strlcpy(user->mbdb_homedir, pw->pw_dir,
+ sizeof(user->mbdb_homedir));
+ (void) sm_strlcpy(user->mbdb_shell, pw->pw_shell,
+ sizeof(user->mbdb_shell));
+}
+
+/*
+** SM_PWFULLNAME -- build full name of user from pw_gecos field.
+**
+** This routine interprets the strange entry that would appear
+** in the GECOS field of the password file.
+**
+** Parameters:
+** gecos -- name to build.
+** user -- the login name of this user (for &).
+** buf -- place to put the result.
+** buflen -- length of buf.
+**
+** Returns:
+** none.
+*/
+
+#if _FFR_HANDLE_ISO8859_GECOS
+static char Latin1ToASCII[128] =
+{
+ 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
+ 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 33,
+ 99, 80, 36, 89, 124, 36, 34, 99, 97, 60, 45, 45, 114, 45, 111, 42,
+ 50, 51, 39, 117, 80, 46, 44, 49, 111, 62, 42, 42, 42, 63, 65, 65,
+ 65, 65, 65, 65, 65, 67, 69, 69, 69, 69, 73, 73, 73, 73, 68, 78, 79,
+ 79, 79, 79, 79, 88, 79, 85, 85, 85, 85, 89, 80, 66, 97, 97, 97, 97,
+ 97, 97, 97, 99, 101, 101, 101, 101, 105, 105, 105, 105, 100, 110,
+ 111, 111, 111, 111, 111, 47, 111, 117, 117, 117, 117, 121, 112, 121
+};
+#endif /* _FFR_HANDLE_ISO8859_GECOS */
+
+void
+sm_pwfullname(gecos, user, buf, buflen)
+ register char *gecos;
+ char *user;
+ char *buf;
+ size_t buflen;
+{
+ register char *p;
+ register char *bp = buf;
+
+ if (*gecos == '*')
+ gecos++;
+
+ /* copy gecos, interpolating & to be full name */
+ for (p = gecos; *p != '\0' && *p != ',' && *p != ';' && *p != '%'; p++)
+ {
+ if (bp >= &buf[buflen - 1])
+ {
+ /* buffer overflow -- just use login name */
+ (void) sm_strlcpy(buf, user, buflen);
+ return;
+ }
+ if (*p == '&')
+ {
+ /* interpolate full name */
+ (void) sm_strlcpy(bp, user, buflen - (bp - buf));
+ *bp = toupper(*bp);
+ bp += strlen(bp);
+ }
+ else
+ {
+#if _FFR_HANDLE_ISO8859_GECOS
+ if ((unsigned char) *p >= 128)
+ *bp++ = Latin1ToASCII[(unsigned char) *p - 128];
+ else
+#endif /* _FFR_HANDLE_ISO8859_GECOS */
+ *bp++ = *p;
+ }
+ }
+ *bp = '\0';
+}
+
+/*
+** /etc/passwd implementation.
+*/
+
+/*
+** MBDB_PW_INITIALIZE -- initialize getpwnam() version
+**
+** Parameters:
+** arg -- unused.
+**
+** Results:
+** EX_OK.
+*/
+
+/* ARGSUSED0 */
+static int
+mbdb_pw_initialize(arg)
+ char *arg;
+{
+ return EX_OK;
+}
+
+/*
+** MBDB_PW_LOOKUP -- look up a local mail recipient, given name
+**
+** Parameters:
+** name -- name of local mail recipient
+** user -- pointer to structure to fill in on success
+**
+** Results:
+** On success, fill in *user and return EX_OK.
+** Failure: EX_NOUSER.
+*/
+
+static int
+mbdb_pw_lookup(name, user)
+ char *name;
+ SM_MBDB_T *user;
+{
+ struct passwd *pw;
+
+#ifdef HESIOD
+ /* DEC Hesiod getpwnam accepts numeric strings -- short circuit it */
+ {
+ char *p;
+
+ for (p = name; *p != '\0'; p++)
+ if (!isascii(*p) || !isdigit(*p))
+ break;
+ if (*p == '\0')
+ return EX_NOUSER;
+ }
+#endif /* HESIOD */
+
+ errno = 0;
+ pw = getpwnam(name);
+ if (pw == NULL)
+ {
+#if 0
+ /*
+ ** getpwnam() isn't advertised as setting errno.
+ ** In fact, under FreeBSD, non-root getpwnam() on
+ ** non-existant users returns NULL with errno = EPERM.
+ ** This test won't work.
+ */
+ switch (errno)
+ {
+ case 0:
+ return EX_NOUSER;
+ case EIO:
+ return EX_OSERR;
+ default:
+ return EX_TEMPFAIL;
+ }
+#endif /* 0 */
+ return EX_NOUSER;
+ }
+
+ sm_mbdb_frompw(user, pw);
+ return EX_OK;
+}
+
+/*
+** MBDB_PW_TERMINATE -- terminate connection to the mailbox database
+**
+** Parameters:
+** none.
+**
+** Results:
+** none.
+*/
+
+static void
+mbdb_pw_terminate()
+{
+ endpwent();
+}
+
+#if LDAPMAP
+# if _LDAP_EXAMPLE_
+/*
+** LDAP example implementation based on RFC 2307, "An Approach for Using
+** LDAP as a Network Information Service":
+**
+** ( nisSchema.1.0 NAME 'uidNumber'
+** DESC 'An integer uniquely identifying a user in an
+** administrative domain'
+** EQUALITY integerMatch SYNTAX 'INTEGER' SINGLE-VALUE )
+**
+** ( nisSchema.1.1 NAME 'gidNumber'
+** DESC 'An integer uniquely identifying a group in an
+** administrative domain'
+** EQUALITY integerMatch SYNTAX 'INTEGER' SINGLE-VALUE )
+**
+** ( nisSchema.1.2 NAME 'gecos'
+** DESC 'The GECOS field; the common name'
+** EQUALITY caseIgnoreIA5Match
+** SUBSTRINGS caseIgnoreIA5SubstringsMatch
+** SYNTAX 'IA5String' SINGLE-VALUE )
+**
+** ( nisSchema.1.3 NAME 'homeDirectory'
+** DESC 'The absolute path to the home directory'
+** EQUALITY caseExactIA5Match
+** SYNTAX 'IA5String' SINGLE-VALUE )
+**
+** ( nisSchema.1.4 NAME 'loginShell'
+** DESC 'The path to the login shell'
+** EQUALITY caseExactIA5Match
+** SYNTAX 'IA5String' SINGLE-VALUE )
+**
+** ( nisSchema.2.0 NAME 'posixAccount' SUP top AUXILIARY
+** DESC 'Abstraction of an account with POSIX attributes'
+** MUST ( cn $ uid $ uidNumber $ gidNumber $ homeDirectory )
+** MAY ( userPassword $ loginShell $ gecos $ description ) )
+**
+*/
+
+# define MBDB_LDAP_LABEL "MailboxDatabase"
+
+# ifndef MBDB_LDAP_FILTER
+# define MBDB_LDAP_FILTER "(&(objectClass=posixAccount)(uid=%0))"
+# endif /* MBDB_LDAP_FILTER */
+
+# ifndef MBDB_DEFAULT_LDAP_BASEDN
+# define MBDB_DEFAULT_LDAP_BASEDN NULL
+# endif /* MBDB_DEFAULT_LDAP_BASEDN */
+
+# ifndef MBDB_DEFAULT_LDAP_SERVER
+# define MBDB_DEFAULT_LDAP_SERVER NULL
+# endif /* MBDB_DEFAULT_LDAP_SERVER */
+
+/*
+** MBDB_LDAP_INITIALIZE -- initialize LDAP version
+**
+** Parameters:
+** arg -- LDAP specification
+**
+** Results:
+** EX_OK on success, or an EX_* code on failure.
+*/
+
+static int
+mbdb_ldap_initialize(arg)
+ char *arg;
+{
+ sm_ldap_clear(&LDAPLMAP);
+ LDAPLMAP.ldap_base = MBDB_DEFAULT_LDAP_BASEDN;
+ LDAPLMAP.ldap_host = MBDB_DEFAULT_LDAP_SERVER;
+ LDAPLMAP.ldap_filter = MBDB_LDAP_FILTER;
+
+ /* Only want one match */
+ LDAPLMAP.ldap_sizelimit = 1;
+
+ /* interpolate new ldap_base and ldap_host from arg if given */
+ if (arg != NULL && *arg != '\0')
+ {
+ char *new;
+ char *sep;
+ size_t len;
+
+ len = strlen(arg) + 1;
+ new = sm_malloc(len);
+ if (new == NULL)
+ return EX_TEMPFAIL;
+ (void) sm_strlcpy(new, arg, len);
+ sep = strrchr(new, '@');
+ if (sep != NULL)
+ {
+ *sep++ = '\0';
+ LDAPLMAP.ldap_host = sep;
+ }
+ LDAPLMAP.ldap_base = new;
+ }
+ return EX_OK;
+}
+
+
+/*
+** MBDB_LDAP_LOOKUP -- look up a local mail recipient, given name
+**
+** Parameters:
+** name -- name of local mail recipient
+** user -- pointer to structure to fill in on success
+**
+** Results:
+** On success, fill in *user and return EX_OK.
+** Failure: EX_NOUSER.
+*/
+
+#define NEED_FULLNAME 0x01
+#define NEED_HOMEDIR 0x02
+#define NEED_SHELL 0x04
+#define NEED_UID 0x08
+#define NEED_GID 0x10
+
+static int
+mbdb_ldap_lookup(name, user)
+ char *name;
+ SM_MBDB_T *user;
+{
+ int msgid;
+ int need;
+ int ret;
+ int save_errno;
+ LDAPMessage *entry;
+ BerElement *ber;
+ char *attr = NULL;
+
+ if (strlen(name) >= sizeof(user->mbdb_name))
+ {
+ errno = EINVAL;
+ return EX_NOUSER;
+ }
+
+ if (LDAPLMAP.ldap_filter == NULL)
+ {
+ /* map not initialized, but don't have arg here */
+ errno = EFAULT;
+ return EX_TEMPFAIL;
+ }
+
+ if (LDAPLMAP.ldap_pid != getpid())
+ {
+ /* re-open map in this child process */
+ LDAPLMAP.ldap_ld = NULL;
+ }
+
+ if (LDAPLMAP.ldap_ld == NULL)
+ {
+ /* map not open, try to open now */
+ if (!sm_ldap_start(MBDB_LDAP_LABEL, &LDAPLMAP))
+ return EX_TEMPFAIL;
+ }
+
+ sm_ldap_setopts(LDAPLMAP.ldap_ld, &LDAPLMAP);
+ msgid = sm_ldap_search(&LDAPLMAP, name);
+ if (msgid == -1)
+ {
+ save_errno = sm_ldap_geterrno(LDAPLMAP.ldap_ld) + E_LDAPBASE;
+# ifdef LDAP_SERVER_DOWN
+ if (errno == LDAP_SERVER_DOWN)
+ {
+ /* server disappeared, try reopen on next search */
+ sm_ldap_close(&LDAPLMAP);
+ }
+# endif /* LDAP_SERVER_DOWN */
+ errno = save_errno;
+ return EX_TEMPFAIL;
+ }
+
+ /* Get results */
+ ret = ldap_result(LDAPLMAP.ldap_ld, msgid, 1,
+ (LDAPLMAP.ldap_timeout.tv_sec == 0 ? NULL :
+ &(LDAPLMAP.ldap_timeout)),
+ &(LDAPLMAP.ldap_res));
+
+ if (ret != LDAP_RES_SEARCH_RESULT &&
+ ret != LDAP_RES_SEARCH_ENTRY)
+ {
+ if (ret == 0)
+ errno = ETIMEDOUT;
+ else
+ errno = sm_ldap_geterrno(LDAPLMAP.ldap_ld);
+ ret = EX_TEMPFAIL;
+ goto abort;
+ }
+
+ entry = ldap_first_entry(LDAPLMAP.ldap_ld, LDAPLMAP.ldap_res);
+ if (entry == NULL)
+ {
+ save_errno = sm_ldap_geterrno(LDAPLMAP.ldap_ld);
+ if (save_errno == LDAP_SUCCESS)
+ {
+ errno = ENOENT;
+ ret = EX_NOUSER;
+ }
+ else
+ {
+ errno = save_errno;
+ ret = EX_TEMPFAIL;
+ }
+ goto abort;
+ }
+
+# if !defined(LDAP_VERSION_MAX) && !defined(LDAP_OPT_SIZELIMIT)
+ /*
+ ** Reset value to prevent lingering
+ ** LDAP_DECODING_ERROR due to
+ ** OpenLDAP 1.X's hack (see below)
+ */
+
+ LDAPLMAP.ldap_ld->ld_errno = LDAP_SUCCESS;
+# endif /* !defined(LDAP_VERSION_MAX) !defined(LDAP_OPT_SIZELIMIT) */
+
+ ret = EX_OK;
+ need = NEED_FULLNAME|NEED_HOMEDIR|NEED_SHELL|NEED_UID|NEED_GID;
+ for (attr = ldap_first_attribute(LDAPLMAP.ldap_ld, entry, &ber);
+ attr != NULL;
+ attr = ldap_next_attribute(LDAPLMAP.ldap_ld, entry, ber))
+ {
+ char **vals;
+
+ vals = ldap_get_values(LDAPLMAP.ldap_ld, entry, attr);
+ if (vals == NULL)
+ {
+ errno = sm_ldap_geterrno(LDAPLMAP.ldap_ld);
+ if (errno == LDAP_SUCCESS)
+ {
+ ldap_memfree(attr);
+ continue;
+ }
+
+ /* Must be an error */
+ errno += E_LDAPBASE;
+ ret = EX_TEMPFAIL;
+ goto abort;
+ }
+
+# if !defined(LDAP_VERSION_MAX) && !defined(LDAP_OPT_SIZELIMIT)
+ /*
+ ** Reset value to prevent lingering
+ ** LDAP_DECODING_ERROR due to
+ ** OpenLDAP 1.X's hack (see below)
+ */
+
+ LDAPLMAP.ldap_ld->ld_errno = LDAP_SUCCESS;
+# endif /* !defined(LDAP_VERSION_MAX) !defined(LDAP_OPT_SIZELIMIT) */
+
+ if (vals[0] == NULL || vals[0][0] == '\0')
+ goto skip;
+
+ if (strcasecmp(attr, "gecos") == 0)
+ {
+ if (!bitset(NEED_FULLNAME, need) ||
+ strlen(vals[0]) >= sizeof(user->mbdb_fullname))
+ goto skip;
+
+ sm_pwfullname(vals[0], name, user->mbdb_fullname,
+ sizeof(user->mbdb_fullname));
+ need &= ~NEED_FULLNAME;
+ }
+ else if (strcasecmp(attr, "homeDirectory") == 0)
+ {
+ if (!bitset(NEED_HOMEDIR, need) ||
+ strlen(vals[0]) >= sizeof(user->mbdb_homedir))
+ goto skip;
+
+ (void) sm_strlcpy(user->mbdb_homedir, vals[0],
+ sizeof(user->mbdb_homedir));
+ need &= ~NEED_HOMEDIR;
+ }
+ else if (strcasecmp(attr, "loginShell") == 0)
+ {
+ if (!bitset(NEED_SHELL, need) ||
+ strlen(vals[0]) >= sizeof(user->mbdb_shell))
+ goto skip;
+
+ (void) sm_strlcpy(user->mbdb_shell, vals[0],
+ sizeof(user->mbdb_shell));
+ need &= ~NEED_SHELL;
+ }
+ else if (strcasecmp(attr, "uidNumber") == 0)
+ {
+ char *p;
+
+ if (!bitset(NEED_UID, need))
+ goto skip;
+
+ for (p = vals[0]; *p != '\0'; p++)
+ {
+ /* allow negative numbers */
+ if (p == vals[0] && *p == '-')
+ {
+ /* but not simply '-' */
+ if (*(p + 1) == '\0')
+ goto skip;
+ }
+ else if (!isascii(*p) || !isdigit(*p))
+ goto skip;
+ }
+ user->mbdb_uid = atoi(vals[0]);
+ need &= ~NEED_UID;
+ }
+ else if (strcasecmp(attr, "gidNumber") == 0)
+ {
+ char *p;
+
+ if (!bitset(NEED_GID, need))
+ goto skip;
+
+ for (p = vals[0]; *p != '\0'; p++)
+ {
+ /* allow negative numbers */
+ if (p == vals[0] && *p == '-')
+ {
+ /* but not simply '-' */
+ if (*(p + 1) == '\0')
+ goto skip;
+ }
+ else if (!isascii(*p) || !isdigit(*p))
+ goto skip;
+ }
+ user->mbdb_gid = atoi(vals[0]);
+ need &= ~NEED_GID;
+ }
+
+skip:
+ ldap_value_free(vals);
+ ldap_memfree(attr);
+ }
+
+ errno = sm_ldap_geterrno(LDAPLMAP.ldap_ld);
+
+ /*
+ ** We check errno != LDAP_DECODING_ERROR since
+ ** OpenLDAP 1.X has a very ugly *undocumented*
+ ** hack of returning this error code from
+ ** ldap_next_attribute() if the library freed the
+ ** ber attribute. See:
+ ** http://www.openldap.org/lists/openldap-devel/9901/msg00064.html
+ */
+
+ if (errno != LDAP_SUCCESS &&
+ errno != LDAP_DECODING_ERROR)
+ {
+ /* Must be an error */
+ errno += E_LDAPBASE;
+ ret = EX_TEMPFAIL;
+ goto abort;
+ }
+
+ abort:
+ save_errno = errno;
+ if (attr != NULL)
+ {
+ ldap_memfree(attr);
+ attr = NULL;
+ }
+ if (LDAPLMAP.ldap_res != NULL)
+ {
+ ldap_msgfree(LDAPLMAP.ldap_res);
+ LDAPLMAP.ldap_res = NULL;
+ }
+ if (ret == EX_OK)
+ {
+ if (need == 0)
+ {
+ (void) sm_strlcpy(user->mbdb_name, name,
+ sizeof(user->mbdb_name));
+ save_errno = 0;
+ }
+ else
+ {
+ ret = EX_NOUSER;
+ save_errno = EINVAL;
+ }
+ }
+ errno = save_errno;
+ return ret;
+}
+
+/*
+** MBDB_LDAP_TERMINATE -- terminate connection to the mailbox database
+**
+** Parameters:
+** none.
+**
+** Results:
+** none.
+*/
+
+static void
+mbdb_ldap_terminate()
+{
+ sm_ldap_close(&LDAPLMAP);
+}
+# endif /* _LDAP_EXAMPLE_ */
+#endif /* LDAPMAP */
diff --git a/usr/src/cmd/sendmail/libsm/niprop.c b/usr/src/cmd/sendmail/libsm/niprop.c
new file mode 100644
index 0000000000..ed1201d479
--- /dev/null
+++ b/usr/src/cmd/sendmail/libsm/niprop.c
@@ -0,0 +1,215 @@
+/*
+ * Copyright (c) 2001 Sendmail, Inc. and its suppliers.
+ * All rights reserved.
+ *
+ * By using this file, you agree to the terms and conditions set
+ * forth in the LICENSE file which can be found at the top level of
+ * the sendmail distribution.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#include <sm/gen.h>
+SM_RCSID("@(#)$Id: niprop.c,v 1.6 2001/09/04 22:41:27 ca Exp $")
+
+#if NETINFO
+#include <ctype.h>
+#include <stdlib.h>
+#include <sm/io.h>
+#include <sm/assert.h>
+#include <sm/debug.h>
+#include <sm/string.h>
+#include <sm/varargs.h>
+#include <sm/heap.h>
+
+/*
+** NI_PROPVAL -- NetInfo property value lookup routine
+**
+** Parameters:
+** keydir -- the NetInfo directory name in which to search
+** for the key.
+** keyprop -- the name of the property in which to find the
+** property we are interested. Defaults to "name".
+** keyval -- the value for which we are really searching.
+** valprop -- the property name for the value in which we
+** are interested.
+** sepchar -- if non-nil, this can be multiple-valued, and
+** we should return a string separated by this
+** character.
+**
+** Returns:
+** NULL -- if:
+** 1. the directory is not found
+** 2. the property name is not found
+** 3. the property contains multiple values
+** 4. some error occurred
+** else -- the value of the lookup.
+**
+** Example:
+** To search for an alias value, use:
+** ni_propval("/aliases", "name", aliasname, "members", ',')
+**
+** Notes:
+** Caller should free the return value of ni_proval
+*/
+
+# include <netinfo/ni.h>
+
+# define LOCAL_NETINFO_DOMAIN "."
+# define PARENT_NETINFO_DOMAIN ".."
+# define MAX_NI_LEVELS 256
+
+char *
+ni_propval(keydir, keyprop, keyval, valprop, sepchar)
+ char *keydir;
+ char *keyprop;
+ char *keyval;
+ char *valprop;
+ int sepchar;
+{
+ char *propval = NULL;
+ int i;
+ int j, alen, l;
+ void *ni = NULL;
+ void *lastni = NULL;
+ ni_status nis;
+ ni_id nid;
+ ni_namelist ninl;
+ register char *p;
+ char keybuf[1024];
+
+ /*
+ ** Create the full key from the two parts.
+ **
+ ** Note that directory can end with, e.g., "name=" to specify
+ ** an alternate search property.
+ */
+
+ i = strlen(keydir) + strlen(keyval) + 2;
+ if (keyprop != NULL)
+ i += strlen(keyprop) + 1;
+ if (i >= sizeof keybuf)
+ return NULL;
+ (void) sm_strlcpyn(keybuf, sizeof keybuf, 2, keydir, "/");
+ if (keyprop != NULL)
+ {
+ (void) sm_strlcat2(keybuf, keyprop, "=", sizeof keybuf);
+ }
+ (void) sm_strlcat(keybuf, keyval, sizeof keybuf);
+
+#if 0
+ if (tTd(38, 21))
+ sm_dprintf("ni_propval(%s, %s, %s, %s, %d) keybuf='%s'\n",
+ keydir, keyprop, keyval, valprop, sepchar, keybuf);
+#endif /* 0 */
+
+ /*
+ ** If the passed directory and property name are found
+ ** in one of netinfo domains we need to search (starting
+ ** from the local domain moving all the way back to the
+ ** root domain) set propval to the property's value
+ ** and return it.
+ */
+
+ for (i = 0; i < MAX_NI_LEVELS && propval == NULL; i++)
+ {
+ if (i == 0)
+ {
+ nis = ni_open(NULL, LOCAL_NETINFO_DOMAIN, &ni);
+#if 0
+ if (tTd(38, 20))
+ sm_dprintf("ni_open(LOCAL) = %d\n", nis);
+#endif /* 0 */
+ }
+ else
+ {
+ if (lastni != NULL)
+ ni_free(lastni);
+ lastni = ni;
+ nis = ni_open(lastni, PARENT_NETINFO_DOMAIN, &ni);
+#if 0
+ if (tTd(38, 20))
+ sm_dprintf("ni_open(PARENT) = %d\n", nis);
+#endif /* 0 */
+ }
+
+ /*
+ ** Don't bother if we didn't get a handle on a
+ ** proper domain. This is not necessarily an error.
+ ** We would get a positive ni_status if, for instance
+ ** we never found the directory or property and tried
+ ** to open the parent of the root domain!
+ */
+
+ if (nis != 0)
+ break;
+
+ /*
+ ** Find the path to the server information.
+ */
+
+ if (ni_pathsearch(ni, &nid, keybuf) != 0)
+ continue;
+
+ /*
+ ** Find associated value information.
+ */
+
+ if (ni_lookupprop(ni, &nid, valprop, &ninl) != 0)
+ continue;
+
+#if 0
+ if (tTd(38, 20))
+ sm_dprintf("ni_lookupprop: len=%d\n",
+ ninl.ni_namelist_len);
+#endif /* 0 */
+
+ /*
+ ** See if we have an acceptable number of values.
+ */
+
+ if (ninl.ni_namelist_len <= 0)
+ continue;
+
+ if (sepchar == '\0' && ninl.ni_namelist_len > 1)
+ {
+ ni_namelist_free(&ninl);
+ continue;
+ }
+
+ /*
+ ** Calculate number of bytes needed and build result
+ */
+
+ alen = 1;
+ for (j = 0; j < ninl.ni_namelist_len; j++)
+ alen += strlen(ninl.ni_namelist_val[j]) + 1;
+ propval = p = sm_malloc(alen);
+ if (propval == NULL)
+ goto cleanup;
+ for (j = 0; j < ninl.ni_namelist_len; j++)
+ {
+ (void) sm_strlcpy(p, ninl.ni_namelist_val[j], alen);
+ l = strlen(p);
+ p += l;
+ *p++ = sepchar;
+ alen -= l + 1;
+ }
+ *--p = '\0';
+
+ ni_namelist_free(&ninl);
+ }
+
+ cleanup:
+ if (ni != NULL)
+ ni_free(ni);
+ if (lastni != NULL && ni != lastni)
+ ni_free(lastni);
+#if 0
+ if (tTd(38, 20))
+ sm_dprintf("ni_propval returns: '%s'\n", propval);
+#endif /* 0 */
+
+ return propval;
+}
+#endif /* NETINFO */
diff --git a/usr/src/cmd/sendmail/libsm/path.c b/usr/src/cmd/sendmail/libsm/path.c
new file mode 100644
index 0000000000..517a948dd5
--- /dev/null
+++ b/usr/src/cmd/sendmail/libsm/path.c
@@ -0,0 +1,17 @@
+/*
+ * Copyright (c) 2000-2001 Sendmail, Inc. and its suppliers.
+ * All rights reserved.
+ *
+ * By using this file, you agree to the terms and conditions set
+ * forth in the LICENSE file which can be found at the top level of
+ * the sendmail distribution.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#include <sm/gen.h>
+SM_RCSID("@(#)$Id: path.c,v 1.7 2001/08/28 16:06:59 gshapiro Exp $")
+
+#include <sm/path.h>
+#include <sm/string.h>
+
diff --git a/usr/src/cmd/sendmail/libsm/put.c b/usr/src/cmd/sendmail/libsm/put.c
new file mode 100644
index 0000000000..7940d102b3
--- /dev/null
+++ b/usr/src/cmd/sendmail/libsm/put.c
@@ -0,0 +1,82 @@
+/*
+ * Copyright (c) 2000-2001 Sendmail, Inc. and its suppliers.
+ * All rights reserved.
+ * Copyright (c) 1990, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Chris Torek.
+ *
+ * By using this file, you agree to the terms and conditions set
+ * forth in the LICENSE file which can be found at the top level of
+ * the sendmail distribution.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#include <sm/gen.h>
+SM_RCSID("@(#)$Id: put.c,v 1.27 2001/12/19 05:19:35 ca Exp $")
+#include <string.h>
+#include <errno.h>
+#include <sm/io.h>
+#include <sm/assert.h>
+#include <sm/errstring.h>
+#include <sm/string.h>
+#include "local.h"
+#include "fvwrite.h"
+
+/*
+** SM_IO_PUTC -- output a character to the file
+**
+** Function version of the macro sm_io_putc (in <sm/io.h>).
+**
+** Parameters:
+** fp -- file to output to
+** timeout -- time to complete putc
+** c -- int value of character to output
+**
+** Returns:
+** Failure: returns SM_IO_EOF _and_ sets errno
+** Success: returns sm_putc() value.
+**
+*/
+
+#undef sm_io_putc
+
+int
+sm_io_putc(fp, timeout, c)
+ SM_FILE_T *fp;
+ int timeout;
+ int c;
+{
+ SM_REQUIRE_ISA(fp, SmFileMagic);
+ if (cantwrite(fp))
+ {
+ errno = EBADF;
+ return SM_IO_EOF;
+ }
+ return sm_putc(fp, timeout, c);
+}
+
+
+/*
+** SM_PERROR -- print system error messages to smioerr
+**
+** Parameters:
+** s -- message to print
+**
+** Returns:
+** none
+*/
+
+void
+sm_perror(s)
+ const char *s;
+{
+ int save_errno = errno;
+
+ if (s != NULL && *s != '\0')
+ (void) sm_io_fprintf(smioerr, SM_TIME_DEFAULT, "%s: ", s);
+ (void) sm_io_fprintf(smioerr, SM_TIME_DEFAULT, "%s\n",
+ sm_errstring(save_errno));
+}
diff --git a/usr/src/cmd/sendmail/libsm/refill.c b/usr/src/cmd/sendmail/libsm/refill.c
new file mode 100644
index 0000000000..6e1eb5b2a6
--- /dev/null
+++ b/usr/src/cmd/sendmail/libsm/refill.c
@@ -0,0 +1,301 @@
+/*
+ * Copyright (c) 2000-2001 Sendmail, Inc. and its suppliers.
+ * All rights reserved.
+ * Copyright (c) 1990, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Chris Torek.
+ *
+ * By using this file, you agree to the terms and conditions set
+ * forth in the LICENSE file which can be found at the top level of
+ * the sendmail distribution.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#include <sm/gen.h>
+SM_RCSID("@(#)$Id: refill.c,v 1.49.2.1 2002/09/09 21:38:08 gshapiro Exp $")
+#include <stdlib.h>
+#include <unistd.h>
+#include <errno.h>
+#include <setjmp.h>
+#include <signal.h>
+#include <sys/time.h>
+#include <fcntl.h>
+#include <string.h>
+#include <sm/io.h>
+#include <sm/conf.h>
+#include <sm/assert.h>
+#include "local.h"
+
+static int sm_lflush __P((SM_FILE_T *, int *));
+
+/*
+** SM_IO_RD_TIMEOUT -- measured timeout for reads
+**
+** This #define uses a select() to wait for the 'fd' to become readable.
+** The select() can be active for up to 'To' time. The select() may not
+** use all of the the 'To' time. Hence, the amount of "wall-clock" time is
+** measured to decide how much to subtract from 'To' to update it. On some
+** BSD-based/like systems the timeout for a select() is updated for the
+** amount of time used. On many/most systems this does not happen. Therefore
+** the updating of 'To' must be done ourselves; a copy of 'To' is passed
+** since a BSD-like system will have updated it and we don't want to
+** double the time used!
+** Note: if a valid 'fd' doesn't exist yet, don't use this (e.g. the
+** sendmail buffered file type in sendmail/bf.c; see use below).
+**
+** Parameters
+** fp -- the file pointer for the active file
+** fd -- raw file descriptor (from 'fp') to use for select()
+** to -- struct timeval of the timeout
+** timeout -- the original timeout value
+** sel_ret -- the return value from the select()
+**
+** Returns:
+** nothing, flow through code
+*/
+
+#define SM_IO_RD_TIMEOUT(fp, fd, to, timeout, sel_ret) \
+{ \
+ struct timeval sm_io_to_before, sm_io_to_after, sm_io_to_diff; \
+ fd_set sm_io_to_mask, sm_io_x_mask; \
+ errno = 0; \
+ if (timeout == SM_TIME_IMMEDIATE) \
+ { \
+ errno = EAGAIN; \
+ return SM_IO_EOF; \
+ } \
+ if (FD_SETSIZE > 0 && (fd) >= FD_SETSIZE) \
+ { \
+ errno = EINVAL; \
+ return SM_IO_EOF; \
+ } \
+ FD_ZERO(&sm_io_to_mask); \
+ FD_SET((fd), &sm_io_to_mask); \
+ FD_ZERO(&sm_io_x_mask); \
+ FD_SET((fd), &sm_io_x_mask); \
+ if (gettimeofday(&sm_io_to_before, NULL) < 0) \
+ return SM_IO_EOF; \
+ (sel_ret) = select((fd) + 1, &sm_io_to_mask, NULL, \
+ &sm_io_x_mask, (to)); \
+ if ((sel_ret) < 0) \
+ { \
+ /* something went wrong, errno set */ \
+ fp->f_r = 0; \
+ fp->f_flags |= SMERR; \
+ return SM_IO_EOF; \
+ } \
+ else if ((sel_ret) == 0) \
+ { \
+ /* timeout */ \
+ errno = EAGAIN; \
+ return SM_IO_EOF; \
+ } \
+ /* calulate wall-clock time used */ \
+ if (gettimeofday(&sm_io_to_after, NULL) < 0) \
+ return SM_IO_EOF; \
+ timersub(&sm_io_to_before, &sm_io_to_after, &sm_io_to_diff); \
+ timersub((to), &sm_io_to_diff, (to)); \
+}
+
+/*
+** SM_LFLUSH -- flush a file if it is line buffered and writable
+**
+** Parameters:
+** fp -- file pointer to flush
+** timeout -- original timeout value (in milliseconds)
+**
+** Returns:
+** Failure: returns SM_IO_EOF and sets errno
+** Success: returns 0
+*/
+
+static int
+sm_lflush(fp, timeout)
+ SM_FILE_T *fp;
+ int *timeout;
+{
+
+ if ((fp->f_flags & (SMLBF|SMWR)) == (SMLBF|SMWR))
+ return sm_flush(fp, timeout);
+ return 0;
+}
+
+/*
+** SM_REFILL -- refill a buffer
+**
+** Parameters:
+** fp -- file pointer for buffer refill
+** timeout -- time to complete filling the buffer in milliseconds
+**
+** Returns:
+** Success: returns 0
+** Failure: returns SM_IO_EOF
+*/
+
+int
+sm_refill(fp, timeout)
+ register SM_FILE_T *fp;
+ int timeout;
+{
+ int ret, r;
+ struct timeval to;
+ int fd;
+
+ if (timeout == SM_TIME_DEFAULT)
+ timeout = fp->f_timeout;
+ if (timeout == SM_TIME_IMMEDIATE)
+ {
+ /*
+ ** Filling the buffer will take time and we are wanted to
+ ** return immediately. And we're not EOF or ERR really.
+ ** So... the failure is we couldn't do it in time.
+ */
+
+ errno = EAGAIN;
+ fp->f_r = 0; /* just to be sure */
+ return 0;
+ }
+
+ /* make sure stdio is set up */
+ if (!Sm_IO_DidInit)
+ sm_init();
+
+ fp->f_r = 0; /* largely a convenience for callers */
+
+ if (fp->f_flags & SMFEOF)
+ return SM_IO_EOF;
+
+ SM_CONVERT_TIME(fp, fd, timeout, &to);
+
+ /* if not already reading, have to be reading and writing */
+ if ((fp->f_flags & SMRD) == 0)
+ {
+ if ((fp->f_flags & SMRW) == 0)
+ {
+ errno = EBADF;
+ fp->f_flags |= SMERR;
+ return SM_IO_EOF;
+ }
+
+ /* switch to reading */
+ if (fp->f_flags & SMWR)
+ {
+ if (sm_flush(fp, &timeout))
+ return SM_IO_EOF;
+ fp->f_flags &= ~SMWR;
+ fp->f_w = 0;
+ fp->f_lbfsize = 0;
+ }
+ fp->f_flags |= SMRD;
+ }
+ else
+ {
+ /*
+ ** We were reading. If there is an ungetc buffer,
+ ** we must have been reading from that. Drop it,
+ ** restoring the previous buffer (if any). If there
+ ** is anything in that buffer, return.
+ */
+
+ if (HASUB(fp))
+ {
+ FREEUB(fp);
+ if ((fp->f_r = fp->f_ur) != 0)
+ {
+ fp->f_p = fp->f_up;
+
+ /* revert blocking state */
+ return 0;
+ }
+ }
+ }
+
+ if (fp->f_bf.smb_base == NULL)
+ sm_makebuf(fp);
+
+ /*
+ ** Before reading from a line buffered or unbuffered file,
+ ** flush all line buffered output files, per the ANSI C standard.
+ */
+
+ if (fp->f_flags & (SMLBF|SMNBF))
+ (void) sm_fwalk(sm_lflush, &timeout);
+
+ /*
+ ** If this file is linked to another, and we are going to hang
+ ** on the read, flush the linked file before continuing.
+ */
+
+ if (fp->f_flushfp != NULL &&
+ (*fp->f_getinfo)(fp, SM_IO_IS_READABLE, NULL) <= 0)
+ sm_flush(fp->f_flushfp, &timeout);
+
+ fp->f_p = fp->f_bf.smb_base;
+
+ /*
+ ** The do-while loop stops trying to read when something is read
+ ** or it appears that the timeout has expired before finding
+ ** something available to be read (via select()).
+ */
+
+ ret = 0;
+ do
+ {
+ errno = 0; /* needed to ensure EOF correctly found */
+ r = (*fp->f_read)(fp, (char *)fp->f_p, fp->f_bf.smb_size);
+ if (r <= 0)
+ {
+ if (r == 0 && errno == 0)
+ break; /* EOF found */
+ if (IS_IO_ERROR(fd, r, timeout))
+ goto err; /* errno set */
+
+ /* read would block */
+ SM_IO_RD_TIMEOUT(fp, fd, &to, timeout, ret);
+ }
+ } while (r <= 0 && ret > 0);
+
+err:
+ if (r <= 0)
+ {
+ if (r == 0)
+ fp->f_flags |= SMFEOF;
+ else
+ fp->f_flags |= SMERR;
+ fp->f_r = 0;
+ return SM_IO_EOF;
+ }
+ fp->f_r = r;
+ return 0;
+}
+
+/*
+** SM_RGET -- refills buffer and returns first character
+**
+** Handle sm_getc() when the buffer ran out:
+** Refill, then return the first character in the newly-filled buffer.
+**
+** Parameters:
+** fp -- file pointer to work on
+** timeout -- time to complete refill
+**
+** Returns:
+** Success: first character in refilled buffer as an int
+** Failure: SM_IO_EOF
+*/
+
+int
+sm_rget(fp, timeout)
+ register SM_FILE_T *fp;
+ int timeout;
+{
+ if (sm_refill(fp, timeout) == 0)
+ {
+ fp->f_r--;
+ return *fp->f_p++;
+ }
+ return SM_IO_EOF;
+}
diff --git a/usr/src/cmd/sendmail/libsm/rewind.c b/usr/src/cmd/sendmail/libsm/rewind.c
new file mode 100644
index 0000000000..dc290affcf
--- /dev/null
+++ b/usr/src/cmd/sendmail/libsm/rewind.c
@@ -0,0 +1,46 @@
+/*
+ * Copyright (c) 2000-2001 Sendmail, Inc. and its suppliers.
+ * All rights reserved.
+ * Copyright (c) 1990, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Chris Torek.
+ *
+ * By using this file, you agree to the terms and conditions set
+ * forth in the LICENSE file which can be found at the top level of
+ * the sendmail distribution.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#include <sm/gen.h>
+SM_RCSID("@(#)$Id: rewind.c,v 1.16 2001/04/03 01:46:40 ca Exp $")
+#include <errno.h>
+#include <sm/io.h>
+#include <sm/assert.h>
+#include "local.h"
+
+/*
+** SM_IO_REWIND -- rewind the file
+**
+** Seeks the file to the begining and clears any outstanding errors.
+**
+** Parameters:
+** fp -- the flie pointer for rewind
+** timeout -- time to complete the rewind
+**
+** Returns:
+** none.
+*/
+
+void
+sm_io_rewind(fp, timeout)
+ register SM_FILE_T *fp;
+ int timeout;
+{
+ SM_REQUIRE_ISA(fp, SmFileMagic);
+ (void) sm_io_seek(fp, timeout, 0L, SM_IO_SEEK_SET);
+ (void) sm_io_clearerr(fp);
+ errno = 0; /* not required, but seems reasonable */
+}
diff --git a/usr/src/cmd/sendmail/libsm/rpool.c b/usr/src/cmd/sendmail/libsm/rpool.c
new file mode 100644
index 0000000000..2597052f27
--- /dev/null
+++ b/usr/src/cmd/sendmail/libsm/rpool.c
@@ -0,0 +1,526 @@
+/*
+ * Copyright (c) 2000-2004 Sendmail, Inc. and its suppliers.
+ * All rights reserved.
+ *
+ * By using this file, you agree to the terms and conditions set
+ * forth in the LICENSE file which can be found at the top level of
+ * the sendmail distribution.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#include <sm/gen.h>
+SM_RCSID("@(#)$Id: rpool.c,v 1.28 2004/08/03 20:44:04 ca Exp $")
+
+/*
+** resource pools
+** For documentation, see rpool.html
+*/
+
+#include <sm/exc.h>
+#include <sm/heap.h>
+#include <sm/rpool.h>
+#include <sm/varargs.h>
+#include <sm/conf.h>
+#if _FFR_PERF_RPOOL
+# include <syslog.h>
+#endif /* _FFR_PERF_RPOOL */
+
+const char SmRpoolMagic[] = "sm_rpool";
+
+typedef union
+{
+ SM_POOLLINK_T link;
+ char align[SM_ALIGN_SIZE];
+} SM_POOLHDR_T;
+
+static char *sm_rpool_allocblock_x __P((SM_RPOOL_T *, size_t));
+static char *sm_rpool_allocblock __P((SM_RPOOL_T *, size_t));
+
+/*
+** Tune this later
+*/
+
+#define POOLSIZE 4096
+#define BIG_OBJECT_RATIO 10
+
+/*
+** SM_RPOOL_ALLOCBLOCK_X -- allocate a new block for an rpool.
+**
+** Parameters:
+** rpool -- rpool to which the block should be added.
+** size -- size of block.
+**
+** Returns:
+** Pointer to block.
+**
+** Exceptions:
+** F:sm_heap -- out of memory
+*/
+
+static char *
+sm_rpool_allocblock_x(rpool, size)
+ SM_RPOOL_T *rpool;
+ size_t size;
+{
+ SM_POOLLINK_T *p;
+
+ p = sm_malloc_x(sizeof(SM_POOLHDR_T) + size);
+ p->sm_pnext = rpool->sm_pools;
+ rpool->sm_pools = p;
+ return (char*) p + sizeof(SM_POOLHDR_T);
+}
+
+/*
+** SM_RPOOL_ALLOCBLOCK -- allocate a new block for an rpool.
+**
+** Parameters:
+** rpool -- rpool to which the block should be added.
+** size -- size of block.
+**
+** Returns:
+** Pointer to block, NULL on failure.
+*/
+
+static char *
+sm_rpool_allocblock(rpool, size)
+ SM_RPOOL_T *rpool;
+ size_t size;
+{
+ SM_POOLLINK_T *p;
+
+ p = sm_malloc(sizeof(SM_POOLHDR_T) + size);
+ if (p == NULL)
+ return NULL;
+ p->sm_pnext = rpool->sm_pools;
+ rpool->sm_pools = p;
+ return (char*) p + sizeof(SM_POOLHDR_T);
+}
+
+/*
+** SM_RPOOL_MALLOC_TAGGED_X -- allocate memory from rpool
+**
+** Parameters:
+** rpool -- rpool from which memory should be allocated;
+** can be NULL, use sm_malloc() then.
+** size -- size of block.
+** file -- filename.
+** line -- line number in file.
+** group -- heap group for debugging.
+**
+** Returns:
+** Pointer to block.
+**
+** Exceptions:
+** F:sm_heap -- out of memory
+**
+** Notice: XXX
+** if size == 0 and the rpool is new (no memory
+** allocated yet) NULL is returned!
+** We could solve this by
+** - wasting 1 byte (size < avail)
+** - checking for rpool->sm_poolptr != NULL
+** - not asking for 0 sized buffer
+*/
+
+void *
+#if SM_HEAP_CHECK
+sm_rpool_malloc_tagged_x(rpool, size, file, line, group)
+ SM_RPOOL_T *rpool;
+ size_t size;
+ char *file;
+ int line;
+ int group;
+#else /* SM_HEAP_CHECK */
+sm_rpool_malloc_x(rpool, size)
+ SM_RPOOL_T *rpool;
+ size_t size;
+#endif /* SM_HEAP_CHECK */
+{
+ char *ptr;
+
+ if (rpool == NULL)
+ return sm_malloc_tagged_x(size, file, line, group);
+
+ /* Ensure that size is properly aligned. */
+ if (size & SM_ALIGN_BITS)
+ size = (size & ~SM_ALIGN_BITS) + SM_ALIGN_SIZE;
+
+ /* The common case. This is optimized for speed. */
+ if (size <= rpool->sm_poolavail)
+ {
+ ptr = rpool->sm_poolptr;
+ rpool->sm_poolptr += size;
+ rpool->sm_poolavail -= size;
+ return ptr;
+ }
+
+ /*
+ ** The slow case: we need to call malloc.
+ ** The SM_REQUIRE assertion is deferred until now, for speed.
+ ** That's okay: we set rpool->sm_poolavail to 0 when we free an rpool,
+ ** so the common case code won't be triggered on a dangling pointer.
+ */
+
+ SM_REQUIRE(rpool->sm_magic == SmRpoolMagic);
+
+ /*
+ ** If size > sm_poolsize, then malloc a new block especially for
+ ** this request. Future requests will be allocated from the
+ ** current pool.
+ **
+ ** What if the current pool is mostly unallocated, and the current
+ ** request is larger than the available space, but < sm_poolsize?
+ ** If we discard the current pool, and start allocating from a new
+ ** pool, then we will be wasting a lot of space. For this reason,
+ ** we malloc a block just for the current request if size >
+ ** sm_bigobjectsize, where sm_bigobjectsize <= sm_poolsize.
+ ** Thus, the most space that we will waste at the end of a pool
+ ** is sm_bigobjectsize - 1.
+ */
+
+ if (size > rpool->sm_bigobjectsize)
+ {
+#if _FFR_PERF_RPOOL
+ ++rpool->sm_nbigblocks;
+#endif /* _FFR_PERF_RPOOL */
+ return sm_rpool_allocblock_x(rpool, size);
+ }
+ SM_ASSERT(rpool->sm_bigobjectsize <= rpool->sm_poolsize);
+ ptr = sm_rpool_allocblock_x(rpool, rpool->sm_poolsize);
+ rpool->sm_poolptr = ptr + size;
+ rpool->sm_poolavail = rpool->sm_poolsize - size;
+#if _FFR_PERF_RPOOL
+ ++rpool->sm_npools;
+#endif /* _FFR_PERF_RPOOL */
+ return ptr;
+}
+
+/*
+** SM_RPOOL_MALLOC_TAGGED -- allocate memory from rpool
+**
+** Parameters:
+** rpool -- rpool from which memory should be allocated;
+** can be NULL, use sm_malloc() then.
+** size -- size of block.
+** file -- filename.
+** line -- line number in file.
+** group -- heap group for debugging.
+**
+** Returns:
+** Pointer to block, NULL on failure.
+**
+** Notice: XXX
+** if size == 0 and the rpool is new (no memory
+** allocated yet) NULL is returned!
+** We could solve this by
+** - wasting 1 byte (size < avail)
+** - checking for rpool->sm_poolptr != NULL
+** - not asking for 0 sized buffer
+*/
+
+void *
+#if SM_HEAP_CHECK
+sm_rpool_malloc_tagged(rpool, size, file, line, group)
+ SM_RPOOL_T *rpool;
+ size_t size;
+ char *file;
+ int line;
+ int group;
+#else /* SM_HEAP_CHECK */
+sm_rpool_malloc(rpool, size)
+ SM_RPOOL_T *rpool;
+ size_t size;
+#endif /* SM_HEAP_CHECK */
+{
+ char *ptr;
+
+ if (rpool == NULL)
+ return sm_malloc_tagged(size, file, line, group);
+
+ /* Ensure that size is properly aligned. */
+ if (size & SM_ALIGN_BITS)
+ size = (size & ~SM_ALIGN_BITS) + SM_ALIGN_SIZE;
+
+ /* The common case. This is optimized for speed. */
+ if (size <= rpool->sm_poolavail)
+ {
+ ptr = rpool->sm_poolptr;
+ rpool->sm_poolptr += size;
+ rpool->sm_poolavail -= size;
+ return ptr;
+ }
+
+ /*
+ ** The slow case: we need to call malloc.
+ ** The SM_REQUIRE assertion is deferred until now, for speed.
+ ** That's okay: we set rpool->sm_poolavail to 0 when we free an rpool,
+ ** so the common case code won't be triggered on a dangling pointer.
+ */
+
+ SM_REQUIRE(rpool->sm_magic == SmRpoolMagic);
+
+ /*
+ ** If size > sm_poolsize, then malloc a new block especially for
+ ** this request. Future requests will be allocated from the
+ ** current pool.
+ **
+ ** What if the current pool is mostly unallocated, and the current
+ ** request is larger than the available space, but < sm_poolsize?
+ ** If we discard the current pool, and start allocating from a new
+ ** pool, then we will be wasting a lot of space. For this reason,
+ ** we malloc a block just for the current request if size >
+ ** sm_bigobjectsize, where sm_bigobjectsize <= sm_poolsize.
+ ** Thus, the most space that we will waste at the end of a pool
+ ** is sm_bigobjectsize - 1.
+ */
+
+ if (size > rpool->sm_bigobjectsize)
+ {
+#if _FFR_PERF_RPOOL
+ ++rpool->sm_nbigblocks;
+#endif /* _FFR_PERF_RPOOL */
+ return sm_rpool_allocblock(rpool, size);
+ }
+ SM_ASSERT(rpool->sm_bigobjectsize <= rpool->sm_poolsize);
+ ptr = sm_rpool_allocblock(rpool, rpool->sm_poolsize);
+ if (ptr == NULL)
+ return NULL;
+ rpool->sm_poolptr = ptr + size;
+ rpool->sm_poolavail = rpool->sm_poolsize - size;
+#if _FFR_PERF_RPOOL
+ ++rpool->sm_npools;
+#endif /* _FFR_PERF_RPOOL */
+ return ptr;
+}
+
+/*
+** SM_RPOOL_NEW_X -- create a new rpool.
+**
+** Parameters:
+** parent -- pointer to parent rpool, can be NULL.
+**
+** Returns:
+** Pointer to new rpool.
+*/
+
+SM_RPOOL_T *
+sm_rpool_new_x(parent)
+ SM_RPOOL_T *parent;
+{
+ SM_RPOOL_T *rpool;
+
+ rpool = sm_malloc_x(sizeof(SM_RPOOL_T));
+ if (parent == NULL)
+ rpool->sm_parentlink = NULL;
+ else
+ {
+ SM_TRY
+ rpool->sm_parentlink = sm_rpool_attach_x(parent,
+ (SM_RPOOL_RFREE_T) sm_rpool_free,
+ (void *) rpool);
+ SM_EXCEPT(exc, "*")
+ sm_free(rpool);
+ sm_exc_raise_x(exc);
+ SM_END_TRY
+ }
+ rpool->sm_magic = SmRpoolMagic;
+
+ rpool->sm_poolsize = POOLSIZE - sizeof(SM_POOLHDR_T);
+ rpool->sm_bigobjectsize = rpool->sm_poolsize / BIG_OBJECT_RATIO;
+ rpool->sm_poolptr = NULL;
+ rpool->sm_poolavail = 0;
+ rpool->sm_pools = NULL;
+
+ rpool->sm_rptr = NULL;
+ rpool->sm_ravail = 0;
+ rpool->sm_rlists = NULL;
+#if _FFR_PERF_RPOOL
+ rpool->sm_nbigblocks = 0;
+ rpool->sm_npools = 0;
+#endif /* _FFR_PERF_RPOOL */
+
+ return rpool;
+}
+
+/*
+** SM_RPOOL_SETSIZES -- set sizes for rpool.
+**
+** Parameters:
+** poolsize -- size of a single rpool block.
+** bigobjectsize -- if this size is exceeded, an individual
+** block is allocated (must be less or equal poolsize).
+**
+** Returns:
+** none.
+*/
+
+void
+sm_rpool_setsizes(rpool, poolsize, bigobjectsize)
+ SM_RPOOL_T *rpool;
+ size_t poolsize;
+ size_t bigobjectsize;
+{
+ SM_REQUIRE(poolsize >= bigobjectsize);
+ if (poolsize == 0)
+ poolsize = POOLSIZE - sizeof(SM_POOLHDR_T);
+ if (bigobjectsize == 0)
+ bigobjectsize = poolsize / BIG_OBJECT_RATIO;
+ rpool->sm_poolsize = poolsize;
+ rpool->sm_bigobjectsize = bigobjectsize;
+}
+
+/*
+** SM_RPOOL_FREE -- free an rpool and release all of its resources.
+**
+** Parameters:
+** rpool -- rpool to free.
+**
+** Returns:
+** none.
+*/
+
+void
+sm_rpool_free(rpool)
+ SM_RPOOL_T *rpool;
+{
+ SM_RLIST_T *rl, *rnext;
+ SM_RESOURCE_T *r, *rmax;
+ SM_POOLLINK_T *pp, *pnext;
+
+ if (rpool == NULL)
+ return;
+
+ /*
+ ** It's important to free the resources before the memory pools,
+ ** because the resource free functions might modify the contents
+ ** of the memory pools.
+ */
+
+ rl = rpool->sm_rlists;
+ if (rl != NULL)
+ {
+ rmax = rpool->sm_rptr;
+ for (;;)
+ {
+ for (r = rl->sm_rvec; r < rmax; ++r)
+ {
+ if (r->sm_rfree != NULL)
+ r->sm_rfree(r->sm_rcontext);
+ }
+ rnext = rl->sm_rnext;
+ sm_free(rl);
+ if (rnext == NULL)
+ break;
+ rl = rnext;
+ rmax = &rl->sm_rvec[SM_RLIST_MAX];
+ }
+ }
+
+ /*
+ ** Now free the memory pools.
+ */
+
+ for (pp = rpool->sm_pools; pp != NULL; pp = pnext)
+ {
+ pnext = pp->sm_pnext;
+ sm_free(pp);
+ }
+
+ /*
+ ** Disconnect rpool from its parent.
+ */
+
+ if (rpool->sm_parentlink != NULL)
+ *rpool->sm_parentlink = NULL;
+
+ /*
+ ** Setting these fields to zero means that any future to attempt
+ ** to use the rpool after it is freed will cause an assertion failure.
+ */
+
+ rpool->sm_magic = NULL;
+ rpool->sm_poolavail = 0;
+ rpool->sm_ravail = 0;
+
+#if _FFR_PERF_RPOOL
+ if (rpool->sm_nbigblocks > 0 || rpool->sm_npools > 1)
+ syslog(LOG_NOTICE,
+ "perf: rpool=%lx, sm_nbigblocks=%d, sm_npools=%d",
+ (long) rpool, rpool->sm_nbigblocks, rpool->sm_npools);
+ rpool->sm_nbigblocks = 0;
+ rpool->sm_npools = 0;
+#endif /* _FFR_PERF_RPOOL */
+ sm_free(rpool);
+}
+
+/*
+** SM_RPOOL_ATTACH_X -- attach a resource to an rpool.
+**
+** Parameters:
+** rpool -- rpool to which resource should be attached.
+** rfree -- function to call when rpool is freed.
+** rcontext -- argument for function to call when rpool is freed.
+**
+** Returns:
+** Pointer to allocated function.
+**
+** Exceptions:
+** F:sm_heap -- out of memory
+*/
+
+SM_RPOOL_ATTACH_T
+sm_rpool_attach_x(rpool, rfree, rcontext)
+ SM_RPOOL_T *rpool;
+ SM_RPOOL_RFREE_T rfree;
+ void *rcontext;
+{
+ SM_RLIST_T *rl;
+ SM_RPOOL_ATTACH_T a;
+
+ SM_REQUIRE_ISA(rpool, SmRpoolMagic);
+
+ if (rpool->sm_ravail == 0)
+ {
+ rl = sm_malloc_x(sizeof(SM_RLIST_T));
+ rl->sm_rnext = rpool->sm_rlists;
+ rpool->sm_rlists = rl;
+ rpool->sm_rptr = rl->sm_rvec;
+ rpool->sm_ravail = SM_RLIST_MAX;
+ }
+
+ a = &rpool->sm_rptr->sm_rfree;
+ rpool->sm_rptr->sm_rfree = rfree;
+ rpool->sm_rptr->sm_rcontext = rcontext;
+ ++rpool->sm_rptr;
+ --rpool->sm_ravail;
+ return a;
+}
+
+#if DO_NOT_USE_STRCPY
+/*
+** SM_RPOOL_STRDUP_X -- Create a copy of a C string
+**
+** Parameters:
+** rpool -- rpool to use.
+** s -- the string to copy.
+**
+** Returns:
+** pointer to newly allocated string.
+*/
+
+char *
+sm_rpool_strdup_x(rpool, s)
+ SM_RPOOL_T *rpool;
+ const char *s;
+{
+ size_t l;
+ char *n;
+
+ l = strlen(s);
+ SM_ASSERT(l + 1 > l);
+ n = sm_rpool_malloc_x(rpool, l + 1);
+ sm_strlcpy(n, s, l + 1);
+ return n;
+}
+#endif /* DO_NOT_USE_STRCPY */
diff --git a/usr/src/cmd/sendmail/libsm/sem.c b/usr/src/cmd/sendmail/libsm/sem.c
new file mode 100644
index 0000000000..2d33427ead
--- /dev/null
+++ b/usr/src/cmd/sendmail/libsm/sem.c
@@ -0,0 +1,203 @@
+/*
+ * Copyright (c) 2000-2001, 2005 Sendmail, Inc. and its suppliers.
+ * All rights reserved.
+ *
+ * By using this file, you agree to the terms and conditions set
+ * forth in the LICENSE file which can be found at the top level of
+ * the sendmail distribution.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#include <sm/gen.h>
+SM_RCSID("@(#)$Id: sem.c,v 1.12 2005/03/25 21:27:02 ca Exp $")
+
+#if SM_CONF_SEM
+# include <stdlib.h>
+# include <unistd.h>
+# include <sm/sem.h>
+# include <sm/heap.h>
+
+/*
+** SM_SEM_START -- initialize semaphores
+**
+** Parameters:
+** key -- key for semaphores.
+** nsem -- number of semaphores.
+** semflg -- flag for semget(), if 0, use a default.
+** owner -- create semaphores.
+**
+** Returns:
+** id for semaphores.
+** < 0 on failure.
+*/
+
+int
+sm_sem_start(key, nsem, semflg, owner)
+ key_t key;
+ int nsem;
+ int semflg;
+ bool owner;
+{
+ int semid, i;
+ unsigned short *semvals;
+
+ semvals = NULL;
+ if (semflg == 0)
+ semflg = (SEM_A|SEM_R)|((SEM_A|SEM_R) >> 3);
+ if (owner)
+ semflg |= IPC_CREAT|IPC_EXCL;
+ semid = semget(key, nsem, semflg);
+ if (semid < 0)
+ goto error;
+
+ if (owner)
+ {
+ union semun semarg;
+
+ semvals = (unsigned short *) sm_malloc(nsem * sizeof semvals);
+ if (semvals == NULL)
+ goto error;
+ semarg.array = semvals;
+
+ /* initialize semaphore values to be available */
+ for (i = 0; i < nsem; i++)
+ semvals[i] = 1;
+ if (semctl(semid, 0, SETALL, semarg) < 0)
+ goto error;
+ }
+ return semid;
+
+error:
+ if (semvals != NULL)
+ sm_free(semvals);
+ if (semid >= 0)
+ sm_sem_stop(semid);
+ return -1;
+}
+
+/*
+** SM_SEM_STOP -- stop using semaphores.
+**
+** Parameters:
+** semid -- id for semaphores.
+**
+** Returns:
+** 0 on success.
+** < 0 on failure.
+*/
+
+int
+sm_sem_stop(semid)
+ int semid;
+{
+ return semctl(semid, 0, IPC_RMID, NULL);
+}
+
+/*
+** SM_SEM_ACQ -- acquire semaphore.
+**
+** Parameters:
+** semid -- id for semaphores.
+** semnum -- number of semaphore.
+** timeout -- how long to wait for operation to succeed.
+**
+** Returns:
+** 0 on success.
+** < 0 on failure.
+*/
+
+int
+sm_sem_acq(semid, semnum, timeout)
+ int semid;
+ int semnum;
+ int timeout;
+{
+ int r;
+ struct sembuf semops[1];
+
+ semops[0].sem_num = semnum;
+ semops[0].sem_op = -1;
+ semops[0].sem_flg = SEM_UNDO |
+ (timeout != SM_TIME_FOREVER ? 0 : IPC_NOWAIT);
+ if (timeout == SM_TIME_IMMEDIATE || timeout == SM_TIME_FOREVER)
+ return semop(semid, semops, 1);
+ do
+ {
+ r = semop(semid, semops, 1);
+ if (r == 0)
+ return r;
+ sleep(1);
+ --timeout;
+ } while (timeout > 0);
+ return r;
+}
+
+/*
+** SM_SEM_REL -- release semaphore.
+**
+** Parameters:
+** semid -- id for semaphores.
+** semnum -- number of semaphore.
+** timeout -- how long to wait for operation to succeed.
+**
+** Returns:
+** 0 on success.
+** < 0 on failure.
+*/
+
+int
+sm_sem_rel(semid, semnum, timeout)
+ int semid;
+ int semnum;
+ int timeout;
+{
+ int r;
+ struct sembuf semops[1];
+
+#if PARANOID
+ /* XXX should we check whether the value is already 0 ? */
+ SM_REQUIRE(sm_get_sem(semid, semnum) > 0);
+#endif /* PARANOID */
+
+ semops[0].sem_num = semnum;
+ semops[0].sem_op = 1;
+ semops[0].sem_flg = SEM_UNDO |
+ (timeout != SM_TIME_FOREVER ? 0 : IPC_NOWAIT);
+ if (timeout == SM_TIME_IMMEDIATE || timeout == SM_TIME_FOREVER)
+ return semop(semid, semops, 1);
+ do
+ {
+ r = semop(semid, semops, 1);
+ if (r == 0)
+ return r;
+ sleep(1);
+ --timeout;
+ } while (timeout > 0);
+ return r;
+}
+
+/*
+** SM_SEM_GET -- get semaphore value.
+**
+** Parameters:
+** semid -- id for semaphores.
+** semnum -- number of semaphore.
+**
+** Returns:
+** value of semaphore on success.
+** < 0 on failure.
+*/
+
+int
+sm_sem_get(semid, semnum)
+ int semid;
+ int semnum;
+{
+ int semval;
+
+ if ((semval = semctl(semid, semnum, GETVAL, NULL)) < 0)
+ return -1;
+ return semval;
+}
+#endif /* SM_CONF_SEM */
diff --git a/usr/src/cmd/sendmail/libsm/setvbuf.c b/usr/src/cmd/sendmail/libsm/setvbuf.c
new file mode 100644
index 0000000000..ccc5da99e2
--- /dev/null
+++ b/usr/src/cmd/sendmail/libsm/setvbuf.c
@@ -0,0 +1,192 @@
+/*
+ * Copyright (c) 2000-2001 Sendmail, Inc. and its suppliers.
+ * All rights reserved.
+ * Copyright (c) 1990, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Chris Torek.
+ *
+ * By using this file, you agree to the terms and conditions set
+ * forth in the LICENSE file which can be found at the top level of
+ * the sendmail distribution.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#include <sm/gen.h>
+SM_RCSID("@(#)$Id: setvbuf.c,v 1.30 2001/02/28 20:25:18 rodney Exp $")
+#include <stdlib.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <sm/io.h>
+#include <sm/heap.h>
+#include <sm/assert.h>
+#include <sm/conf.h>
+#include "local.h"
+
+/*
+** SM_IO_SETVBUF -- set the buffering type for a file
+**
+** Set one of the different kinds of buffering, optionally including
+** a buffer.
+** If 'size' is == 0 then an "optimal" size will be selected.
+** If 'buf' is == NULL then space will be allocated at 'size'.
+**
+** Parameters:
+** fp -- the file that buffering is to be changed for
+** timeout -- time allowed for completing the function
+** buf -- buffer to use
+** mode -- buffering method to use
+** size -- size of 'buf'
+**
+** Returns:
+** Failure: SM_IO_EOF
+** Success: 0 (zero)
+*/
+
+int
+sm_io_setvbuf(fp, timeout, buf, mode, size)
+ SM_FILE_T *fp;
+ int timeout;
+ char *buf;
+ int mode;
+ size_t size;
+{
+ int ret, flags;
+ size_t iosize;
+ int ttyflag;
+ int fd;
+ struct timeval to;
+
+ SM_REQUIRE_ISA(fp, SmFileMagic);
+
+ /*
+ ** Verify arguments. The `int' limit on `size' is due to this
+ ** particular implementation. Note, buf and size are ignored
+ ** when setting SM_IO_NBF.
+ */
+
+ if (mode != SM_IO_NBF)
+ if ((mode != SM_IO_FBF && mode != SM_IO_LBF &&
+ mode != SM_IO_NOW) || (int) size < 0)
+ return SM_IO_EOF;
+
+ /*
+ ** Write current buffer, if any. Discard unread input (including
+ ** ungetc data), cancel line buffering, and free old buffer if
+ ** malloc()ed. We also clear any eof condition, as if this were
+ ** a seek.
+ */
+
+ ret = 0;
+ SM_CONVERT_TIME(fp, fd, timeout, &to);
+ (void) sm_flush(fp, &timeout);
+ if (HASUB(fp))
+ FREEUB(fp);
+ fp->f_r = fp->f_lbfsize = 0;
+ flags = fp->f_flags;
+ if (flags & SMMBF)
+ {
+ sm_free((void *) fp->f_bf.smb_base);
+ fp->f_bf.smb_base = NULL;
+ }
+ flags &= ~(SMLBF | SMNBF | SMMBF | SMOPT | SMNPT | SMFEOF | SMNOW |
+ SMFBF);
+
+ /* If setting unbuffered mode, skip all the hard work. */
+ if (mode == SM_IO_NBF)
+ goto nbf;
+
+ /*
+ ** Find optimal I/O size for seek optimization. This also returns
+ ** a `tty flag' to suggest that we check isatty(fd), but we do not
+ ** care since our caller told us how to buffer.
+ */
+
+ flags |= sm_whatbuf(fp, &iosize, &ttyflag);
+ if (size == 0)
+ {
+ buf = NULL; /* force local allocation */
+ size = iosize;
+ }
+
+ /* Allocate buffer if needed. */
+ if (buf == NULL)
+ {
+ if ((buf = sm_malloc(size)) == NULL)
+ {
+ /*
+ ** Unable to honor user's request. We will return
+ ** failure, but try again with file system size.
+ */
+
+ ret = SM_IO_EOF;
+ if (size != iosize)
+ {
+ size = iosize;
+ buf = sm_malloc(size);
+ }
+ }
+ if (buf == NULL)
+ {
+ /* No luck; switch to unbuffered I/O. */
+nbf:
+ fp->f_flags = flags | SMNBF;
+ fp->f_w = 0;
+ fp->f_bf.smb_base = fp->f_p = fp->f_nbuf;
+ fp->f_bf.smb_size = 1;
+ return ret;
+ }
+ flags |= SMMBF;
+ }
+
+ /*
+ ** Kill any seek optimization if the buffer is not the
+ ** right size.
+ **
+ ** SHOULD WE ALLOW MULTIPLES HERE (i.e., ok iff (size % iosize) == 0)?
+ */
+
+ if (size != iosize)
+ flags |= SMNPT;
+
+ /*
+ ** Fix up the SM_FILE_T fields, and set sm_cleanup for output flush on
+ ** exit (since we are buffered in some way).
+ */
+
+ if (mode == SM_IO_LBF)
+ flags |= SMLBF;
+ else if (mode == SM_IO_NOW)
+ flags |= SMNOW;
+ else if (mode == SM_IO_FBF)
+ flags |= SMFBF;
+ fp->f_flags = flags;
+ fp->f_bf.smb_base = fp->f_p = (unsigned char *)buf;
+ fp->f_bf.smb_size = size;
+ /* fp->f_lbfsize is still 0 */
+ if (flags & SMWR)
+ {
+ /*
+ ** Begin or continue writing: see sm_wsetup(). Note
+ ** that SMNBF is impossible (it was handled earlier).
+ */
+
+ if (flags & SMLBF)
+ {
+ fp->f_w = 0;
+ fp->f_lbfsize = -fp->f_bf.smb_size;
+ }
+ else
+ fp->f_w = size;
+ }
+ else
+ {
+ /* begin/continue reading, or stay in intermediate state */
+ fp->f_w = 0;
+ }
+
+ atexit(sm_cleanup);
+ return ret;
+}
diff --git a/usr/src/cmd/sendmail/libsm/shm.c b/usr/src/cmd/sendmail/libsm/shm.c
new file mode 100644
index 0000000000..3da9c96dd1
--- /dev/null
+++ b/usr/src/cmd/sendmail/libsm/shm.c
@@ -0,0 +1,143 @@
+/*
+ * Copyright (c) 2000-2001, 2003, 2005 Sendmail, Inc. and its suppliers.
+ * All rights reserved.
+ *
+ * By using this file, you agree to the terms and conditions set
+ * forth in the LICENSE file which can be found at the top level of
+ * the sendmail distribution.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#include <sm/gen.h>
+SM_RCSID("@(#)$Id: shm.c,v 1.18 2005/02/09 01:54:51 ca Exp $")
+
+#if SM_CONF_SHM
+# include <stdlib.h>
+# include <unistd.h>
+# include <errno.h>
+# include <sm/shm.h>
+
+
+/*
+** SM_SHMSTART -- initialize shared memory segment.
+**
+** Parameters:
+** key -- key for shared memory.
+** size -- size of segment.
+** shmflag -- initial flags.
+** shmid -- pointer to return id.
+** owner -- create segment.
+**
+** Returns:
+** pointer to shared memory segment,
+** NULL on failure.
+**
+** Side Effects:
+** attaches shared memory segment.
+*/
+
+void *
+sm_shmstart(key, size, shmflg, shmid, owner)
+ key_t key;
+ int size;
+ int shmflg;
+ int *shmid;
+ bool owner;
+{
+ int save_errno;
+ void *shm = SM_SHM_NULL;
+
+ /* default: user/group accessible */
+ if (shmflg == 0)
+ shmflg = SHM_R|SHM_W|(SHM_R>>3)|(SHM_W>>3);
+ if (owner)
+ shmflg |= IPC_CREAT|IPC_EXCL;
+ *shmid = shmget(key, size, shmflg);
+ if (*shmid < 0)
+ goto error;
+
+ shm = shmat(*shmid, (void *) 0, 0);
+ if (shm == SM_SHM_NULL)
+ goto error;
+
+ return shm;
+
+ error:
+ save_errno = errno;
+ if (shm != SM_SHM_NULL || *shmid >= 0)
+ sm_shmstop(shm, *shmid, owner);
+ *shmid = SM_SHM_NO_ID;
+ errno = save_errno;
+ return (void *) 0;
+}
+
+
+/*
+** SM_SHMSTOP -- stop using shared memory segment.
+**
+** Parameters:
+** shm -- pointer to shared memory.
+** shmid -- id.
+** owner -- delete segment.
+**
+** Returns:
+** 0 on success.
+** < 0 on failure.
+**
+** Side Effects:
+** detaches (and maybe removes) shared memory segment.
+*/
+
+
+int
+sm_shmstop(shm, shmid, owner)
+ void *shm;
+ int shmid;
+ bool owner;
+{
+ int r;
+
+ if (shm != SM_SHM_NULL && (r = shmdt(shm)) < 0)
+ return r;
+ if (owner && shmid >= 0 && (r = shmctl(shmid, IPC_RMID, NULL)) < 0)
+ return r;
+ return 0;
+}
+
+
+/*
+** SM_SHMSETOWNER -- set owner/group/mode of shared memory segment.
+**
+** Parameters:
+** shmid -- id.
+** uid -- uid to use
+** gid -- gid to use
+** mode -- mode to use
+**
+** Returns:
+** 0 on success.
+** < 0 on failure.
+*/
+
+int
+sm_shmsetowner(shmid, uid, gid, mode)
+ int shmid;
+ uid_t uid;
+ gid_t gid;
+ mode_t mode;
+{
+ int r;
+ struct shmid_ds shmid_ds;
+
+ memset(&shmid_ds, 0, sizeof(shmid_ds));
+ if ((r = shmctl(shmid, IPC_STAT, &shmid_ds)) < 0)
+ return r;
+ shmid_ds.shm_perm.uid = uid;
+ shmid_ds.shm_perm.gid = gid;
+ shmid_ds.shm_perm.mode = mode;
+ if ((r = shmctl(shmid, IPC_SET, &shmid_ds)) < 0)
+ return r;
+ return 0;
+}
+#endif /* SM_CONF_SHM */
diff --git a/usr/src/cmd/sendmail/libsm/signal.c b/usr/src/cmd/sendmail/libsm/signal.c
new file mode 100644
index 0000000000..c5807e7f1f
--- /dev/null
+++ b/usr/src/cmd/sendmail/libsm/signal.c
@@ -0,0 +1,342 @@
+/*
+ * Copyright (c) 2000-2001 Sendmail, Inc. and its suppliers.
+ * All rights reserved.
+ *
+ * By using this file, you agree to the terms and conditions set
+ * forth in the LICENSE file which can be found at the top level of
+ * the sendmail distribution.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#include <sm/gen.h>
+SM_RCSID("@(#)$Id: signal.c,v 1.13 2001/08/14 16:05:47 ca Exp $")
+
+#if SM_CONF_SETITIMER
+# include <sys/time.h>
+#endif /* SM_CONF_SETITIMER */
+#include <errno.h>
+#include <stdlib.h>
+#include <time.h>
+#include <unistd.h>
+#include <sm/clock.h>
+#include <sm/signal.h>
+#include <signal.h>
+#include <sm/string.h>
+
+unsigned int volatile InCriticalSection; /* >0 if inside critical section */
+int volatile PendingSignal; /* pending signal to resend */
+
+ /*
+** SM_SIGNAL -- set a signal handler
+**
+** This is essentially old BSD "signal(3)".
+**
+** NOTE: THIS CAN BE CALLED FROM A SIGNAL HANDLER. DO NOT ADD
+** ANYTHING TO THIS ROUTINE UNLESS YOU KNOW WHAT YOU ARE
+** DOING.
+*/
+
+sigfunc_t
+sm_signal(sig, handler)
+ int sig;
+ sigfunc_t handler;
+{
+# if defined(SA_RESTART) || (!defined(SYS5SIGNALS) && !defined(BSD4_3))
+ struct sigaction n, o;
+# endif /* defined(SA_RESTART) || (!defined(SYS5SIGNALS) && !defined(BSD4_3)) */
+
+ /*
+ ** First, try for modern signal calls
+ ** and restartable syscalls
+ */
+
+# ifdef SA_RESTART
+ (void) memset(&n, '\0', sizeof n);
+# if USE_SA_SIGACTION
+ n.sa_sigaction = (void(*)(int, siginfo_t *, void *)) handler;
+ n.sa_flags = SA_RESTART|SA_SIGINFO;
+# else /* USE_SA_SIGACTION */
+ n.sa_handler = handler;
+ n.sa_flags = SA_RESTART;
+# endif /* USE_SA_SIGACTION */
+ if (sigaction(sig, &n, &o) < 0)
+ return SIG_ERR;
+ return o.sa_handler;
+# else /* SA_RESTART */
+
+ /*
+ ** Else check for SYS5SIGNALS or
+ ** BSD4_3 signals
+ */
+
+# if defined(SYS5SIGNALS) || defined(BSD4_3)
+# ifdef BSD4_3
+ return signal(sig, handler);
+# else /* BSD4_3 */
+ return sigset(sig, handler);
+# endif /* BSD4_3 */
+# else /* defined(SYS5SIGNALS) || defined(BSD4_3) */
+
+ /*
+ ** Finally, if nothing else is available,
+ ** go for a default
+ */
+
+ (void) memset(&n, '\0', sizeof n);
+ n.sa_handler = handler;
+ if (sigaction(sig, &n, &o) < 0)
+ return SIG_ERR;
+ return o.sa_handler;
+# endif /* defined(SYS5SIGNALS) || defined(BSD4_3) */
+# endif /* SA_RESTART */
+}
+ /*
+** SM_BLOCKSIGNAL -- hold a signal to prevent delivery
+**
+** Parameters:
+** sig -- the signal to block.
+**
+** Returns:
+** 1 signal was previously blocked
+** 0 signal was not previously blocked
+** -1 on failure.
+*/
+
+int
+sm_blocksignal(sig)
+ int sig;
+{
+# ifdef BSD4_3
+# ifndef sigmask
+# define sigmask(s) (1 << ((s) - 1))
+# endif /* ! sigmask */
+ return (sigblock(sigmask(sig)) & sigmask(sig)) != 0;
+# else /* BSD4_3 */
+# ifdef ALTOS_SYSTEM_V
+ sigfunc_t handler;
+
+ handler = sigset(sig, SIG_HOLD);
+ if (handler == SIG_ERR)
+ return -1;
+ else
+ return handler == SIG_HOLD;
+# else /* ALTOS_SYSTEM_V */
+ sigset_t sset, oset;
+
+ (void) sigemptyset(&sset);
+ (void) sigaddset(&sset, sig);
+ if (sigprocmask(SIG_BLOCK, &sset, &oset) < 0)
+ return -1;
+ else
+ return sigismember(&oset, sig);
+# endif /* ALTOS_SYSTEM_V */
+# endif /* BSD4_3 */
+}
+ /*
+** SM_RELEASESIGNAL -- release a held signal
+**
+** Parameters:
+** sig -- the signal to release.
+**
+** Returns:
+** 1 signal was previously blocked
+** 0 signal was not previously blocked
+** -1 on failure.
+*/
+
+int
+sm_releasesignal(sig)
+ int sig;
+{
+# ifdef BSD4_3
+ return (sigsetmask(sigblock(0) & ~sigmask(sig)) & sigmask(sig)) != 0;
+# else /* BSD4_3 */
+# ifdef ALTOS_SYSTEM_V
+ sigfunc_t handler;
+
+ handler = sigset(sig, SIG_HOLD);
+ if (sigrelse(sig) < 0)
+ return -1;
+ else
+ return handler == SIG_HOLD;
+# else /* ALTOS_SYSTEM_V */
+ sigset_t sset, oset;
+
+ (void) sigemptyset(&sset);
+ (void) sigaddset(&sset, sig);
+ if (sigprocmask(SIG_UNBLOCK, &sset, &oset) < 0)
+ return -1;
+ else
+ return sigismember(&oset, sig);
+# endif /* ALTOS_SYSTEM_V */
+# endif /* BSD4_3 */
+}
+ /*
+** PEND_SIGNAL -- Add a signal to the pending signal list
+**
+** Parameters:
+** sig -- signal to add
+**
+** Returns:
+** none.
+**
+** NOTE: THIS CAN BE CALLED FROM A SIGNAL HANDLER. DO NOT ADD
+** ANYTHING TO THIS ROUTINE UNLESS YOU KNOW WHAT YOU ARE
+** DOING.
+*/
+
+void
+pend_signal(sig)
+ int sig;
+{
+ int sigbit;
+ int save_errno = errno;
+#if SM_CONF_SETITIMER
+ struct itimerval clr;
+#endif /* SM_CONF_SETITIMER */
+
+ /*
+ ** Don't want to interrupt something critical, hence delay
+ ** the alarm for one second. Hopefully, by then we
+ ** will be out of the critical section. If not, then
+ ** we will just delay again. The events to be run will
+ ** still all be run, maybe just a little bit late.
+ */
+
+ switch (sig)
+ {
+ case SIGHUP:
+ sigbit = PEND_SIGHUP;
+ break;
+
+ case SIGINT:
+ sigbit = PEND_SIGINT;
+ break;
+
+ case SIGTERM:
+ sigbit = PEND_SIGTERM;
+ break;
+
+ case SIGUSR1:
+ sigbit = PEND_SIGUSR1;
+ break;
+
+ case SIGALRM:
+ /* don't have to pend these */
+ sigbit = 0;
+ break;
+
+ default:
+ /* If we get here, we are in trouble */
+ abort();
+
+ /* NOTREACHED */
+ /* shut up stupid compiler warning on HP-UX 11 */
+ sigbit = 0;
+ break;
+ }
+
+ if (sigbit != 0)
+ PendingSignal |= sigbit;
+ (void) sm_signal(SIGALRM, sm_tick);
+#if SM_CONF_SETITIMER
+ clr.it_interval.tv_sec = 0;
+ clr.it_interval.tv_usec = 0;
+ clr.it_value.tv_sec = 1;
+ clr.it_value.tv_usec = 0;
+ (void) setitimer(ITIMER_REAL, &clr, NULL);
+#else /* SM_CONF_SETITIMER */
+ (void) alarm(1);
+#endif /* SM_CONF_SETITIMER */
+ errno = save_errno;
+}
+ /*
+** SM_ALLSIGNALS -- act on all signals
+**
+** Parameters:
+** block -- whether to block or release all signals.
+**
+** Returns:
+** none.
+*/
+
+void
+sm_allsignals(block)
+ bool block;
+{
+# ifdef BSD4_3
+# ifndef sigmask
+# define sigmask(s) (1 << ((s) - 1))
+# endif /* ! sigmask */
+ if (block)
+ {
+ int mask = 0;
+
+ mask |= sigmask(SIGALRM);
+ mask |= sigmask(SIGCHLD);
+ mask |= sigmask(SIGHUP);
+ mask |= sigmask(SIGINT);
+ mask |= sigmask(SIGTERM);
+ mask |= sigmask(SIGUSR1);
+
+ (void) sigblock(mask);
+ }
+ else
+ sigsetmask(0);
+# else /* BSD4_3 */
+# ifdef ALTOS_SYSTEM_V
+ if (block)
+ {
+ (void) sigset(SIGALRM, SIG_HOLD);
+ (void) sigset(SIGCHLD, SIG_HOLD);
+ (void) sigset(SIGHUP, SIG_HOLD);
+ (void) sigset(SIGINT, SIG_HOLD);
+ (void) sigset(SIGTERM, SIG_HOLD);
+ (void) sigset(SIGUSR1, SIG_HOLD);
+ }
+ else
+ {
+ (void) sigset(SIGALRM, SIG_DFL);
+ (void) sigset(SIGCHLD, SIG_DFL);
+ (void) sigset(SIGHUP, SIG_DFL);
+ (void) sigset(SIGINT, SIG_DFL);
+ (void) sigset(SIGTERM, SIG_DFL);
+ (void) sigset(SIGUSR1, SIG_DFL);
+ }
+# else /* ALTOS_SYSTEM_V */
+ sigset_t sset;
+
+ (void) sigemptyset(&sset);
+ (void) sigaddset(&sset, SIGALRM);
+ (void) sigaddset(&sset, SIGCHLD);
+ (void) sigaddset(&sset, SIGHUP);
+ (void) sigaddset(&sset, SIGINT);
+ (void) sigaddset(&sset, SIGTERM);
+ (void) sigaddset(&sset, SIGUSR1);
+ (void) sigprocmask(block ? SIG_BLOCK : SIG_UNBLOCK, &sset, NULL);
+# endif /* ALTOS_SYSTEM_V */
+# endif /* BSD4_3 */
+}
+ /*
+** SM_SIGNAL_NOOP -- A signal no-op function
+**
+** Parameters:
+** sig -- signal received
+**
+** Returns:
+** SIGFUNC_RETURN
+*/
+
+/* ARGSUSED */
+SIGFUNC_DECL
+sm_signal_noop(sig)
+ int sig;
+{
+ int save_errno = errno;
+
+ FIX_SYSV_SIGNAL(sig, sm_signal_noop);
+ errno = save_errno;
+ return SIGFUNC_RETURN;
+}
+
diff --git a/usr/src/cmd/sendmail/libsm/sm_os.h b/usr/src/cmd/sendmail/libsm/sm_os.h
new file mode 100644
index 0000000000..4c28daf335
--- /dev/null
+++ b/usr/src/cmd/sendmail/libsm/sm_os.h
@@ -0,0 +1,8 @@
+/*
+ * Copyright (c) 2001 by Sun Microsystems, Inc.
+ * All rights reserved.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#include "../include/sm/os/sm_os_sunos.h"
diff --git a/usr/src/cmd/sendmail/libsm/smstdio.c b/usr/src/cmd/sendmail/libsm/smstdio.c
new file mode 100644
index 0000000000..b99c5efb68
--- /dev/null
+++ b/usr/src/cmd/sendmail/libsm/smstdio.c
@@ -0,0 +1,365 @@
+/*
+ * Copyright (c) 2000-2002, 2004 Sendmail, Inc. and its suppliers.
+ * All rights reserved.
+ *
+ * By using this file, you agree to the terms and conditions set
+ * forth in the LICENSE file which can be found at the top level of
+ * the sendmail distribution.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#include <sm/gen.h>
+SM_IDSTR(id, "@(#)$Id: smstdio.c,v 1.34 2004/08/03 20:46:34 ca Exp $")
+#include <unistd.h>
+#include <stdio.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <sys/stat.h>
+#include <sm/assert.h>
+#include <sm/io.h>
+#include <sm/string.h>
+#include "local.h"
+
+static void setup __P((SM_FILE_T *));
+
+/*
+** Overall:
+** This is a file type which implements a layer on top of the system
+** stdio. fp->f_cookie is the FILE* of stdio. The cookie may be
+** "bound late" because of the manner which Linux implements stdio.
+** When binding late (when fp->f_cookie==NULL) then the value of
+** fp->f_ival is used (0, 1 or 2) to map to stdio's stdin, stdout or
+** stderr.
+*/
+
+/*
+** SM_STDIOOPEN -- open a file to system stdio implementation
+**
+** Parameters:
+** fp -- file pointer assign for this open
+** info -- info about file to open
+** flags -- indicating method of opening
+** rpool -- ignored
+**
+** Returns:
+** Failure: -1
+** Success: 0 (zero)
+*/
+
+/* ARGSUSED3 */
+int
+sm_stdioopen(fp, info, flags, rpool)
+ SM_FILE_T *fp;
+ const void *info;
+ int flags;
+ const void *rpool;
+{
+ register FILE *s;
+ char *stdiomode;
+
+ switch (flags)
+ {
+ case SM_IO_RDONLY:
+ stdiomode = "r";
+ break;
+ case SM_IO_WRONLY:
+ stdiomode = "w";
+ break;
+ case SM_IO_APPEND:
+ stdiomode = "a";
+ break;
+ case SM_IO_APPENDRW:
+ stdiomode = "a+";
+ break;
+#if SM_IO_BINARY != 0
+ case SM_IO_RDONLY_B:
+ stdiomode = "rb";
+ break;
+ case SM_IO_WRONLY_B:
+ stdiomode = "wb";
+ break;
+ case SM_IO_APPEND_B:
+ stdiomode = "ab";
+ break;
+ case SM_IO_APPENDRW_B:
+ stdiomode = "a+b";
+ break;
+ case SM_IO_RDWR_B:
+ stdiomode = "r+b";
+ break;
+#endif /* SM_IO_BINARY != 0 */
+ case SM_IO_RDWR:
+ default:
+ stdiomode = "r+";
+ break;
+ }
+
+ if ((s = fopen((char *)info, stdiomode)) == NULL)
+ return -1;
+ fp->f_cookie = s;
+ return 0;
+}
+
+/*
+** SETUP -- assign file type cookie when not already assigned
+**
+** Parameters:
+** fp - the file pointer to get the cookie assigned
+**
+** Return:
+** none.
+*/
+
+static void
+setup(fp)
+ SM_FILE_T *fp;
+{
+ if (fp->f_cookie == NULL)
+ {
+ switch (fp->f_ival)
+ {
+ case 0:
+ fp->f_cookie = stdin;
+ break;
+ case 1:
+ fp->f_cookie = stdout;
+ break;
+ case 2:
+ fp->f_cookie = stderr;
+ break;
+ default:
+ sm_abort("fp->f_ival=%d: out of range (0...2)", fp->f_ival);
+ break;
+ }
+ }
+}
+
+/*
+** SM_STDIOREAD -- read from the file
+**
+** Parameters:
+** fp -- the file pointer
+** buf -- location to place the read data
+** n - number of bytes to read
+**
+** Returns:
+** result from fread().
+*/
+
+ssize_t
+sm_stdioread(fp, buf, n)
+ SM_FILE_T *fp;
+ char *buf;
+ size_t n;
+{
+ register FILE *s;
+
+ if (fp->f_cookie == NULL)
+ setup(fp);
+ s = fp->f_cookie;
+ return fread(buf, 1, n, s);
+}
+
+/*
+** SM_STDIOWRITE -- write to the file
+**
+** Parameters:
+** fp -- the file pointer
+** buf -- location of data to write
+** n - number of bytes to write
+**
+** Returns:
+** result from fwrite().
+*/
+
+ssize_t
+sm_stdiowrite(fp, buf, n)
+ SM_FILE_T *fp;
+ char const *buf;
+ size_t n;
+{
+ register FILE *s;
+
+ if (fp->f_cookie == NULL)
+ setup(fp);
+ s = fp->f_cookie;
+ return fwrite(buf, 1, n, s);
+}
+
+/*
+** SM_STDIOSEEK -- set position within file
+**
+** Parameters:
+** fp -- the file pointer
+** offset -- new location based on 'whence'
+** whence -- indicates "base" for 'offset'
+**
+** Returns:
+** result from fseek().
+*/
+
+off_t
+sm_stdioseek(fp, offset, whence)
+ SM_FILE_T *fp;
+ off_t offset;
+ int whence;
+{
+ register FILE *s;
+
+ if (fp->f_cookie == NULL)
+ setup(fp);
+ s = fp->f_cookie;
+ return fseek(s, offset, whence);
+}
+
+/*
+** SM_STDIOCLOSE -- close the file
+**
+** Parameters:
+** fp -- close file pointer
+**
+** Return:
+** status from fclose()
+*/
+
+int
+sm_stdioclose(fp)
+ SM_FILE_T *fp;
+{
+ register FILE *s;
+
+ if (fp->f_cookie == NULL)
+ setup(fp);
+ s = fp->f_cookie;
+ return fclose(s);
+}
+
+/*
+** SM_STDIOSETINFO -- set info for this open file
+**
+** Parameters:
+** fp -- the file pointer
+** what -- type of information setting
+** valp -- memory location of info to set
+**
+** Return:
+** Failure: -1 and sets errno
+** Success: none (currently).
+*/
+
+/* ARGSUSED2 */
+int
+sm_stdiosetinfo(fp, what, valp)
+ SM_FILE_T *fp;
+ int what;
+ void *valp;
+{
+ switch (what)
+ {
+ case SM_IO_WHAT_MODE:
+ default:
+ errno = EINVAL;
+ return -1;
+ }
+}
+
+/*
+** SM_STDIOGETINFO -- get info for this open file
+**
+** Parameters:
+** fp -- the file pointer
+** what -- type of information request
+** valp -- memory location to place info
+**
+** Return:
+** Failure: -1 and sets errno
+** Success: none (currently).
+*/
+
+/* ARGSUSED2 */
+int
+sm_stdiogetinfo(fp, what, valp)
+ SM_FILE_T *fp;
+ int what;
+ void *valp;
+{
+ switch (what)
+ {
+ case SM_IO_WHAT_SIZE:
+ {
+ int fd;
+ struct stat st;
+
+ if (fp->f_cookie == NULL)
+ setup(fp);
+ fd = fileno((FILE *) fp->f_cookie);
+ if (fd < 0)
+ return -1;
+ if (fstat(fd, &st) == 0)
+ return st.st_size;
+ else
+ return -1;
+ }
+
+ case SM_IO_WHAT_MODE:
+ default:
+ errno = EINVAL;
+ return -1;
+ }
+}
+
+/*
+** SM_IO_STDIOOPEN -- create an SM_FILE which interfaces to a stdio FILE
+**
+** Parameters:
+** stream -- an open stdio stream, as returned by fopen()
+** mode -- the mode argument to fopen() which describes stream
+**
+** Return:
+** On success, return a pointer to an SM_FILE object which
+** can be used for reading and writing 'stream'.
+** Abort if mode is gibberish or stream is bad.
+** Raise an exception if we can't allocate memory.
+*/
+
+SM_FILE_T *
+sm_io_stdioopen(stream, mode)
+ FILE *stream;
+ char *mode;
+{
+ int fd;
+ bool r, w;
+ int ioflags;
+ SM_FILE_T *fp;
+
+ fd = fileno(stream);
+ SM_REQUIRE(fd >= 0);
+
+ r = w = false;
+ switch (mode[0])
+ {
+ case 'r':
+ r = true;
+ break;
+ case 'w':
+ case 'a':
+ w = true;
+ break;
+ default:
+ sm_abort("sm_io_stdioopen: mode '%s' is bad", mode);
+ }
+ if (strchr(&mode[1], '+') != NULL)
+ r = w = true;
+ if (r && w)
+ ioflags = SMRW;
+ else if (r)
+ ioflags = SMRD;
+ else
+ ioflags = SMWR;
+
+ fp = sm_fp(SmFtRealStdio, ioflags, NULL);
+ fp->f_file = fd;
+ fp->f_cookie = stream;
+ return fp;
+}
diff --git a/usr/src/cmd/sendmail/libsm/snprintf.c b/usr/src/cmd/sendmail/libsm/snprintf.c
new file mode 100644
index 0000000000..c31f8cf227
--- /dev/null
+++ b/usr/src/cmd/sendmail/libsm/snprintf.c
@@ -0,0 +1,88 @@
+/*
+ * Copyright (c) 2000-2001 Sendmail, Inc. and its suppliers.
+ * All rights reserved.
+ * Copyright (c) 1990, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Chris Torek.
+ *
+ * By using this file, you agree to the terms and conditions set
+ * forth in the LICENSE file which can be found at the top level of
+ * the sendmail distribution.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#include <sm/gen.h>
+SM_RCSID("@(#)$Id: snprintf.c,v 1.21 2001/03/02 23:53:41 ca Exp $")
+#include <limits.h>
+#include <sm/varargs.h>
+#include <sm/io.h>
+#include "local.h"
+
+/*
+** SM_SNPRINTF -- format a string to a memory location of restricted size
+**
+** Parameters:
+** str -- memory location to place formatted string
+** n -- size of buffer pointed to by str, capped to
+** a maximum of INT_MAX
+** fmt -- the formatting directives
+** ... -- the data to satisfy the formatting
+**
+** Returns:
+** Failure: -1
+** Success: number of bytes that would have been written
+** to str, not including the trailing '\0',
+** up to a maximum of INT_MAX, as if there was
+** no buffer size limitation. If the result >= n
+** then the output was truncated.
+**
+** Side Effects:
+** If n > 0, then between 0 and n-1 bytes of formatted output
+** are written into 'str', followed by a '\0'.
+*/
+
+int
+#if SM_VA_STD
+sm_snprintf(char *str, size_t n, char const *fmt, ...)
+#else /* SM_VA_STD */
+sm_snprintf(str, n, fmt, va_alist)
+ char *str;
+ size_t n;
+ char *fmt;
+ va_dcl
+#endif /* SM_VA_STD */
+{
+ int ret;
+ SM_VA_LOCAL_DECL
+ SM_FILE_T fake;
+
+ /* While snprintf(3) specifies size_t stdio uses an int internally */
+ if (n > INT_MAX)
+ n = INT_MAX;
+ SM_VA_START(ap, fmt);
+
+ /* XXX put this into a static? */
+ fake.sm_magic = SmFileMagic;
+ fake.f_file = -1;
+ fake.f_flags = SMWR | SMSTR;
+ fake.f_cookie = &fake;
+ fake.f_bf.smb_base = fake.f_p = (unsigned char *)str;
+ fake.f_bf.smb_size = fake.f_w = n ? n - 1 : 0;
+ fake.f_timeout = SM_TIME_FOREVER;
+ fake.f_timeoutstate = SM_TIME_BLOCK;
+ fake.f_close = NULL;
+ fake.f_open = NULL;
+ fake.f_read = NULL;
+ fake.f_write = NULL;
+ fake.f_seek = NULL;
+ fake.f_setinfo = fake.f_getinfo = NULL;
+ fake.f_type = "sm_snprintf:fake";
+ ret = sm_io_vfprintf(&fake, SM_TIME_FOREVER, fmt, ap);
+ if (n > 0)
+ *fake.f_p = '\0';
+ SM_VA_END(ap);
+ return ret;
+}
diff --git a/usr/src/cmd/sendmail/libsm/sscanf.c b/usr/src/cmd/sendmail/libsm/sscanf.c
new file mode 100644
index 0000000000..9be1e58869
--- /dev/null
+++ b/usr/src/cmd/sendmail/libsm/sscanf.c
@@ -0,0 +1,104 @@
+/*
+ * Copyright (c) 2000-2001 Sendmail, Inc. and its suppliers.
+ * All rights reserved.
+ * Copyright (c) 1990, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Chris Torek.
+ *
+ * By using this file, you agree to the terms and conditions set
+ * forth in the LICENSE file which can be found at the top level of
+ * the sendmail distribution.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#include <sm/gen.h>
+SM_RCSID("@(#)$Id: sscanf.c,v 1.25 2002/02/01 02:28:00 ca Exp $")
+#include <string.h>
+#include <sm/varargs.h>
+#include <sm/io.h>
+#include "local.h"
+
+/*
+** SM_EOFREAD -- dummy read function for faked file below
+**
+** Parameters:
+** fp -- file pointer
+** buf -- location to place read data
+** len -- number of bytes to read
+**
+** Returns:
+** 0 (zero) always
+*/
+
+static ssize_t
+sm_eofread __P((
+ SM_FILE_T *fp,
+ char *buf,
+ size_t len));
+
+/* ARGSUSED0 */
+static ssize_t
+sm_eofread(fp, buf, len)
+ SM_FILE_T *fp;
+ char *buf;
+ size_t len;
+{
+ return 0;
+}
+
+/*
+** SM_IO_SSCANF -- scan a string to find data units
+**
+** Parameters:
+** str -- strings containing data
+** fmt -- format directive for finding data units
+** ... -- memory locations to place format found data units
+**
+** Returns:
+** Failure: SM_IO_EOF
+** Success: number of data units found
+**
+** Side Effects:
+** Attempts to strlen() 'str'; if not a '\0' terminated string
+** then the call may SEGV/fail.
+** Faking the string 'str' as a file.
+*/
+
+int
+#if SM_VA_STD
+sm_io_sscanf(const char *str, char const *fmt, ...)
+#else /* SM_VA_STD */
+sm_io_sscanf(str, fmt, va_alist)
+ const char *str;
+ char *fmt;
+ va_dcl
+#endif /* SM_VA_STD */
+{
+ int ret;
+ SM_FILE_T fake;
+ SM_VA_LOCAL_DECL
+
+ fake.sm_magic = SmFileMagic;
+ fake.f_flags = SMRD;
+ fake.f_bf.smb_base = fake.f_p = (unsigned char *) str;
+ fake.f_bf.smb_size = fake.f_r = strlen(str);
+ fake.f_file = -1;
+ fake.f_read = sm_eofread;
+ fake.f_write = NULL;
+ fake.f_close = NULL;
+ fake.f_open = NULL;
+ fake.f_seek = NULL;
+ fake.f_setinfo = fake.f_getinfo = NULL;
+ fake.f_type = "sm_io_sscanf:fake";
+ fake.f_flushfp = NULL;
+ fake.f_ub.smb_base = NULL;
+ fake.f_timeout = SM_TIME_FOREVER;
+ fake.f_timeoutstate = SM_TIME_BLOCK;
+ SM_VA_START(ap, fmt);
+ ret = sm_vfscanf(&fake, SM_TIME_FOREVER, fmt, ap);
+ SM_VA_END(ap);
+ return ret;
+}
diff --git a/usr/src/cmd/sendmail/libsm/stdio.c b/usr/src/cmd/sendmail/libsm/stdio.c
new file mode 100644
index 0000000000..84d538733f
--- /dev/null
+++ b/usr/src/cmd/sendmail/libsm/stdio.c
@@ -0,0 +1,521 @@
+/*
+ * Copyright (c) 2000-2004 Sendmail, Inc. and its suppliers.
+ * All rights reserved.
+ * Copyright (c) 1990, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Chris Torek.
+ *
+ * By using this file, you agree to the terms and conditions set
+ * forth in the LICENSE file which can be found at the top level of
+ * the sendmail distribution.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#include <sm/gen.h>
+SM_RCSID("@(#)$Id: stdio.c,v 1.69 2004/08/03 20:46:34 ca Exp $")
+#include <unistd.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <string.h> /* FreeBSD: FD_ZERO needs <string.h> */
+#include <sys/stat.h>
+#include <sys/time.h>
+#include <sm/heap.h>
+#include <sm/assert.h>
+#include <sm/varargs.h>
+#include <sm/io.h>
+#include <sm/setjmp.h>
+#include <sm/conf.h>
+#include <sm/fdset.h>
+#include "local.h"
+
+static int sm_stdsetmode __P((SM_FILE_T *, const int *));
+static int sm_stdgetmode __P((SM_FILE_T *, int *));
+
+/*
+** Overall:
+** Small standard I/O/seek/close functions.
+** These maintain the `known seek offset' for seek optimization.
+*/
+
+/*
+** SM_STDOPEN -- open a file with stdio behavior
+**
+** Not associated with the system's stdio in libc.
+**
+** Parameters:
+** fp -- file pointer to be associated with the open
+** info -- pathname of the file to be opened
+** flags -- indicates type of access methods
+** rpool -- ignored
+**
+** Returns:
+** Failure: -1 and set errno
+** Success: 0 or greater (fd of file from open(2)).
+**
+*/
+
+/* ARGSUSED3 */
+int
+sm_stdopen(fp, info, flags, rpool)
+ SM_FILE_T *fp;
+ const void *info;
+ int flags;
+ const void *rpool;
+{
+ char *path = (char *) info;
+ int oflags;
+
+ switch (SM_IO_MODE(flags))
+ {
+ case SM_IO_RDWR:
+ oflags = O_RDWR;
+ break;
+ case SM_IO_RDWRTR:
+ oflags = O_RDWR | O_CREAT | O_TRUNC;
+ break;
+ case SM_IO_RDONLY:
+ oflags = O_RDONLY;
+ break;
+ case SM_IO_WRONLY:
+ oflags = O_WRONLY | O_CREAT | O_TRUNC;
+ break;
+ case SM_IO_APPEND:
+ oflags = O_APPEND | O_WRONLY | O_CREAT;
+ break;
+ case SM_IO_APPENDRW:
+ oflags = O_APPEND | O_RDWR | O_CREAT;
+ break;
+ default:
+ errno = EINVAL;
+ return -1;
+ }
+#ifdef O_BINARY
+ if (SM_IS_BINARY(flags))
+ oflags |= O_BINARY;
+#endif /* O_BINARY */
+ fp->f_file = open(path, oflags,
+ S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP|S_IROTH|S_IWOTH);
+ if (fp->f_file < 0)
+ return -1; /* errno set by open() */
+
+ if (oflags & O_APPEND)
+ (void) (*fp->f_seek)((void *)fp, (off_t)0, SEEK_END);
+
+ return fp->f_file;
+}
+
+/*
+** SM_STDREAD -- read from the file
+**
+** Parameters:
+** fp -- file pointer to read from
+** buf -- location to place read data
+** n -- number of bytes to read
+**
+** Returns:
+** Failure: -1 and sets errno
+** Success: number of bytes read
+**
+** Side Effects:
+** Updates internal offset into file.
+*/
+
+ssize_t
+sm_stdread(fp, buf, n)
+ SM_FILE_T *fp;
+ char *buf;
+ size_t n;
+{
+ register int ret;
+
+ ret = read(fp->f_file, buf, n);
+
+ /* if the read succeeded, update the current offset */
+ if (ret > 0)
+ fp->f_lseekoff += ret;
+ return ret;
+}
+
+/*
+** SM_STDWRITE -- write to the file
+**
+** Parameters:
+** fp -- file pointer ro write to
+** buf -- location of data to be written
+** n - number of bytes to write
+**
+** Returns:
+** Failure: -1 and sets errno
+** Success: number of bytes written
+*/
+
+ssize_t
+sm_stdwrite(fp, buf, n)
+ SM_FILE_T *fp;
+ char const *buf;
+ size_t n;
+{
+ return write(fp->f_file, buf, n);
+}
+
+/*
+** SM_STDSEEK -- set the file offset position
+**
+** Parmeters:
+** fp -- file pointer to position
+** offset -- how far to position from "base" (set by 'whence')
+** whence -- indicates where the "base" of the 'offset' to start
+**
+** Results:
+** Failure: -1 and sets errno
+** Success: the current offset
+**
+** Side Effects:
+** Updates the internal value of the offset.
+*/
+
+off_t
+sm_stdseek(fp, offset, whence)
+ SM_FILE_T *fp;
+ off_t offset;
+ int whence;
+{
+ register off_t ret;
+
+ ret = lseek(fp->f_file, (off_t) offset, whence);
+ if (ret != (off_t) -1)
+ fp->f_lseekoff = ret;
+ return ret;
+}
+
+/*
+** SM_STDCLOSE -- close the file
+**
+** Parameters:
+** fp -- the file pointer to close
+**
+** Returns:
+** Success: 0 (zero)
+** Failure: -1 and sets errno
+*/
+
+int
+sm_stdclose(fp)
+ SM_FILE_T *fp;
+{
+ return close(fp->f_file);
+}
+
+/*
+** SM_STDSETMODE -- set the access mode for the file
+**
+** Called by sm_stdsetinfo().
+**
+** Parameters:
+** fp -- file pointer
+** mode -- new mode to set the file access to
+**
+** Results:
+** Success: 0 (zero);
+** Failure: -1 and sets errno
+*/
+
+int
+sm_stdsetmode(fp, mode)
+ SM_FILE_T *fp;
+ const int *mode;
+{
+ int flags = 0;
+
+ switch (SM_IO_MODE(*mode))
+ {
+ case SM_IO_RDWR:
+ flags |= SMRW;
+ break;
+ case SM_IO_RDONLY:
+ flags |= SMRD;
+ break;
+ case SM_IO_WRONLY:
+ flags |= SMWR;
+ break;
+ case SM_IO_APPEND:
+ default:
+ errno = EINVAL;
+ return -1;
+ }
+ fp->f_flags = fp->f_flags & ~SMMODEMASK;
+ fp->f_flags |= flags;
+ return 0;
+}
+
+/*
+** SM_STDGETMODE -- for getinfo determine open mode
+**
+** Called by sm_stdgetinfo().
+**
+** Parameters:
+** fp -- the file mode being determined
+** mode -- internal mode to map to external value
+**
+** Results:
+** Failure: -1 and sets errno
+** Success: external mode value
+*/
+
+static int
+sm_stdgetmode(fp, mode)
+ SM_FILE_T *fp;
+ int *mode;
+{
+ switch (fp->f_flags & SMMODEMASK)
+ {
+ case SMRW:
+ *mode = SM_IO_RDWR;
+ break;
+ case SMRD:
+ *mode = SM_IO_RDONLY;
+ break;
+ case SMWR:
+ *mode = SM_IO_WRONLY;
+ break;
+ default:
+ errno = EINVAL;
+ return -1;
+ }
+ return 0;
+}
+
+/*
+** SM_STDSETINFO -- set/modify information for a file
+**
+** Parameters:
+** fp -- file to set info for
+** what -- type of info to set
+** valp -- location of data used for setting
+**
+** Returns:
+** Failure: -1 and sets errno
+** Success: >=0
+*/
+
+int
+sm_stdsetinfo(fp, what, valp)
+ SM_FILE_T *fp;
+ int what;
+ void *valp;
+{
+ switch (what)
+ {
+ case SM_IO_WHAT_MODE:
+ return sm_stdsetmode(fp, (const int *)valp);
+
+ default:
+ errno = EINVAL;
+ return -1;
+ }
+}
+
+/*
+** SM_GETINFO -- get information about the open file
+**
+** Parameters:
+** fp -- file to get info for
+** what -- type of info to get
+** valp -- location to place found info
+**
+** Returns:
+** Success: may or may not place info in 'valp' depending
+** on 'what' value, and returns values >=0. Return
+** value may be the obtained info
+** Failure: -1 and sets errno
+*/
+
+int
+sm_stdgetinfo(fp, what, valp)
+ SM_FILE_T *fp;
+ int what;
+ void *valp;
+{
+ switch (what)
+ {
+ case SM_IO_WHAT_MODE:
+ return sm_stdgetmode(fp, (int *)valp);
+
+ case SM_IO_WHAT_FD:
+ return fp->f_file;
+
+ case SM_IO_WHAT_SIZE:
+ {
+ struct stat st;
+
+ if (fstat(fp->f_file, &st) == 0)
+ return st.st_size;
+ else
+ return -1;
+ }
+
+ case SM_IO_IS_READABLE:
+ {
+ fd_set readfds;
+ struct timeval timeout;
+
+ if (SM_FD_SETSIZE > 0 && fp->f_file >= SM_FD_SETSIZE)
+ {
+ errno = EINVAL;
+ return -1;
+ }
+ FD_ZERO(&readfds);
+ SM_FD_SET(fp->f_file, &readfds);
+ timeout.tv_sec = 0;
+ timeout.tv_usec = 0;
+ if (select(fp->f_file + 1, FDSET_CAST &readfds,
+ NULL, NULL, &timeout) > 0 &&
+ SM_FD_ISSET(fp->f_file, &readfds))
+ return 1;
+ return 0;
+ }
+
+ default:
+ errno = EINVAL;
+ return -1;
+ }
+}
+
+/*
+** SM_STDFDOPEN -- open file by primitive 'fd' rather than pathname
+**
+** I/O function to handle fdopen() stdio equivalence. The rest of
+** the functions are the same as the sm_stdopen() above.
+**
+** Parameters:
+** fp -- the file pointer to be associated with the open
+** name -- the primitive file descriptor for association
+** flags -- indicates type of access methods
+** rpool -- ignored
+**
+** Results:
+** Success: primitive file descriptor value
+** Failure: -1 and sets errno
+*/
+
+/* ARGSUSED3 */
+int
+sm_stdfdopen(fp, info, flags, rpool)
+ SM_FILE_T *fp;
+ const void *info;
+ int flags;
+ const void *rpool;
+{
+ int oflags, tmp, fdflags, fd = *((int *) info);
+
+ switch (SM_IO_MODE(flags))
+ {
+ case SM_IO_RDWR:
+ oflags = O_RDWR | O_CREAT;
+ break;
+ case SM_IO_RDONLY:
+ oflags = O_RDONLY;
+ break;
+ case SM_IO_WRONLY:
+ oflags = O_WRONLY | O_CREAT | O_TRUNC;
+ break;
+ case SM_IO_APPEND:
+ oflags = O_APPEND | O_WRONLY | O_CREAT;
+ break;
+ case SM_IO_APPENDRW:
+ oflags = O_APPEND | O_RDWR | O_CREAT;
+ break;
+ default:
+ errno = EINVAL;
+ return -1;
+ }
+#ifdef O_BINARY
+ if (SM_IS_BINARY(flags))
+ oflags |= O_BINARY;
+#endif /* O_BINARY */
+
+ /* Make sure the mode the user wants is a subset of the actual mode. */
+ if ((fdflags = fcntl(fd, F_GETFL, 0)) < 0)
+ return -1;
+ tmp = fdflags & O_ACCMODE;
+ if (tmp != O_RDWR && (tmp != (oflags & O_ACCMODE)))
+ {
+ errno = EINVAL;
+ return -1;
+ }
+ fp->f_file = fd;
+ if (oflags & O_APPEND)
+ (void) (*fp->f_seek)(fp, (off_t)0, SEEK_END);
+ return fp->f_file;
+}
+
+/*
+** SM_IO_FOPEN -- open a file
+**
+** Same interface and semantics as the open() system call,
+** except that it returns SM_FILE_T* instead of a file descriptor.
+**
+** Parameters:
+** pathname -- path of file to open
+** flags -- flags controlling the open
+** ... -- option "mode" for opening the file
+**
+** Returns:
+** Raises an exception on heap exhaustion.
+** Returns NULL and sets errno if open() fails.
+** Returns an SM_FILE_T pointer on success.
+*/
+
+SM_FILE_T *
+#if SM_VA_STD
+sm_io_fopen(char *pathname, int flags, ...)
+#else /* SM_VA_STD */
+sm_io_fopen(pathname, flags, va_alist)
+ char *pathname;
+ int flags;
+ va_dcl
+#endif /* SM_VA_STD */
+{
+ MODE_T mode;
+ SM_FILE_T *fp;
+ int ioflags;
+
+ if (flags & O_CREAT)
+ {
+ SM_VA_LOCAL_DECL
+
+ SM_VA_START(ap, flags);
+ mode = (MODE_T) SM_VA_ARG(ap, int);
+ SM_VA_END(ap);
+ }
+ else
+ mode = 0;
+
+ switch (flags & O_ACCMODE)
+ {
+ case O_RDONLY:
+ ioflags = SMRD;
+ break;
+ case O_WRONLY:
+ ioflags = SMWR;
+ break;
+ case O_RDWR:
+ ioflags = SMRW;
+ break;
+ default:
+ sm_abort("sm_io_fopen: bad flags 0%o", flags);
+ }
+
+ fp = sm_fp(SmFtStdio, ioflags, NULL);
+ fp->f_file = open(pathname, flags, mode);
+ if (fp->f_file == -1)
+ {
+ fp->f_flags = 0;
+ fp->sm_magic = NULL;
+ return NULL;
+ }
+ return fp;
+}
diff --git a/usr/src/cmd/sendmail/libsm/strcasecmp.c b/usr/src/cmd/sendmail/libsm/strcasecmp.c
new file mode 100644
index 0000000000..e2a7b798e8
--- /dev/null
+++ b/usr/src/cmd/sendmail/libsm/strcasecmp.c
@@ -0,0 +1,108 @@
+/*
+ * Copyright (c) 2000-2001 Sendmail, Inc. and its suppliers.
+ * All rights reserved.
+ * Copyright (c) 1983, 1995-1997 Eric P. Allman. All rights reserved.
+ * Copyright (c) 1987, 1988, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * By using this file, you agree to the terms and conditions set
+ * forth in the LICENSE file which can be found at the top level of
+ * the sendmail distribution.
+ *
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#include <sm/gen.h>
+SM_RCSID("@(#)$Id: strcasecmp.c,v 1.12 2001/08/27 22:09:15 gshapiro Exp $")
+
+#include <sm/config.h>
+#include <sm/string.h>
+#include <string.h>
+
+ /*
+** SM_STRCASECMP -- 8-bit clean version of strcasecmp
+**
+** Thank you, vendors, for making this all necessary.
+*/
+
+/*
+** This array is designed for mapping upper and lower case letter
+** together for a case independent comparison. The mappings are
+** based upon ascii character sequences.
+*/
+
+const unsigned char charmap[] =
+{
+ 0000, 0001, 0002, 0003, 0004, 0005, 0006, 0007,
+ 0010, 0011, 0012, 0013, 0014, 0015, 0016, 0017,
+ 0020, 0021, 0022, 0023, 0024, 0025, 0026, 0027,
+ 0030, 0031, 0032, 0033, 0034, 0035, 0036, 0037,
+ 0040, 0041, 0042, 0043, 0044, 0045, 0046, 0047,
+ 0050, 0051, 0052, 0053, 0054, 0055, 0056, 0057,
+ 0060, 0061, 0062, 0063, 0064, 0065, 0066, 0067,
+ 0070, 0071, 0072, 0073, 0074, 0075, 0076, 0077,
+ 0100, 0141, 0142, 0143, 0144, 0145, 0146, 0147,
+ 0150, 0151, 0152, 0153, 0154, 0155, 0156, 0157,
+ 0160, 0161, 0162, 0163, 0164, 0165, 0166, 0167,
+ 0170, 0171, 0172, 0133, 0134, 0135, 0136, 0137,
+ 0140, 0141, 0142, 0143, 0144, 0145, 0146, 0147,
+ 0150, 0151, 0152, 0153, 0154, 0155, 0156, 0157,
+ 0160, 0161, 0162, 0163, 0164, 0165, 0166, 0167,
+ 0170, 0171, 0172, 0173, 0174, 0175, 0176, 0177,
+ 0200, 0201, 0202, 0203, 0204, 0205, 0206, 0207,
+ 0210, 0211, 0212, 0213, 0214, 0215, 0216, 0217,
+ 0220, 0221, 0222, 0223, 0224, 0225, 0226, 0227,
+ 0230, 0231, 0232, 0233, 0234, 0235, 0236, 0237,
+ 0240, 0241, 0242, 0243, 0244, 0245, 0246, 0247,
+ 0250, 0251, 0252, 0253, 0254, 0255, 0256, 0257,
+ 0260, 0261, 0262, 0263, 0264, 0265, 0266, 0267,
+ 0270, 0271, 0272, 0273, 0274, 0275, 0276, 0277,
+ 0300, 0301, 0302, 0303, 0304, 0305, 0306, 0307,
+ 0310, 0311, 0312, 0313, 0314, 0315, 0316, 0317,
+ 0320, 0321, 0322, 0323, 0324, 0325, 0326, 0327,
+ 0330, 0331, 0332, 0333, 0334, 0335, 0336, 0337,
+ 0340, 0341, 0342, 0343, 0344, 0345, 0346, 0347,
+ 0350, 0351, 0352, 0353, 0354, 0355, 0356, 0357,
+ 0360, 0361, 0362, 0363, 0364, 0365, 0366, 0367,
+ 0370, 0371, 0372, 0373, 0374, 0375, 0376, 0377,
+};
+
+int
+sm_strcasecmp(s1, s2)
+ const char *s1, *s2;
+{
+ const unsigned char *us1 = (const unsigned char *)s1;
+ const unsigned char *us2 = (const unsigned char *)s2;
+
+ while (charmap[*us1] == charmap[*us2])
+ {
+ if (*us1 == '\0')
+ return 0;
+ ++us1;
+ ++us2;
+ }
+ return charmap[*us1] - charmap[*us2];
+}
+
+int
+sm_strncasecmp(s1, s2, n)
+ const char *s1, *s2;
+ register size_t n;
+{
+ if (n != 0)
+ {
+ register const unsigned char *cm = charmap;
+ register const unsigned char *us1 = (const unsigned char *)s1;
+ register const unsigned char *us2 = (const unsigned char *)s2;
+
+ do
+ {
+ if (cm[*us1] != cm[*us2++])
+ return (cm[*us1] - cm[*--us2]);
+ if (*us1++ == '\0')
+ break;
+ } while (--n != 0);
+ }
+ return 0;
+}
diff --git a/usr/src/cmd/sendmail/libsm/strdup.c b/usr/src/cmd/sendmail/libsm/strdup.c
new file mode 100644
index 0000000000..13425ddc70
--- /dev/null
+++ b/usr/src/cmd/sendmail/libsm/strdup.c
@@ -0,0 +1,168 @@
+/*
+ * Copyright (c) 2000-2001, 2003 Sendmail, Inc. and its suppliers.
+ * All rights reserved.
+ *
+ * By using this file, you agree to the terms and conditions set
+ * forth in the LICENSE file which can be found at the top level of
+ * the sendmail distribution.
+ *
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#include <sm/gen.h>
+SM_RCSID("@(#)$Id: strdup.c,v 1.15 2003/10/10 17:56:57 ca Exp $")
+
+#include <sm/heap.h>
+#include <sm/string.h>
+
+/*
+** SM_STRNDUP_X -- Duplicate a string of a given length
+**
+** Allocates memory and copies source string (of given length) into it.
+**
+** Parameters:
+** s -- string to copy.
+** n -- length to copy.
+**
+** Returns:
+** copy of string, raises exception if out of memory.
+**
+** Side Effects:
+** allocate memory for new string.
+*/
+
+char *
+sm_strndup_x(s, n)
+ const char *s;
+ size_t n;
+{
+ char *d = sm_malloc_x(n + 1);
+
+ (void) memcpy(d, s, n);
+ d[n] = '\0';
+ return d;
+}
+
+/*
+** SM_STRDUP -- Duplicate a string
+**
+** Allocates memory and copies source string into it.
+**
+** Parameters:
+** s -- string to copy.
+**
+** Returns:
+** copy of string, NULL if out of memory.
+**
+** Side Effects:
+** allocate memory for new string.
+*/
+
+char *
+sm_strdup(s)
+ char *s;
+{
+ size_t l;
+ char *d;
+
+ l = strlen(s) + 1;
+ d = sm_malloc_tagged(l, "sm_strdup", 0, sm_heap_group());
+ if (d != NULL)
+ (void) sm_strlcpy(d, s, l);
+ return d;
+}
+
+#if DO_NOT_USE_STRCPY
+
+/*
+** SM_STRDUP_X -- Duplicate a string
+**
+** Allocates memory and copies source string into it.
+**
+** Parameters:
+** s -- string to copy.
+**
+** Returns:
+** copy of string, exception if out of memory.
+**
+** Side Effects:
+** allocate memory for new string.
+*/
+
+char *
+sm_strdup_x(s)
+ const char *s;
+{
+ size_t l;
+ char *d;
+
+ l = strlen(s) + 1;
+ d = sm_malloc_tagged_x(l, "sm_strdup_x", 0, sm_heap_group());
+ (void) sm_strlcpy(d, s, l);
+ return d;
+}
+
+/*
+** SM_PSTRDUP_X -- Duplicate a string (using "permanent" memory)
+**
+** Allocates memory and copies source string into it.
+**
+** Parameters:
+** s -- string to copy.
+**
+** Returns:
+** copy of string, exception if out of memory.
+**
+** Side Effects:
+** allocate memory for new string.
+*/
+
+char *
+sm_pstrdup_x(s)
+ const char *s;
+{
+ size_t l;
+ char *d;
+
+ l = strlen(s) + 1;
+ d = sm_pmalloc_x(l);
+ (void) sm_strlcpy(d, s, l);
+ return d;
+}
+
+/*
+** SM_STRDUP_X -- Duplicate a string
+**
+** Allocates memory and copies source string into it.
+**
+** Parameters:
+** s -- string to copy.
+** file -- name of source file
+** line -- line in source file
+** group -- heap group
+**
+** Returns:
+** copy of string, exception if out of memory.
+**
+** Side Effects:
+** allocate memory for new string.
+*/
+
+char *
+sm_strdup_tagged_x(s, file, line, group)
+ const char *s;
+ char *file;
+ int line, group;
+{
+ size_t l;
+ char *d;
+
+ l = strlen(s) + 1;
+ d = sm_malloc_tagged_x(l, file, line, group);
+ (void) sm_strlcpy(d, s, l);
+ return d;
+}
+
+#endif /* DO_NOT_USE_STRCPY */
+
diff --git a/usr/src/cmd/sendmail/libsm/strerror.c b/usr/src/cmd/sendmail/libsm/strerror.c
new file mode 100644
index 0000000000..f58f6626b2
--- /dev/null
+++ b/usr/src/cmd/sendmail/libsm/strerror.c
@@ -0,0 +1,62 @@
+/*
+ * Copyright (c) 2000-2001 Sendmail, Inc. and its suppliers.
+ * All rights reserved.
+ * Copyright (c) 1983, 1995-1997 Eric P. Allman. All rights reserved.
+ * Copyright (c) 1988, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * By using this file, you agree to the terms and conditions set
+ * forth in the LICENSE file which can be found at the top level of
+ * the sendmail distribution.
+ *
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#include <sm/gen.h>
+SM_RCSID("@(#)$Id: strerror.c,v 1.21 2001/06/17 21:31:41 ca Exp $")
+
+/*
+** define strerror for platforms that lack it.
+*/
+
+#include <errno.h>
+#include <stdio.h> /* sys_errlist, on some platforms */
+
+#include <sm/io.h> /* sm_snprintf */
+#include <sm/string.h>
+#include <sm/conf.h>
+#include <sm/errstring.h>
+
+#if !defined(ERRLIST_PREDEFINED)
+extern char *sys_errlist[];
+extern int sys_nerr;
+#endif /* !defined(ERRLIST_PREDEFINED) */
+
+#if !HASSTRERROR
+
+/*
+** STRERROR -- return error message string corresponding to an error number.
+**
+** Parameters:
+** err -- error number.
+**
+** Returns:
+** Error string (might be pointer to static buffer).
+*/
+
+char *
+strerror(err)
+ int err;
+{
+ static char buf[64];
+
+ if (err >= 0 && err < sys_nerr)
+ return (char *) sys_errlist[err];
+ else
+ {
+ (void) sm_snprintf(buf, sizeof(buf), "Error %d", err);
+ return buf;
+ }
+}
+#endif /* !HASSTRERROR */
diff --git a/usr/src/cmd/sendmail/libsm/strexit.c b/usr/src/cmd/sendmail/libsm/strexit.c
new file mode 100644
index 0000000000..4c2b5be0ef
--- /dev/null
+++ b/usr/src/cmd/sendmail/libsm/strexit.c
@@ -0,0 +1,129 @@
+/*
+ * Copyright (c) 2001 Sendmail, Inc. and its suppliers.
+ * All rights reserved.
+ *
+ * By using this file, you agree to the terms and conditions set
+ * forth in the LICENSE file which can be found at the top level of
+ * the sendmail distribution.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#include <sm/gen.h>
+SM_RCSID("@(#)$Id: strexit.c,v 1.3 2001/01/15 18:39:11 ca Exp $")
+#include <sm/string.h>
+#include <sm/sysexits.h>
+
+/*
+** SM_STREXIT -- convert EX_* value from <sm/sysexits.h> to a character string
+**
+** This function is analogous to strerror(), except that it
+** operates on EX_* values from <sm/sysexits.h>.
+**
+** Parameters:
+** ex -- EX_* value
+**
+** Results:
+** pointer to a static message string
+*/
+
+char *
+sm_strexit(ex)
+ int ex;
+{
+ char *msg;
+ static char buf[64];
+
+ msg = sm_sysexitmsg(ex);
+ if (msg == NULL)
+ {
+ (void) sm_snprintf(buf, sizeof buf, "Unknown exit status %d",
+ ex);
+ msg = buf;
+ }
+ return msg;
+}
+
+/*
+** SM_SYSEXITMSG -- convert an EX_* value to a character string, or NULL
+**
+** Parameters:
+** ex -- EX_* value
+**
+** Results:
+** If ex is a known exit value, then a pointer to a static
+** message string is returned. Otherwise NULL is returned.
+*/
+
+char *
+sm_sysexitmsg(ex)
+ int ex;
+{
+ char *msg;
+
+ msg = sm_sysexmsg(ex);
+ if (msg != NULL)
+ return &msg[11];
+ else
+ return msg;
+}
+
+/*
+** SM_SYSEXMSG -- convert an EX_* value to a character string, or NULL
+**
+** Parameters:
+** ex -- EX_* value
+**
+** Results:
+** If ex is a known exit value, then a pointer to a static
+** string is returned. Otherwise NULL is returned.
+** The string contains the following fixed width fields:
+** [0] ':' if there is an errno value associated with this
+** exit value, otherwise ' '.
+** [1,3] 3 digit SMTP error code
+** [4] ' '
+** [5,9] 3 digit SMTP extended error code
+** [10] ' '
+** [11,] message string
+*/
+
+char *
+sm_sysexmsg(ex)
+ int ex;
+{
+ switch (ex)
+ {
+ case EX_USAGE:
+ return " 500 5.0.0 Command line usage error";
+ case EX_DATAERR:
+ return " 501 5.6.0 Data format error";
+ case EX_NOINPUT:
+ return ":550 5.3.0 Cannot open input";
+ case EX_NOUSER:
+ return " 550 5.1.1 User unknown";
+ case EX_NOHOST:
+ return " 550 5.1.2 Host unknown";
+ case EX_UNAVAILABLE:
+ return " 554 5.0.0 Service unavailable";
+ case EX_SOFTWARE:
+ return ":554 5.3.0 Internal error";
+ case EX_OSERR:
+ return ":451 4.0.0 Operating system error";
+ case EX_OSFILE:
+ return ":554 5.3.5 System file missing";
+ case EX_CANTCREAT:
+ return ":550 5.0.0 Can't create output";
+ case EX_IOERR:
+ return ":451 4.0.0 I/O error";
+ case EX_TEMPFAIL:
+ return " 450 4.0.0 Deferred";
+ case EX_PROTOCOL:
+ return " 554 5.5.0 Remote protocol error";
+ case EX_NOPERM:
+ return ":550 5.0.0 Insufficient permission";
+ case EX_CONFIG:
+ return " 554 5.3.5 Local configuration error";
+ default:
+ return NULL;
+ }
+}
diff --git a/usr/src/cmd/sendmail/libsm/string.c b/usr/src/cmd/sendmail/libsm/string.c
new file mode 100644
index 0000000000..9e7715b89c
--- /dev/null
+++ b/usr/src/cmd/sendmail/libsm/string.c
@@ -0,0 +1,58 @@
+/*
+ * Copyright (c) 2001 Sendmail, Inc. and its suppliers.
+ * All rights reserved.
+ *
+ * By using this file, you agree to the terms and conditions set
+ * forth in the LICENSE file which can be found at the top level of
+ * the sendmail distribution.
+ *
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#include <sm/gen.h>
+SM_RCSID("@(#)$Id: string.c,v 1.1 2001/02/15 21:04:50 ca Exp $")
+
+#include <ctype.h>
+#include <errno.h>
+
+#include <sm/string.h>
+
+/*
+** STRIPQUOTES -- Strip quotes & quote bits from a string.
+**
+** Runs through a string and strips off unquoted quote
+** characters and quote bits. This is done in place.
+**
+** Parameters:
+** s -- the string to strip.
+**
+** Returns:
+** none.
+**
+** Side Effects:
+** none.
+*/
+
+void
+stripquotes(s)
+ char *s;
+{
+ register char *p;
+ register char *q;
+ register char c;
+
+ if (s == NULL)
+ return;
+
+ p = q = s;
+ do
+ {
+ c = *p++;
+ if (c == '\\')
+ c = *p++;
+ else if (c == '"')
+ continue;
+ *q++ = c;
+ } while (c != '\0');
+}
diff --git a/usr/src/cmd/sendmail/libsm/stringf.c b/usr/src/cmd/sendmail/libsm/stringf.c
new file mode 100644
index 0000000000..b839f2cef1
--- /dev/null
+++ b/usr/src/cmd/sendmail/libsm/stringf.c
@@ -0,0 +1,88 @@
+/*
+ * Copyright (c) 2000-2001 Sendmail, Inc. and its suppliers.
+ * All rights reserved.
+ *
+ * By using this file, you agree to the terms and conditions set
+ * forth in the LICENSE file which can be found at the top level of
+ * the sendmail distribution.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#include <sm/gen.h>
+SM_RCSID("@(#)$Id: stringf.c,v 1.13 2001/03/03 03:40:43 ca Exp $")
+#include <errno.h>
+#include <stdio.h>
+#include <sm/exc.h>
+#include <sm/heap.h>
+#include <sm/string.h>
+#include <sm/varargs.h>
+
+/*
+** SM_STRINGF_X -- printf() to dynamically allocated string.
+**
+** Takes the same arguments as printf.
+** It returns a pointer to a dynamically allocated string
+** containing the text that printf would print to standard output.
+** It raises an exception on error.
+** The name comes from a PWB Unix function called stringf.
+**
+** Parameters:
+** fmt -- format string.
+** ... -- arguments for format.
+**
+** Returns:
+** Pointer to a dynamically allocated string.
+**
+** Exceptions:
+** F:sm_heap -- out of memory (via sm_vstringf_x()).
+*/
+
+char *
+#if SM_VA_STD
+sm_stringf_x(const char *fmt, ...)
+#else /* SM_VA_STD */
+sm_stringf_x(fmt, va_alist)
+ const char *fmt;
+ va_dcl
+#endif /* SM_VA_STD */
+{
+ SM_VA_LOCAL_DECL
+ char *s;
+
+ SM_VA_START(ap, fmt);
+ s = sm_vstringf_x(fmt, ap);
+ SM_VA_END(ap);
+ return s;
+}
+
+/*
+** SM_VSTRINGF_X -- printf() to dynamically allocated string.
+**
+** Parameters:
+** fmt -- format string.
+** ap -- arguments for format.
+**
+** Returns:
+** Pointer to a dynamically allocated string.
+**
+** Exceptions:
+** F:sm_heap -- out of memory
+*/
+
+char *
+sm_vstringf_x(fmt, ap)
+ const char *fmt;
+ SM_VA_LOCAL_DECL
+{
+ char *s;
+
+ sm_vasprintf(&s, fmt, ap);
+ if (s == NULL)
+ {
+ if (errno == ENOMEM)
+ sm_exc_raise_x(&SmHeapOutOfMemory);
+ sm_exc_raisenew_x(&SmEtypeOs, errno, "sm_vasprintf", NULL);
+ }
+ return s;
+}
diff --git a/usr/src/cmd/sendmail/libsm/strio.c b/usr/src/cmd/sendmail/libsm/strio.c
new file mode 100644
index 0000000000..7948f12613
--- /dev/null
+++ b/usr/src/cmd/sendmail/libsm/strio.c
@@ -0,0 +1,492 @@
+/*
+ * Copyright (c) 2000-2002, 2004 Sendmail, Inc. and its suppliers.
+ * All rights reserved.
+ * Copyright (c) 1990, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Chris Torek.
+ *
+ * By using this file, you agree to the terms and conditions set
+ * forth in the LICENSE file which can be found at the top level of
+ * the sendmail distribution.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#include <sm/gen.h>
+SM_IDSTR(id, "@(#)$Id: strio.c,v 1.43 2004/08/03 20:48:30 ca Exp $")
+#include <stdlib.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <string.h>
+#include <errno.h>
+#include <sm/rpool.h>
+#include <sm/io.h>
+#include <sm/heap.h>
+#include <sm/conf.h>
+#include "local.h"
+
+static int sm_strsetmode __P((SM_FILE_T*, const int *));
+static int sm_strgetmode __P((SM_FILE_T*, int *));
+
+/*
+** Cookie structure for the "strio" file type
+*/
+
+struct sm_str_obj
+{
+ char *strio_base;
+ char *strio_end;
+ size_t strio_size;
+ size_t strio_offset;
+ int strio_flags;
+ const void *strio_rpool;
+};
+
+typedef struct sm_str_obj SM_STR_OBJ_T;
+
+/*
+** SM_STRGROW -- increase storage space for string
+**
+** Parameters:
+** s -- current cookie
+** size -- new storage size request
+**
+** Returns:
+** true iff successful.
+*/
+
+static bool sm_strgrow __P((SM_STR_OBJ_T *, size_t));
+
+static bool
+sm_strgrow(s, size)
+ SM_STR_OBJ_T *s;
+ size_t size;
+{
+ register void *p;
+
+ if (s->strio_size >= size)
+ return true;
+ p = sm_realloc(s->strio_base, size);
+ if (p == NULL)
+ return false;
+ s->strio_base = p;
+ s->strio_end = s->strio_base + size;
+ s->strio_size = size;
+ return true;
+}
+
+/*
+** SM_STRREAD -- read a portion of the string
+**
+** Parameters:
+** fp -- the file pointer
+** buf -- location to place read data
+** n -- number of bytes to read
+**
+** Returns:
+** Failure: -1 and sets errno
+** Success: >=0, number of bytes read
+*/
+
+ssize_t
+sm_strread(fp, buf, n)
+ SM_FILE_T *fp;
+ char *buf;
+ size_t n;
+{
+ register SM_STR_OBJ_T *s = fp->f_cookie;
+ int len;
+
+ if (!(s->strio_flags & SMRD) && !(s->strio_flags & SMRW))
+ {
+ errno = EBADF;
+ return -1;
+ }
+ len = SM_MIN(s->strio_size - s->strio_offset, n);
+ (void) memmove(buf, s->strio_base + s->strio_offset, len);
+ s->strio_offset += len;
+ return len;
+}
+
+/*
+** SM_STRWRITE -- write a portion of the string
+**
+** Parameters:
+** fp -- the file pointer
+** buf -- location of data for writing
+** n -- number of bytes to write
+**
+** Returns:
+** Failure: -1 and sets errno
+** Success: >=0, number of bytes written
+*/
+
+ssize_t
+sm_strwrite(fp, buf, n)
+ SM_FILE_T *fp;
+ char const *buf;
+ size_t n;
+{
+ register SM_STR_OBJ_T *s = fp->f_cookie;
+
+ if (!(s->strio_flags & SMWR) && !(s->strio_flags & SMRW))
+ {
+ errno = EBADF;
+ return -1;
+ }
+ if (n + s->strio_offset > s->strio_size)
+ {
+ if (!sm_strgrow(s, n + s->strio_offset))
+ return 0;
+ }
+ (void) memmove(s->strio_base + s->strio_offset, buf, n);
+ s->strio_offset += n;
+ return n;
+}
+
+/*
+** SM_STRSEEK -- position the offset pointer for the string
+**
+** Only SM_IO_SEEK_SET, SM_IO_SEEK_CUR and SM_IO_SEEK_END are valid
+** values for whence.
+**
+** Parameters:
+** fp -- the file pointer
+** offset -- number of bytes offset from "base"
+** whence -- determines "base" for 'offset'
+**
+** Returns:
+** Failure: -1 and sets errno
+** Success: >=0, number of bytes read
+*/
+
+off_t
+sm_strseek(fp, offset, whence)
+ SM_FILE_T *fp;
+ off_t offset;
+ int whence;
+{
+ register off_t ret;
+ register SM_STR_OBJ_T *s = fp->f_cookie;
+
+reseek:
+ switch (whence)
+ {
+ case SM_IO_SEEK_SET:
+ ret = offset;
+ break;
+ case SM_IO_SEEK_CUR:
+ ret = s->strio_offset + offset;
+ break;
+ case SM_IO_SEEK_END:
+ ret = s->strio_size;
+ break;
+ default:
+ errno = EINVAL;
+ return -1;
+ }
+ if (ret < 0 || ret > (off_t)(size_t)(-1)) /* XXX ugly */
+ return -1;
+ if ((size_t) ret > s->strio_size)
+ {
+ if (sm_strgrow(s, (size_t)ret))
+ goto reseek;
+
+ /* errno set by sm_strgrow */
+ return -1;
+ }
+ s->strio_offset = (size_t) ret;
+ return ret;
+}
+
+/*
+** SM_STROPEN -- open a string file type
+**
+** Parameters:
+** fp -- file pointer open to be associated with
+** info -- initial contents (NULL for none)
+** flags -- flags for methods of access (was mode)
+** rpool -- resource pool to use memory from (if applicable)
+**
+** Results:
+** Success: 0 (zero)
+** Failure: -1 and sets errno
+*/
+
+int
+sm_stropen(fp, info, flags, rpool)
+ SM_FILE_T *fp;
+ const void *info;
+ int flags;
+ const void *rpool;
+{
+ register SM_STR_OBJ_T *s;
+
+#if SM_RPOOL
+ s = sm_rpool_malloc_x(rpool, sizeof(SM_STR_OBJ_T));
+#else /* SM_RPOOL */
+ s = sm_malloc(sizeof(SM_STR_OBJ_T));
+ if (s == NULL)
+ return -1;
+#endif /* SM_RPOOL */
+
+ fp->f_cookie = s;
+ s->strio_rpool = rpool;
+ s->strio_offset = 0;
+ s->strio_size = 0;
+ s->strio_base = NULL;
+ s->strio_end = 0;
+
+ switch (flags)
+ {
+ case SM_IO_RDWR:
+ s->strio_flags = SMRW;
+ break;
+ case SM_IO_RDONLY:
+ s->strio_flags = SMRD;
+ break;
+ case SM_IO_WRONLY:
+ s->strio_flags = SMWR;
+ break;
+ case SM_IO_APPEND:
+ if (s->strio_rpool == NULL)
+ sm_free(s);
+ errno = EINVAL;
+ return -1;
+ default:
+ if (s->strio_rpool == NULL)
+ sm_free(s);
+ errno = EINVAL;
+ return -1;
+ }
+
+ if (info != NULL)
+ {
+ s->strio_base = sm_strdup_x(info);
+ if (s->strio_base == NULL)
+ {
+ int save_errno = errno;
+
+ if (s->strio_rpool == NULL)
+ sm_free(s);
+ errno = save_errno;
+ return -1;
+ }
+ s->strio_size = strlen(info);
+ s->strio_end = s->strio_base + s->strio_size;
+ }
+ return 0;
+}
+
+/*
+** SM_STRCLOSE -- close the string file type and free resources
+**
+** Parameters:
+** fp -- file pointer
+**
+** Results:
+** Success: 0 (zero)
+*/
+
+int
+sm_strclose(fp)
+ SM_FILE_T *fp;
+{
+ SM_STR_OBJ_T *s = fp->f_cookie;
+
+#if !SM_RPOOL
+ sm_free(s->strio_base);
+ s->strio_base = NULL;
+#endif /* !SM_RPOOL */
+ return 0;
+}
+
+/*
+** SM_STRSETMODE -- set mode info for the file
+**
+** Note: changing the mode can be a safe way to have the "parent"
+** set up a string that the "child" is not to modify
+**
+** Parameters:
+** fp -- the file pointer
+** mode -- location of new mode to set
+**
+** Results:
+** Success: 0 (zero)
+** Failure: -1 and sets errno
+*/
+
+static int
+sm_strsetmode(fp, mode)
+ SM_FILE_T *fp;
+ const int *mode;
+{
+ register SM_STR_OBJ_T *s = fp->f_cookie;
+ int flags;
+
+ switch (*mode)
+ {
+ case SM_IO_RDWR:
+ flags = SMRW;
+ break;
+ case SM_IO_RDONLY:
+ flags = SMRD;
+ break;
+ case SM_IO_WRONLY:
+ flags = SMWR;
+ break;
+ case SM_IO_APPEND:
+ errno = EINVAL;
+ return -1;
+ default:
+ errno = EINVAL;
+ return -1;
+ }
+ s->strio_flags &= ~SMMODEMASK;
+ s->strio_flags |= flags;
+ return 0;
+}
+
+/*
+** SM_STRGETMODE -- get mode info for the file
+**
+** Parameters:
+** fp -- the file pointer
+** mode -- location to store current mode
+**
+** Results:
+** Success: 0 (zero)
+** Failure: -1 and sets errno
+*/
+
+int
+sm_strgetmode(fp, mode)
+ SM_FILE_T *fp;
+ int *mode;
+{
+ register SM_STR_OBJ_T *s = fp->f_cookie;
+
+ switch (s->strio_flags & SMMODEMASK)
+ {
+ case SMRW:
+ *mode = SM_IO_RDWR;
+ break;
+ case SMRD:
+ *mode = SM_IO_RDONLY;
+ break;
+ case SMWR:
+ *mode = SM_IO_WRONLY;
+ break;
+ default:
+ errno = EINVAL;
+ return -1;
+ }
+ return 0;
+}
+
+/*
+** SM_STRSETINFO -- set info for the file
+**
+** Currently only SM_IO_WHAT_MODE is supported for 'what'.
+**
+** Parameters:
+** fp -- the file pointer
+** what -- type of information to set
+** valp -- location to data for doing set
+**
+** Results:
+** Failure: -1 and sets errno
+** Success: sm_strsetmode() return [0 (zero)]
+*/
+
+int
+sm_strsetinfo(fp, what, valp)
+ SM_FILE_T *fp;
+ int what;
+ void *valp;
+{
+ switch(what)
+ {
+ case SM_IO_WHAT_MODE:
+ return sm_strsetmode(fp, (int *) valp);
+ default:
+ errno = EINVAL;
+ return -1;
+ }
+}
+
+/*
+** SM_STRGETINFO -- get info for the file
+**
+** Currently only SM_IO_WHAT_MODE is supported for 'what'.
+**
+** Parameters:
+** fp -- the file pointer
+** what -- type of information requested
+** valp -- location to return information in
+**
+** Results:
+** Failure: -1 and sets errno
+** Success: sm_strgetmode() return [0 (zero)]
+*/
+
+int
+sm_strgetinfo(fp, what, valp)
+ SM_FILE_T *fp;
+ int what;
+ void *valp;
+{
+ switch(what)
+ {
+ case SM_IO_WHAT_MODE:
+ return sm_strgetmode(fp, (int *) valp);
+ default:
+ errno = EINVAL;
+ return -1;
+ }
+}
+
+/*
+** SM_STRIO_INIT -- initializes a write-only string type
+**
+** Original comments below. This function does not appear to be used anywhere.
+** The same functionality can be done by changing the mode of the file.
+** ------------
+** sm_strio_init initializes an SM_FILE_T structure as a write-only file
+** that writes into the specified buffer:
+** - Use sm_io_putc, sm_io_fprintf, etc, to write into the buffer.
+** Attempts to write more than size-1 characters into the buffer will fail
+** silently (no error is reported).
+** - Use sm_io_fflush to nul terminate the string in the buffer
+** (the write pointer is not advanced).
+** No memory is allocated either by sm_strio_init or by sm_io_{putc,write} etc.
+**
+** Parameters:
+** fp -- file pointer
+** buf -- memory location for stored data
+** size -- size of 'buf'
+**
+** Results:
+** none.
+*/
+
+void
+sm_strio_init(fp, buf, size)
+ SM_FILE_T *fp;
+ char *buf;
+ size_t size;
+{
+ fp->sm_magic = SmFileMagic;
+ fp->f_flags = SMWR | SMSTR;
+ fp->f_file = -1;
+ fp->f_bf.smb_base = fp->f_p = (unsigned char *) buf;
+ fp->f_bf.smb_size = fp->f_w = (size ? size - 1 : 0);
+ fp->f_lbfsize = 0;
+ fp->f_r = 0;
+ fp->f_read = NULL;
+ fp->f_seek = NULL;
+ fp->f_getinfo = NULL;
+ fp->f_setinfo = NULL;
+}
diff --git a/usr/src/cmd/sendmail/libsm/strl.c b/usr/src/cmd/sendmail/libsm/strl.c
new file mode 100644
index 0000000000..702c2c9291
--- /dev/null
+++ b/usr/src/cmd/sendmail/libsm/strl.c
@@ -0,0 +1,326 @@
+/*
+ * Copyright (c) 1999-2002 Sendmail, Inc. and its suppliers.
+ * All rights reserved.
+ *
+ * By using this file, you agree to the terms and conditions set
+ * forth in the LICENSE file which can be found at the top level of
+ * the sendmail distribution.
+ *
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#include <sm/gen.h>
+SM_RCSID("@(#)$Id: strl.c,v 1.31 2002/01/20 01:41:25 gshapiro Exp $")
+#include <sm/config.h>
+#include <sm/string.h>
+
+/*
+** Notice: this file is used by libmilter. Please try to avoid
+** using libsm specific functions.
+*/
+
+/*
+** XXX the type of the length parameter has been changed
+** from size_t to ssize_t to avoid theoretical problems with negative
+** numbers passed into these functions.
+** The real solution to this problem is to make sure that this doesn't
+** happen, but for now we'll use this workaround.
+*/
+
+/*
+** SM_STRLCPY -- size bounded string copy
+**
+** This is a bounds-checking variant of strcpy.
+** If size > 0, copy up to size-1 characters from the nul terminated
+** string src to dst, nul terminating the result. If size == 0,
+** the dst buffer is not modified.
+** Additional note: this function has been "tuned" to run fast and tested
+** as such (versus versions in some OS's libc).
+**
+** The result is strlen(src). You can detect truncation (not all
+** of the characters in the source string were copied) using the
+** following idiom:
+**
+** char *s, buf[BUFSIZ];
+** ...
+** if (sm_strlcpy(buf, s, sizeof(buf)) >= sizeof(buf))
+** goto overflow;
+**
+** Parameters:
+** dst -- destination buffer
+** src -- source string
+** size -- size of destination buffer
+**
+** Returns:
+** strlen(src)
+*/
+
+size_t
+sm_strlcpy(dst, src, size)
+ register char *dst;
+ register const char *src;
+ ssize_t size;
+{
+ register ssize_t i;
+
+ if (size-- <= 0)
+ return strlen(src);
+ for (i = 0; i < size && (dst[i] = src[i]) != 0; i++)
+ continue;
+ dst[i] = '\0';
+ if (src[i] == '\0')
+ return i;
+ else
+ return i + strlen(src + i);
+}
+
+/*
+** SM_STRLCAT -- size bounded string concatenation
+**
+** This is a bounds-checking variant of strcat.
+** If strlen(dst) < size, then append at most size - strlen(dst) - 1
+** characters from the source string to the destination string,
+** nul terminating the result. Otherwise, dst is not modified.
+**
+** The result is the initial length of dst + the length of src.
+** You can detect overflow (not all of the characters in the
+** source string were copied) using the following idiom:
+**
+** char *s, buf[BUFSIZ];
+** ...
+** if (sm_strlcat(buf, s, sizeof(buf)) >= sizeof(buf))
+** goto overflow;
+**
+** Parameters:
+** dst -- nul-terminated destination string buffer
+** src -- nul-terminated source string
+** size -- size of destination buffer
+**
+** Returns:
+** total length of the string tried to create
+** (= initial length of dst + length of src)
+*/
+
+size_t
+sm_strlcat(dst, src, size)
+ register char *dst;
+ register const char *src;
+ ssize_t size;
+{
+ register ssize_t i, j, o;
+
+ o = strlen(dst);
+ if (size < o + 1)
+ return o + strlen(src);
+ size -= o + 1;
+ for (i = 0, j = o; i < size && (dst[j] = src[i]) != 0; i++, j++)
+ continue;
+ dst[j] = '\0';
+ if (src[i] == '\0')
+ return j;
+ else
+ return j + strlen(src + i);
+}
+/*
+** SM_STRLCAT2 -- append two strings to dst obeying length and
+** '\0' terminate it
+**
+** strlcat2 will append at most len - strlen(dst) - 1 chars.
+** terminates with '\0' if len > 0
+** dst = dst "+" src1 "+" src2
+** use this instead of sm_strlcat(dst,src1); sm_strlcat(dst,src2);
+** for better speed.
+**
+** Parameters:
+** dst -- "destination" string.
+** src1 -- "from" string 1.
+** src2 -- "from" string 2.
+** len -- max. length of "destination" string.
+**
+** Returns:
+** total length of the string tried to create
+** (= initial length of dst + length of src)
+** if this is greater than len then an overflow would have
+** occurred.
+**
+*/
+
+size_t
+sm_strlcat2(dst, src1, src2, len)
+ register char *dst;
+ register const char *src1;
+ register const char *src2;
+ ssize_t len;
+{
+ register ssize_t i, j, o;
+
+ /* current size of dst */
+ o = strlen(dst);
+
+ /* max. size is less than current? */
+ if (len < o + 1)
+ return o + strlen(src1) + strlen(src2);
+
+ len -= o + 1; /* space left in dst */
+
+ /* copy the first string; i: index in src1; j: index in dst */
+ for (i = 0, j = o; i < len && (dst[j] = src1[i]) != 0; i++, j++)
+ continue;
+
+ /* src1: end reached? */
+ if (src1[i] != '\0')
+ {
+ /* no: terminate dst; there is space since i < len */
+ dst[j] = '\0';
+ return j + strlen(src1 + i) + strlen(src2);
+ }
+
+ len -= i; /* space left in dst */
+
+ /* copy the second string; i: index in src2; j: index in dst */
+ for (i = 0; i < len && (dst[j] = src2[i]) != 0; i++, j++)
+ continue;
+ dst[j] = '\0'; /* terminate dst; there is space since i < len */
+ if (src2[i] == '\0')
+ return j;
+ else
+ return j + strlen(src2 + i);
+}
+
+/*
+** SM_STRLCPYN -- concatenate n strings and assign the result to dst
+** while obeying length and '\0' terminate it
+**
+** dst = src1 "+" src2 "+" ...
+** use this instead of sm_snprintf() for string values
+** and repeated sm_strlc*() calls for better speed.
+**
+** Parameters:
+** dst -- "destination" string.
+** len -- max. length of "destination" string.
+** n -- number of strings
+** strings...
+**
+** Returns:
+** total length of the string tried to create
+** (= initial length of dst + length of src)
+** if this is greater than len then an overflow would have
+** occurred.
+*/
+
+size_t
+#ifdef __STDC__
+sm_strlcpyn(char *dst, ssize_t len, int n, ...)
+#else /* __STDC__ */
+sm_strlcpyn(dst, len, n, va_alist)
+ register char *dst;
+ ssize_t len;
+ int n;
+ va_dcl
+#endif /* __STDC__ */
+{
+ register ssize_t i, j;
+ char *str;
+ SM_VA_LOCAL_DECL
+
+ SM_VA_START(ap, n);
+
+ if (len-- <= 0) /* This allows space for the terminating '\0' */
+ {
+ i = 0;
+ while (n-- > 0)
+ i += strlen(SM_VA_ARG(ap, char *));
+ SM_VA_END(ap);
+ return i;
+ }
+
+ j = 0; /* index in dst */
+
+ /* loop through all source strings */
+ while (n-- > 0)
+ {
+ str = SM_VA_ARG(ap, char *);
+
+ /* copy string; i: index in str; j: index in dst */
+ for (i = 0; j < len && (dst[j] = str[i]) != 0; i++, j++)
+ continue;
+
+ /* str: end reached? */
+ if (str[i] != '\0')
+ {
+ /* no: terminate dst; there is space since j < len */
+ dst[j] = '\0';
+ j += strlen(str + i);
+ while (n-- > 0)
+ j += strlen(SM_VA_ARG(ap, char *));
+ SM_VA_END(ap);
+ return j;
+ }
+ }
+ SM_VA_END(ap);
+
+ dst[j] = '\0'; /* terminate dst; there is space since j < len */
+ return j;
+}
+
+#if 0
+/*
+** SM_STRLAPP -- append string if it fits into buffer.
+**
+** If size > 0, copy up to size-1 characters from the nul terminated
+** string src to dst, nul terminating the result. If size == 0,
+** the dst buffer is not modified.
+**
+** This routine is useful for appending strings in a loop, e.g, instead of
+** s = buf;
+** for (ptr, ptr != NULL, ptr = next->ptr)
+** {
+** (void) sm_strlcpy(s, ptr->string, sizeof buf - (s - buf));
+** s += strlen(s);
+** }
+** replace the loop body with:
+** if (!sm_strlapp(*s, ptr->string, sizeof buf - (s - buf)))
+** break;
+** it's faster...
+**
+** XXX interface isn't completely clear (yet), hence this code is
+** not available.
+**
+**
+** Parameters:
+** dst -- (pointer to) destination buffer
+** src -- source string
+** size -- size of destination buffer
+**
+** Returns:
+** true if strlen(src) < size
+**
+** Side Effects:
+** modifies dst if append succeeds (enough space).
+*/
+
+bool
+sm_strlapp(dst, src, size)
+ register char **dst;
+ register const char *src;
+ ssize_t size;
+{
+ register size_t i;
+
+ if (size-- <= 0)
+ return false;
+ for (i = 0; i < size && ((*dst)[i] = src[i]) != '\0'; i++)
+ continue;
+ (*dst)[i] = '\0';
+ if (src[i] == '\0')
+ {
+ *dst += i;
+ return true;
+ }
+
+ /* undo */
+ (*dst)[0] = '\0';
+ return false;
+}
+#endif /* 0 */
diff --git a/usr/src/cmd/sendmail/libsm/strrevcmp.c b/usr/src/cmd/sendmail/libsm/strrevcmp.c
new file mode 100644
index 0000000000..f2714aeea8
--- /dev/null
+++ b/usr/src/cmd/sendmail/libsm/strrevcmp.c
@@ -0,0 +1,103 @@
+/*
+ * Copyright (c) 2001 Sendmail, Inc. and its suppliers.
+ * All rights reserved.
+ *
+ * By using this file, you agree to the terms and conditions set
+ * forth in the LICENSE file which can be found at the top level of
+ * the sendmail distribution.
+ *
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#include <sm/gen.h>
+SM_RCSID("@(#)$Id: strrevcmp.c,v 1.2 2001/08/27 22:21:51 gshapiro Exp $")
+
+#include <sm/config.h>
+#include <sm/string.h>
+#include <string.h>
+
+/* strcasecmp.c */
+extern const unsigned char charmap[];
+
+/*
+** SM_STRREVCASECMP -- compare two strings starting at the end (ignore case)
+**
+** Parameters:
+** s1 -- first string.
+** s2 -- second string.
+**
+** Returns:
+** strcasecmp(reverse(s1), reverse(s2))
+*/
+
+int
+sm_strrevcasecmp(s1, s2)
+ const char *s1, *s2;
+{
+ register int i1, i2;
+
+ i1 = strlen(s1) - 1;
+ i2 = strlen(s2) - 1;
+ while (i1 >= 0 && i2 >= 0 &&
+ charmap[(unsigned char) s1[i1]] ==
+ charmap[(unsigned char) s2[i2]])
+ {
+ --i1;
+ --i2;
+ }
+ if (i1 < 0)
+ {
+ if (i2 < 0)
+ return 0;
+ else
+ return -1;
+ }
+ else
+ {
+ if (i2 < 0)
+ return 1;
+ else
+ return (charmap[(unsigned char) s1[i1]] -
+ charmap[(unsigned char) s2[i2]]);
+ }
+}
+ /*
+** SM_STRREVCMP -- compare two strings starting at the end
+**
+** Parameters:
+** s1 -- first string.
+** s2 -- second string.
+**
+** Returns:
+** strcmp(reverse(s1), reverse(s2))
+*/
+
+int
+sm_strrevcmp(s1, s2)
+ const char *s1, *s2;
+{
+ register int i1, i2;
+
+ i1 = strlen(s1) - 1;
+ i2 = strlen(s2) - 1;
+ while (i1 >= 0 && i2 >= 0 && s1[i1] == s2[i2])
+ {
+ --i1;
+ --i2;
+ }
+ if (i1 < 0)
+ {
+ if (i2 < 0)
+ return 0;
+ else
+ return -1;
+ }
+ else
+ {
+ if (i2 < 0)
+ return 1;
+ else
+ return s1[i1] - s2[i2];
+ }
+}
diff --git a/usr/src/cmd/sendmail/libsm/strto.c b/usr/src/cmd/sendmail/libsm/strto.c
new file mode 100644
index 0000000000..78a9dffcab
--- /dev/null
+++ b/usr/src/cmd/sendmail/libsm/strto.c
@@ -0,0 +1,256 @@
+/*
+ * Copyright (c) 2000-2001 Sendmail, Inc. and its suppliers.
+ * All rights reserved.
+ * Copyright (c) 1992
+ * The Regents of the University of California. All rights reserved.
+ *
+ * By using this file, you agree to the terms and conditions set
+ * forth in the LICENSE file which can be found at the top level of
+ * the sendmail distribution.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#include <sm/gen.h>
+SM_IDSTR(id, "@(#)$Id: strto.c,v 1.18 2001/12/30 04:59:37 gshapiro Exp $")
+
+#include <sys/param.h>
+#include <sys/types.h>
+#include <stdlib.h>
+#include <ctype.h>
+#include <errno.h>
+#include <sm/limits.h>
+#include <sm/conf.h>
+#include <sm/string.h>
+
+/*
+** SM_STRTOLL -- Convert a string to a (signed) long long integer.
+**
+** Ignores `locale' stuff. Assumes that the upper and lower case
+** alphabets and digits are each contiguous.
+**
+** Parameters:
+** nptr -- string containing number
+** endptr -- location of first invalid character
+** base -- numeric base that 'nptr' number is based in
+**
+** Returns:
+** Failure: on underflow LLONG_MIN is returned; on overflow
+** LLONG_MAX is returned and errno is set.
+** When 'endptr' == '\0' then the entire string 'nptr'
+** was valid.
+** Success: returns the converted number
+*/
+
+LONGLONG_T
+sm_strtoll(nptr, endptr, base)
+ const char *nptr;
+ char **endptr;
+ register int base;
+{
+ register bool neg;
+ register const char *s;
+ register LONGLONG_T acc, cutoff;
+ register int c;
+ register int any, cutlim;
+
+ /*
+ ** Skip white space and pick up leading +/- sign if any.
+ ** If base is 0, allow 0x for hex and 0 for octal, else
+ ** assume decimal; if base is already 16, allow 0x.
+ */
+
+ s = nptr;
+ do
+ {
+ c = (unsigned char) *s++;
+ } while (isascii(c) && isspace(c));
+ if (c == '-')
+ {
+ neg = true;
+ c = *s++;
+ }
+ else
+ {
+ neg = false;
+ if (c == '+')
+ c = *s++;
+ }
+ if ((base == 0 || base == 16) &&
+ c == '0' && (*s == 'x' || *s == 'X'))
+ {
+ c = s[1];
+ s += 2;
+ base = 16;
+ }
+ if (base == 0)
+ base = c == '0' ? 8 : 10;
+
+ /*
+ ** Compute the cutoff value between legal numbers and illegal
+ ** numbers. That is the largest legal value, divided by the
+ ** base. An input number that is greater than this value, if
+ ** followed by a legal input character, is too big. One that
+ ** is equal to this value may be valid or not; the limit
+ ** between valid and invalid numbers is then based on the last
+ ** digit. For instance, if the range for long-long's is
+ ** [-9223372036854775808..9223372036854775807] and the input base
+ ** is 10, cutoff will be set to 922337203685477580 and cutlim to
+ ** either 7 (!neg) or 8 (neg), meaning that if we have
+ ** accumulated a value > 922337203685477580, or equal but the
+ ** next digit is > 7 (or 8), the number is too big, and we will
+ ** return a range error.
+ **
+ ** Set any if any `digits' consumed; make it negative to indicate
+ ** overflow.
+ */
+
+ cutoff = neg ? LLONG_MIN : LLONG_MAX;
+ cutlim = cutoff % base;
+ cutoff /= base;
+ if (neg)
+ {
+ if (cutlim > 0)
+ {
+ cutlim -= base;
+ cutoff += 1;
+ }
+ cutlim = -cutlim;
+ }
+ for (acc = 0, any = 0;; c = (unsigned char) *s++)
+ {
+ if (isascii(c) && isdigit(c))
+ c -= '0';
+ else if (isascii(c) && isalpha(c))
+ c -= isupper(c) ? 'A' - 10 : 'a' - 10;
+ else
+ break;
+ if (c >= base)
+ break;
+ if (any < 0)
+ continue;
+ if (neg)
+ {
+ if (acc < cutoff || (acc == cutoff && c > cutlim))
+ {
+ any = -1;
+ acc = LLONG_MIN;
+ errno = ERANGE;
+ }
+ else
+ {
+ any = 1;
+ acc *= base;
+ acc -= c;
+ }
+ }
+ else
+ {
+ if (acc > cutoff || (acc == cutoff && c > cutlim))
+ {
+ any = -1;
+ acc = LLONG_MAX;
+ errno = ERANGE;
+ }
+ else
+ {
+ any = 1;
+ acc *= base;
+ acc += c;
+ }
+ }
+ }
+ if (endptr != 0)
+ *endptr = (char *) (any ? s - 1 : nptr);
+ return acc;
+}
+
+/*
+** SM_STRTOULL -- Convert a string to an unsigned long long integer.
+**
+** Ignores `locale' stuff. Assumes that the upper and lower case
+** alphabets and digits are each contiguous.
+**
+** Parameters:
+** nptr -- string containing (unsigned) number
+** endptr -- location of first invalid character
+** base -- numeric base that 'nptr' number is based in
+**
+** Returns:
+** Failure: on overflow ULLONG_MAX is returned and errno is set.
+** When 'endptr' == '\0' then the entire string 'nptr'
+** was valid.
+** Success: returns the converted number
+*/
+
+ULONGLONG_T
+sm_strtoull(nptr, endptr, base)
+ const char *nptr;
+ char **endptr;
+ register int base;
+{
+ register const char *s;
+ register ULONGLONG_T acc, cutoff;
+ register int c;
+ register bool neg;
+ register int any, cutlim;
+
+ /* See sm_strtoll for comments as to the logic used. */
+ s = nptr;
+ do
+ {
+ c = (unsigned char) *s++;
+ } while (isascii(c) && isspace(c));
+ neg = (c == '-');
+ if (neg)
+ {
+ c = *s++;
+ }
+ else
+ {
+ if (c == '+')
+ c = *s++;
+ }
+ if ((base == 0 || base == 16) &&
+ c == '0' && (*s == 'x' || *s == 'X'))
+ {
+ c = s[1];
+ s += 2;
+ base = 16;
+ }
+ if (base == 0)
+ base = c == '0' ? 8 : 10;
+
+ cutoff = ULLONG_MAX / (ULONGLONG_T)base;
+ cutlim = ULLONG_MAX % (ULONGLONG_T)base;
+ for (acc = 0, any = 0;; c = (unsigned char) *s++)
+ {
+ if (isascii(c) && isdigit(c))
+ c -= '0';
+ else if (isascii(c) && isalpha(c))
+ c -= isupper(c) ? 'A' - 10 : 'a' - 10;
+ else
+ break;
+ if (c >= base)
+ break;
+ if (any < 0)
+ continue;
+ if (acc > cutoff || (acc == cutoff && c > cutlim))
+ {
+ any = -1;
+ acc = ULLONG_MAX;
+ errno = ERANGE;
+ }
+ else
+ {
+ any = 1;
+ acc *= (ULONGLONG_T)base;
+ acc += c;
+ }
+ }
+ if (neg && any > 0)
+ acc = -((LONGLONG_T) acc);
+ if (endptr != 0)
+ *endptr = (char *) (any ? s - 1 : nptr);
+ return acc;
+}
diff --git a/usr/src/cmd/sendmail/libsm/t-event.c b/usr/src/cmd/sendmail/libsm/t-event.c
new file mode 100644
index 0000000000..0b56b07900
--- /dev/null
+++ b/usr/src/cmd/sendmail/libsm/t-event.c
@@ -0,0 +1,90 @@
+/*
+ * Copyright (c) 2001-2002, 2004 Sendmail, Inc. and its suppliers.
+ * All rights reserved.
+ *
+ * By using this file, you agree to the terms and conditions set
+ * forth in the LICENSE file which can be found at the top level of
+ * the sendmail distribution.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#include <sm/gen.h>
+SM_RCSID("@(#)$Id: t-event.c,v 1.12 2004/08/03 20:50:32 ca Exp $")
+
+#include <stdio.h>
+
+#include <stdlib.h>
+#include <unistd.h>
+# include <sys/wait.h>
+#if SM_CONF_SETITIMER
+# include <sys/time.h>
+#endif /* SM_CONF_SETITIMER */
+
+#include <sm/clock.h>
+#include <sm/test.h>
+
+static void evcheck __P((int));
+static void ev1 __P((int));
+
+static int check;
+
+static void
+evcheck(arg)
+ int arg;
+{
+ SM_TEST(arg == 3);
+ SM_TEST(check == 0);
+ check++;
+}
+
+static void
+ev1(arg)
+ int arg;
+{
+ SM_TEST(arg == 1);
+}
+
+/* define as x if you want debug output */
+#define DBG_OUT(x)
+
+int
+main(argc, argv)
+ int argc;
+ char *argv[];
+{
+ SM_EVENT *ev;
+
+ sm_test_begin(argc, argv, "test event handling");
+ fprintf(stdout, "This test may hang. If there is no output within twelve seconds, abort it\nand recompile with -DSM_CONF_SETITIMER=%d\n",
+ SM_CONF_SETITIMER == 0 ? 1 : 0);
+ sleep(1);
+ SM_TEST(1 == 1);
+ DBG_OUT(fprintf(stdout, "We're back, test 1 seems to work.\n"));
+ ev = sm_seteventm(1000, ev1, 1);
+ sleep(1);
+ SM_TEST(2 == 2);
+ DBG_OUT(fprintf(stdout, "We're back, test 2 seems to work.\n"));
+
+ /* schedule an event in 9s */
+ ev = sm_seteventm(9000, ev1, 2);
+ sleep(1);
+
+ /* clear the event before it can fire */
+ sm_clrevent(ev);
+ SM_TEST(3 == 3);
+ DBG_OUT(fprintf(stdout, "We're back, test 3 seems to work.\n"));
+
+ /* schedule an event in 1s */
+ check = 0;
+ ev = sm_seteventm(1000, evcheck, 3);
+ sleep(2);
+
+ /* clear the event */
+ sm_clrevent(ev);
+ SM_TEST(4 == 4);
+ SM_TEST(check == 1);
+ DBG_OUT(fprintf(stdout, "We're back, test 4 seems to work.\n"));
+
+ return sm_test_end();
+}
diff --git a/usr/src/cmd/sendmail/libsm/t-exc.c b/usr/src/cmd/sendmail/libsm/t-exc.c
new file mode 100644
index 0000000000..1af39e0b7f
--- /dev/null
+++ b/usr/src/cmd/sendmail/libsm/t-exc.c
@@ -0,0 +1,147 @@
+/*
+ * Copyright (c) 2000-2001 Sendmail, Inc. and its suppliers.
+ * All rights reserved.
+ *
+ * By using this file, you agree to the terms and conditions set
+ * forth in the LICENSE file which can be found at the top level of
+ * the sendmail distribution.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#include <sm/gen.h>
+SM_IDSTR(id, "@(#)$Id: t-exc.c,v 1.18 2001/07/05 22:46:35 gshapiro Exp $")
+
+#include <string.h>
+#include <sm/heap.h>
+#include <sm/io.h>
+#include <sm/test.h>
+
+const SM_EXC_TYPE_T EtypeTest1 =
+{
+ SmExcTypeMagic,
+ "E:test1",
+ "i",
+ sm_etype_printf,
+ "test1 exception argv[0]=%0",
+};
+
+const SM_EXC_TYPE_T EtypeTest2 =
+{
+ SmExcTypeMagic,
+ "E:test2",
+ "i",
+ sm_etype_printf,
+ "test2 exception argv[0]=%0",
+};
+
+int
+main(argc, argv)
+ int argc;
+ char **argv;
+{
+ void *p;
+ int volatile x;
+ char *unknown, *cant;
+
+ sm_test_begin(argc, argv, "test exception handling");
+
+ /*
+ ** SM_TRY
+ */
+
+ cant = "can't happen";
+ x = 0;
+ SM_TRY
+ x = 1;
+ SM_END_TRY
+ SM_TEST(x == 1);
+
+ /*
+ ** SM_FINALLY-0
+ */
+
+ x = 0;
+ SM_TRY
+ x = 1;
+ SM_FINALLY
+ x = 2;
+ SM_END_TRY
+ SM_TEST(x == 2);
+
+ /*
+ ** SM_FINALLY-1
+ */
+
+ x = 0;
+ SM_TRY
+ SM_TRY
+ x = 1;
+ sm_exc_raisenew_x(&EtypeTest1, 17);
+ SM_FINALLY
+ x = 2;
+ sm_exc_raisenew_x(&EtypeTest2, 42);
+ SM_END_TRY
+ SM_EXCEPT(exc, "E:test2")
+ (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
+ "got exception test2: can't happen\n");
+ SM_EXCEPT(exc, "E:test1")
+ SM_TEST(x == 2 && exc->exc_argv[0].v_int == 17);
+ if (!(x == 2 && exc->exc_argv[0].v_int == 17))
+ {
+ (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
+ "can't happen: x=%d argv[0]=%d\n",
+ x, exc->exc_argv[0].v_int);
+ }
+ SM_EXCEPT(exc, "*")
+ {
+ unknown = "unknown exception: ";
+ SM_TEST(strcmp(unknown, cant) == 0);
+ }
+ SM_END_TRY
+
+ x = 3;
+ SM_TRY
+ x = 4;
+ sm_exc_raisenew_x(&EtypeTest1, 94);
+ SM_FINALLY
+ x = 5;
+ sm_exc_raisenew_x(&EtypeTest2, 95);
+ SM_EXCEPT(exc, "E:test2")
+ {
+ unknown = "got exception test2: ";
+ SM_TEST(strcmp(unknown, cant) == 0);
+ }
+ SM_EXCEPT(exc, "E:test1")
+ SM_TEST(x == 5 && exc->exc_argv[0].v_int == 94);
+ if (!(x == 5 && exc->exc_argv[0].v_int == 94))
+ {
+ (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
+ "can't happen: x=%d argv[0]=%d\n",
+ x, exc->exc_argv[0].v_int);
+ }
+ SM_EXCEPT(exc, "*")
+ {
+ unknown = "unknown exception: ";
+ SM_TEST(strcmp(unknown, cant) == 0);
+ }
+ SM_END_TRY
+
+ SM_TRY
+ sm_exc_raisenew_x(&SmEtypeErr, "test %d", 0);
+ SM_EXCEPT(exc, "*")
+#if DEBUG
+ (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
+ "test 0 got an exception, as expected:\n");
+ sm_exc_print(exc, smioout);
+#endif /* DEBUG */
+ return sm_test_end();
+ SM_END_TRY
+
+ p = sm_malloc_x((size_t)(-1));
+ (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
+ "sm_malloc_x unexpectedly succeeded, returning %p\n", p);
+ unknown = "sm_malloc_x unexpectedly succeeded";
+ SM_TEST(strcmp(unknown, cant) == 0);
+ return sm_test_end();
+}
diff --git a/usr/src/cmd/sendmail/libsm/t-float.c b/usr/src/cmd/sendmail/libsm/t-float.c
new file mode 100644
index 0000000000..b4d416ab97
--- /dev/null
+++ b/usr/src/cmd/sendmail/libsm/t-float.c
@@ -0,0 +1,74 @@
+/*
+ * Copyright (c) 2000-2001 Sendmail, Inc. and its suppliers.
+ * All rights reserved.
+ *
+ * By using this file, you agree to the terms and conditions set
+ * forth in the LICENSE file which can be found at the top level of
+ * the sendmail distribution.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#include <sm/gen.h>
+SM_IDSTR(id, "@(#)$Id: t-float.c,v 1.16 2001/02/02 23:11:46 ca Exp $")
+
+#include <sm/limits.h>
+#include <sm/io.h>
+#include <sm/string.h>
+#include <sm/test.h>
+#include <sm/types.h>
+
+int
+main(argc, argv)
+ int argc;
+ char **argv;
+{
+ double d, d2;
+ double ld;
+ char buf[128];
+ char *r;
+
+ /*
+ ** Sendmail uses printf and scanf with doubles,
+ ** so make sure that this works.
+ */
+
+ sm_test_begin(argc, argv, "test floating point stuff");
+
+ d = 1.125;
+ sm_snprintf(buf, sizeof(buf), "%d %.3f %d", 0, d, 1);
+ r = "0 1.125 1";
+ if (!SM_TEST(strcmp(buf, r) == 0))
+ (void) sm_io_fprintf(smioerr, SM_TIME_DEFAULT,
+ "got %s instead\n", buf);
+
+ d = 1.125;
+ sm_snprintf(buf, sizeof(buf), "%.3f", d);
+ r = "1.125";
+ if (!SM_TEST(strcmp(buf, r) == 0))
+ (void) sm_io_fprintf(smioerr, SM_TIME_DEFAULT,
+ "got %s instead\n", buf);
+ d2 = 0.0;
+ sm_io_sscanf(buf, "%lf", &d2);
+#if SM_CONF_BROKEN_STRTOD
+ if (d != d2)
+ {
+ (void) sm_io_fprintf(smioerr, SM_TIME_DEFAULT,
+ "wanted %f, got %f\n", d, d2);
+ (void) sm_io_fprintf(smioerr, SM_TIME_DEFAULT,
+ "error ignored since SM_CONF_BROKEN_STRTOD is set for this OS\n");
+ }
+#else /* SM_CONF_BROKEN_STRTOD */
+ if (!SM_TEST(d == d2))
+ (void) sm_io_fprintf(smioerr, SM_TIME_DEFAULT,
+ "wanted %f, got %f\n", d, d2);
+#endif /* SM_CONF_BROKEN_STRTOD */
+
+ ld = 2.5;
+ sm_snprintf(buf, sizeof(buf), "%.3f %.1f", d, ld);
+ r = "1.125 2.5";
+ if (!SM_TEST(strcmp(buf, r) == 0))
+ (void) sm_io_fprintf(smioerr, SM_TIME_DEFAULT,
+ "got %s instead\n", buf);
+ return sm_test_end();
+}
diff --git a/usr/src/cmd/sendmail/libsm/t-fopen.c b/usr/src/cmd/sendmail/libsm/t-fopen.c
new file mode 100644
index 0000000000..2fcb2839e6
--- /dev/null
+++ b/usr/src/cmd/sendmail/libsm/t-fopen.c
@@ -0,0 +1,40 @@
+/*
+ * Copyright (c) 2000-2002 Sendmail, Inc. and its suppliers.
+ * All rights reserved.
+ *
+ * By using this file, you agree to the terms and conditions set
+ * forth in the LICENSE file which can be found at the top level of
+ * the sendmail distribution.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#include <sm/gen.h>
+SM_IDSTR(id, "@(#)$Id: t-fopen.c,v 1.9 2002/02/06 23:57:45 ca Exp $")
+
+#include <fcntl.h>
+#include <sm/io.h>
+#include <sm/test.h>
+
+/* ARGSUSED0 */
+int
+main(argc, argv)
+ int argc;
+ char *argv[];
+{
+ int m, r;
+ SM_FILE_T *out;
+
+ sm_test_begin(argc, argv, "test sm_io_fopen");
+ out = sm_io_fopen("foo", O_WRONLY|O_APPEND|O_CREAT, 0666);
+ SM_TEST(out != NULL);
+ if (out != NULL)
+ {
+ (void) sm_io_fprintf(out, SM_TIME_DEFAULT, "foo\n");
+ r = sm_io_getinfo(out, SM_IO_WHAT_MODE, &m);
+ SM_TEST(r == 0);
+ SM_TEST(m == SM_IO_WRONLY);
+ sm_io_close(out, SM_TIME_DEFAULT);
+ }
+ return sm_test_end();
+}
diff --git a/usr/src/cmd/sendmail/libsm/t-heap.c b/usr/src/cmd/sendmail/libsm/t-heap.c
new file mode 100644
index 0000000000..290b07e17f
--- /dev/null
+++ b/usr/src/cmd/sendmail/libsm/t-heap.c
@@ -0,0 +1,66 @@
+/*
+ * Copyright (c) 2000-2001 Sendmail, Inc. and its suppliers.
+ * All rights reserved.
+ *
+ * By using this file, you agree to the terms and conditions set
+ * forth in the LICENSE file which can be found at the top level of
+ * the sendmail distribution.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#include <sm/gen.h>
+SM_IDSTR(id, "@(#)$Id: t-heap.c,v 1.8 2001/03/06 17:27:36 ca Exp $")
+
+#include <sm/debug.h>
+#include <sm/heap.h>
+#include <sm/io.h>
+#include <sm/test.h>
+#include <sm/xtrap.h>
+
+#if SM_HEAP_CHECK
+extern SM_DEBUG_T SmHeapCheck;
+# define HEAP_CHECK sm_debug_active(&SmHeapCheck, 1)
+#else /* SM_HEAP_CHECK */
+# define HEAP_CHECK 0
+#endif /* SM_HEAP_CHECK */
+
+int
+main(argc, argv)
+ int argc;
+ char **argv;
+{
+ void *p;
+
+ sm_test_begin(argc, argv, "test heap handling");
+ if (argc > 1)
+ sm_debug_addsettings_x(argv[1]);
+
+ p = sm_malloc(10);
+ SM_TEST(p != NULL);
+ p = sm_realloc_x(p, 20);
+ SM_TEST(p != NULL);
+ p = sm_realloc(p, 30);
+ SM_TEST(p != NULL);
+ if (HEAP_CHECK)
+ {
+ sm_dprintf("heap with 1 30-byte block allocated:\n");
+ sm_heap_report(smioout, 3);
+ }
+
+ if (HEAP_CHECK)
+ {
+ sm_free(p);
+ sm_dprintf("heap with 0 blocks allocated:\n");
+ sm_heap_report(smioout, 3);
+ sm_dprintf("xtrap count = %d\n", SmXtrapCount);
+ }
+
+#if DEBUG
+ /* this will cause a core dump */
+ sm_dprintf("about to free %p for the second time\n", p);
+ sm_free(p);
+#endif /* DEBUG */
+
+ return sm_test_end();
+}
diff --git a/usr/src/cmd/sendmail/libsm/t-match.c b/usr/src/cmd/sendmail/libsm/t-match.c
new file mode 100644
index 0000000000..01012fa188
--- /dev/null
+++ b/usr/src/cmd/sendmail/libsm/t-match.c
@@ -0,0 +1,49 @@
+/*
+ * Copyright (c) 2000 Sendmail, Inc. and its suppliers.
+ * All rights reserved.
+ *
+ * By using this file, you agree to the terms and conditions set
+ * forth in the LICENSE file which can be found at the top level of
+ * the sendmail distribution.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#include <sm/gen.h>
+SM_IDSTR(id, "@(#)$Id: t-match.c,v 1.7 2000/12/18 18:12:12 ca Exp $")
+
+#include <sm/string.h>
+#include <sm/io.h>
+#include <sm/test.h>
+
+#define try(str, pat, want) \
+ got = sm_match(str, pat); \
+ if (!SM_TEST(got == want)) \
+ (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, \
+ "sm_match(\"%s\", \"%s\") returns %s\n", \
+ str, pat, got ? "true" : "false");
+
+int
+main(argc, argv)
+ int argc;
+ char **argv;
+{
+ bool got;
+
+ sm_test_begin(argc, argv, "test sm_match");
+
+ try("foo", "foo", true);
+ try("foo", "bar", false);
+ try("foo[bar", "foo[bar", true);
+ try("foo[bar]", "foo[bar]", false);
+ try("foob", "foo[bar]", true);
+ try("a-b", "a[]-]b", true);
+ try("abcde", "a*e", true);
+ try("[", "[[]", true);
+ try("c", "[a-z]", true);
+ try("C", "[a-z]", false);
+ try("F:sm.heap", "[!F]*", false);
+ try("E:sm.err", "[!F]*", true);
+
+ return sm_test_end();
+}
diff --git a/usr/src/cmd/sendmail/libsm/t-path.c b/usr/src/cmd/sendmail/libsm/t-path.c
new file mode 100644
index 0000000000..066d2bbaa4
--- /dev/null
+++ b/usr/src/cmd/sendmail/libsm/t-path.c
@@ -0,0 +1,37 @@
+/*
+ * Copyright (c) 2000-2001 Sendmail, Inc. and its suppliers.
+ * All rights reserved.
+ *
+ * By using this file, you agree to the terms and conditions set
+ * forth in the LICENSE file which can be found at the top level of
+ * the sendmail distribution.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#include <sm/gen.h>
+SM_IDSTR(id, "@(#)$Id: t-path.c,v 1.6 2001/07/05 22:47:29 gshapiro Exp $")
+
+#include <string.h>
+#include <sm/path.h>
+#include <sm/test.h>
+
+int
+main(argc, argv)
+ int argc;
+ char **argv;
+{
+ char *r;
+
+ sm_test_begin(argc, argv, "test path handling");
+
+ SM_TEST(sm_path_isdevnull(SM_PATH_DEVNULL));
+ r = "/dev/null";
+ SM_TEST(sm_path_isdevnull(r));
+ r = "/nev/dull";
+ SM_TEST(!sm_path_isdevnull(r));
+ r = "nul";
+ SM_TEST(!sm_path_isdevnull(r));
+
+ return sm_test_end();
+}
diff --git a/usr/src/cmd/sendmail/libsm/t-rpool.c b/usr/src/cmd/sendmail/libsm/t-rpool.c
new file mode 100644
index 0000000000..98de40662e
--- /dev/null
+++ b/usr/src/cmd/sendmail/libsm/t-rpool.c
@@ -0,0 +1,71 @@
+/*
+ * Copyright (c) 2000-2001 Sendmail, Inc. and its suppliers.
+ * All rights reserved.
+ *
+ * By using this file, you agree to the terms and conditions set
+ * forth in the LICENSE file which can be found at the top level of
+ * the sendmail distribution.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#include <sm/gen.h>
+SM_IDSTR(id, "@(#)$Id: t-rpool.c,v 1.16 2001/03/04 18:38:47 ca Exp $")
+
+#include <sm/debug.h>
+#include <sm/heap.h>
+#include <sm/rpool.h>
+#include <sm/io.h>
+#include <sm/test.h>
+
+static void
+rfree __P((
+ void *cx));
+
+static void
+rfree(cx)
+ void *cx;
+{
+ (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, "rfree freeing `%s'\n",
+ (char *) cx);
+}
+
+int
+main(argc, argv)
+ int argc;
+ char *argv[];
+{
+ SM_RPOOL_T *rpool;
+ char *a[26];
+ int i, j;
+ SM_RPOOL_ATTACH_T att;
+
+ sm_test_begin(argc, argv, "test rpool");
+ sm_debug_addsetting_x("sm_check_heap", 1);
+ rpool = sm_rpool_new_x(NULL);
+ SM_TEST(rpool != NULL);
+ att = sm_rpool_attach_x(rpool, rfree, "attachment #1");
+ SM_TEST(att != NULL);
+ for (i = 0; i < 26; ++i)
+ {
+ size_t sz = i * i * i;
+
+ a[i] = sm_rpool_malloc_x(rpool, sz);
+ for (j = 0; j < sz; ++j)
+ a[i][j] = 'a' + i;
+ }
+ att = sm_rpool_attach_x(rpool, rfree, "attachment #2");
+ (void) sm_rpool_attach_x(rpool, rfree, "attachment #3");
+ sm_rpool_detach(att);
+
+ /* XXX more tests? */
+#if DEBUG
+ sm_dprintf("heap after filling up rpool:\n");
+ sm_heap_report(smioout, 3);
+ sm_dprintf("freeing rpool:\n");
+ sm_rpool_free(rpool);
+ sm_dprintf("heap after freeing rpool:\n");
+ sm_heap_report(smioout, 3);
+#endif /* DEBUG */
+ return sm_test_end();
+}
diff --git a/usr/src/cmd/sendmail/libsm/t-scanf.c b/usr/src/cmd/sendmail/libsm/t-scanf.c
new file mode 100644
index 0000000000..3dac7d4a8e
--- /dev/null
+++ b/usr/src/cmd/sendmail/libsm/t-scanf.c
@@ -0,0 +1,61 @@
+/*
+ * Copyright (c) 2001 Sendmail, Inc. and its suppliers.
+ * All rights reserved.
+ *
+ * By using this file, you agree to the terms and conditions set
+ * forth in the LICENSE file which can be found at the top level of
+ * the sendmail distribution.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#include <sm/gen.h>
+SM_IDSTR(id, "@(#)$Id: t-scanf.c,v 1.5 2001/11/13 00:51:28 ca Exp $")
+
+#include <sm/limits.h>
+#include <sm/io.h>
+#include <sm/string.h>
+#include <sm/test.h>
+#include <sm/types.h>
+
+int
+main(argc, argv)
+ int argc;
+ char **argv;
+{
+ int i, d, h;
+ char buf[128];
+ char *r;
+
+ sm_test_begin(argc, argv, "test scanf point stuff");
+#if !SM_CONF_BROKEN_SIZE_T
+ (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
+"If tests for \"h == 2\" fail, check whether size_t is signed on your OS.\n\
+If that is the case, add -DSM_CONF_BROKEN_SIZE_T to confENVDEF\n\
+and start over. Otherwise contact sendmail.org.\n");
+#endif /* !SM_CONF_BROKEN_SIZE_T */
+
+ d = 2;
+ sm_snprintf(buf, sizeof(buf), "%d", d);
+ r = "2";
+ if (!SM_TEST(strcmp(buf, r) == 0))
+ (void) sm_io_fprintf(smioerr, SM_TIME_DEFAULT,
+ "got %s instead\n", buf);
+
+ i = sm_io_sscanf(buf, "%d", &h);
+ SM_TEST(i == 1);
+ SM_TEST(h == 2);
+
+ d = 2;
+ sm_snprintf(buf, sizeof(buf), "%d\n", d);
+ r = "2\n";
+ if (!SM_TEST(strcmp(buf, r) == 0))
+ (void) sm_io_fprintf(smioerr, SM_TIME_DEFAULT,
+ "got %s instead\n", buf);
+
+ i = sm_io_sscanf(buf, "%d", &h);
+ SM_TEST(i == 1);
+ SM_TEST(h == 2);
+
+ return sm_test_end();
+}
diff --git a/usr/src/cmd/sendmail/libsm/t-sem.c b/usr/src/cmd/sendmail/libsm/t-sem.c
new file mode 100644
index 0000000000..c8ee8a48a1
--- /dev/null
+++ b/usr/src/cmd/sendmail/libsm/t-sem.c
@@ -0,0 +1,346 @@
+/*
+ * Copyright (c) 2000-2001 Sendmail, Inc. and its suppliers.
+ * All rights reserved.
+ *
+ * By using this file, you agree to the terms and conditions set
+ * forth in the LICENSE file which can be found at the top level of
+ * the sendmail distribution.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#include <sm/gen.h>
+SM_RCSID("@(#)$Id: t-sem.c,v 1.14 2005/03/25 21:27:41 ca Exp $")
+
+#include <stdio.h>
+
+#if SM_CONF_SEM
+# include <stdlib.h>
+# include <unistd.h>
+# include <sysexits.h>
+# include <sm/heap.h>
+# include <sm/string.h>
+# include <sm/signal.h>
+# include <sm/test.h>
+# include <sm/sem.h>
+
+static void
+delay(t, s)
+ int t;
+ char *s;
+{
+ if (t > 0)
+ {
+#if DEBUG
+ fprintf(stderr, "sleep(%d) before %s\n", t, s);
+#endif /* DEBUG */
+ sleep(t);
+ }
+#if DEBUG
+ fprintf(stderr, "%s\n", s);
+#endif /* DEBUG */
+}
+
+
+/*
+** SEMINTER -- interactive testing of semaphores.
+**
+** Parameters:
+** owner -- create semaphores.
+**
+** Returns:
+** 0 on success
+** < 0 on failure.
+*/
+
+static int
+seminter(owner)
+ bool owner;
+{
+ int semid;
+ int t;
+
+ semid = sm_sem_start(SM_SEM_KEY, SM_NSEM, 0, owner);
+ if (semid < 0)
+ {
+ perror("sm_sem_start failed");
+ return 1;
+ }
+
+ while ((t = getchar()) != EOF)
+ {
+ switch (t)
+ {
+ case 'a':
+ delay(0, "try to acq");
+ if (sm_sem_acq(semid, 0, 2) < 0)
+ {
+ perror("sm_sem_acq failed");
+ return 1;
+ }
+ delay(0, "acquired");
+ break;
+
+ case 'r':
+ delay(0, "try to rel");
+ if (sm_sem_rel(semid, 0, 2) < 0)
+ {
+ perror("sm_sem_rel failed");
+ return 1;
+ }
+ delay(0, "released");
+ break;
+
+ case 'v':
+ if ((t = sm_sem_get(semid, 0)) < 0)
+ {
+ perror("get_sem failed");
+ return 1;
+ }
+ printf("semval: %d\n", t);
+ break;
+
+ }
+ }
+ if (owner)
+ return sm_sem_stop(semid);
+ return 0;
+}
+
+/*
+** SEM_CLEANUP -- cleanup if something breaks
+**
+** Parameters:
+** sig -- signal.
+**
+** Returns:
+** none.
+*/
+
+static int semid_c = -1;
+void
+sem_cleanup(sig)
+ int sig;
+{
+ if (semid_c >= 0)
+ (void) sm_sem_stop(semid_c);
+ exit(EX_UNAVAILABLE);
+}
+
+/*
+** SEMTEST -- test of semaphores
+**
+** Parameters:
+** owner -- create semaphores.
+**
+** Returns:
+** 0 on success
+** < 0 on failure.
+*/
+
+# define MAX_CNT 10
+
+static int
+semtest(owner)
+ int owner;
+{
+ int semid, r;
+ int cnt = 0;
+
+ semid = sm_sem_start(SM_SEM_KEY, 1, 0, owner);
+ if (semid < 0)
+ {
+ perror("sm_sem_start failed");
+ return -1;
+ }
+
+ if (owner)
+ {
+ /* just in case someone kills the program... */
+ semid_c = semid;
+ (void) sm_signal(SIGHUP, sem_cleanup);
+ (void) sm_signal(SIGINT, sem_cleanup);
+ (void) sm_signal(SIGTERM, sem_cleanup);
+
+ delay(1, "parent: acquire 1");
+ cnt = 0;
+ do
+ {
+ r = sm_sem_acq(semid, 0, 0);
+ if (r < 0)
+ {
+ sleep(1);
+ ++cnt;
+ }
+ } while (r < 0 && cnt <= MAX_CNT);
+ SM_TEST(r >= 0);
+ if (r < 0)
+ return r;
+
+ delay(3, "parent: release 1");
+ cnt = 0;
+ do
+ {
+ r = sm_sem_rel(semid, 0, 0);
+ if (r < 0)
+ {
+ sleep(1);
+ ++cnt;
+ }
+ } while (r < 0 && cnt <= MAX_CNT);
+ SM_TEST(r >= 0);
+ if (r < 0)
+ return r;
+
+ delay(1, "parent: getval");
+ cnt = 0;
+ do
+ {
+ r = sm_sem_get(semid, 0);
+ if (r <= 0)
+ {
+ sleep(1);
+ ++cnt;
+ }
+ } while (r <= 0 && cnt <= MAX_CNT);
+ SM_TEST(r > 0);
+ if (r <= 0)
+ return r;
+
+ delay(1, "parent: acquire 2");
+ cnt = 0;
+ do
+ {
+ r = sm_sem_acq(semid, 0, 0);
+ if (r < 0)
+ {
+ sleep(1);
+ ++cnt;
+ }
+ } while (r < 0 && cnt <= MAX_CNT);
+ SM_TEST(r >= 0);
+ if (r < 0)
+ return r;
+
+ cnt = 0;
+ do
+ {
+ r = sm_sem_rel(semid, 0, 0);
+ if (r < 0)
+ {
+ sleep(1);
+ ++cnt;
+ }
+ } while (r < 0 && cnt <= MAX_CNT);
+ SM_TEST(r >= 0);
+ if (r < 0)
+ return r;
+ }
+ else
+ {
+ delay(1, "child: acquire 1");
+ cnt = 0;
+ do
+ {
+ r = sm_sem_acq(semid, 0, 0);
+ if (r < 0)
+ {
+ sleep(1);
+ ++cnt;
+ }
+ } while (r < 0 && cnt <= MAX_CNT);
+ SM_TEST(r >= 0);
+ if (r < 0)
+ return r;
+
+ delay(1, "child: release 1");
+ cnt = 0;
+ do
+ {
+ r = sm_sem_rel(semid, 0, 0);
+ if (r < 0)
+ {
+ sleep(1);
+ ++cnt;
+ }
+ } while (r < 0 && cnt <= MAX_CNT);
+ SM_TEST(r >= 0);
+ if (r < 0)
+ return r;
+
+ }
+ if (owner)
+ return sm_sem_stop(semid);
+ return 0;
+}
+
+int
+main(argc, argv)
+ int argc;
+ char *argv[];
+{
+ bool interactive = false;
+ bool owner = false;
+ int ch;
+ int r = 0;
+
+# define OPTIONS "io"
+ while ((ch = getopt(argc, argv, OPTIONS)) != -1)
+ {
+ switch ((char) ch)
+ {
+ case 'i':
+ interactive = true;
+ break;
+
+ case 'o':
+ owner = true;
+ break;
+
+ default:
+ break;
+ }
+ }
+
+ if (interactive)
+ r = seminter(owner);
+ else
+ {
+ pid_t pid;
+
+ printf("This test takes about 8 seconds.\n");
+ printf("If it takes longer than 30 second, please interrupt it\n");
+ printf("and compile again without semaphore support, i.e.,");
+ printf("-DSM_CONF_SEM=0\n");
+ if ((pid = fork()) < 0)
+ {
+ perror("fork failed\n");
+ return -1;
+ }
+
+ sm_test_begin(argc, argv, "test semaphores");
+ if (pid == 0)
+ {
+ /* give the parent the chance to setup data */
+ sleep(1);
+ r = semtest(false);
+ }
+ else
+ {
+ r = semtest(true);
+ }
+ SM_TEST(r == 0);
+ return sm_test_end();
+ }
+ return r;
+}
+#else /* SM_CONF_SEM */
+int
+main(argc, argv)
+ int argc;
+ char *argv[];
+{
+ printf("No support for semaphores configured on this machine\n");
+ return 0;
+}
+#endif /* SM_CONF_SEM */
diff --git a/usr/src/cmd/sendmail/libsm/t-shm.c b/usr/src/cmd/sendmail/libsm/t-shm.c
new file mode 100644
index 0000000000..e44addd596
--- /dev/null
+++ b/usr/src/cmd/sendmail/libsm/t-shm.c
@@ -0,0 +1,278 @@
+/*
+ * Copyright (c) 2000-2002, 2004, 2005 Sendmail, Inc. and its suppliers.
+ * All rights reserved.
+ *
+ * By using this file, you agree to the terms and conditions set
+ * forth in the LICENSE file which can be found at the top level of
+ * the sendmail distribution.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#include <sm/gen.h>
+SM_RCSID("@(#)$Id: t-shm.c,v 1.22 2005/01/14 02:14:10 ca Exp $")
+
+#include <stdio.h>
+
+#if SM_CONF_SHM
+# include <stdlib.h>
+# include <unistd.h>
+# include <sys/wait.h>
+
+# include <sm/heap.h>
+# include <sm/string.h>
+# include <sm/test.h>
+# include <sm/shm.h>
+
+# define SHMSIZE 1024
+# define SHM_MAX 6400000
+# define T_SHMKEY 21
+
+
+/*
+** SHMINTER -- interactive testing of shared memory
+**
+** Parameters:
+** owner -- create segment.
+**
+** Returns:
+** 0 on success
+** < 0 on failure.
+*/
+
+int shminter __P((bool));
+
+int
+shminter(owner)
+ bool owner;
+{
+ int *shm, shmid;
+ int i, t;
+
+ shm = (int *) sm_shmstart(T_SHMKEY, SHMSIZE, 0, &shmid, owner);
+ if (shm == (int *) 0)
+ {
+ perror("shminit failed");
+ return -1;
+ }
+
+ while ((t = getchar()) != EOF)
+ {
+ switch (t)
+ {
+ case 'c':
+ *shm = 0;
+ break;
+ case 'i':
+ ++*shm;
+ break;
+ case 'd':
+ --*shm;
+ break;
+ case 's':
+ sleep(1);
+ break;
+ case 'l':
+ t = *shm;
+ for (i = 0; i < SHM_MAX; i++)
+ {
+ ++*shm;
+ }
+ if (*shm != SHM_MAX + t)
+ fprintf(stderr, "error: %d != %d\n",
+ *shm, SHM_MAX + t);
+ break;
+ case 'v':
+ printf("shmval: %d\n", *shm);
+ break;
+ case 'S':
+ i = sm_shmsetowner(shmid, getuid(), getgid(), 0644);
+ printf("sm_shmsetowner=%d\n", i);
+ break;
+ }
+ }
+ return sm_shmstop((void *) shm, shmid, owner);
+}
+
+
+/*
+** SHMBIG -- testing of shared memory
+**
+** Parameters:
+** owner -- create segment.
+** size -- size of segment.
+**
+** Returns:
+** 0 on success
+** < 0 on failure.
+*/
+
+int shmbig __P((bool, int));
+
+int
+shmbig(owner, size)
+ bool owner;
+ int size;
+{
+ int *shm, shmid;
+ int i;
+
+ shm = (int *) sm_shmstart(T_SHMKEY, size, 0, &shmid, owner);
+ if (shm == (int *) 0)
+ {
+ perror("shminit failed");
+ return -1;
+ }
+
+ for (i = 0; i < size / sizeof(int); i++)
+ shm[i] = i;
+ for (i = 0; i < size / sizeof(int); i++)
+ {
+ if (shm[i] != i)
+ {
+ fprintf(stderr, "failed at %d: %d", i, shm[i]);
+ }
+ }
+
+ return sm_shmstop((void *) shm, shmid, owner);
+}
+
+
+/*
+** SHMTEST -- test of shared memory
+**
+** Parameters:
+** owner -- create segment.
+**
+** Returns:
+** 0 on success
+** < 0 on failure.
+*/
+
+# define MAX_CNT 10
+
+int shmtest __P((int));
+
+int
+shmtest(owner)
+ int owner;
+{
+ int *shm, shmid;
+ int cnt = 0;
+
+ shm = (int *) sm_shmstart(T_SHMKEY, SHMSIZE, 0, &shmid, owner);
+ if (shm == (int *) 0)
+ {
+ perror("shminit failed");
+ return -1;
+ }
+
+ if (owner)
+ {
+ int r;
+
+ r = sm_shmsetowner(shmid, getuid(), getgid(), 0660);
+ SM_TEST(r == 0);
+ *shm = 1;
+ while (*shm == 1 && cnt++ < MAX_CNT)
+ sleep(1);
+ SM_TEST(cnt <= MAX_CNT);
+
+ /* release and re-acquire the segment */
+ r = sm_shmstop((void *) shm, shmid, owner);
+ SM_TEST(r == 0);
+ shm = (int *) sm_shmstart(T_SHMKEY, SHMSIZE, 0, &shmid, owner);
+ SM_TEST(shm != (int *) 0);
+ }
+ else
+ {
+ while (*shm != 1 && cnt++ < MAX_CNT)
+ sleep(1);
+ SM_TEST(cnt <= MAX_CNT);
+ *shm = 2;
+
+ /* wait a momemt so the segment is still in use */
+ sleep(2);
+ }
+ return sm_shmstop((void *) shm, shmid, owner);
+}
+
+int
+main(argc, argv)
+ int argc;
+ char *argv[];
+{
+ bool interactive = false;
+ bool owner = false;
+ int big = -1;
+ int ch;
+ int r = 0;
+ int status;
+ extern char *optarg;
+
+# define OPTIONS "b:io"
+ while ((ch = getopt(argc, argv, OPTIONS)) != -1)
+ {
+ switch ((char) ch)
+ {
+ case 'b':
+ big = atoi(optarg);
+ break;
+
+ case 'i':
+ interactive = true;
+ break;
+
+ case 'o':
+ owner = true;
+ break;
+
+ default:
+ break;
+ }
+ }
+
+ if (interactive)
+ r = shminter(owner);
+ else if (big > 0)
+ r = shmbig(true, big);
+ else
+ {
+ pid_t pid;
+ extern int SmTestNumErrors;
+
+ if ((pid = fork()) < 0)
+ {
+ perror("fork failed\n");
+ return -1;
+ }
+
+ sm_test_begin(argc, argv, "test shared memory");
+ if (pid == 0)
+ {
+ /* give the parent the chance to setup data */
+ sleep(1);
+ r = shmtest(false);
+ }
+ else
+ {
+ r = shmtest(true);
+ (void) wait(&status);
+ }
+ SM_TEST(r == 0);
+ if (SmTestNumErrors > 0)
+ printf("add -DSM_CONF_SHM=0 to confENVDEF in devtools/Site/site.config.m4\nand start over.\n");
+ return sm_test_end();
+ }
+ return r;
+}
+#else /* SM_CONF_SHM */
+int
+main(argc, argv)
+ int argc;
+ char *argv[];
+{
+ printf("No support for shared memory configured on this machine\n");
+ return 0;
+}
+#endif /* SM_CONF_SHM */
diff --git a/usr/src/cmd/sendmail/libsm/t-smstdio.c b/usr/src/cmd/sendmail/libsm/t-smstdio.c
new file mode 100644
index 0000000000..6a555cdf57
--- /dev/null
+++ b/usr/src/cmd/sendmail/libsm/t-smstdio.c
@@ -0,0 +1,77 @@
+/*
+ * Copyright (c) 2000-2001 Sendmail, Inc. and its suppliers.
+ * All rights reserved.
+ *
+ * By using this file, you agree to the terms and conditions set
+ * forth in the LICENSE file which can be found at the top level of
+ * the sendmail distribution.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#include <sm/gen.h>
+SM_IDSTR(id, "@(#)$Id: t-smstdio.c,v 1.9 2001/03/21 18:30:41 ca Exp $")
+
+#include <sm/io.h>
+#include <sm/string.h>
+#include <sm/test.h>
+
+int
+main(argc, argv)
+ int argc;
+ char **argv;
+{
+ FILE *stream;
+ SM_FILE_T *fp;
+ char buf[128];
+ size_t n;
+ static char testmsg[] = "hello, world\n";
+
+ sm_test_begin(argc, argv,
+ "test sm_io_stdioopen, smiostdin, smiostdout");
+
+ stream = fopen("t-smstdio.1", "w");
+ SM_TEST(stream != NULL);
+
+ fp = sm_io_stdioopen(stream, "w");
+ SM_TEST(fp != NULL);
+
+ (void) sm_io_fprintf(fp, SM_TIME_DEFAULT, "%s", testmsg);
+ sm_io_close(fp, SM_TIME_DEFAULT);
+
+#if 0
+ /*
+ ** stream should now be closed. This is a tricky way to test
+ ** if it is still open. Alas, it core dumps on Linux.
+ */
+
+ fprintf(stream, "oops! stream is still open!\n");
+ fclose(stream);
+#endif
+
+ stream = fopen("t-smstdio.1", "r");
+ SM_TEST(stream != NULL);
+
+ fp = sm_io_stdioopen(stream, "r");
+ SM_TEST(fp != NULL);
+
+ n = sm_io_read(fp, SM_TIME_DEFAULT, buf, sizeof(buf));
+ if (SM_TEST(n == strlen(testmsg)))
+ {
+ buf[n] = '\0';
+ SM_TEST(strcmp(buf, testmsg) == 0);
+ }
+
+#if 0
+
+ /*
+ ** Copy smiostdin to smiostdout
+ ** gotta think some more about how to test smiostdin and smiostdout
+ */
+
+ while ((c = sm_io_getc(smiostdin)) != SM_IO_EOF)
+ sm_io_putc(smiostdout, c);
+#endif
+
+ return sm_test_end();
+}
diff --git a/usr/src/cmd/sendmail/libsm/t-string.c b/usr/src/cmd/sendmail/libsm/t-string.c
new file mode 100644
index 0000000000..30cef8d236
--- /dev/null
+++ b/usr/src/cmd/sendmail/libsm/t-string.c
@@ -0,0 +1,48 @@
+/*
+ * Copyright (c) 2000-2001 Sendmail, Inc. and its suppliers.
+ * All rights reserved.
+ *
+ * By using this file, you agree to the terms and conditions set
+ * forth in the LICENSE file which can be found at the top level of
+ * the sendmail distribution.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#include <sm/gen.h>
+SM_IDSTR(id, "@(#)$Id: t-string.c,v 1.9 2001/01/26 03:28:43 ca Exp $")
+
+#include <sm/exc.h>
+#include <sm/io.h>
+#include <sm/string.h>
+#include <sm/test.h>
+
+int
+main(argc, argv)
+ int argc;
+ char **argv;
+{
+ char *s;
+ char buf[4096];
+ char foo[4];
+ char *r;
+ int n;
+
+ sm_test_begin(argc, argv, "test string utilities");
+
+ s = sm_stringf_x("%.3s%03d", "foobar", 42);
+ r = "foo042";
+ SM_TEST(strcmp(s, r) == 0);
+
+ s = sm_stringf_x("+%*x+", 2000, 0xCAFE);
+ sm_snprintf(buf, 4096, "+%*x+", 2000, 0xCAFE);
+ SM_TEST(strcmp(s, buf) == 0);
+
+ foo[3] = 1;
+ n = sm_snprintf(foo, sizeof(foo), "foobar%dbaz", 42);
+ SM_TEST(n == 11);
+ r = "foo";
+ SM_TEST(strcmp(foo, r) == 0);
+
+ return sm_test_end();
+}
diff --git a/usr/src/cmd/sendmail/libsm/t-strio.c b/usr/src/cmd/sendmail/libsm/t-strio.c
new file mode 100644
index 0000000000..841de427c5
--- /dev/null
+++ b/usr/src/cmd/sendmail/libsm/t-strio.c
@@ -0,0 +1,35 @@
+/*
+ * Copyright (c) 2000-2001 Sendmail, Inc. and its suppliers.
+ * All rights reserved.
+ *
+ * By using this file, you agree to the terms and conditions set
+ * forth in the LICENSE file which can be found at the top level of
+ * the sendmail distribution.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#include <sm/gen.h>
+SM_IDSTR(id, "@(#)$Id: t-strio.c,v 1.9 2001/03/03 04:00:53 ca Exp $")
+#include <sm/string.h>
+#include <sm/io.h>
+#include <sm/test.h>
+
+int
+main(argc, argv)
+ int argc;
+ char *argv[];
+{
+ char buf[20];
+ char *r;
+ SM_FILE_T f;
+
+ sm_test_begin(argc, argv, "test strio");
+ (void) memset(buf, '.', 20);
+ sm_strio_init(&f, buf, 10);
+ (void) sm_io_fprintf(&f, SM_TIME_DEFAULT, "foobarbazoom");
+ sm_io_flush(&f, SM_TIME_DEFAULT);
+ r = "foobarbaz";
+ SM_TEST(strcmp(buf, r) == 0);
+ return sm_test_end();
+}
diff --git a/usr/src/cmd/sendmail/libsm/t-strl.c b/usr/src/cmd/sendmail/libsm/t-strl.c
new file mode 100644
index 0000000000..dbafbb9bcd
--- /dev/null
+++ b/usr/src/cmd/sendmail/libsm/t-strl.c
@@ -0,0 +1,138 @@
+/*
+ * Copyright (c) 2000-2001 Sendmail, Inc. and its suppliers.
+ * All rights reserved.
+ *
+ * By using this file, you agree to the terms and conditions set
+ * forth in the LICENSE file which can be found at the top level of
+ * the sendmail distribution.
+ *
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#include <sm/gen.h>
+SM_IDSTR(id, "@(#)$Id: t-strl.c,v 1.13 2001/08/27 23:00:05 gshapiro Exp $")
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <sm/heap.h>
+#include <sm/string.h>
+#include <sm/test.h>
+
+#define MAXL 16
+#define N 5
+#define SIZE 128
+
+int
+main(argc, argv)
+ int argc;
+ char *argv[];
+{
+ char *s1, *s2, *s3;
+ int one, two, k;
+ char src1[N][SIZE], dst1[SIZE], dst2[SIZE];
+ char *r;
+
+ sm_test_begin(argc, argv, "test strl* string functions");
+ s1 = "abc";
+ s2 = "123";
+ s3 = sm_malloc_x(MAXL);
+
+ SM_TEST(sm_strlcpy(s3, s1, 4) == 3);
+ SM_TEST(strcmp(s1, s3) == 0);
+
+ SM_TEST(sm_strlcat(s3, s2, 8) == 6);
+ r ="abc123";
+ SM_TEST(strcmp(s3, r) == 0);
+
+ SM_TEST(sm_strlcpy(s3, s1, 2) == 3);
+ r = "a";
+ SM_TEST(strcmp(s3, r) == 0);
+
+ SM_TEST(sm_strlcat(s3, s2, 3) == 4);
+ r = "a1";
+ SM_TEST(strcmp(s3, r) == 0);
+
+ SM_TEST(sm_strlcpy(s3, s1, 4) == 3);
+ r = ":";
+ SM_TEST(sm_strlcat2(s3, r, s2, MAXL) == 7);
+ r = "abc:123";
+ SM_TEST(strcmp(s3, r) == 0);
+
+ SM_TEST(sm_strlcpy(s3, s1, 4) == 3);
+ r = ":";
+ SM_TEST(sm_strlcat2(s3, r, s2, 6) == 7);
+ r = "abc:1";
+ SM_TEST(strcmp(s3, r) == 0);
+
+ SM_TEST(sm_strlcpy(s3, s1, 4) == 3);
+ r = ":";
+ SM_TEST(sm_strlcat2(s3, r, s2, 2) == 7);
+ r = "abc";
+ SM_TEST(strcmp(s3, r) == 0);
+
+ SM_TEST(sm_strlcpy(s3, s1, 4) == 3);
+ r = ":";
+ SM_TEST(sm_strlcat2(s3, r, s2, 4) == 7);
+ r = "abc";
+ SM_TEST(strcmp(s3, r) == 0);
+
+ SM_TEST(sm_strlcpy(s3, s1, 4) == 3);
+ r = ":";
+ SM_TEST(sm_strlcat2(s3, r, s2, 5) == 7);
+ r = "abc:";
+ SM_TEST(strcmp(s3, r) == 0);
+
+ SM_TEST(sm_strlcpy(s3, s1, 4) == 3);
+ r = ":";
+ SM_TEST(sm_strlcat2(s3, r, s2, 6) == 7);
+ r = "abc:1";
+ SM_TEST(strcmp(s3, r) == 0);
+
+ for (k = 0; k < N; k++)
+ {
+ (void) sm_strlcpy(src1[k], "abcdef", sizeof src1);
+ }
+
+ one = sm_strlcpyn(dst1, sizeof dst1, 3, src1[0], "/", src1[1]);
+ two = sm_snprintf(dst2, sizeof dst2, "%s/%s", src1[0], src1[1]);
+ SM_TEST(one == two);
+ SM_TEST(strcmp(dst1, dst2) == 0);
+ one = sm_strlcpyn(dst1, 10, 3, src1[0], "/", src1[1]);
+ two = sm_snprintf(dst2, 10, "%s/%s", src1[0], src1[1]);
+ SM_TEST(one == two);
+ SM_TEST(strcmp(dst1, dst2) == 0);
+ one = sm_strlcpyn(dst1, 5, 3, src1[0], "/", src1[1]);
+ two = sm_snprintf(dst2, 5, "%s/%s", src1[0], src1[1]);
+ SM_TEST(one == two);
+ SM_TEST(strcmp(dst1, dst2) == 0);
+ one = sm_strlcpyn(dst1, 0, 3, src1[0], "/", src1[1]);
+ two = sm_snprintf(dst2, 0, "%s/%s", src1[0], src1[1]);
+ SM_TEST(one == two);
+ SM_TEST(strcmp(dst1, dst2) == 0);
+ one = sm_strlcpyn(dst1, sizeof dst1, 5, src1[0], "/", src1[1], "/", src1[2]);
+ two = sm_snprintf(dst2, sizeof dst2, "%s/%s/%s", src1[0], src1[1], src1[2]);
+ SM_TEST(one == two);
+ SM_TEST(strcmp(dst1, dst2) == 0);
+ one = sm_strlcpyn(dst1, 15, 5, src1[0], "/", src1[1], "/", src1[2]);
+ two = sm_snprintf(dst2, 15, "%s/%s/%s", src1[0], src1[1], src1[2]);
+ SM_TEST(one == two);
+ SM_TEST(strcmp(dst1, dst2) == 0);
+ one = sm_strlcpyn(dst1, 20, 5, src1[0], "/", src1[1], "/", src1[2]);
+ two = sm_snprintf(dst2, 20, "%s/%s/%s", src1[0], src1[1], src1[2]);
+ SM_TEST(one == two);
+ SM_TEST(strcmp(dst1, dst2) == 0);
+
+ one = sm_strlcpyn(dst1, sizeof dst1, 0);
+ SM_TEST(one == 0);
+ r = "";
+ SM_TEST(strcmp(dst1, r) == 0);
+ one = sm_strlcpyn(dst1, 20, 1, src1[0]);
+ two = sm_snprintf(dst2, 20, "%s", src1[0]);
+ SM_TEST(one == two);
+ one = sm_strlcpyn(dst1, 2, 1, src1[0]);
+ two = sm_snprintf(dst2, 2, "%s", src1[0]);
+ SM_TEST(one == two);
+
+ return sm_test_end();
+}
diff --git a/usr/src/cmd/sendmail/libsm/t-strrevcmp.c b/usr/src/cmd/sendmail/libsm/t-strrevcmp.c
new file mode 100644
index 0000000000..7ce06d5e0f
--- /dev/null
+++ b/usr/src/cmd/sendmail/libsm/t-strrevcmp.c
@@ -0,0 +1,55 @@
+/*
+ * Copyright (c) 2001 Sendmail, Inc. and its suppliers.
+ * All rights reserved.
+ *
+ * By using this file, you agree to the terms and conditions set
+ * forth in the LICENSE file which can be found at the top level of
+ * the sendmail distribution.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#include <sm/gen.h>
+SM_IDSTR(id, "@(#)$Id: t-strrevcmp.c,v 1.1 2001/07/16 21:35:28 ca Exp $")
+
+#include <sm/exc.h>
+#include <sm/io.h>
+#include <sm/string.h>
+#include <sm/test.h>
+
+int
+main(argc, argv)
+ int argc;
+ char **argv;
+{
+ char *s1;
+ char *s2;
+
+ sm_test_begin(argc, argv, "test string compare");
+
+ s1 = "equal";
+ s2 = "equal";
+ SM_TEST(sm_strrevcmp(s1, s2) == 0);
+
+ s1 = "equal";
+ s2 = "qual";
+ SM_TEST(sm_strrevcmp(s1, s2) > 0);
+
+ s1 = "qual";
+ s2 = "equal";
+ SM_TEST(sm_strrevcmp(s1, s2) < 0);
+
+ s1 = "Equal";
+ s2 = "equal";
+ SM_TEST(sm_strrevcmp(s1, s2) < 0);
+
+ s1 = "Equal";
+ s2 = "equal";
+ SM_TEST(sm_strrevcasecmp(s1, s2) == 0);
+
+ s1 = "Equal";
+ s2 = "eQuaL";
+ SM_TEST(sm_strrevcasecmp(s1, s2) == 0);
+
+ return sm_test_end();
+}
diff --git a/usr/src/cmd/sendmail/libsm/test.c b/usr/src/cmd/sendmail/libsm/test.c
new file mode 100644
index 0000000000..46b3b2bdd1
--- /dev/null
+++ b/usr/src/cmd/sendmail/libsm/test.c
@@ -0,0 +1,157 @@
+/*
+ * Copyright (c) 2000-2002 Sendmail, Inc. and its suppliers.
+ * All rights reserved.
+ *
+ * By using this file, you agree to the terms and conditions set
+ * forth in the LICENSE file which can be found at the top level of
+ * the sendmail distribution.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#include <sm/gen.h>
+SM_IDSTR(Id, "@(#)$Id: test.c,v 1.16 2002/01/08 17:54:40 ca Exp $")
+
+/*
+** Abstractions for writing libsm test programs.
+*/
+
+#include <stdlib.h>
+#include <unistd.h>
+#include <stdio.h>
+#include <sm/debug.h>
+#include <sm/test.h>
+
+extern char *optarg;
+extern int optind;
+extern int optopt;
+extern int opterr;
+
+int SmTestIndex;
+int SmTestNumErrors;
+bool SmTestVerbose;
+
+static char Help[] = "\
+%s [-h] [-d debugging] [-v]\n\
+\n\
+%s\n\
+\n\
+-h Display this help information.\n\
+-d debugging Set debug activation levels.\n\
+-v Verbose output.\n\
+";
+
+static char Usage[] = "\
+Usage: %s [-h] [-v]\n\
+Use %s -h for help.\n\
+";
+
+/*
+** SM_TEST_BEGIN -- initialize test system.
+**
+** Parameters:
+** argc -- argument counter.
+** argv -- argument vector.
+** testname -- description of tests.
+**
+** Results:
+** none.
+*/
+
+void
+sm_test_begin(argc, argv, testname)
+ int argc;
+ char **argv;
+ char *testname;
+{
+ int c;
+
+ SmTestIndex = 0;
+ SmTestNumErrors = 0;
+ SmTestVerbose = false;
+ opterr = 0;
+
+ while ((c = getopt(argc, argv, "vhd:")) != -1)
+ {
+ switch (c)
+ {
+ case 'v':
+ SmTestVerbose = true;
+ break;
+ case 'd':
+ sm_debug_addsettings_x(optarg);
+ break;
+ case 'h':
+ (void) fprintf(stdout, Help, argv[0], testname);
+ exit(0);
+ default:
+ (void) fprintf(stderr,
+ "Unknown command line option -%c\n",
+ optopt);
+ (void) fprintf(stderr, Usage, argv[0], argv[0]);
+ exit(1);
+ }
+ }
+}
+
+/*
+** SM_TEST -- single test.
+**
+** Parameters:
+** success -- did test succeeed?
+** expr -- expression that has been evaluated.
+** filename -- guess...
+** lineno -- line number.
+**
+** Results:
+** value of success.
+*/
+
+bool
+sm_test(success, expr, filename, lineno)
+ bool success;
+ char *expr;
+ char *filename;
+ int lineno;
+{
+ ++SmTestIndex;
+ if (SmTestVerbose)
+ (void) fprintf(stderr, "%d..", SmTestIndex);
+ if (!success)
+ {
+ ++SmTestNumErrors;
+ if (!SmTestVerbose)
+ (void) fprintf(stderr, "%d..", SmTestIndex);
+ (void) fprintf(stderr, "bad! %s:%d %s\n", filename, lineno,
+ expr);
+ }
+ else
+ {
+ if (SmTestVerbose)
+ (void) fprintf(stderr, "ok\n");
+ }
+ return success;
+}
+
+/*
+** SM_TEST_END -- end of test system.
+**
+** Parameters:
+** none.
+**
+** Results:
+** number of errors.
+*/
+
+int
+sm_test_end()
+{
+ (void) fprintf(stderr, "%d of %d tests completed successfully\n",
+ SmTestIndex - SmTestNumErrors, SmTestIndex);
+ if (SmTestNumErrors != 0)
+ (void) fprintf(stderr, "*** %d error%s in test! ***\n",
+ SmTestNumErrors,
+ SmTestNumErrors > 1 ? "s" : "");
+
+ return SmTestNumErrors;
+}
diff --git a/usr/src/cmd/sendmail/libsm/ungetc.c b/usr/src/cmd/sendmail/libsm/ungetc.c
new file mode 100644
index 0000000000..0b4b8c5dd9
--- /dev/null
+++ b/usr/src/cmd/sendmail/libsm/ungetc.c
@@ -0,0 +1,183 @@
+/*
+ * Copyright (c) 2000-2001, 2004 Sendmail, Inc. and its suppliers.
+ * All rights reserved.
+ * Copyright (c) 1990, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Chris Torek.
+ *
+ * By using this file, you agree to the terms and conditions set
+ * forth in the LICENSE file which can be found at the top level of
+ * the sendmail distribution.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#include <sm/gen.h>
+SM_IDSTR(id, "@(#)$Id: ungetc.c,v 1.29 2004/08/03 20:54:49 ca Exp $")
+
+#include <stdlib.h>
+#include <string.h>
+#include <signal.h>
+#include <sys/time.h>
+#include <errno.h>
+#include <sm/io.h>
+#include <sm/heap.h>
+#include <sm/assert.h>
+#include <sm/conf.h>
+#include "local.h"
+
+static void sm_submore_x __P((SM_FILE_T *));
+
+/*
+** SM_SUBMORE_X -- expand ungetc buffer
+**
+** Expand the ungetc buffer `in place'. That is, adjust fp->f_p when
+** the buffer moves, so that it points the same distance from the end,
+** and move the bytes in the buffer around as necessary so that they
+** are all at the end (stack-style).
+**
+** Parameters:
+** fp -- the file pointer
+**
+** Results:
+** none.
+**
+** Exceptions:
+** F:sm_heap -- out of memory
+*/
+
+static void
+sm_submore_x(fp)
+ SM_FILE_T *fp;
+{
+ register int i;
+ register unsigned char *p;
+
+ if (fp->f_ub.smb_base == fp->f_ubuf)
+ {
+ /* Get a buffer; f_ubuf is fixed size. */
+ p = sm_malloc_x((size_t) SM_IO_BUFSIZ);
+ fp->f_ub.smb_base = p;
+ fp->f_ub.smb_size = SM_IO_BUFSIZ;
+ p += SM_IO_BUFSIZ - sizeof(fp->f_ubuf);
+ for (i = sizeof(fp->f_ubuf); --i >= 0;)
+ p[i] = fp->f_ubuf[i];
+ fp->f_p = p;
+ return;
+ }
+ i = fp->f_ub.smb_size;
+ p = sm_realloc_x(fp->f_ub.smb_base, i << 1);
+
+ /* no overlap (hence can use memcpy) because we doubled the size */
+ (void) memcpy((void *) (p + i), (void *) p, (size_t) i);
+ fp->f_p = p + i;
+ fp->f_ub.smb_base = p;
+ fp->f_ub.smb_size = i << 1;
+}
+
+/*
+** SM_IO_UNGETC -- place a character back into the buffer just read
+**
+** Parameters:
+** fp -- the file pointer affected
+** timeout -- time to complete ungetc
+** c -- the character to place back
+**
+** Results:
+** On success, returns value of character placed back, 0-255.
+** Returns SM_IO_EOF if c == SM_IO_EOF or if last operation
+** was a write and flush failed.
+**
+** Exceptions:
+** F:sm_heap -- out of memory
+*/
+
+int
+sm_io_ungetc(fp, timeout, c)
+ register SM_FILE_T *fp;
+ int timeout;
+ int c;
+{
+ SM_REQUIRE_ISA(fp, SmFileMagic);
+ if (c == SM_IO_EOF)
+ return SM_IO_EOF;
+ if (timeout == SM_TIME_IMMEDIATE)
+ {
+ /*
+ ** Ungetting the buffer will take time and we are wanted to
+ ** return immediately. So...
+ */
+
+ errno = EAGAIN;
+ return SM_IO_EOF;
+ }
+
+ if (!Sm_IO_DidInit)
+ sm_init();
+ if ((fp->f_flags & SMRD) == 0)
+ {
+ /*
+ ** Not already reading: no good unless reading-and-writing.
+ ** Otherwise, flush any current write stuff.
+ */
+
+ if ((fp->f_flags & SMRW) == 0)
+ return SM_IO_EOF;
+ if (fp->f_flags & SMWR)
+ {
+ if (sm_flush(fp, &timeout))
+ return SM_IO_EOF;
+ fp->f_flags &= ~SMWR;
+ fp->f_w = 0;
+ fp->f_lbfsize = 0;
+ }
+ fp->f_flags |= SMRD;
+ }
+ c = (unsigned char) c;
+
+ /*
+ ** If we are in the middle of ungetc'ing, just continue.
+ ** This may require expanding the current ungetc buffer.
+ */
+
+ if (HASUB(fp))
+ {
+ if (fp->f_r >= fp->f_ub.smb_size)
+ sm_submore_x(fp);
+ *--fp->f_p = c;
+ fp->f_r++;
+ return c;
+ }
+ fp->f_flags &= ~SMFEOF;
+
+ /*
+ ** If we can handle this by simply backing up, do so,
+ ** but never replace the original character.
+ ** (This makes sscanf() work when scanning `const' data.)
+ */
+
+ if (fp->f_bf.smb_base != NULL && fp->f_p > fp->f_bf.smb_base &&
+ fp->f_p[-1] == c)
+ {
+ fp->f_p--;
+ fp->f_r++;
+ return c;
+ }
+
+ /*
+ ** Create an ungetc buffer.
+ ** Initially, we will use the `reserve' buffer.
+ */
+
+ fp->f_ur = fp->f_r;
+ fp->f_up = fp->f_p;
+ fp->f_ub.smb_base = fp->f_ubuf;
+ fp->f_ub.smb_size = sizeof(fp->f_ubuf);
+ fp->f_ubuf[sizeof(fp->f_ubuf) - 1] = c;
+ fp->f_p = &fp->f_ubuf[sizeof(fp->f_ubuf) - 1];
+ fp->f_r = 1;
+
+ return c;
+}
diff --git a/usr/src/cmd/sendmail/libsm/vasprintf.c b/usr/src/cmd/sendmail/libsm/vasprintf.c
new file mode 100644
index 0000000000..d8a173ce13
--- /dev/null
+++ b/usr/src/cmd/sendmail/libsm/vasprintf.c
@@ -0,0 +1,106 @@
+/*
+ * Copyright (c) 2000-2001 Sendmail, Inc. and its suppliers.
+ * All rights reserved.
+ *
+ * By using this file, you agree to the terms and conditions set
+ * forth in the LICENSE file which can be found at the top level of
+ * the sendmail distribution.
+ */
+
+/*
+ * Copyright (c) 1997 Todd C. Miller <Todd.Miller@courtesan.com>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND TODD C. MILLER DISCLAIMS ALL
+ * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL TODD C. MILLER BE LIABLE
+ * FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
+ * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
+ * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#include <sm/gen.h>
+SM_RCSID("@(#)$Id: vasprintf.c,v 1.26.2.1 2003/06/03 02:14:09 ca Exp $")
+#include <stdlib.h>
+#include <errno.h>
+#include <sm/io.h>
+#include <sm/heap.h>
+#include "local.h"
+
+/*
+** SM_VASPRINTF -- printf to a dynamically allocated string
+**
+** Write 'printf' output to a dynamically allocated string
+** buffer which is returned to the caller.
+**
+** Parameters:
+** str -- *str receives a pointer to the allocated string
+** fmt -- format directives for printing
+** ap -- variable argument list
+**
+** Results:
+** On failure, set *str to NULL, set errno, and return -1.
+**
+** On success, set *str to a pointer to a nul-terminated
+** string buffer containing printf output, and return the
+** length of the string (not counting the nul).
+*/
+
+#define SM_VA_BUFSIZE 128
+
+int
+sm_vasprintf(str, fmt, ap)
+ char **str;
+ const char *fmt;
+ SM_VA_LOCAL_DECL
+{
+ int ret;
+ SM_FILE_T fake;
+ unsigned char *base;
+
+ fake.sm_magic = SmFileMagic;
+ fake.f_timeout = SM_TIME_FOREVER;
+ fake.f_timeoutstate = SM_TIME_BLOCK;
+ fake.f_file = -1;
+ fake.f_flags = SMWR | SMSTR | SMALC;
+ fake.f_bf.smb_base = fake.f_p = (unsigned char *)sm_malloc(SM_VA_BUFSIZE);
+ if (fake.f_bf.smb_base == NULL)
+ goto err2;
+ fake.f_close = NULL;
+ fake.f_open = NULL;
+ fake.f_read = NULL;
+ fake.f_write = NULL;
+ fake.f_seek = NULL;
+ fake.f_setinfo = fake.f_getinfo = NULL;
+ fake.f_type = "sm_vasprintf:fake";
+ fake.f_bf.smb_size = fake.f_w = SM_VA_BUFSIZE - 1;
+ fake.f_timeout = SM_TIME_FOREVER;
+ ret = sm_io_vfprintf(&fake, SM_TIME_FOREVER, fmt, ap);
+ if (ret == -1)
+ goto err;
+ *fake.f_p = '\0';
+
+ /* use no more space than necessary */
+ base = (unsigned char *) sm_realloc(fake.f_bf.smb_base, ret + 1);
+ if (base == NULL)
+ goto err;
+ *str = (char *)base;
+ return ret;
+
+err:
+ if (fake.f_bf.smb_base != NULL)
+ {
+ sm_free(fake.f_bf.smb_base);
+ fake.f_bf.smb_base = NULL;
+ }
+err2:
+ *str = NULL;
+ errno = ENOMEM;
+ return -1;
+}
diff --git a/usr/src/cmd/sendmail/libsm/vfprintf.c b/usr/src/cmd/sendmail/libsm/vfprintf.c
new file mode 100644
index 0000000000..2ca7e9e58c
--- /dev/null
+++ b/usr/src/cmd/sendmail/libsm/vfprintf.c
@@ -0,0 +1,1110 @@
+/*
+ * Copyright (c) 2000-2001, 2004 Sendmail, Inc. and its suppliers.
+ * All rights reserved.
+ * Copyright (c) 1990
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Chris Torek.
+ *
+ * By using this file, you agree to the terms and conditions set
+ * forth in the LICENSE file which can be found at the top level of
+ * the sendmail distribution.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#include <sm/gen.h>
+SM_IDSTR(id, "@(#)$Id: vfprintf.c,v 1.53 2004/08/03 20:54:49 ca Exp $")
+
+/*
+** Overall:
+** Actual printing innards.
+** This code is large and complicated...
+*/
+
+#include <sys/types.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <sm/config.h>
+#include <sm/varargs.h>
+#include <sm/io.h>
+#include <sm/heap.h>
+#include <sm/conf.h>
+#include "local.h"
+#include "fvwrite.h"
+
+static int sm_bprintf __P((SM_FILE_T *, const char *, va_list));
+static void sm_find_arguments __P((const char *, va_list , va_list **));
+static void sm_grow_type_table_x __P((unsigned char **, int *));
+static int sm_print __P((SM_FILE_T *, int, struct sm_uio *));
+
+/*
+** SM_PRINT -- print/flush to the file
+**
+** Flush out all the vectors defined by the given uio,
+** then reset it so that it can be reused.
+**
+** Parameters:
+** fp -- file pointer
+** timeout -- time to complete operation (milliseconds)
+** uio -- vector list of memory locations of data for printing
+**
+** Results:
+** Success: 0 (zero)
+** Failure:
+*/
+
+static int
+sm_print(fp, timeout, uio)
+ SM_FILE_T *fp;
+ int timeout;
+ register struct sm_uio *uio;
+{
+ register int err;
+
+ if (uio->uio_resid == 0)
+ {
+ uio->uio_iovcnt = 0;
+ return 0;
+ }
+ err = sm_fvwrite(fp, timeout, uio);
+ uio->uio_resid = 0;
+ uio->uio_iovcnt = 0;
+ return err;
+}
+
+/*
+** SM_BPRINTF -- allow formating to an unbuffered file.
+**
+** Helper function for `fprintf to unbuffered unix file': creates a
+** temporary buffer (via a "fake" file pointer).
+** We only work on write-only files; this avoids
+** worries about ungetc buffers and so forth.
+**
+** Parameters:
+** fp -- the file to send the o/p to
+** fmt -- format instructions for the o/p
+** ap -- vectors of data units used for formating
+**
+** Results:
+** Failure: SM_IO_EOF and errno set
+** Success: number of data units used in the formating
+**
+** Side effects:
+** formatted o/p can be SM_IO_BUFSIZ length maximum
+*/
+
+static int
+sm_bprintf(fp, fmt, ap)
+ SM_FILE_T *fp;
+ const char *fmt;
+ SM_VA_LOCAL_DECL
+{
+ int ret;
+ SM_FILE_T fake;
+ unsigned char buf[SM_IO_BUFSIZ];
+ extern const char SmFileMagic[];
+
+ /* copy the important variables */
+ fake.sm_magic = SmFileMagic;
+ fake.f_timeout = SM_TIME_FOREVER;
+ fake.f_timeoutstate = SM_TIME_BLOCK;
+ fake.f_flags = fp->f_flags & ~SMNBF;
+ fake.f_file = fp->f_file;
+ fake.f_cookie = fp->f_cookie;
+ fake.f_write = fp->f_write;
+ fake.f_close = NULL;
+ fake.f_open = NULL;
+ fake.f_read = NULL;
+ fake.f_seek = NULL;
+ fake.f_setinfo = fake.f_getinfo = NULL;
+ fake.f_type = "sm_bprintf:fake";
+
+ /* set up the buffer */
+ fake.f_bf.smb_base = fake.f_p = buf;
+ fake.f_bf.smb_size = fake.f_w = sizeof(buf);
+ fake.f_lbfsize = 0; /* not actually used, but Just In Case */
+
+ /* do the work, then copy any error status */
+ ret = sm_io_vfprintf(&fake, SM_TIME_FOREVER, fmt, ap);
+ if (ret >= 0 && sm_io_flush(&fake, SM_TIME_FOREVER))
+ ret = SM_IO_EOF; /* errno set by sm_io_flush */
+ if (fake.f_flags & SMERR)
+ fp->f_flags |= SMERR;
+ return ret;
+}
+
+
+#define BUF 40
+
+#define STATIC_ARG_TBL_SIZE 8 /* Size of static argument table. */
+
+
+/* Macros for converting digits to letters and vice versa */
+#define to_digit(c) ((c) - '0')
+#define is_digit(c) ((unsigned) to_digit(c) <= 9)
+#define to_char(n) ((char) (n) + '0')
+
+/* Flags used during conversion. */
+#define ALT 0x001 /* alternate form */
+#define HEXPREFIX 0x002 /* add 0x or 0X prefix */
+#define LADJUST 0x004 /* left adjustment */
+#define LONGINT 0x010 /* long integer */
+#define QUADINT 0x020 /* quad integer */
+#define SHORTINT 0x040 /* short integer */
+#define ZEROPAD 0x080 /* zero (as opposed to blank) pad */
+#define FPT 0x100 /* Floating point number */
+
+/*
+** SM_IO_VPRINTF -- performs actual formating for o/p
+**
+** Parameters:
+** fp -- file pointer for o/p
+** timeout -- time to complete the print
+** fmt0 -- formating directives
+** ap -- vectors with data units for formating
+**
+** Results:
+** Success: number of data units used for formatting
+** Failure: SM_IO_EOF and sets errno
+*/
+
+int
+sm_io_vfprintf(fp, timeout, fmt0, ap)
+ SM_FILE_T *fp;
+ int timeout;
+ const char *fmt0;
+ SM_VA_LOCAL_DECL
+{
+ register char *fmt; /* format string */
+ register int ch; /* character from fmt */
+ register int n, m, n2; /* handy integers (short term usage) */
+ register char *cp; /* handy char pointer (short term usage) */
+ register struct sm_iov *iovp;/* for PRINT macro */
+ register int flags; /* flags as above */
+ int ret; /* return value accumulator */
+ int width; /* width from format (%8d), or 0 */
+ int prec; /* precision from format (%.3d), or -1 */
+ char sign; /* sign prefix (' ', '+', '-', or \0) */
+ wchar_t wc;
+ ULONGLONG_T _uquad; /* integer arguments %[diouxX] */
+ enum { OCT, DEC, HEX } base;/* base for [diouxX] conversion */
+ int dprec; /* a copy of prec if [diouxX], 0 otherwise */
+ int realsz; /* field size expanded by dprec */
+ int size; /* size of converted field or string */
+ char *xdigs="0123456789abcdef"; /* digits for [xX] conversion */
+#define NIOV 8
+ struct sm_uio uio; /* output information: summary */
+ struct sm_iov iov[NIOV];/* ... and individual io vectors */
+ char buf[BUF]; /* space for %c, %[diouxX], %[eEfgG] */
+ char ox[2]; /* space for 0x hex-prefix */
+ va_list *argtable; /* args, built due to positional arg */
+ va_list statargtable[STATIC_ARG_TBL_SIZE];
+ int nextarg; /* 1-based argument index */
+ va_list orgap; /* original argument pointer */
+
+ /*
+ ** Choose PADSIZE to trade efficiency vs. size. If larger printf
+ ** fields occur frequently, increase PADSIZE and make the initialisers
+ ** below longer.
+ */
+#define PADSIZE 16 /* pad chunk size */
+ static char blanks[PADSIZE] =
+ {' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' '};
+ static char zeroes[PADSIZE] =
+ {'0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0'};
+
+ /*
+ ** BEWARE, these `goto error' on error, and PAD uses `n'.
+ */
+#define PRINT(ptr, len) do { \
+ iovp->iov_base = (ptr); \
+ iovp->iov_len = (len); \
+ uio.uio_resid += (len); \
+ iovp++; \
+ if (++uio.uio_iovcnt >= NIOV) \
+ { \
+ if (sm_print(fp, timeout, &uio)) \
+ goto error; \
+ iovp = iov; \
+ } \
+} while (0)
+#define PAD(howmany, with) do \
+{ \
+ if ((n = (howmany)) > 0) \
+ { \
+ while (n > PADSIZE) { \
+ PRINT(with, PADSIZE); \
+ n -= PADSIZE; \
+ } \
+ PRINT(with, n); \
+ } \
+} while (0)
+#define FLUSH() do \
+{ \
+ if (uio.uio_resid && sm_print(fp, timeout, &uio)) \
+ goto error; \
+ uio.uio_iovcnt = 0; \
+ iovp = iov; \
+} while (0)
+
+ /*
+ ** To extend shorts properly, we need both signed and unsigned
+ ** argument extraction methods.
+ */
+#define SARG() \
+ (flags&QUADINT ? SM_VA_ARG(ap, LONGLONG_T) : \
+ flags&LONGINT ? GETARG(long) : \
+ flags&SHORTINT ? (long) (short) GETARG(int) : \
+ (long) GETARG(int))
+#define UARG() \
+ (flags&QUADINT ? SM_VA_ARG(ap, ULONGLONG_T) : \
+ flags&LONGINT ? GETARG(unsigned long) : \
+ flags&SHORTINT ? (unsigned long) (unsigned short) GETARG(int) : \
+ (unsigned long) GETARG(unsigned int))
+
+ /*
+ ** Get * arguments, including the form *nn$. Preserve the nextarg
+ ** that the argument can be gotten once the type is determined.
+ */
+#define GETASTER(val) \
+ n2 = 0; \
+ cp = fmt; \
+ while (is_digit(*cp)) \
+ { \
+ n2 = 10 * n2 + to_digit(*cp); \
+ cp++; \
+ } \
+ if (*cp == '$') \
+ { \
+ int hold = nextarg; \
+ if (argtable == NULL) \
+ { \
+ argtable = statargtable; \
+ sm_find_arguments(fmt0, orgap, &argtable); \
+ } \
+ nextarg = n2; \
+ val = GETARG(int); \
+ nextarg = hold; \
+ fmt = ++cp; \
+ } \
+ else \
+ { \
+ val = GETARG(int); \
+ }
+
+/*
+** Get the argument indexed by nextarg. If the argument table is
+** built, use it to get the argument. If its not, get the next
+** argument (and arguments must be gotten sequentially).
+*/
+
+#if SM_VA_STD
+# define GETARG(type) \
+ (((argtable != NULL) ? (void) (ap = argtable[nextarg]) : (void) 0), \
+ nextarg++, SM_VA_ARG(ap, type))
+#else /* SM_VA_STD */
+# define GETARG(type) \
+ ((argtable != NULL) ? (*((type*)(argtable[nextarg++]))) : \
+ (nextarg++, SM_VA_ARG(ap, type)))
+#endif /* SM_VA_STD */
+
+ /* sorry, fprintf(read_only_file, "") returns SM_IO_EOF, not 0 */
+ if (cantwrite(fp))
+ {
+ errno = EBADF;
+ return SM_IO_EOF;
+ }
+
+ /* optimise fprintf(stderr) (and other unbuffered Unix files) */
+ if ((fp->f_flags & (SMNBF|SMWR|SMRW)) == (SMNBF|SMWR) &&
+ fp->f_file >= 0)
+ return sm_bprintf(fp, fmt0, ap);
+
+ fmt = (char *) fmt0;
+ argtable = NULL;
+ nextarg = 1;
+ SM_VA_COPY(orgap, ap);
+ uio.uio_iov = iovp = iov;
+ uio.uio_resid = 0;
+ uio.uio_iovcnt = 0;
+ ret = 0;
+
+ /* Scan the format for conversions (`%' character). */
+ for (;;)
+ {
+ cp = fmt;
+ n = 0;
+ while ((wc = *fmt) != '\0')
+ {
+ if (wc == '%')
+ {
+ n = 1;
+ break;
+ }
+ fmt++;
+ }
+ if ((m = fmt - cp) != 0)
+ {
+ PRINT(cp, m);
+ ret += m;
+ }
+ if (n <= 0)
+ goto done;
+ fmt++; /* skip over '%' */
+
+ flags = 0;
+ dprec = 0;
+ width = 0;
+ prec = -1;
+ sign = '\0';
+
+rflag: ch = *fmt++;
+reswitch: switch (ch)
+ {
+ case ' ':
+
+ /*
+ ** ``If the space and + flags both appear, the space
+ ** flag will be ignored.''
+ ** -- ANSI X3J11
+ */
+
+ if (!sign)
+ sign = ' ';
+ goto rflag;
+ case '#':
+ flags |= ALT;
+ goto rflag;
+ case '*':
+
+ /*
+ ** ``A negative field width argument is taken as a
+ ** - flag followed by a positive field width.''
+ ** -- ANSI X3J11
+ ** They don't exclude field widths read from args.
+ */
+
+ GETASTER(width);
+ if (width >= 0)
+ goto rflag;
+ width = -width;
+ /* FALLTHROUGH */
+ case '-':
+ flags |= LADJUST;
+ goto rflag;
+ case '+':
+ sign = '+';
+ goto rflag;
+ case '.':
+ if ((ch = *fmt++) == '*')
+ {
+ GETASTER(n);
+ prec = n < 0 ? -1 : n;
+ goto rflag;
+ }
+ n = 0;
+ while (is_digit(ch))
+ {
+ n = 10 * n + to_digit(ch);
+ ch = *fmt++;
+ }
+ if (ch == '$')
+ {
+ nextarg = n;
+ if (argtable == NULL)
+ {
+ argtable = statargtable;
+ sm_find_arguments(fmt0, orgap,
+ &argtable);
+ }
+ goto rflag;
+ }
+ prec = n < 0 ? -1 : n;
+ goto reswitch;
+ case '0':
+
+ /*
+ ** ``Note that 0 is taken as a flag, not as the
+ ** beginning of a field width.''
+ ** -- ANSI X3J11
+ */
+
+ flags |= ZEROPAD;
+ goto rflag;
+ case '1': case '2': case '3': case '4':
+ case '5': case '6': case '7': case '8': case '9':
+ n = 0;
+ do
+ {
+ n = 10 * n + to_digit(ch);
+ ch = *fmt++;
+ } while (is_digit(ch));
+ if (ch == '$')
+ {
+ nextarg = n;
+ if (argtable == NULL)
+ {
+ argtable = statargtable;
+ sm_find_arguments(fmt0, orgap,
+ &argtable);
+ }
+ goto rflag;
+ }
+ width = n;
+ goto reswitch;
+ case 'h':
+ flags |= SHORTINT;
+ goto rflag;
+ case 'l':
+ if (*fmt == 'l')
+ {
+ fmt++;
+ flags |= QUADINT;
+ }
+ else
+ {
+ flags |= LONGINT;
+ }
+ goto rflag;
+ case 'q':
+ flags |= QUADINT;
+ goto rflag;
+ case 'c':
+ *(cp = buf) = GETARG(int);
+ size = 1;
+ sign = '\0';
+ break;
+ case 'D':
+ flags |= LONGINT;
+ /*FALLTHROUGH*/
+ case 'd':
+ case 'i':
+ _uquad = SARG();
+ if ((LONGLONG_T) _uquad < 0)
+ {
+ _uquad = -(LONGLONG_T) _uquad;
+ sign = '-';
+ }
+ base = DEC;
+ goto number;
+ case 'e':
+ case 'E':
+ case 'f':
+ case 'g':
+ case 'G':
+ {
+ double val;
+ char *p;
+ char fmt[16];
+ char out[150];
+ size_t len;
+
+ /*
+ ** This code implements floating point output
+ ** in the most portable manner possible,
+ ** relying only on 'sprintf' as defined by
+ ** the 1989 ANSI C standard.
+ ** We silently cap width and precision
+ ** at 120, to avoid buffer overflow.
+ */
+
+ val = GETARG(double);
+
+ p = fmt;
+ *p++ = '%';
+ if (sign)
+ *p++ = sign;
+ if (flags & ALT)
+ *p++ = '#';
+ if (flags & LADJUST)
+ *p++ = '-';
+ if (flags & ZEROPAD)
+ *p++ = '0';
+ *p++ = '*';
+ if (prec >= 0)
+ {
+ *p++ = '.';
+ *p++ = '*';
+ }
+ *p++ = ch;
+ *p = '\0';
+
+ if (width > 120)
+ width = 120;
+ if (prec > 120)
+ prec = 120;
+ if (prec >= 0)
+ sprintf(out, fmt, width, prec, val);
+ else
+ sprintf(out, fmt, width, val);
+ len = strlen(out);
+ PRINT(out, len);
+ FLUSH();
+ continue;
+ }
+ case 'n':
+ if (flags & QUADINT)
+ *GETARG(LONGLONG_T *) = ret;
+ else if (flags & LONGINT)
+ *GETARG(long *) = ret;
+ else if (flags & SHORTINT)
+ *GETARG(short *) = ret;
+ else
+ *GETARG(int *) = ret;
+ continue; /* no output */
+ case 'O':
+ flags |= LONGINT;
+ /*FALLTHROUGH*/
+ case 'o':
+ _uquad = UARG();
+ base = OCT;
+ goto nosign;
+ case 'p':
+
+ /*
+ ** ``The argument shall be a pointer to void. The
+ ** value of the pointer is converted to a sequence
+ ** of printable characters, in an implementation-
+ ** defined manner.''
+ ** -- ANSI X3J11
+ */
+
+ /* NOSTRICT */
+ {
+ union
+ {
+ void *p;
+ ULONGLONG_T ll;
+ unsigned long l;
+ unsigned i;
+ } u;
+ u.p = GETARG(void *);
+ if (sizeof(void *) == sizeof(ULONGLONG_T))
+ _uquad = u.ll;
+ else if (sizeof(void *) == sizeof(long))
+ _uquad = u.l;
+ else
+ _uquad = u.i;
+ }
+ base = HEX;
+ xdigs = "0123456789abcdef";
+ flags |= HEXPREFIX;
+ ch = 'x';
+ goto nosign;
+ case 's':
+ if ((cp = GETARG(char *)) == NULL)
+ cp = "(null)";
+ if (prec >= 0)
+ {
+ /*
+ ** can't use strlen; can only look for the
+ ** NUL in the first `prec' characters, and
+ ** strlen() will go further.
+ */
+
+ char *p = memchr(cp, 0, prec);
+
+ if (p != NULL)
+ {
+ size = p - cp;
+ if (size > prec)
+ size = prec;
+ }
+ else
+ size = prec;
+ }
+ else
+ size = strlen(cp);
+ sign = '\0';
+ break;
+ case 'U':
+ flags |= LONGINT;
+ /*FALLTHROUGH*/
+ case 'u':
+ _uquad = UARG();
+ base = DEC;
+ goto nosign;
+ case 'X':
+ xdigs = "0123456789ABCDEF";
+ goto hex;
+ case 'x':
+ xdigs = "0123456789abcdef";
+hex: _uquad = UARG();
+ base = HEX;
+ /* leading 0x/X only if non-zero */
+ if (flags & ALT && _uquad != 0)
+ flags |= HEXPREFIX;
+
+ /* unsigned conversions */
+nosign: sign = '\0';
+
+ /*
+ ** ``... diouXx conversions ... if a precision is
+ ** specified, the 0 flag will be ignored.''
+ ** -- ANSI X3J11
+ */
+
+number: if ((dprec = prec) >= 0)
+ flags &= ~ZEROPAD;
+
+ /*
+ ** ``The result of converting a zero value with an
+ ** explicit precision of zero is no characters.''
+ ** -- ANSI X3J11
+ */
+
+ cp = buf + BUF;
+ if (_uquad != 0 || prec != 0)
+ {
+ /*
+ ** Unsigned mod is hard, and unsigned mod
+ ** by a constant is easier than that by
+ ** a variable; hence this switch.
+ */
+
+ switch (base)
+ {
+ case OCT:
+ do
+ {
+ *--cp = to_char(_uquad & 7);
+ _uquad >>= 3;
+ } while (_uquad);
+ /* handle octal leading 0 */
+ if (flags & ALT && *cp != '0')
+ *--cp = '0';
+ break;
+
+ case DEC:
+ /* many numbers are 1 digit */
+ while (_uquad >= 10)
+ {
+ *--cp = to_char(_uquad % 10);
+ _uquad /= 10;
+ }
+ *--cp = to_char(_uquad);
+ break;
+
+ case HEX:
+ do
+ {
+ *--cp = xdigs[_uquad & 15];
+ _uquad >>= 4;
+ } while (_uquad);
+ break;
+
+ default:
+ cp = "bug in sm_io_vfprintf: bad base";
+ size = strlen(cp);
+ goto skipsize;
+ }
+ }
+ size = buf + BUF - cp;
+ skipsize:
+ break;
+ default: /* "%?" prints ?, unless ? is NUL */
+ if (ch == '\0')
+ goto done;
+ /* pretend it was %c with argument ch */
+ cp = buf;
+ *cp = ch;
+ size = 1;
+ sign = '\0';
+ break;
+ }
+
+ /*
+ ** All reasonable formats wind up here. At this point, `cp'
+ ** points to a string which (if not flags&LADJUST) should be
+ ** padded out to `width' places. If flags&ZEROPAD, it should
+ ** first be prefixed by any sign or other prefix; otherwise,
+ ** it should be blank padded before the prefix is emitted.
+ ** After any left-hand padding and prefixing, emit zeroes
+ ** required by a decimal [diouxX] precision, then print the
+ ** string proper, then emit zeroes required by any leftover
+ ** floating precision; finally, if LADJUST, pad with blanks.
+ **
+ ** Compute actual size, so we know how much to pad.
+ ** size excludes decimal prec; realsz includes it.
+ */
+
+ realsz = dprec > size ? dprec : size;
+ if (sign)
+ realsz++;
+ else if (flags & HEXPREFIX)
+ realsz+= 2;
+
+ /* right-adjusting blank padding */
+ if ((flags & (LADJUST|ZEROPAD)) == 0)
+ PAD(width - realsz, blanks);
+
+ /* prefix */
+ if (sign)
+ {
+ PRINT(&sign, 1);
+ }
+ else if (flags & HEXPREFIX)
+ {
+ ox[0] = '0';
+ ox[1] = ch;
+ PRINT(ox, 2);
+ }
+
+ /* right-adjusting zero padding */
+ if ((flags & (LADJUST|ZEROPAD)) == ZEROPAD)
+ PAD(width - realsz, zeroes);
+
+ /* leading zeroes from decimal precision */
+ PAD(dprec - size, zeroes);
+
+ /* the string or number proper */
+ PRINT(cp, size);
+ /* left-adjusting padding (always blank) */
+ if (flags & LADJUST)
+ PAD(width - realsz, blanks);
+
+ /* finally, adjust ret */
+ ret += width > realsz ? width : realsz;
+
+ FLUSH(); /* copy out the I/O vectors */
+ }
+done:
+ FLUSH();
+error:
+ if ((argtable != NULL) && (argtable != statargtable))
+ sm_free(argtable);
+ return sm_error(fp) ? SM_IO_EOF : ret;
+ /* NOTREACHED */
+}
+
+/* Type ids for argument type table. */
+#define T_UNUSED 0
+#define T_SHORT 1
+#define T_U_SHORT 2
+#define TP_SHORT 3
+#define T_INT 4
+#define T_U_INT 5
+#define TP_INT 6
+#define T_LONG 7
+#define T_U_LONG 8
+#define TP_LONG 9
+#define T_QUAD 10
+#define T_U_QUAD 11
+#define TP_QUAD 12
+#define T_DOUBLE 13
+#define TP_CHAR 15
+#define TP_VOID 16
+
+/*
+** SM_FIND_ARGUMENTS -- find all args when a positional parameter is found.
+**
+** Find all arguments when a positional parameter is encountered. Returns a
+** table, indexed by argument number, of pointers to each arguments. The
+** initial argument table should be an array of STATIC_ARG_TBL_SIZE entries.
+** It will be replaced with a malloc-ed one if it overflows.
+**
+** Parameters:
+** fmt0 -- formating directives
+** ap -- vector list of data unit for formating consumption
+** argtable -- an indexable table (returned) of 'ap'
+**
+** Results:
+** none.
+*/
+
+static void
+sm_find_arguments(fmt0, ap, argtable)
+ const char *fmt0;
+ SM_VA_LOCAL_DECL
+ va_list **argtable;
+{
+ register char *fmt; /* format string */
+ register int ch; /* character from fmt */
+ register int n, n2; /* handy integer (short term usage) */
+ register char *cp; /* handy char pointer (short term usage) */
+ register int flags; /* flags as above */
+ unsigned char *typetable; /* table of types */
+ unsigned char stattypetable[STATIC_ARG_TBL_SIZE];
+ int tablesize; /* current size of type table */
+ int tablemax; /* largest used index in table */
+ int nextarg; /* 1-based argument index */
+
+ /* Add an argument type to the table, expanding if necessary. */
+#define ADDTYPE(type) \
+ ((nextarg >= tablesize) ? \
+ (sm_grow_type_table_x(&typetable, &tablesize), 0) : 0, \
+ typetable[nextarg++] = type, \
+ (nextarg > tablemax) ? tablemax = nextarg : 0)
+
+#define ADDSARG() \
+ ((flags & LONGINT) ? ADDTYPE(T_LONG) : \
+ ((flags & SHORTINT) ? ADDTYPE(T_SHORT) : ADDTYPE(T_INT)))
+
+#define ADDUARG() \
+ ((flags & LONGINT) ? ADDTYPE(T_U_LONG) : \
+ ((flags & SHORTINT) ? ADDTYPE(T_U_SHORT) : ADDTYPE(T_U_INT)))
+
+ /* Add * arguments to the type array. */
+#define ADDASTER() \
+ n2 = 0; \
+ cp = fmt; \
+ while (is_digit(*cp)) \
+ { \
+ n2 = 10 * n2 + to_digit(*cp); \
+ cp++; \
+ } \
+ if (*cp == '$') \
+ { \
+ int hold = nextarg; \
+ nextarg = n2; \
+ ADDTYPE (T_INT); \
+ nextarg = hold; \
+ fmt = ++cp; \
+ } \
+ else \
+ { \
+ ADDTYPE (T_INT); \
+ }
+ fmt = (char *) fmt0;
+ typetable = stattypetable;
+ tablesize = STATIC_ARG_TBL_SIZE;
+ tablemax = 0;
+ nextarg = 1;
+ (void) memset(typetable, T_UNUSED, STATIC_ARG_TBL_SIZE);
+
+ /* Scan the format for conversions (`%' character). */
+ for (;;)
+ {
+ for (cp = fmt; (ch = *fmt) != '\0' && ch != '%'; fmt++)
+ /* void */;
+ if (ch == '\0')
+ goto done;
+ fmt++; /* skip over '%' */
+
+ flags = 0;
+
+rflag: ch = *fmt++;
+reswitch: switch (ch)
+ {
+ case ' ':
+ case '#':
+ goto rflag;
+ case '*':
+ ADDASTER();
+ goto rflag;
+ case '-':
+ case '+':
+ goto rflag;
+ case '.':
+ if ((ch = *fmt++) == '*')
+ {
+ ADDASTER();
+ goto rflag;
+ }
+ while (is_digit(ch))
+ {
+ ch = *fmt++;
+ }
+ goto reswitch;
+ case '0':
+ goto rflag;
+ case '1': case '2': case '3': case '4':
+ case '5': case '6': case '7': case '8': case '9':
+ n = 0;
+ do
+ {
+ n = 10 * n + to_digit(ch);
+ ch = *fmt++;
+ } while (is_digit(ch));
+ if (ch == '$')
+ {
+ nextarg = n;
+ goto rflag;
+ }
+ goto reswitch;
+ case 'h':
+ flags |= SHORTINT;
+ goto rflag;
+ case 'l':
+ flags |= LONGINT;
+ goto rflag;
+ case 'q':
+ flags |= QUADINT;
+ goto rflag;
+ case 'c':
+ ADDTYPE(T_INT);
+ break;
+ case 'D':
+ flags |= LONGINT;
+ /*FALLTHROUGH*/
+ case 'd':
+ case 'i':
+ if (flags & QUADINT)
+ {
+ ADDTYPE(T_QUAD);
+ }
+ else
+ {
+ ADDSARG();
+ }
+ break;
+ case 'e':
+ case 'E':
+ case 'f':
+ case 'g':
+ case 'G':
+ ADDTYPE(T_DOUBLE);
+ break;
+ case 'n':
+ if (flags & QUADINT)
+ ADDTYPE(TP_QUAD);
+ else if (flags & LONGINT)
+ ADDTYPE(TP_LONG);
+ else if (flags & SHORTINT)
+ ADDTYPE(TP_SHORT);
+ else
+ ADDTYPE(TP_INT);
+ continue; /* no output */
+ case 'O':
+ flags |= LONGINT;
+ /*FALLTHROUGH*/
+ case 'o':
+ if (flags & QUADINT)
+ ADDTYPE(T_U_QUAD);
+ else
+ ADDUARG();
+ break;
+ case 'p':
+ ADDTYPE(TP_VOID);
+ break;
+ case 's':
+ ADDTYPE(TP_CHAR);
+ break;
+ case 'U':
+ flags |= LONGINT;
+ /*FALLTHROUGH*/
+ case 'u':
+ if (flags & QUADINT)
+ ADDTYPE(T_U_QUAD);
+ else
+ ADDUARG();
+ break;
+ case 'X':
+ case 'x':
+ if (flags & QUADINT)
+ ADDTYPE(T_U_QUAD);
+ else
+ ADDUARG();
+ break;
+ default: /* "%?" prints ?, unless ? is NUL */
+ if (ch == '\0')
+ goto done;
+ break;
+ }
+ }
+done:
+ /* Build the argument table. */
+ if (tablemax >= STATIC_ARG_TBL_SIZE)
+ {
+ *argtable = (va_list *)
+ sm_malloc(sizeof(va_list) * (tablemax + 1));
+ }
+
+ for (n = 1; n <= tablemax; n++)
+ {
+ SM_VA_COPY((*argtable)[n], ap);
+ switch (typetable [n])
+ {
+ case T_UNUSED:
+ (void) SM_VA_ARG(ap, int);
+ break;
+ case T_SHORT:
+ (void) SM_VA_ARG(ap, int);
+ break;
+ case T_U_SHORT:
+ (void) SM_VA_ARG(ap, int);
+ break;
+ case TP_SHORT:
+ (void) SM_VA_ARG(ap, short *);
+ break;
+ case T_INT:
+ (void) SM_VA_ARG(ap, int);
+ break;
+ case T_U_INT:
+ (void) SM_VA_ARG(ap, unsigned int);
+ break;
+ case TP_INT:
+ (void) SM_VA_ARG(ap, int *);
+ break;
+ case T_LONG:
+ (void) SM_VA_ARG(ap, long);
+ break;
+ case T_U_LONG:
+ (void) SM_VA_ARG(ap, unsigned long);
+ break;
+ case TP_LONG:
+ (void) SM_VA_ARG(ap, long *);
+ break;
+ case T_QUAD:
+ (void) SM_VA_ARG(ap, LONGLONG_T);
+ break;
+ case T_U_QUAD:
+ (void) SM_VA_ARG(ap, ULONGLONG_T);
+ break;
+ case TP_QUAD:
+ (void) SM_VA_ARG(ap, LONGLONG_T *);
+ break;
+ case T_DOUBLE:
+ (void) SM_VA_ARG(ap, double);
+ break;
+ case TP_CHAR:
+ (void) SM_VA_ARG(ap, char *);
+ break;
+ case TP_VOID:
+ (void) SM_VA_ARG(ap, void *);
+ break;
+ }
+ }
+
+ if ((typetable != NULL) && (typetable != stattypetable))
+ sm_free(typetable);
+}
+
+/*
+** SM_GROW_TYPE_TABLE -- Increase the size of the type table.
+**
+** Parameters:
+** tabletype -- type of table to grow
+** tablesize -- requested new table size
+**
+** Results:
+** Raises an exception if can't allocate memory.
+*/
+
+static void
+sm_grow_type_table_x(typetable, tablesize)
+ unsigned char **typetable;
+ int *tablesize;
+{
+ unsigned char *oldtable = *typetable;
+ int newsize = *tablesize * 2;
+
+ if (*tablesize == STATIC_ARG_TBL_SIZE)
+ {
+ *typetable = (unsigned char *) sm_malloc_x(sizeof(unsigned char)
+ * newsize);
+ (void) memmove(*typetable, oldtable, *tablesize);
+ }
+ else
+ {
+ *typetable = (unsigned char *) sm_realloc_x(typetable,
+ sizeof(unsigned char) * newsize);
+ }
+ (void) memset(&typetable [*tablesize], T_UNUSED,
+ (newsize - *tablesize));
+
+ *tablesize = newsize;
+}
diff --git a/usr/src/cmd/sendmail/libsm/vfscanf.c b/usr/src/cmd/sendmail/libsm/vfscanf.c
new file mode 100644
index 0000000000..bc0f1d004d
--- /dev/null
+++ b/usr/src/cmd/sendmail/libsm/vfscanf.c
@@ -0,0 +1,877 @@
+/*
+ * Copyright (c) 2000-2001, 2004 Sendmail, Inc. and its suppliers.
+ * All rights reserved.
+ * Copyright (c) 1990, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Chris Torek.
+ *
+ * By using this file, you agree to the terms and conditions set
+ * forth in the LICENSE file which can be found at the top level of
+ * the sendmail distribution.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#include <sm/gen.h>
+SM_IDSTR(id, "@(#)$Id: vfscanf.c,v 1.52 2004/08/03 20:56:32 ca Exp $")
+
+#include <ctype.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <setjmp.h>
+#include <sys/time.h>
+#include <sm/varargs.h>
+#include <sm/config.h>
+#include <sm/io.h>
+#include <sm/signal.h>
+#include <sm/clock.h>
+#include <sm/string.h>
+#include "local.h"
+
+#define BUF 513 /* Maximum length of numeric string. */
+
+/* Flags used during conversion. */
+#define LONG 0x01 /* l: long or double */
+#define SHORT 0x04 /* h: short */
+#define QUAD 0x08 /* q: quad (same as ll) */
+#define SUPPRESS 0x10 /* suppress assignment */
+#define POINTER 0x20 /* weird %p pointer (`fake hex') */
+#define NOSKIP 0x40 /* do not skip blanks */
+
+/*
+** The following are used in numeric conversions only:
+** SIGNOK, NDIGITS, DPTOK, and EXPOK are for floating point;
+** SIGNOK, NDIGITS, PFXOK, and NZDIGITS are for integral.
+*/
+
+#define SIGNOK 0x080 /* +/- is (still) legal */
+#define NDIGITS 0x100 /* no digits detected */
+
+#define DPTOK 0x200 /* (float) decimal point is still legal */
+#define EXPOK 0x400 /* (float) exponent (e+3, etc) still legal */
+
+#define PFXOK 0x200 /* 0x prefix is (still) legal */
+#define NZDIGITS 0x400 /* no zero digits detected */
+
+/* Conversion types. */
+#define CT_CHAR 0 /* %c conversion */
+#define CT_CCL 1 /* %[...] conversion */
+#define CT_STRING 2 /* %s conversion */
+#define CT_INT 3 /* integer, i.e., strtoll or strtoull */
+#define CT_FLOAT 4 /* floating, i.e., strtod */
+
+static void scanalrm __P((int));
+static unsigned char *sm_sccl __P((char *, unsigned char *));
+static jmp_buf ScanTimeOut;
+
+/*
+** SCANALRM -- handler when timeout activated for sm_io_vfscanf()
+**
+** Returns flow of control to where setjmp(ScanTimeOut) was set.
+**
+** Parameters:
+** sig -- unused
+**
+** Returns:
+** does not return
+**
+** Side Effects:
+** returns flow of control to setjmp(ScanTimeOut).
+**
+** NOTE: THIS CAN BE CALLED FROM A SIGNAL HANDLER. DO NOT ADD
+** ANYTHING TO THIS ROUTINE UNLESS YOU KNOW WHAT YOU ARE
+** DOING.
+*/
+
+/* ARGSUSED0 */
+static void
+scanalrm(sig)
+ int sig;
+{
+ longjmp(ScanTimeOut, 1);
+}
+
+/*
+** SM_VFSCANF -- convert input into data units
+**
+** Parameters:
+** fp -- file pointer for input data
+** timeout -- time intvl allowed to complete (milliseconds)
+** fmt0 -- format for finding data units
+** ap -- vectors for memory location for storing data units
+**
+** Results:
+** Success: number of data units assigned
+** Failure: SM_IO_EOF
+*/
+
+int
+sm_vfscanf(fp, timeout, fmt0, ap)
+ register SM_FILE_T *fp;
+ int SM_NONVOLATILE timeout;
+ char const *fmt0;
+ va_list SM_NONVOLATILE ap;
+{
+ register unsigned char *SM_NONVOLATILE fmt = (unsigned char *) fmt0;
+ register int c; /* character from format, or conversion */
+ register size_t width; /* field width, or 0 */
+ register char *p; /* points into all kinds of strings */
+ register int n; /* handy integer */
+ register int flags; /* flags as defined above */
+ register char *p0; /* saves original value of p when necessary */
+ int nassigned; /* number of fields assigned */
+ int nread; /* number of characters consumed from fp */
+ int base; /* base argument to strtoll/strtoull */
+ ULONGLONG_T (*ccfn)(); /* conversion function (strtoll/strtoull) */
+ char ccltab[256]; /* character class table for %[...] */
+ char buf[BUF]; /* buffer for numeric conversions */
+ SM_EVENT *evt = NULL;
+
+ /* `basefix' is used to avoid `if' tests in the integer scanner */
+ static short basefix[17] =
+ { 10, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16 };
+
+ if (timeout == SM_TIME_DEFAULT)
+ timeout = fp->f_timeout;
+ if (timeout == SM_TIME_IMMEDIATE)
+ {
+ /*
+ ** Filling the buffer will take time and we are wanted to
+ ** return immediately. So...
+ */
+
+ errno = EAGAIN;
+ return SM_IO_EOF;
+ }
+
+ if (timeout != SM_TIME_FOREVER)
+ {
+ if (setjmp(ScanTimeOut) != 0)
+ {
+ errno = EAGAIN;
+ return SM_IO_EOF;
+ }
+
+ evt = sm_seteventm(timeout, scanalrm, 0);
+ }
+
+ nassigned = 0;
+ nread = 0;
+ base = 0; /* XXX just to keep gcc happy */
+ ccfn = NULL; /* XXX just to keep gcc happy */
+ for (;;)
+ {
+ c = *fmt++;
+ if (c == 0)
+ {
+ if (evt != NULL)
+ sm_clrevent(evt); /* undo our timeout */
+ return nassigned;
+ }
+ if (isspace(c))
+ {
+ while ((fp->f_r > 0 || sm_refill(fp, SM_TIME_FOREVER)
+ == 0) &&
+ isspace(*fp->f_p))
+ nread++, fp->f_r--, fp->f_p++;
+ continue;
+ }
+ if (c != '%')
+ goto literal;
+ width = 0;
+ flags = 0;
+
+ /*
+ ** switch on the format. continue if done;
+ ** break once format type is derived.
+ */
+
+again: c = *fmt++;
+ switch (c)
+ {
+ case '%':
+literal:
+ if (fp->f_r <= 0 && sm_refill(fp, SM_TIME_FOREVER))
+ goto input_failure;
+ if (*fp->f_p != c)
+ goto match_failure;
+ fp->f_r--, fp->f_p++;
+ nread++;
+ continue;
+
+ case '*':
+ flags |= SUPPRESS;
+ goto again;
+ case 'h':
+ flags |= SHORT;
+ goto again;
+ case 'l':
+ if (*fmt == 'l')
+ {
+ fmt++;
+ flags |= QUAD;
+ }
+ else
+ {
+ flags |= LONG;
+ }
+ goto again;
+ case 'q':
+ flags |= QUAD;
+ goto again;
+
+ case '0': case '1': case '2': case '3': case '4':
+ case '5': case '6': case '7': case '8': case '9':
+ width = width * 10 + c - '0';
+ goto again;
+
+ /*
+ ** Conversions.
+ ** Those marked `compat' are for 4.[123]BSD compatibility.
+ **
+ ** (According to ANSI, E and X formats are supposed
+ ** to the same as e and x. Sorry about that.)
+ */
+
+ case 'D': /* compat */
+ flags |= LONG;
+ /* FALLTHROUGH */
+ case 'd':
+ c = CT_INT;
+ ccfn = (ULONGLONG_T (*)())sm_strtoll;
+ base = 10;
+ break;
+
+ case 'i':
+ c = CT_INT;
+ ccfn = (ULONGLONG_T (*)())sm_strtoll;
+ base = 0;
+ break;
+
+ case 'O': /* compat */
+ flags |= LONG;
+ /* FALLTHROUGH */
+ case 'o':
+ c = CT_INT;
+ ccfn = sm_strtoull;
+ base = 8;
+ break;
+
+ case 'u':
+ c = CT_INT;
+ ccfn = sm_strtoull;
+ base = 10;
+ break;
+
+ case 'X':
+ case 'x':
+ flags |= PFXOK; /* enable 0x prefixing */
+ c = CT_INT;
+ ccfn = sm_strtoull;
+ base = 16;
+ break;
+
+ case 'E':
+ case 'G':
+ case 'e':
+ case 'f':
+ case 'g':
+ c = CT_FLOAT;
+ break;
+
+ case 's':
+ c = CT_STRING;
+ break;
+
+ case '[':
+ fmt = sm_sccl(ccltab, fmt);
+ flags |= NOSKIP;
+ c = CT_CCL;
+ break;
+
+ case 'c':
+ flags |= NOSKIP;
+ c = CT_CHAR;
+ break;
+
+ case 'p': /* pointer format is like hex */
+ flags |= POINTER | PFXOK;
+ c = CT_INT;
+ ccfn = sm_strtoull;
+ base = 16;
+ break;
+
+ case 'n':
+ if (flags & SUPPRESS) /* ??? */
+ continue;
+ if (flags & SHORT)
+ *SM_VA_ARG(ap, short *) = nread;
+ else if (flags & LONG)
+ *SM_VA_ARG(ap, long *) = nread;
+ else
+ *SM_VA_ARG(ap, int *) = nread;
+ continue;
+
+ /* Disgusting backwards compatibility hacks. XXX */
+ case '\0': /* compat */
+ if (evt != NULL)
+ sm_clrevent(evt); /* undo our timeout */
+ return SM_IO_EOF;
+
+ default: /* compat */
+ if (isupper(c))
+ flags |= LONG;
+ c = CT_INT;
+ ccfn = (ULONGLONG_T (*)()) sm_strtoll;
+ base = 10;
+ break;
+ }
+
+ /* We have a conversion that requires input. */
+ if (fp->f_r <= 0 && sm_refill(fp, SM_TIME_FOREVER))
+ goto input_failure;
+
+ /*
+ ** Consume leading white space, except for formats
+ ** that suppress this.
+ */
+
+ if ((flags & NOSKIP) == 0)
+ {
+ while (isspace(*fp->f_p))
+ {
+ nread++;
+ if (--fp->f_r > 0)
+ fp->f_p++;
+ else if (sm_refill(fp, SM_TIME_FOREVER))
+ goto input_failure;
+ }
+ /*
+ ** Note that there is at least one character in
+ ** the buffer, so conversions that do not set NOSKIP
+ ** can no longer result in an input failure.
+ */
+ }
+
+ /* Do the conversion. */
+ switch (c)
+ {
+ case CT_CHAR:
+ /* scan arbitrary characters (sets NOSKIP) */
+ if (width == 0)
+ width = 1;
+ if (flags & SUPPRESS)
+ {
+ size_t sum = 0;
+ for (;;)
+ {
+ if ((size_t) (n = fp->f_r) < width)
+ {
+ sum += n;
+ width -= n;
+ fp->f_p += n;
+ if (sm_refill(fp,
+ SM_TIME_FOREVER))
+ {
+ if (sum == 0)
+ goto input_failure;
+ break;
+ }
+ }
+ else
+ {
+ sum += width;
+ fp->f_r -= width;
+ fp->f_p += width;
+ break;
+ }
+ }
+ nread += sum;
+ }
+ else
+ {
+ size_t r;
+
+ r = sm_io_read(fp, SM_TIME_FOREVER,
+ (void *) SM_VA_ARG(ap, char *),
+ width);
+ if (r == 0)
+ goto input_failure;
+ nread += r;
+ nassigned++;
+ }
+ break;
+
+ case CT_CCL:
+ /* scan a (nonempty) character class (sets NOSKIP) */
+ if (width == 0)
+ width = (size_t)~0; /* `infinity' */
+
+ /* take only those things in the class */
+ if (flags & SUPPRESS)
+ {
+ n = 0;
+ while (ccltab[*fp->f_p] != '\0')
+ {
+ n++, fp->f_r--, fp->f_p++;
+ if (--width == 0)
+ break;
+ if (fp->f_r <= 0 &&
+ sm_refill(fp, SM_TIME_FOREVER))
+ {
+ if (n == 0) /* XXX how? */
+ goto input_failure;
+ break;
+ }
+ }
+ if (n == 0)
+ goto match_failure;
+ }
+ else
+ {
+ p0 = p = SM_VA_ARG(ap, char *);
+ while (ccltab[*fp->f_p] != '\0')
+ {
+ fp->f_r--;
+ *p++ = *fp->f_p++;
+ if (--width == 0)
+ break;
+ if (fp->f_r <= 0 &&
+ sm_refill(fp, SM_TIME_FOREVER))
+ {
+ if (p == p0)
+ goto input_failure;
+ break;
+ }
+ }
+ n = p - p0;
+ if (n == 0)
+ goto match_failure;
+ *p = 0;
+ nassigned++;
+ }
+ nread += n;
+ break;
+
+ case CT_STRING:
+ /* like CCL, but zero-length string OK, & no NOSKIP */
+ if (width == 0)
+ width = (size_t)~0;
+ if (flags & SUPPRESS)
+ {
+ n = 0;
+ while (!isspace(*fp->f_p))
+ {
+ n++, fp->f_r--, fp->f_p++;
+ if (--width == 0)
+ break;
+ if (fp->f_r <= 0 &&
+ sm_refill(fp, SM_TIME_FOREVER))
+ break;
+ }
+ nread += n;
+ }
+ else
+ {
+ p0 = p = SM_VA_ARG(ap, char *);
+ while (!isspace(*fp->f_p))
+ {
+ fp->f_r--;
+ *p++ = *fp->f_p++;
+ if (--width == 0)
+ break;
+ if (fp->f_r <= 0 &&
+ sm_refill(fp, SM_TIME_FOREVER))
+ break;
+ }
+ *p = 0;
+ nread += p - p0;
+ nassigned++;
+ }
+ continue;
+
+ case CT_INT:
+ /* scan an integer as if by strtoll/strtoull */
+#if SM_CONF_BROKEN_SIZE_T
+ if (width == 0 || width > sizeof(buf) - 1)
+ width = sizeof(buf) - 1;
+#else /* SM_CONF_BROKEN_SIZE_T */
+ /* size_t is unsigned, hence this optimisation */
+ if (--width > sizeof(buf) - 2)
+ width = sizeof(buf) - 2;
+ width++;
+#endif /* SM_CONF_BROKEN_SIZE_T */
+ flags |= SIGNOK | NDIGITS | NZDIGITS;
+ for (p = buf; width > 0; width--)
+ {
+ c = *fp->f_p;
+
+ /*
+ ** Switch on the character; `goto ok'
+ ** if we accept it as a part of number.
+ */
+
+ switch (c)
+ {
+
+ /*
+ ** The digit 0 is always legal, but is
+ ** special. For %i conversions, if no
+ ** digits (zero or nonzero) have been
+ ** scanned (only signs), we will have
+ ** base==0. In that case, we should set
+ ** it to 8 and enable 0x prefixing.
+ ** Also, if we have not scanned zero digits
+ ** before this, do not turn off prefixing
+ ** (someone else will turn it off if we
+ ** have scanned any nonzero digits).
+ */
+
+ case '0':
+ if (base == 0)
+ {
+ base = 8;
+ flags |= PFXOK;
+ }
+ if (flags & NZDIGITS)
+ flags &= ~(SIGNOK|NZDIGITS|NDIGITS);
+ else
+ flags &= ~(SIGNOK|PFXOK|NDIGITS);
+ goto ok;
+
+ /* 1 through 7 always legal */
+ case '1': case '2': case '3':
+ case '4': case '5': case '6': case '7':
+ base = basefix[base];
+ flags &= ~(SIGNOK | PFXOK | NDIGITS);
+ goto ok;
+
+ /* digits 8 and 9 ok iff decimal or hex */
+ case '8': case '9':
+ base = basefix[base];
+ if (base <= 8)
+ break; /* not legal here */
+ flags &= ~(SIGNOK | PFXOK | NDIGITS);
+ goto ok;
+
+ /* letters ok iff hex */
+ case 'A': case 'B': case 'C':
+ case 'D': case 'E': case 'F':
+ case 'a': case 'b': case 'c':
+ case 'd': case 'e': case 'f':
+
+ /* no need to fix base here */
+ if (base <= 10)
+ break; /* not legal here */
+ flags &= ~(SIGNOK | PFXOK | NDIGITS);
+ goto ok;
+
+ /* sign ok only as first character */
+ case '+': case '-':
+ if (flags & SIGNOK)
+ {
+ flags &= ~SIGNOK;
+ goto ok;
+ }
+ break;
+
+ /* x ok iff flag still set & 2nd char */
+ case 'x': case 'X':
+ if (flags & PFXOK && p == buf + 1)
+ {
+ base = 16; /* if %i */
+ flags &= ~PFXOK;
+ goto ok;
+ }
+ break;
+ }
+
+ /*
+ ** If we got here, c is not a legal character
+ ** for a number. Stop accumulating digits.
+ */
+
+ break;
+ ok:
+ /* c is legal: store it and look at the next. */
+ *p++ = c;
+ if (--fp->f_r > 0)
+ fp->f_p++;
+ else if (sm_refill(fp, SM_TIME_FOREVER))
+ break; /* SM_IO_EOF */
+ }
+
+ /*
+ ** If we had only a sign, it is no good; push
+ ** back the sign. If the number ends in `x',
+ ** it was [sign] '0' 'x', so push back the x
+ ** and treat it as [sign] '0'.
+ */
+
+ if (flags & NDIGITS)
+ {
+ if (p > buf)
+ (void) sm_io_ungetc(fp, SM_TIME_DEFAULT,
+ *(unsigned char *)--p);
+ goto match_failure;
+ }
+ c = ((unsigned char *)p)[-1];
+ if (c == 'x' || c == 'X')
+ {
+ --p;
+ (void) sm_io_ungetc(fp, SM_TIME_DEFAULT, c);
+ }
+ if ((flags & SUPPRESS) == 0)
+ {
+ ULONGLONG_T res;
+
+ *p = 0;
+ res = (*ccfn)(buf, (char **)NULL, base);
+ if (flags & POINTER)
+ *SM_VA_ARG(ap, void **) =
+ (void *)(long) res;
+ else if (flags & QUAD)
+ *SM_VA_ARG(ap, LONGLONG_T *) = res;
+ else if (flags & LONG)
+ *SM_VA_ARG(ap, long *) = res;
+ else if (flags & SHORT)
+ *SM_VA_ARG(ap, short *) = res;
+ else
+ *SM_VA_ARG(ap, int *) = res;
+ nassigned++;
+ }
+ nread += p - buf;
+ break;
+
+ case CT_FLOAT:
+ /* scan a floating point number as if by strtod */
+ if (width == 0 || width > sizeof(buf) - 1)
+ width = sizeof(buf) - 1;
+ flags |= SIGNOK | NDIGITS | DPTOK | EXPOK;
+ for (p = buf; width; width--)
+ {
+ c = *fp->f_p;
+
+ /*
+ ** This code mimicks the integer conversion
+ ** code, but is much simpler.
+ */
+
+ switch (c)
+ {
+
+ case '0': case '1': case '2': case '3':
+ case '4': case '5': case '6': case '7':
+ case '8': case '9':
+ flags &= ~(SIGNOK | NDIGITS);
+ goto fok;
+
+ case '+': case '-':
+ if (flags & SIGNOK)
+ {
+ flags &= ~SIGNOK;
+ goto fok;
+ }
+ break;
+ case '.':
+ if (flags & DPTOK)
+ {
+ flags &= ~(SIGNOK | DPTOK);
+ goto fok;
+ }
+ break;
+ case 'e': case 'E':
+
+ /* no exponent without some digits */
+ if ((flags&(NDIGITS|EXPOK)) == EXPOK)
+ {
+ flags =
+ (flags & ~(EXPOK|DPTOK)) |
+ SIGNOK | NDIGITS;
+ goto fok;
+ }
+ break;
+ }
+ break;
+ fok:
+ *p++ = c;
+ if (--fp->f_r > 0)
+ fp->f_p++;
+ else if (sm_refill(fp, SM_TIME_FOREVER))
+ break; /* SM_IO_EOF */
+ }
+
+ /*
+ ** If no digits, might be missing exponent digits
+ ** (just give back the exponent) or might be missing
+ ** regular digits, but had sign and/or decimal point.
+ */
+
+ if (flags & NDIGITS)
+ {
+ if (flags & EXPOK)
+ {
+ /* no digits at all */
+ while (p > buf)
+ (void) sm_io_ungetc(fp,
+ SM_TIME_DEFAULT,
+ *(unsigned char *)--p);
+ goto match_failure;
+ }
+
+ /* just a bad exponent (e and maybe sign) */
+ c = *(unsigned char *) --p;
+ if (c != 'e' && c != 'E')
+ {
+ (void) sm_io_ungetc(fp, SM_TIME_DEFAULT,
+ c); /* sign */
+ c = *(unsigned char *)--p;
+ }
+ (void) sm_io_ungetc(fp, SM_TIME_DEFAULT, c);
+ }
+ if ((flags & SUPPRESS) == 0)
+ {
+ double res;
+
+ *p = 0;
+ res = strtod(buf, (char **) NULL);
+ if (flags & LONG)
+ *SM_VA_ARG(ap, double *) = res;
+ else
+ *SM_VA_ARG(ap, float *) = res;
+ nassigned++;
+ }
+ nread += p - buf;
+ break;
+ }
+ }
+input_failure:
+ if (evt != NULL)
+ sm_clrevent(evt); /* undo our timeout */
+ return nassigned ? nassigned : -1;
+match_failure:
+ if (evt != NULL)
+ sm_clrevent(evt); /* undo our timeout */
+ return nassigned;
+}
+
+/*
+** SM_SCCL -- sequenced character comparison list
+**
+** Fill in the given table from the scanset at the given format
+** (just after `['). Return a pointer to the character past the
+** closing `]'. The table has a 1 wherever characters should be
+** considered part of the scanset.
+**
+** Parameters:
+** tab -- array flagging "active" char's to match (returned)
+** fmt -- character list (within "[]")
+**
+** Results:
+*/
+
+static unsigned char *
+sm_sccl(tab, fmt)
+ register char *tab;
+ register unsigned char *fmt;
+{
+ register int c, n, v;
+
+ /* first `clear' the whole table */
+ c = *fmt++; /* first char hat => negated scanset */
+ if (c == '^')
+ {
+ v = 1; /* default => accept */
+ c = *fmt++; /* get new first char */
+ }
+ else
+ v = 0; /* default => reject */
+
+ /* should probably use memset here */
+ for (n = 0; n < 256; n++)
+ tab[n] = v;
+ if (c == 0)
+ return fmt - 1; /* format ended before closing ] */
+
+ /*
+ ** Now set the entries corresponding to the actual scanset
+ ** to the opposite of the above.
+ **
+ ** The first character may be ']' (or '-') without being special;
+ ** the last character may be '-'.
+ */
+
+ v = 1 - v;
+ for (;;)
+ {
+ tab[c] = v; /* take character c */
+doswitch:
+ n = *fmt++; /* and examine the next */
+ switch (n)
+ {
+
+ case 0: /* format ended too soon */
+ return fmt - 1;
+
+ case '-':
+ /*
+ ** A scanset of the form
+ ** [01+-]
+ ** is defined as `the digit 0, the digit 1,
+ ** the character +, the character -', but
+ ** the effect of a scanset such as
+ ** [a-zA-Z0-9]
+ ** is implementation defined. The V7 Unix
+ ** scanf treats `a-z' as `the letters a through
+ ** z', but treats `a-a' as `the letter a, the
+ ** character -, and the letter a'.
+ **
+ ** For compatibility, the `-' is not considerd
+ ** to define a range if the character following
+ ** it is either a close bracket (required by ANSI)
+ ** or is not numerically greater than the character
+ ** we just stored in the table (c).
+ */
+
+ n = *fmt;
+ if (n == ']' || n < c)
+ {
+ c = '-';
+ break; /* resume the for(;;) */
+ }
+ fmt++;
+ do
+ {
+ /* fill in the range */
+ tab[++c] = v;
+ } while (c < n);
+#if 1 /* XXX another disgusting compatibility hack */
+
+ /*
+ ** Alas, the V7 Unix scanf also treats formats
+ ** such as [a-c-e] as `the letters a through e'.
+ ** This too is permitted by the standard....
+ */
+
+ goto doswitch;
+#else
+ c = *fmt++;
+ if (c == 0)
+ return fmt - 1;
+ if (c == ']')
+ return fmt;
+ break;
+#endif
+
+ case ']': /* end of scanset */
+ return fmt;
+
+ default: /* just another character */
+ c = n;
+ break;
+ }
+ }
+ /* NOTREACHED */
+}
diff --git a/usr/src/cmd/sendmail/libsm/vprintf.c b/usr/src/cmd/sendmail/libsm/vprintf.c
new file mode 100644
index 0000000000..57acfb471a
--- /dev/null
+++ b/usr/src/cmd/sendmail/libsm/vprintf.c
@@ -0,0 +1,41 @@
+/*
+ * Copyright (c) 2000-2001 Sendmail, Inc. and its suppliers.
+ * All rights reserved.
+ * Copyright (c) 1990, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Chris Torek.
+ *
+ * By using this file, you agree to the terms and conditions set
+ * forth in the LICENSE file which can be found at the top level of
+ * the sendmail distribution.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#include <sm/gen.h>
+SM_RCSID("@(#)$Id: vprintf.c,v 1.12 2001/01/24 01:27:26 gshapiro Exp $")
+#include <sm/io.h>
+#include "local.h"
+
+/*
+** SM_VPRINTF -- print to standard out with variable length args
+**
+** Parameters:
+** timeout -- length of time allow to do the print
+** fmt -- the format of the output
+** ap -- the variable number of args to be used for output
+**
+** Returns:
+** as sm_io_vfprintf() does.
+*/
+
+int
+sm_vprintf(timeout, fmt, ap)
+ int timeout;
+ char const *fmt;
+ SM_VA_LOCAL_DECL
+{
+ return sm_io_vfprintf(smiostdout, timeout, fmt, ap);
+}
diff --git a/usr/src/cmd/sendmail/libsm/vsnprintf.c b/usr/src/cmd/sendmail/libsm/vsnprintf.c
new file mode 100644
index 0000000000..67f9f0b121
--- /dev/null
+++ b/usr/src/cmd/sendmail/libsm/vsnprintf.c
@@ -0,0 +1,80 @@
+/*
+ * Copyright (c) 2000-2001 Sendmail, Inc. and its suppliers.
+ * All rights reserved.
+ * Copyright (c) 1990, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Chris Torek.
+ *
+ * By using this file, you agree to the terms and conditions set
+ * forth in the LICENSE file which can be found at the top level of
+ * the sendmail distribution.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#include <sm/gen.h>
+SM_RCSID("@(#)$Id: vsnprintf.c,v 1.21 2001/03/04 23:28:41 ca Exp $")
+#include <limits.h>
+#include <sm/io.h>
+#include "local.h"
+
+/*
+** SM_VSNPRINTF -- format data for "output" into a string
+**
+** Assigned 'str' to a "fake" file pointer. This allows common
+** o/p formatting function sm_vprintf() to be used.
+**
+** Parameters:
+** str -- location for output
+** n -- maximum size for o/p
+** fmt -- format directives
+** ap -- data unit vectors for use by 'fmt'
+**
+** Results:
+** result from sm_io_vfprintf()
+**
+** Side Effects:
+** Limits the size ('n') to INT_MAX.
+*/
+
+int
+sm_vsnprintf(str, n, fmt, ap)
+ char *str;
+ size_t n;
+ const char *fmt;
+ SM_VA_LOCAL_DECL
+{
+ int ret;
+ char dummy;
+ SM_FILE_T fake;
+
+ /* While snprintf(3) specifies size_t stdio uses an int internally */
+ if (n > INT_MAX)
+ n = INT_MAX;
+
+ /* Stdio internals do not deal correctly with zero length buffer */
+ if (n == 0)
+ {
+ str = &dummy;
+ n = 1;
+ }
+ fake.sm_magic = SmFileMagic;
+ fake.f_timeout = SM_TIME_FOREVER;
+ fake.f_timeoutstate = SM_TIME_BLOCK;
+ fake.f_file = -1;
+ fake.f_flags = SMWR | SMSTR;
+ fake.f_bf.smb_base = fake.f_p = (unsigned char *)str;
+ fake.f_bf.smb_size = fake.f_w = n - 1;
+ fake.f_close = NULL;
+ fake.f_open = NULL;
+ fake.f_read = NULL;
+ fake.f_write = NULL;
+ fake.f_seek = NULL;
+ fake.f_setinfo = fake.f_getinfo = NULL;
+ fake.f_type = "sm_vsnprintf:fake";
+ ret = sm_io_vfprintf(&fake, SM_TIME_FOREVER, fmt, ap);
+ *fake.f_p = 0;
+ return ret;
+}
diff --git a/usr/src/cmd/sendmail/libsm/wbuf.c b/usr/src/cmd/sendmail/libsm/wbuf.c
new file mode 100644
index 0000000000..21ac5c82b6
--- /dev/null
+++ b/usr/src/cmd/sendmail/libsm/wbuf.c
@@ -0,0 +1,90 @@
+/*
+ * Copyright (c) 2000-2001 Sendmail, Inc. and its suppliers.
+ * All rights reserved.
+ * Copyright (c) 1990, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Chris Torek.
+ *
+ * By using this file, you agree to the terms and conditions set
+ * forth in the LICENSE file which can be found at the top level of
+ * the sendmail distribution.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#include <sm/gen.h>
+SM_RCSID("@(#)$Id: wbuf.c,v 1.19 2001/03/05 03:22:41 ca Exp $")
+#include <errno.h>
+#include <sm/io.h>
+#include "local.h"
+
+/* Note: This function is called from a macro located in <sm/io.h> */
+
+/*
+** SM_WBUF -- write character to and flush (likely now full) buffer
+**
+** Write the given character into the (probably full) buffer for
+** the given file. Flush the buffer out if it is or becomes full,
+** or if c=='\n' and the file is line buffered.
+**
+** Parameters:
+** fp -- the file pointer
+** timeout -- time to complete operation (milliseconds)
+** c -- int representation of the character to add
+**
+** Results:
+** Failure: -1 and sets errno
+** Success: int value of 'c'
+*/
+
+int
+sm_wbuf(fp, timeout, c)
+ register SM_FILE_T *fp;
+ int timeout;
+ register int c;
+{
+ register int n;
+
+ /*
+ ** In case we cannot write, or longjmp takes us out early,
+ ** make sure w is 0 (if fully- or un-buffered) or -bf.smb_size
+ ** (if line buffered) so that we will get called again.
+ ** If we did not do this, a sufficient number of sm_io_putc()
+ ** calls might wrap w from negative to positive.
+ */
+
+ fp->f_w = fp->f_lbfsize;
+ if (cantwrite(fp))
+ {
+ errno = EBADF;
+ return SM_IO_EOF;
+ }
+ c = (unsigned char)c;
+
+ /*
+ ** If it is completely full, flush it out. Then, in any case,
+ ** stuff c into the buffer. If this causes the buffer to fill
+ ** completely, or if c is '\n' and the file is line buffered,
+ ** flush it (perhaps a second time). The second flush will always
+ ** happen on unbuffered streams, where bf.smb_size==1; sm_io_flush()
+ ** guarantees that sm_io_putc() will always call sm_wbuf() by setting
+ ** w to 0, so we need not do anything else.
+ ** Note for the timeout, only one of the sm_io_flush's will get called.
+ */
+
+ n = fp->f_p - fp->f_bf.smb_base;
+ if (n >= fp->f_bf.smb_size)
+ {
+ if (sm_io_flush(fp, timeout))
+ return SM_IO_EOF; /* sm_io_flush() sets errno */
+ n = 0;
+ }
+ fp->f_w--;
+ *fp->f_p++ = c;
+ if (++n == fp->f_bf.smb_size || (fp->f_flags & SMLBF && c == '\n'))
+ if (sm_io_flush(fp, timeout))
+ return SM_IO_EOF; /* sm_io_flush() sets errno */
+ return c;
+}
diff --git a/usr/src/cmd/sendmail/libsm/wsetup.c b/usr/src/cmd/sendmail/libsm/wsetup.c
new file mode 100644
index 0000000000..e7a5e7924b
--- /dev/null
+++ b/usr/src/cmd/sendmail/libsm/wsetup.c
@@ -0,0 +1,86 @@
+/*
+ * Copyright (c) 2000-2002 Sendmail, Inc. and its suppliers.
+ * All rights reserved.
+ * Copyright (c) 1990, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Chris Torek.
+ *
+ * By using this file, you agree to the terms and conditions set
+ * forth in the LICENSE file which can be found at the top level of
+ * the sendmail distribution.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#include <sm/gen.h>
+SM_RCSID("@(#)$Id: wsetup.c,v 1.20 2002/02/07 18:02:45 ca Exp $")
+#include <stdlib.h>
+#include <errno.h>
+#include <sm/io.h>
+#include "local.h"
+
+/*
+** SM_WSETUP -- check writing is safe
+**
+** Various output routines call wsetup to be sure it is safe to write,
+** because either flags does not include SMMWR, or buf is NULL.
+** Used in the macro "cantwrite" found in "local.h".
+**
+** Parameters:
+** fp -- the file pointer
+**
+** Results:
+** Failure: SM_IO_EOF and sets errno
+** Success: 0 (zero)
+*/
+
+int
+sm_wsetup(fp)
+ register SM_FILE_T *fp;
+{
+ /* make sure stdio is set up */
+ if (!Sm_IO_DidInit)
+ sm_init();
+
+ /* If we are not writing, we had better be reading and writing. */
+ if ((fp->f_flags & SMWR) == 0)
+ {
+ if ((fp->f_flags & SMRW) == 0)
+ {
+ errno = EBADF;
+ return SM_IO_EOF;
+ }
+ if (fp->f_flags & SMRD)
+ {
+ /* clobber any ungetc data */
+ if (HASUB(fp))
+ FREEUB(fp);
+
+ /* discard read buffer */
+ fp->f_flags &= ~(SMRD|SMFEOF);
+ fp->f_r = 0;
+ fp->f_p = fp->f_bf.smb_base;
+ }
+ fp->f_flags |= SMWR;
+ }
+
+ /* Make a buffer if necessary, then set w. */
+ if (fp->f_bf.smb_base == NULL)
+ sm_makebuf(fp);
+ if (fp->f_flags & SMLBF)
+ {
+ /*
+ ** It is line buffered, so make lbfsize be -bufsize
+ ** for the sm_putc() macro. We will change lbfsize back
+ ** to 0 whenever we turn off SMWR.
+ */
+
+ fp->f_w = 0;
+ fp->f_lbfsize = -fp->f_bf.smb_size;
+ }
+ else
+ fp->f_w = fp->f_flags & SMNBF ? 0 : fp->f_bf.smb_size;
+ return 0;
+}
diff --git a/usr/src/cmd/sendmail/libsm/xtrap.c b/usr/src/cmd/sendmail/libsm/xtrap.c
new file mode 100644
index 0000000000..5f82c3e0a4
--- /dev/null
+++ b/usr/src/cmd/sendmail/libsm/xtrap.c
@@ -0,0 +1,23 @@
+/*
+ * Copyright (c) 2000 Sendmail, Inc. and its suppliers.
+ * All rights reserved.
+ *
+ * By using this file, you agree to the terms and conditions set
+ * forth in the LICENSE file which can be found at the top level of
+ * the sendmail distribution.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#include <sm/gen.h>
+SM_RCSID("@(#)$Id: xtrap.c,v 1.3 2000/12/08 08:03:09 rodney Exp $")
+
+#include <sm/xtrap.h>
+
+SM_ATOMIC_UINT_T SmXtrapCount;
+
+SM_DEBUG_T SmXtrapDebug = SM_DEBUG_INITIALIZER("sm_xtrap",
+ "@(#)$Debug: sm_xtrap - raise exception at N'th xtrap point $");
+
+SM_DEBUG_T SmXtrapReport = SM_DEBUG_INITIALIZER("sm_xtrap_report",
+ "@(#)$Debug: sm_xtrap_report - report xtrap count on exit $");