OpenSolaris_b135/lib/libnisdb/nis_parse_ldap_util.c

Compare this file to the similar file:
Show the results in this format:

/*
 * 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 2004 Sun Microsystems, Inc.  All rights reserved.
 * Use is subject to license terms.
 */

#pragma ident	"%Z%%M%	%I%	%E% SMI"

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <ctype.h>
#include <fcntl.h>
#include <unistd.h>
#include <errno.h>
#include <locale.h>
#include <lber.h>
#include <ldap.h>
#include <syslog.h>
#include <dlfcn.h>	/* for dynamic loading only */

#include "ldap_parse.h"
#include "nis_parse_ldap_conf.h"
#include "nis_parse_ldap_err.h"
#include "ldap_util.h"
#include "ldap_util.h"

void append_dot(char **str);
void	append_comma(char **str);
bool_t make_full_dn(char **dn, const char *base);
bool_t make_fqdn(__nis_object_dn_t *dn, const char *base);
char *get_default_ldap_base(const char *domain);
bool_t add_domain(char **objName, const char *domain);
bool_t add_column(__nis_table_mapping_t *t, const char *col_name);
__nis_mapping_rule_t **dup_mapping_rules(
	__nis_mapping_rule_t **rules, int n_rules);
__nis_mapping_rule_t *dup_mapping_rule(
	__nis_mapping_rule_t *in);
void *s_malloc(size_t size);
__nis_mapping_format_t *dup_format_mapping(
	__nis_mapping_format_t *in);
bool_t dup_mapping_element(__nis_mapping_element_t *in,
	__nis_mapping_element_t *out);

extern FILE *cons;

/*
 * FUNCTION:	free_parse_structs
 *
 *	Release the resources in parse results
 *
 */

void
free_parse_structs()
{
	__nis_table_mapping_t	*t;
	__nis_table_mapping_t	*t1;

	free_proxy_info(&proxyInfo);
	for (t = ldapTableMapping; t != NULL; t = t1) {
		t1 = t->next;
		free_table_mapping(t);
	}
	ldapTableMapping = NULL;
}

/*
 * FUNCTION:	initialize_parse_structs
 *
 *	Initialize fields to unset values
 *
 * INPUT:		__nis_ldap_proxy_info, __nis_config_t
 * 			and __nisdb_table_mapping_t structures
 */

void
initialize_parse_structs(
	__nis_ldap_proxy_info	*proxy_info,
	__nis_config_t		*config_info,
	__nisdb_table_mapping_t	*table_info)
{
	proxy_info->default_servers = NULL;
	proxy_info->auth_method = (auth_method_t)NO_VALUE_SET;
	proxy_info->tls_method = (tls_method_t)NO_VALUE_SET;
	proxy_info->tls_cert_db = NULL;
	proxy_info->default_search_base = NULL;
	proxy_info->proxy_dn = NULL;
	proxy_info->proxy_passwd = NULL;
	proxy_info->default_nis_domain = NULL;
	proxy_info->bind_timeout.tv_sec = (time_t)NO_VALUE_SET;
	proxy_info->bind_timeout.tv_usec = 0;
	proxy_info->search_timeout.tv_sec = (time_t)NO_VALUE_SET;
	proxy_info->search_timeout.tv_usec = 0;
	proxy_info->modify_timeout.tv_sec = (time_t)NO_VALUE_SET;
	proxy_info->modify_timeout.tv_usec = 0;
	proxy_info->add_timeout.tv_sec = (time_t)NO_VALUE_SET;
	proxy_info->add_timeout.tv_usec = 0;
	proxy_info->delete_timeout.tv_sec = (time_t)NO_VALUE_SET;
	proxy_info->delete_timeout.tv_usec = 0;
	proxy_info->search_time_limit = (int)NO_VALUE_SET;
	proxy_info->search_size_limit = (int)NO_VALUE_SET;
	proxy_info->follow_referral = (follow_referral_t)NO_VALUE_SET;


	config_info->initialUpdate = (__nis_initial_update_t)NO_VALUE_SET;
	config_info->threadCreationError =
		(__nis_thread_creation_error_t)NO_VALUE_SET;
	config_info->threadCreationErrorTimeout.attempts = NO_VALUE_SET;
	config_info->threadCreationErrorTimeout.timeout = (time_t)NO_VALUE_SET;
	config_info->dumpError = (__nis_dump_error_t)NO_VALUE_SET;
	config_info->dumpErrorTimeout.attempts = NO_VALUE_SET;
	config_info->dumpErrorTimeout.timeout = (time_t)NO_VALUE_SET;
	config_info->resyncService = (__nis_resync_service_t)NO_VALUE_SET;
	config_info->updateBatching = (__nis_update_batching_t)NO_VALUE_SET;
	config_info->updateBatchingTimeout.timeout = (time_t)NO_VALUE_SET;
	config_info->numberOfServiceThreads = (int)NO_VALUE_SET;
	config_info->emulate_yp = (int)NO_VALUE_SET;
	config_info->maxRPCRecordSize = (int)NO_VALUE_SET;

	table_info->retrieveError = (__nis_retrieve_error_t)NO_VALUE_SET;
	table_info->retrieveErrorRetry.attempts = NO_VALUE_SET;
	table_info->retrieveErrorRetry.timeout = (time_t)NO_VALUE_SET;
	table_info->storeError = (__nis_store_error_t)NO_VALUE_SET;
	table_info->storeErrorRetry.attempts = NO_VALUE_SET;
	table_info->storeErrorRetry.timeout = (time_t)NO_VALUE_SET;
	table_info->refreshError = (__nis_refresh_error_t)NO_VALUE_SET;
	table_info->refreshErrorRetry.attempts = NO_VALUE_SET;
	table_info->refreshErrorRetry.timeout = (time_t)NO_VALUE_SET;
	table_info->matchFetch = (__nis_match_fetch_t)NO_VALUE_SET;
}

/*
 * FUNCTION:	free_mapping_rule
 *
 *	Frees __nis_mapping_rule_t
 *
 * INPUT:		__nis_mapping_rule_t
 */

void
free_mapping_rule(__nis_mapping_rule_t	*rule)
{
	int			i;
	__nis_mapping_rlhs_t	*r;

	if (rule != NULL) {
		r = &rule->lhs;
		for (i = 0; i < r->numElements; i++)
			free_mapping_element(&r->element[i]);
		if (r->element != NULL)
			free(r->element);

		r = &rule->rhs;
		for (i = 0; i < r->numElements; i++)
			free_mapping_element(&r->element[i]);
		if (r->element != NULL)
			free(r->element);

		free(rule);
	}
}

/*
 * FUNCTION:	free_mapping_element
 *
 *	Frees __nis_mapping_element_t
 *
 * INPUT:		__nis_mapping_element_t
 */

void
free_mapping_element(__nis_mapping_element_t *e)
{
	int	i;

	if (e == NULL)
		return;

	switch (e->type) {
	    case me_item:
		free_mapping_item(&e->element.item);
		break;
	    case me_print:
		if (e->element.print.fmt != NULL)
			free_mapping_format(e->element.print.fmt);
		e->element.print.fmt = NULL;
		for (i = 0; i < e->element.print.numSubElements; i++)
			free_mapping_sub_element(
				&e->element.print.subElement[i]);
		e->element.print.numSubElements = 0;
		if (e->element.print.subElement != NULL)
			free(e->element.print.subElement);
		e->element.print.subElement = NULL;
		break;
	    case me_split:
		free_mapping_item(&e->element.split.item);
		break;
	    case me_match:
		if (e->element.match.fmt != NULL)
			free_mapping_format(e->element.match.fmt);
		e->element.match.fmt = NULL;
		for (i = 0; i < e->element.match.numItems; i++)
			free_mapping_item(&e->element.match.item[i]);
		e->element.match.numItems = 0;
		if (e->element.match.item != NULL)
		    free(e->element.match.item);
		e->element.match.item = NULL;
		break;
	    case me_extract:
		if (e->element.extract.fmt != NULL)
			free_mapping_format(e->element.extract.fmt);
		e->element.extract.fmt = NULL;
		free_mapping_item(&e->element.extract.item);
		break;
	}
	e = NULL;
}

/*
 * FUNCTION:	free_table_mapping
 *
 *	Frees __nis_table_mapping_t
 *
 * INPUT:		__nis_table_mapping_t
 */

/*
 * free_table_mapping does not remove the table mapping from
 * its hashed list
 */

void
free_table_mapping(__nis_table_mapping_t *mapping)
{
	int	i;

	if (mapping == NULL)
		return;

	if (mapping->dbId != NULL)
		free(mapping->dbId);
	mapping->dbId = NULL;

	if (mapping->objName != NULL)
		free(mapping->objName);
	mapping->objName = NULL;

	for (i = 0; i < mapping->index.numIndexes; i++) {
		free(mapping->index.name[i]);
		free_mapping_format(mapping->index.value[i]);
	}

	if (mapping->index.name != NULL)
		free(mapping->index.name);
	mapping->index.name = NULL;

	if (mapping->index.value != NULL)
		free(mapping->index.value);
	mapping->index.value = NULL;

	mapping->index.numIndexes = 0;

	if (mapping->column != NULL) {
		for (i = 0; i < mapping->numColumns; i++) {
			free(mapping->column[i]);
		}
		mapping->numColumns = 0;
		free(mapping->column);
		mapping->column = NULL;
	}

	if (mapping->commentChar != NULL)
		mapping->commentChar = NULL;

	if (mapping->objectDN != NULL)
		free_object_dn(mapping->objectDN);
	mapping->objectDN = NULL;

	if (mapping->separatorStr != NULL)
		mapping->separatorStr = NULL;

	for (i = 0; i < mapping->numRulesFromLDAP; i++) {
		if (mapping->ruleFromLDAP[i]) /* See Comment below */
			free_mapping_rule(mapping->ruleFromLDAP[i]);
	}
	mapping->numRulesFromLDAP = 0;

	if (mapping->ruleFromLDAP != NULL)
		free(mapping->ruleFromLDAP);
	mapping->ruleFromLDAP = NULL;

	for (i = 0; i < mapping->numRulesToLDAP; i++) {
		if (mapping->ruleToLDAP[i])
		/*
		 * Normally mapping->ruleToLDAP[i] should
		 * always be non-null if
		 * mapping->numRulesToLDAP is > 0.
		 * However it is possible to have data
		 * corruption where numRulesToLDAP gets
		 * some integer value even though no real
		 * data is present in mapping->ruleToLDAP.
		 */
			free_mapping_rule(mapping->ruleToLDAP[i]);
	}
	mapping->numRulesToLDAP = 0;

	if (mapping->ruleToLDAP != NULL)
		free(mapping->ruleToLDAP);
	mapping->ruleToLDAP = NULL;

	if (mapping->e != NULL) {
		/* Similar logic as in above comment applies. */
		for (i = 0; i <= mapping->numSplits; i++) {
			free_mapping_element(&mapping->e[i]);
		}
		free(mapping->e);
	}
	mapping->e = NULL;

	mapping->numSplits = 0;

	free(mapping);
}

/*
 * FUNCTION:	free_config_info
 *
 *	Frees __nis_config_info_t
 *
 * INPUT:		__nis_config_info_t
 */

void
free_config_info(__nis_config_info_t *config_info)
{
	if (config_info->config_dn != NULL)
		free(config_info->config_dn);
	config_info->config_dn = NULL;

	if (config_info->default_servers != NULL)
		free(config_info->default_servers);
	config_info->default_servers = NULL;

	if (config_info->proxy_dn != NULL)
		free(config_info->proxy_dn);
	config_info->proxy_dn = NULL;

	if (config_info->proxy_passwd != NULL)
		free(config_info->proxy_passwd);
	config_info->proxy_passwd = NULL;

	if (config_info->tls_cert_db != NULL)
		free(config_info->tls_cert_db);
	config_info->tls_cert_db = NULL;
}

/*
 * FUNCTION:	free_proxy_info
 *
 *	Frees __nis_ldap_proxy_info
 *
 * INPUT:		__nis_ldap_proxy_info
 */

void
free_proxy_info(__nis_ldap_proxy_info *proxy_info)
{
	if (proxy_info->tls_cert_db != NULL)
		free(proxy_info->tls_cert_db);
	proxy_info->tls_cert_db = NULL;

	if (proxy_info->default_servers != NULL)
		free(proxy_info->default_servers);
	proxy_info->default_servers = NULL;

	if (proxy_info->default_search_base != NULL)
		free(proxy_info->default_search_base);
	proxy_info->default_search_base = NULL;

	if (proxy_info->proxy_dn != NULL)
		free(proxy_info->proxy_dn);
	proxy_info->proxy_dn = NULL;

	if (proxy_info->proxy_passwd != NULL)
		free(proxy_info->proxy_passwd);
	proxy_info->proxy_passwd = NULL;

	if (proxy_info->default_nis_domain != NULL)
		free(proxy_info->default_nis_domain);
	proxy_info->default_nis_domain = NULL;
}

/*
 * FUNCTION:	free_object_dn
 *
 *	Frees __nis_object_dn_t
 *
 * INPUT:		__nis_object_dn_t
 */

void
free_object_dn(__nis_object_dn_t *obj_dn)
{
	__nis_object_dn_t	*t;
	int			i;

	while (obj_dn != NULL) {
		if (obj_dn->read.base != NULL)
			free(obj_dn->read.base);
		obj_dn->read.base = NULL;
		if (obj_dn->read.attrs != NULL)
			free(obj_dn->read.attrs);
		obj_dn->read.attrs = NULL;
		if (obj_dn->write.base != NULL)
			free(obj_dn->write.base);
		obj_dn->write.base = NULL;
		if (obj_dn->write.attrs != NULL)
			free(obj_dn->write.attrs);
		obj_dn->write.attrs = NULL;
		if (obj_dn->dbIdName != NULL)
			free(obj_dn->dbIdName);
		obj_dn->dbIdName = NULL;
		for (i = 0; i < obj_dn->numDbIds; i++)
			free_mapping_rule(obj_dn->dbId[i]);
		obj_dn->numDbIds = 0;

		if (obj_dn->dbId != NULL)
			free(obj_dn->dbId);
		obj_dn->dbId = NULL;

		t = obj_dn;
		obj_dn = obj_dn->next;
		free(t);
	}
}

/*
 * FUNCTION:	free_index
 *
 *	Frees __nis_index_t
 *
 * INPUT:		__nis_index_t
 */

void
free_index(__nis_index_t *index)
{
	int	i;
	for (i = 0; i < index->numIndexes; i++) {
		free(index->name[i]);
		free_mapping_format(index->value[i]);
	}
	index->numIndexes = 0;
	if (index->name != NULL)
		free(index->name);
	index->name = NULL;
	if (index->value != NULL)
		free(index->value);
	index->value = NULL;
}

/*
 * FUNCTION:	free_mapping_item
 *
 *	Frees __nis_mapping_item_t
 *
 * INPUT:		__nis_mapping_item_t
 */

void
free_mapping_item(__nis_mapping_item_t	*item)
{
	if (item == NULL)
		return;

	if (item->name != NULL)
		free(item->name);
	item->name = NULL;
	if (item->type == mit_nisplus) {
		free_index(&item->searchSpec.obj.index);
		if (item->searchSpec.obj.name != NULL)
			free(item->searchSpec.obj.name);
		item->searchSpec.obj.name = NULL;
	} else if (item->type == mit_ldap) {
		if (item->searchSpec.triple.base != NULL)
			free(item->searchSpec.triple.base);
		item->searchSpec.triple.base = NULL;
		if (item->searchSpec.triple.attrs != NULL)
			free(item->searchSpec.triple.attrs);
		item->searchSpec.triple.attrs = NULL;
		if (item->searchSpec.triple.element != NULL) {
			free_mapping_element(
				item->searchSpec.triple.element);
			free(item->searchSpec.triple.element);
		}
		item->searchSpec.triple.element = NULL;
	}
	if (item->exItem != NULL) {
		free_mapping_item(item->exItem);
		free(item->exItem);
		item->exItem = 0;
	}
}

/*
 * FUNCTION:	free_mapping_format
 *
 *	Frees __nis_mapping_format_t
 *
 * INPUT:		__nis_mapping_format_t
 */

void
free_mapping_format(__nis_mapping_format_t *fmt)
{
	__nis_mapping_format_t *f = fmt;

	while (fmt->type != mmt_end) {
		switch (fmt->type) {
		    case mmt_item:
			break;
		    case mmt_string:
			if (fmt->match.string != NULL)
				free(fmt->match.string);
			fmt->match.string = NULL;
			break;
		    case mmt_single:
			if (fmt->match.single.lo != NULL)
				free(fmt->match.single.lo);
			fmt->match.single.lo = NULL;
			if (fmt->match.single.hi != NULL)
				free(fmt->match.single.hi);
			fmt->match.single.hi = NULL;
			break;
		    case mmt_limit:
			break;
		    case mmt_any:
			break;
		    case mmt_berstring:
		    case mmt_berstring_null:
			if (fmt->match.berString != NULL)
				free(fmt->match.berString);
			fmt->match.berString = NULL;
			break;
		    case mmt_begin:
			break;
		    case mmt_end:
			break;
		}
		fmt++;
	}
	free(f);
}

/*
 * FUNCTION:	free_mapping_sub_element
 *
 *	Frees __nis_mapping_sub_element_t
 *
 * INPUT:		__nis_mapping_sub_element_t
 */

void
free_mapping_sub_element(__nis_mapping_sub_element_t *sub)
{
	int	i;

	switch (sub->type) {
	    case me_item:
		free_mapping_item(&sub->element.item);
		break;
	    case me_print:
		if (sub->element.print.fmt != NULL)
			free_mapping_format(sub->element.print.fmt);
		sub->element.print.fmt = NULL;
		for (i = 0; i < sub->element.print.numItems; i++)
			free_mapping_item(&sub->element.print.item[i]);
		sub->element.print.numItems = 0;
		if (sub->element.print.item != NULL)
			free(sub->element.print.item);
		sub->element.print.item = NULL;
		break;
	    case me_split:
		free_mapping_item(&sub->element.split.item);
		break;
	    case me_extract:
		if (sub->element.extract.fmt != NULL)
			free_mapping_format(sub->element.extract.fmt);
		sub->element.extract.fmt = NULL;
		free_mapping_item(&sub->element.extract.item);
		break;
	}
}

/*
 * FUNCTION:	read_line
 *
 *	Gets next line in buffer - using '\' at end of line
 *  to indicate continuation. Lines beginning with # are
 *	ignored. start_line_num and start_line_num are
 *	maintained to track the line number currently being
 *	parsed.
 *
 * RETURN VALUE:	The number of characters read. 0 for
 *                      eof, -1 for error
 *
 * INPUT:		file descriptor, buffer, and buffer size
 */

int
read_line(int fd, char *buffer, int buflen)
{
	int		linelen;
	int		rc;
	char		c;
	bool_t		skip_line	= FALSE;
	bool_t		begin_line	= TRUE;
	static bool_t	prev_cr		= FALSE;

	start_line_num = cur_line_num;
	(void) memset(buffer, 0, buflen);
	for (; p_error == no_parse_error; ) {
		linelen = 0;
		while (linelen < buflen) {
			rc = read(fd, &c, 1);
			if (1 == rc) {
				if (c == '\n' || c == '\r') {
					if (c == '\n') {
						if (prev_cr) {
							prev_cr = FALSE;
							continue;
						} else {
							if (linelen == 0)
							    start_line_num =
								cur_line_num;
							else {
								if (
								is_string_ok(
								buffer,
								linelen)) {
								(void) memset(
								buffer, 0,
								linelen);
								linelen = 0;
								cur_line_num++;
								begin_line =
									TRUE;
								continue;
								}
							}
							cur_line_num++;
						}
						prev_cr = FALSE;
					} else {
						prev_cr = TRUE;
						if (linelen == 0)
						    start_line_num =
							cur_line_num;
						cur_line_num++;
					}
					if (skip_line) {
						skip_line = FALSE;
						if (linelen == 0)
						    start_line_num =
							cur_line_num;
					} else if (linelen > 0 &&
					    buffer[linelen - 1]
					    == ESCAPE_CHAR) {
						--linelen;
					} else if (linelen > 0) {
						buffer[linelen] = '\0';
						return (linelen);
					}
					begin_line = TRUE;
				} else {
					if (begin_line)
						skip_line = c == POUND_SIGN;
					begin_line = FALSE;
					if (!skip_line)
						buffer[linelen++] = c;
				}
			} else {
				if (linelen > 0 &&
				    buffer[linelen - 1] == ESCAPE_CHAR) {
					/* continuation on last line */
					p_error = parse_bad_continuation_error;
					return (-1);
				} else {
					buffer[linelen] = '\0';
					return (linelen);
				}
			}
		}
		p_error = parse_line_too_long;
	}
	return (-1);
}

/*
 * FUNCTION:	finish_parse
 *
 *	Adds any elements not configured, fully qualifies
 *      names
 *
 * RETURN VALUE:	0 on success, -1 on failure
 */

int
finish_parse(
	__nis_ldap_proxy_info	*proxy_info,
	__nis_table_mapping_t	**table_mapping)
{
	__nis_table_mapping_t	*t;
	__nis_table_mapping_t	*t1;
	__nis_table_mapping_t	*t2;
	__nis_table_mapping_t	*t_del		= NULL;
	int			i;
	int			j;
	int			k;
	__nis_object_dn_t	*objectDN;
	__nis_mapping_rlhs_t	*lhs;
	__nis_mapping_element_t	*e;
	char			*s;
	int			errnum;

	/* set to default those values yet set */
	if (proxy_info->auth_method ==
	    (auth_method_t)NO_VALUE_SET) {
		p_error = parse_no_proxy_auth_error;
		report_error(NULL, NULL);
		return (-1);
	}

	if (proxy_info->default_servers == NULL) {
		p_error = parse_no_ldap_server_error;
		report_error(NULL, NULL);
		return (-1);
	}

	if (proxy_info->tls_method == (tls_method_t)NO_VALUE_SET)
		proxy_info->tls_method = no_tls;
	else if (proxy_info->tls_method == ssl_tls &&
			(proxy_info->tls_cert_db == NULL ||
			*proxy_info->tls_cert_db == '\0')) {
		p_error = parse_no_cert_db;
		report_error(NULL, NULL);
		return (-1);
	}

	if (proxy_info->default_nis_domain == NULL)
		proxy_info->default_nis_domain =
			s_strdup(__nis_rpc_domain());
	else if (*proxy_info->default_nis_domain == '\0') {
		free(proxy_info->default_nis_domain);
		proxy_info->default_nis_domain =
			s_strdup(__nis_rpc_domain());
	}
	if (proxy_info->default_nis_domain != NULL)
		append_dot(&proxy_info->default_nis_domain);

	if (proxy_info->tls_method == ssl_tls) {
		if ((errnum = ldapssl_client_init(
				proxy_info->tls_cert_db, NULL)) < 0) {
			p_error = parse_ldapssl_client_init_error;
			report_error(ldapssl_err2string(errnum), NULL);
			return (-1);
		}
	}

	if (proxy_info->default_search_base == NULL)
	    proxy_info->default_search_base =
		get_default_ldap_base(proxy_info->default_nis_domain);

	/* convert a relative dn to a fullly qualified dn */
	(void) make_full_dn(&proxy_info->proxy_dn,
		proxy_info->default_search_base);

	if (p_error != no_parse_error) {
		report_error(NULL, NULL);
		return (-1);
	}

	/*
	 * Create a list of potential delete mappings
	 * those have NULL objectDNs, but badly also rules
	 * that are missing object dn's will be included.
	 * We will use the ttl field to determine if the
	 * delete rule is actually used
	 */
	t2 = NULL;
	for (t = *table_mapping; t != NULL; t = t1) {
		t1 = t->next;
		if (t->objectDN == NULL) {
			if (t2 == NULL)
				*table_mapping = t1;
			else
				t2->next = t1;
			t->next = t_del;
			t_del = t;
			t->ttl = 0;
		} else
			t2 = t;
	}

	for (t = *table_mapping; t != NULL; t = t->next) {
	    objectDN = t->objectDN;
	    while (objectDN != NULL) {
		if (objectDN->dbIdName != NULL) {
			s = objectDN->dbIdName;
			t1 = find_table_mapping(s, strlen(s), t_del);
			if (t1 == NULL) {
				p_error = parse_no_db_del_mapping_rule;
				report_error2(objectDN->dbIdName, t->dbId);
				return (-1);
			} else if (t1->objName != NULL ||
			    t1->numRulesToLDAP == 0 ||
			    t1->numRulesFromLDAP != 0) {
				p_error = parse_invalid_db_del_mapping_rule;
				report_error(t1->dbId, NULL);
				return (-1);
			}
			objectDN->dbId =
				dup_mapping_rules(t1->ruleToLDAP,
					t1->numRulesToLDAP);
			if (objectDN->dbId == NULL) {
				break;
			}
			objectDN->numDbIds = t1->numRulesToLDAP;
			t1->ttl++;
		}
		objectDN = objectDN->next;
	    }
	}

	for (t = t_del; t != NULL; t = t1) {
		t1 = t->next;
		if (t->ttl == 0) {
			p_error = parse_no_object_dn;
			report_error(t->dbId, NULL);
		}
		free_table_mapping(t);
	}

	if (p_error != no_parse_error)
		return (-1);

	/* set to default those table mapping values yet set */
	for (t = *table_mapping; t != NULL; t = t->next) {
		if (t->objName == 0) {
			p_error = parse_no_object_dn;
			report_error(t->dbId, NULL);
			return (-1);
		}
		if (!yp2ldap) {
			if (!add_domain(&t->objName,
					proxy_info->default_nis_domain)) {
				report_error(NULL, NULL);
				return (-1);
			}
		}
		if (t->initTtlHi == (time_t)NO_VALUE_SET)
			t->initTtlHi = DEFAULT_TTL_HIGH;
		if (t->initTtlLo == (time_t)NO_VALUE_SET)
			t->initTtlLo = DEFAULT_TTL_LOW;
		if (t->ttl == (time_t)NO_VALUE_SET)
			t->ttl = DEFAULT_TTL;
		objectDN = t->objectDN;

		/* fixup relative dn's */
		while (objectDN != NULL) {
			if (!yp2ldap) {
				if (!make_full_dn(&objectDN->read.base,
					proxy_info->default_search_base))
						break;
			}
			if (objectDN->write.scope != LDAP_SCOPE_UNKNOWN) {
				if (objectDN->write.base != NULL &&
					!make_full_dn(&objectDN->write.base,
					proxy_info->default_search_base))
						break;
				if (objectDN->write.base == NULL) {
				    objectDN->write.base =
					s_strdup(objectDN->read.base);
				    if (objectDN->write.base == NULL)
					break;
				}
			}
			objectDN = objectDN->next;
		}

		if (p_error != no_parse_error) {
			report_error(NULL, NULL);
			return (-1);
		}

		/* Check for ruleToLDAP with no rhs */
		for (i = 0; i < t->numRulesToLDAP; i++) {
		    if (t->ruleToLDAP[i]->rhs.numElements == 0) {
			p_error = parse_unexpected_data_end_rule;
			report_error(t->dbId, NULL);
			return (-1);
		    }
		}

		/* populate cols field */
		if (!yp2ldap) {
			for (i = 0; i < t->numRulesFromLDAP; i++) {
				lhs = &t->ruleFromLDAP[i]->lhs;
				for (j = 0; j < lhs->numElements; j++) {
					e = &lhs->element[j];
					switch (e->type) {
						case me_item:
						if (!add_column(t,
						e->element.item.name)) {
							report_error(
							NULL, NULL);
							return (-1);
						}
						break;
						case me_match:
						for (k = 0;
						k < e->element.match.numItems;
						k++)
							if (!add_column(t,
					e->element.match.item[k].name)) {
								report_error(
								NULL, NULL);
								return (-1);
							}
						break;
					}
				}
			}
		}
	}
	return (0);
}

/*
 * FUNCTION:	set_default_values
 *
 *	Sets unconfigured values to their default value
 */

void
set_default_values(__nis_ldap_proxy_info *proxy_info,
    __nis_config_t *config_info, __nisdb_table_mapping_t *table_info)
{
	if (proxy_info->bind_timeout.tv_sec == (time_t)NO_VALUE_SET)
		proxy_info->bind_timeout.tv_sec = DEFAULT_BIND_TIMEOUT;
	if (proxy_info->search_timeout.tv_sec == (time_t)NO_VALUE_SET)
		proxy_info->search_timeout.tv_sec =
			(yp2ldap)?DEFAULT_YP_SEARCH_TIMEOUT:
				DEFAULT_SEARCH_TIMEOUT;
	if (proxy_info->modify_timeout.tv_sec == (time_t)NO_VALUE_SET)
		proxy_info->modify_timeout.tv_sec = DEFAULT_MODIFY_TIMEOUT;
	if (proxy_info->add_timeout.tv_sec == (time_t)NO_VALUE_SET)
		proxy_info->add_timeout.tv_sec = DEFAULT_ADD_TIMEOUT;
	if (proxy_info->delete_timeout.tv_sec == (time_t)NO_VALUE_SET)
		proxy_info->delete_timeout.tv_sec = DEFAULT_DELETE_TIMEOUT;

	if (proxy_info->search_time_limit == (int)NO_VALUE_SET)
		proxy_info->search_time_limit = DEFAULT_SEARCH_TIME_LIMIT;
	if (proxy_info->search_size_limit == (int)NO_VALUE_SET)
		proxy_info->search_size_limit = DEFAULT_SEARCH_SIZE_LIMIT;

	if (proxy_info->follow_referral == (follow_referral_t)NO_VALUE_SET)
		proxy_info->follow_referral = no_follow;

	switch (config_info->initialUpdate) {
		case (__nis_initial_update_t)NO_VALUE_SET:
		case (__nis_initial_update_t)INITIAL_UPDATE_NO_ACTION:
		case (__nis_initial_update_t)NO_INITIAL_UPDATE_NO_ACTION:
			config_info->initialUpdate = ini_none;
			break;
		case (__nis_initial_update_t)FROM_NO_INITIAL_UPDATE:
			config_info->initialUpdate = from_ldap;
			break;
		case (__nis_initial_update_t)TO_NO_INITIAL_UPDATE:
			config_info->initialUpdate = to_ldap;
			break;
	}
	if (config_info->threadCreationError ==
	    (__nis_thread_creation_error_t)NO_VALUE_SET)
		config_info->threadCreationError = pass_error;
	if (config_info->threadCreationErrorTimeout.attempts == NO_VALUE_SET)
		config_info->threadCreationErrorTimeout.attempts =
			DEFAULT_THREAD_ERROR_ATTEMPTS;
	if (config_info->threadCreationErrorTimeout.timeout ==
			(time_t)NO_VALUE_SET)
		config_info->threadCreationErrorTimeout.timeout =
			DEFAULT_THREAD_ERROR_TIME_OUT;
	if (config_info->dumpError ==
	    (__nis_dump_error_t)NO_VALUE_SET)
		config_info->dumpError = de_retry;
	if (config_info->dumpErrorTimeout.attempts == NO_VALUE_SET)
		config_info->dumpErrorTimeout.attempts =
			DEFAULT_DUMP_ERROR_ATTEMPTS;
	if (config_info->dumpErrorTimeout.timeout == (time_t)NO_VALUE_SET)
		config_info->dumpErrorTimeout.timeout =
			DEFAULT_DUMP_ERROR_TIME_OUT;
	if (config_info->resyncService ==
	    (__nis_resync_service_t)NO_VALUE_SET)
		config_info->resyncService = from_copy;
	if (config_info->updateBatching ==
	    (__nis_update_batching_t)NO_VALUE_SET)
		config_info->updateBatching = accumulate;
	if (config_info->updateBatchingTimeout.timeout == (time_t)NO_VALUE_SET)
		config_info->updateBatchingTimeout.timeout =
			DEFAULT_BATCHING_TIME_OUT;
	if (config_info->numberOfServiceThreads == (int)NO_VALUE_SET)
		config_info->numberOfServiceThreads =
			DEFAULT_NUMBER_OF_THREADS;
	if (config_info->emulate_yp == (int)NO_VALUE_SET)
		config_info->emulate_yp =
			DEFAULT_YP_EMULATION;
	if (config_info->maxRPCRecordSize == (int)NO_VALUE_SET)
		config_info->maxRPCRecordSize = RPC_MAXDATASIZE;

	if (table_info->retrieveError ==
	    (__nis_retrieve_error_t)NO_VALUE_SET)
		table_info->retrieveError = use_cached;
	if (table_info->retrieveErrorRetry.attempts == NO_VALUE_SET)
		table_info->retrieveErrorRetry.attempts =
			DEFAULT_RETRIEVE_ERROR_ATTEMPTS;
	if (table_info->retrieveErrorRetry.timeout == (time_t)NO_VALUE_SET)
		table_info->retrieveErrorRetry.timeout =
			DEFAULT_RETRIEVE_ERROR_TIME_OUT;
	if (table_info->storeError ==
	    (__nis_store_error_t)NO_VALUE_SET)
		table_info->storeError = sto_retry;
	if (table_info->storeErrorRetry.attempts == NO_VALUE_SET)
		table_info->storeErrorRetry.attempts =
			DEFAULT_STORE_ERROR_ATTEMPTS;
	if (table_info->storeErrorRetry.timeout == (time_t)NO_VALUE_SET)
		table_info->storeErrorRetry.timeout =
			DEFAULT_STORE_ERROR_TIME_OUT;
	if (table_info->refreshError ==
	    (__nis_refresh_error_t)NO_VALUE_SET)
		table_info->refreshError = continue_using;
	if (table_info->refreshErrorRetry.attempts == NO_VALUE_SET)
		table_info->refreshErrorRetry.attempts =
			DEFAULT_REFRESH_ERROR_ATTEMPTS;
	if (table_info->refreshErrorRetry.timeout == (time_t)NO_VALUE_SET)
		table_info->refreshErrorRetry.timeout =
			DEFAULT_REFRESH_ERROR_TIME_OUT;
	if (table_info->matchFetch ==
	    (__nis_match_fetch_t)NO_VALUE_SET)
		table_info->matchFetch = no_match_only;
}

__nis_table_mapping_t *
find_table_mapping(const char *s, int len, __nis_table_mapping_t *table_mapping)
{
	__nis_table_mapping_t *t;

	for (t = table_mapping; t != NULL; t = t->next)
		if (strlen(t->dbId) == len &&
		    strncasecmp(t->dbId, s, len) == 0)
			break;
	return (t);
}

void
append_dot(char **str)
{
	char	*s	= *str;
	int	len	= strlen(s);

	if (len == 0 || s[len - 1] != PERIOD_CHAR) {
		s = s_realloc(s, len + 2);
		if (s != NULL) {
			s[len] = PERIOD_CHAR;
			s[len+1] = '\0';
			*str = s;
		}
	}
}

void
append_comma(char **str)
{

	char    *s  = *str;
	int len = strlen(s);

	if (len == 0 || s[len - 1] != COMMA_CHAR) {
		s = s_realloc(s, len + 2);
		if (s != NULL) {
			s[len] = COMMA_CHAR;
			s[len+1] = '\0';
			*str = s;
		}
	}
}

/*
 * FUNCTION:	make_full_dn
 *
 *	Appends the base dn if a relative ldap dn
 *	(invoked only for LDAP write cycle)
 *
 * RETURN VALUE:	FALSE if error
 *			TRUE if __nis_index_t returned
 *
 * INPUT:		the relative dn and ldap base
 */

bool_t
make_full_dn(char **dn, const char *base)
{
	int len;
	int len1;

	if (*dn == NULL) {
		*dn = s_strdup(base);
	} else {
		len = strlen(*dn);
		if (len > 0 && (*dn)[len-1] == COMMA_CHAR) {
			len1 = strlen(base) + 1;
			*dn = s_realloc(*dn, len + len1);
			if (*dn != NULL)
				(void) strcpy(*dn + len, base);
		}
	}
	return (*dn != NULL);
}

/*
 * FUNCTION:	make_fqdn
 *
 *	Appends the base dn if a relative ldap dn
 *	(invoked only for LDAP read cycle)
 *
 * RETURN VALUE:	FALSE if error
 *			TRUE if success
 *
 * INPUT:		the relative dn and ldap base
 */
bool_t
make_fqdn(__nis_object_dn_t *dn, const char *base)
{
	int len;
	int len1;

	if (dn == NULL) {
		return (FALSE);
	} else {
		while (dn != NULL && dn->read.base != NULL) {
			len = strlen(dn->read.base);
			if (len > 0 && (dn->read.base)[len-1] == COMMA_CHAR) {
				len1 = strlen(base) + 1;
				dn->read.base =
					s_realloc(dn->read.base, len + len1);
				if (dn->read.base != NULL)
					(void) strlcpy(dn->read.base + len,
							base, len1);
				else
					return (FALSE);
			}
			dn = dn->next;
		}
	}
	return (TRUE);
}

/*
 * FUNCTION:	get_default_ldap_base
 *
 *	Gets the default LDAP search base from the
 *	nis+ default domain
 *
 * RETURN VALUE:	NULL if error
 *			the default base
 *
 * INPUT:		the nis domain
 */

char *
get_default_ldap_base(const char *domain)
{

	int		len	= strlen(domain);
	int		i;
	int		count	= len + 4;
	char		*base;

	for (i = 0; i < len - 1; i++)
		if (domain[i] == PERIOD_CHAR)
			count += 4;
	if ((base = malloc(count)) == NULL) {
		p_error = parse_no_mem_error;
	} else {
		(void) strcpy(base, "dc=");
		count = 3;
		for (i = 0; i < len - 1; i++) {
			if (domain[i] == PERIOD_CHAR) {
				(void) strcpy(base + count, ",dc=");
				count += 4;
			} else {
				base[count++] = domain[i];
			}
		}
		base[count] = '\0';
	}
	return (base);
}

/*
 * FUNCTION:	add_domain
 *
 *	Appends the base domain if a relative object name
 *
 * RETURN VALUE:	FALSE if error
 *			TRUE if OK
 *
 * INPUT:		the relative object name and base domain
 *			name
 */

bool_t
add_domain(char **objName, const char *domain)
{
	int	len;
	int	len1;
	bool_t	trailing_dot;
	char	*obj_name;

	if (domain == NULL || *objName == NULL) {
		p_error = parse_internal_error;
		return (FALSE);
	}
	len1 = strlen(domain);
	trailing_dot = (len1 > 0 && domain[len1 - 1] == PERIOD_CHAR) ?
		0 : 1;
	len = strlen(*objName);
	if (len == 0 || (*objName)[len - 1] != PERIOD_CHAR) {
		obj_name = s_realloc(*objName,
			len + len1 + 2 + trailing_dot);
		if (obj_name != NULL) {
			obj_name[len++] = PERIOD_CHAR;
			(void) strcpy(obj_name + len, domain);
			if (trailing_dot != 0) {
				obj_name[len + len1] = PERIOD_CHAR;
				obj_name[len + len1 + 1] = '\0';
			}
			*objName = obj_name;
		}
	}

	return (*objName != NULL);
}

bool_t
dup_index(__nis_index_t *in, __nis_index_t *out)
{
	int i;
	int j;

	out->name = (char **)s_calloc(in->numIndexes, sizeof (char *));
	if (out->name == NULL)
		return (FALSE);
	out->value = (__nis_mapping_format_t **)
		s_calloc(in->numIndexes, sizeof (__nis_mapping_format_t *));
	if (out->value == NULL) {
		free(out->name);
		out->name = NULL;
		return (FALSE);
	}

	for (i = 0; i < in->numIndexes; i++) {
		out->name[i] = s_strdup(in->name[i]);
		if (out->name[i] == NULL)
			break;
		out->value[i] = dup_format_mapping(in->value[i]);
		if (out->value[i] == NULL)
			break;
	}
	if (i < in->numIndexes) {
		for (j = 0; j <= i; j++) {
			if (out->name[j] != NULL)
				free(out->name[j]);
			if (out->value[j] != NULL)
				free_mapping_format(out->value[j]);
		}
		free(out->name);
		out->name = NULL;
		free(out->value);
		out->value = NULL;
	} else {
		out->numIndexes = in->numIndexes;
	}
	return (i == in->numIndexes);
}

bool_t
dup_mapping_item(__nis_mapping_item_t *in, __nis_mapping_item_t *out)
{
	bool_t	ret;

	if (in->type == mit_nisplus) {
		ret = dup_index(&in->searchSpec.obj.index,
			&out->searchSpec.obj.index);
		if (!ret)
			return (ret);
		if (in->searchSpec.obj.name != NULL) {
		    out->searchSpec.obj.name =
			s_strdup(in->searchSpec.obj.name);
			if (out->searchSpec.obj.name == NULL)
				return (FALSE);
		} else
			out->searchSpec.obj.name = NULL;
	} else if (in->type == mit_ldap) {
		if (in->searchSpec.triple.base != NULL) {
		    out->searchSpec.triple.base =
			s_strdup(in->searchSpec.triple.base);
			if (out->searchSpec.triple.base == NULL)
				return (FALSE);
		} else
			out->searchSpec.triple.base = NULL;
		out->searchSpec.triple.scope =
			in->searchSpec.triple.scope;
		if (in->searchSpec.triple.attrs != NULL) {
		    out->searchSpec.triple.attrs =
			s_strdup(in->searchSpec.triple.attrs);
			if (out->searchSpec.triple.attrs == NULL)
				return (FALSE);
		} else
			out->searchSpec.triple.attrs = NULL;
		if (in->searchSpec.triple.element != NULL) {
			out->searchSpec.triple.element =
				(__nis_mapping_element_t *)
				s_calloc(1, sizeof (__nis_mapping_element_t));
			if (out->searchSpec.triple.element != NULL)
				dup_mapping_element(
					in->searchSpec.triple.element,
					out->searchSpec.triple.element);
			if (out->searchSpec.triple.element == NULL)
				return (FALSE);
		} else
			out->searchSpec.triple.element = NULL;
	}

	if (in->name != NULL) {
		out->name = s_strdup(in->name);
		if (out->name == NULL)
			return (FALSE);
	} else
		out->name = NULL;
	out->type = in->type;
	out->repeat = in->repeat;
	if (in->exItem) {
		out->exItem = (__nis_mapping_item_t *)s_malloc
			(sizeof (__nis_mapping_item_t));
		if (out->exItem == NULL)
			return (FALSE);
		else {
			(void) memset
				(out->exItem, 0, sizeof (out->exItem[0]));
			if (!dup_mapping_item
				(in->exItem, out->exItem))
				p_error = parse_internal_error;
		}
	} else
		out->exItem = NULL;

	return (p_error == no_parse_error);
}

__nis_mapping_format_t *
dup_format_mapping(__nis_mapping_format_t *in)
{
	int			i;
	__nis_mapping_format_t	*out;
	bool_t			got_end;

	i = 0;
	while (in[i].type != mmt_end)
		i++;
	out = (__nis_mapping_format_t *)s_calloc(
		i + 1, sizeof (__nis_mapping_format_t));
	if (out != NULL) {
		got_end = FALSE;
		for (i = 0; !got_end; i++) {
		    switch (in[i].type) {
			case mmt_item:
				break;
			case mmt_string:
				out[i].match.string =
					s_strdup(in[i].match.string);
				break;
			case mmt_single:
				out[i].match.single.numRange =
					in[i].match.single.numRange;
				out[i].match.single.lo =
					s_malloc(in[i].match.single.numRange);
				if (out[i].match.single.lo == NULL)
					break;
				out[i].match.single.hi =
					s_malloc(in[i].match.single.numRange);
				if (out[i].match.single.hi == NULL)
					break;
				memcpy(out[i].match.single.lo,
					in[i].match.single.lo,
					in[i].match.single.numRange);
				memcpy(out[i].match.single.hi,
					in[i].match.single.hi,
					in[i].match.single.numRange);
				break;
			case mmt_limit:
				out[i].match.limit = in[i].match.limit;
				break;
			case mmt_any:
				break;
			case mmt_berstring:
				out[i].match.berString =
					s_strdup(in[i].match.berString);
				break;
			case mmt_begin:
				break;
			case mmt_end:
				got_end = TRUE;
				break;
			default:
				p_error = parse_internal_error;
		    }
		    if (p_error != no_parse_error)
			break;
		    out[i].type = in[i].type;
		}
		if (p_error != no_parse_error) {
			free_mapping_format(out);
			out = NULL;
		}
	}

	return (out);
}

bool_t
dup_mapping_sub_element(
	__nis_mapping_sub_element_t	*in,
	__nis_mapping_sub_element_t	*out)
{
	bool_t	ret = FALSE;
	int	i;

	switch (in->type) {
		case me_item:
			ret = dup_mapping_item(&in->element.item,
				&out->element.item);
			break;
		case me_print:
			out->element.print.fmt =
				dup_format_mapping(in->element.print.fmt);
			if (out->element.print.fmt == NULL)
				break;
			out->element.print.numItems =
				in->element.print.numItems;
			out->element.print.item = (__nis_mapping_item_t *)
				s_calloc(in->element.print.numItems,
					sizeof (__nis_mapping_item_t));
			if (out->element.print.item == NULL)
				break;
			for (i = 0; i < in->element.print.numItems; i++)
				if (!dup_mapping_item(
					&in->element.print.item[i],
					&out->element.print.item[i]))
						break;
			if (i < in->element.print.numItems)
				break;
			ret = TRUE;
			out->element.print.doElide = in->element.print.doElide;
			out->element.print.elide = in->element.print.elide;
			break;
		case me_split:
			ret = dup_mapping_item(&in->element.split.item,
				&out->element.split.item);
			out->element.split.delim = in->element.split.delim;
			break;
		case me_extract:
			out->element.extract.fmt =
				dup_format_mapping(in->element.extract.fmt);
			if (out->element.extract.fmt == NULL)
				break;
			ret = dup_mapping_item(&in->element.extract.item,
				&out->element.extract.item);
			break;
		default:
			p_error = parse_internal_error;
	}
	out->type = in->type;

	return (ret);
}

bool_t
dup_mapping_element(
	__nis_mapping_element_t *in,
	__nis_mapping_element_t *out)
{
	bool_t	ret = FALSE;
	int	i;

	if (in == NULL)
		return (ret);

	switch (in->type) {
		case me_item:
			ret = dup_mapping_item(&in->element.item,
				&out->element.item);
			break;
		case me_print:
			out->element.print.fmt =
				dup_format_mapping(in->element.print.fmt);
			if (out->element.print.fmt == NULL)
				break;
			out->element.print.numSubElements =
				in->element.print.numSubElements;
			out->element.print.subElement =
				(__nis_mapping_sub_element_t *)
				s_calloc(in->element.print.numSubElements,
					sizeof (__nis_mapping_sub_element_t));
			if (out->element.print.subElement == NULL)
				break;
			for (i = 0; i < in->element.print.numSubElements; i++)
				if (!dup_mapping_sub_element(
					&in->element.print.subElement[i],
					&out->element.print.subElement[i]))
						break;
			if (i < in->element.print.numSubElements)
				break;
			ret = TRUE;
			out->element.print.doElide = in->element.print.doElide;
			out->element.print.elide = in->element.print.elide;
			break;
		case me_split:
			ret = dup_mapping_item(&in->element.split.item,
				&out->element.split.item);
			out->element.split.delim = in->element.split.delim;
			break;
		case me_match:
			out->element.match.fmt =
				dup_format_mapping(in->element.match.fmt);
			if (out->element.match.fmt == NULL)
				break;
			out->element.match.numItems =
				in->element.match.numItems;
			out->element.match.item = (__nis_mapping_item_t *)
				s_calloc(in->element.match.numItems,
					sizeof (__nis_mapping_item_t));
			if (out->element.match.item == NULL)
				break;
			for (i = 0; i < in->element.match.numItems; i++)
				if (!dup_mapping_item(
					&in->element.match.item[i],
					&out->element.match.item[i]))
						break;
			if (i < in->element.match.numItems)
				break;
			ret = TRUE;
			break;
		case me_extract:
			out->element.extract.fmt =
				dup_format_mapping(in->element.extract.fmt);
			if (out->element.extract.fmt == NULL)
				break;
			ret = dup_mapping_item(&in->element.extract.item,
				&out->element.extract.item);
			break;
		default:
			p_error = parse_internal_error;
	}
	out->type = in->type;

	return (ret);
}

__nis_mapping_rule_t *
dup_mapping_rule(__nis_mapping_rule_t *in)
{
	int			i;
	__nis_mapping_rlhs_t	*r_in;
	__nis_mapping_rlhs_t	*r_out;
	__nis_mapping_rule_t	*out;

	out = (__nis_mapping_rule_t *)
		s_calloc(1, sizeof (__nis_mapping_rule_t));
	if (out != NULL) {
		r_in = &in->lhs;
		r_out = &out->lhs;
		r_out->numElements = r_in->numElements;
		r_out->element = (__nis_mapping_element_t *)s_calloc
			(r_in->numElements, sizeof (__nis_mapping_element_t));
		if (r_out->element == NULL) {
			free_mapping_rule(out);
			return (NULL);
		}
		for (i = 0; i < r_in->numElements; i++) {
		    if (!dup_mapping_element(&r_in->element[i],
			&r_out->element[i]))
				break;
		}
		if (i < r_in->numElements) {
			free_mapping_rule(out);
			return (NULL);
		}

		r_in = &in->rhs;
		r_out = &out->rhs;
		r_out->numElements = r_in->numElements;
		r_out->element = (__nis_mapping_element_t *)s_calloc
			(r_in->numElements, sizeof (__nis_mapping_element_t));
		if (r_out->element == NULL) {
			free_mapping_rule(out);
			return (NULL);
		}
		for (i = 0; i < r_in->numElements; i++) {
		    if (!dup_mapping_element(&r_in->element[i],
			&r_out->element[i]))
				break;
		}
		if (i < r_in->numElements) {
			free_mapping_rule(out);
			return (NULL);
		}
	}
	return (out);
}

__nis_mapping_rule_t **
dup_mapping_rules(__nis_mapping_rule_t **rules, int n_rules)
{
	int			i, j;
	__nis_mapping_rule_t	**r;

	r = (__nis_mapping_rule_t **)s_calloc(n_rules,
		sizeof (__nis_mapping_rule_t *));
	if (r != NULL) {
		for (i = 0; i < n_rules; i++) {
			r[i] = dup_mapping_rule(rules[i]);
			if (r[i] == NULL) {
				for (j = 0; j < i; j++)
					free_mapping_rule(r[j]);
				free(r);
				r = NULL;
				break;
			}
		}
	}
	return (r);
}

/*
 * FUNCTION:	add_column
 *
 *	Adds a column name to the column list in __nis_table_mapping_t
 *
 * RETURN VALUE:	FALSE if error
 *			TRUE if __nis_index_t returned
 *
 * INPUT:		the __nis_table_mapping_t and column name
 */

bool_t
add_column(__nis_table_mapping_t *t, const char *col_name)
{
	int i;
	char **cols = NULL;

	if (!yp2ldap) {
		for (i = 0; i < t->numColumns; i++) {
			if (strcasecmp(col_name, t->column[i]) == 0)
				return (TRUE);
		}
	}
	cols = (char **)s_realloc(t->column, (t->numColumns + 1) *
		sizeof (char *));
	if (cols == NULL)
		return (FALSE);
	t->column = cols;
	cols[t->numColumns] = s_strdup(col_name);
	if (cols[t->numColumns] == NULL)
		return (FALSE);
	t->numColumns++;
	return (TRUE);
}

/*
 * FUNCTION:	add_element
 *
 *	Adds a __nis_mapping_element_t to __nis_mapping_rlhs_t
 *
 * RETURN VALUE:	FALSE if error
 *			TRUE if __nis_index_t returned
 *
 * INPUT:		the __nis_mapping_element_t and
 *			__nis_mapping_rlhs_t
 */

bool_t
add_element(
	__nis_mapping_element_t	*e,
	__nis_mapping_rlhs_t	*m)
{
	__nis_mapping_element_t *e1;
	int			i;
	int			n	= m->numElements;

	e1 = (__nis_mapping_element_t *)s_realloc(m->element,
		(n + 1) * sizeof (__nis_mapping_element_t));
	if (e1 == NULL) {
		e1 = m->element;
		for (i = 0; i < n; i++)
			free_mapping_element(e1++);
		if (m->element != NULL)
			free(m->element);
		m->element = NULL;
		m->numElements = 0;
	} else {
		e1[m->numElements++] = *e;
		free(e);
		m->element = (__nis_mapping_element_t *)e1;
	}
	return (e1 != NULL);
}

/*
 * FUNCTION:	get_next_object_dn_token
 *
 *	Get the next token in parsing object_dn
 *
 * RETURN VALUE:	NULL if error
 *			position of beginning next token after
 *			token
 *
 * INPUT:		the attribute value
 */

const char *
get_next_object_dn_token(
	const char	**begin_ret,
	const char	**end_ret,
	object_dn_token	*token)
{
	object_dn_token	t		= dn_no_token;
	const char	*s		= *begin_ret;
	const char	*begin;
	const char	*end		= *end_ret;
	const char	*s1;
	bool_t		in_quotes;

	while (s < end && is_whitespace(*s))
		s++;
	if (s >= end) {
		/* EMPTY */
	} else if (*s == SEMI_COLON_CHAR) {
		t = dn_semi_token;
		s++;
	} else if (*s == QUESTION_MARK) {
		t = dn_ques_token;
		s++;
	} else if (*s == COLON_CHAR) {
		t = dn_colon_token;
		s++;
	} else if (*s == OPEN_PAREN_CHAR) {
		begin = s;
		s = get_ldap_filter(&begin, &end);
		if (s != NULL) {
			t = dn_text_token;
			*begin_ret = begin;
			*end_ret = end;
		}
	} else {
		begin = s;
		in_quotes = FALSE;
		while (s < end) {
			if (*s == ESCAPE_CHAR) {
			    if (s + 2 > end) {
				p_error = parse_unmatched_escape;
				s = NULL;
				break;
			    }
			    s++;
			} else if (*s == DOUBLE_QUOTE_CHAR) {
				in_quotes = ! in_quotes;
			} else if (in_quotes)
				;
			else if (*s == SEMI_COLON_CHAR ||
				*s == QUESTION_MARK ||
				*s == COLON_CHAR)
					break;
			s++;
		}
		if (s != NULL) {
			s1 = s - 1;
			while (is_whitespace(*s1))
				s1--;
			s1++;
			if (same_string("base", begin, s1 - begin))
				t = dn_base_token;
			else if (same_string("one", begin, s1 - begin))
				t = dn_one_token;
			else if (same_string("sub", begin, s1 - begin))
				t = dn_sub_token;
			else
				t = dn_text_token;
			*begin_ret = begin;
			*end_ret = s1;
		}
	}
	*token = t;
	return (s);
}

/*
 * FUNCTION:	get_next_token
 *
 *	Get the next token in parsing mapping attribute
 *
 * RETURN VALUE:	NULL if error
 *			position of beginning next token after
 *			token
 *
 * INPUT:		the attribute value
 */

const char *
get_next_token(const char **begin_token, const char **end_token, token_type *t)
{
	const char	*s		= *begin_token;
	const char	*end_s		= *end_token;
	const char	*s_begin;

	while (s < end_s && is_whitespace(*s))
		s++;
	if (s == end_s) {
		*t = no_token;
		return (s);
	}

	s_begin = s;

	if (*s == OPEN_PAREN_CHAR) {
		*begin_token = s;
		s++;
		*end_token = s;
		while (s < end_s && is_whitespace(*s))
			s++;
		*t = open_paren_token;
	} else if (*s == DOUBLE_QUOTE_CHAR) {
		s++;
		while (s < end_s) {
			if (*s == ESCAPE_CHAR)
				s += 2;
			else if (*s == DOUBLE_QUOTE_CHAR)
				break;
			else
				s++;
		}
		if (s >= end_s) {
			p_error = parse_unmatched_escape;
			return (NULL);
		}

		*t = quoted_string_token;
		*begin_token = s_begin + 1;
		*end_token = s++;
	} else if (*s == EQUAL_CHAR || *s == COMMA_CHAR ||
	    *s == CLOSE_PAREN_CHAR || *s == COLON_CHAR) {
		if (*s == EQUAL_CHAR)
			*t = equal_token;
		else if (*s == COMMA_CHAR)
			*t = comma_token;
		else if (*s == CLOSE_PAREN_CHAR)
			*t = close_paren_token;
		else
			*t = colon_token;
		*begin_token = s;
		*end_token = ++s;
	} else {
		s_begin = s;
		while (s < end_s && !is_whitespace(*s)) {
			if (*s == ESCAPE_CHAR)
				s += 2;
			else if (*s == EQUAL_CHAR || *s == CLOSE_PAREN_CHAR ||
			    *s == OPEN_PAREN_CHAR || *s == COMMA_CHAR ||
			    *s == COLON_CHAR || *s == OPEN_BRACKET ||
			    *s == CLOSE_BRACKET)
				break;
			else
				s++;
		}
		if (s > end_s) {
			p_error = parse_unmatched_escape;
			return (NULL);
		}
		*t = string_token;
		*end_token = s;
		*begin_token = s_begin;
	}
	if (s) {
		while (s < end_s && is_whitespace(*s))
			s++;
	}
	return (s);
}

/*
 * FUNCTION:	skip_token
 *
 *	Skip over the specified token - An error is set if
 *	next token does not match expected token
 *
 * RETURN VALUE:	NULL if error
 *			position of beginning next token after
 *			token
 *
 * INPUT:		the attribute value
 */

const char *
skip_token(const char *s, const char *end_s, token_type t)
{
	bool_t	match;
	char	c	= 0;

	if (s == NULL)
		return (s);
	while (s < end_s && is_whitespace(*s))
		s++;
	c = (s == end_s) ? 0 : *s;
	switch (t) {
		case equal_token:
			match = c == EQUAL_CHAR;
			if (!match)
				p_error = parse_equal_expected_error;
			break;
		case comma_token:
			match = c == COMMA_CHAR;
			if (!match)
				p_error = parse_comma_expected_error;
			break;
		case close_paren_token:
			match = c == CLOSE_PAREN_CHAR;
			if (!match)
				p_error = parse_close_paren_expected_error;
			break;
		default:
			match = FALSE;
			break;
	}
	if (match) {
		s++;
		while (s < end_s && is_whitespace(*s))
			s++;
	} else {
		s = NULL;
	}
	return (s);
}

/*
 * FUNCTION:	get_next_extract_format_item
 *
 *	Get the next format token from the string. Note that
 *	get_next_extract_format_item may change the input string.
 *
 * RETURN VALUE:	NULL if error
 *			position of beginning next token after
 *			token
 *
 * INPUT:		the format string
 */

const char *
get_next_extract_format_item(
	const char		*begin_fmt,
	const char		*end_fmt,
	__nis_mapping_format_t	*fmt)
{
	const char	*s		= begin_fmt;
	const char	*s_end		= end_fmt;
	bool_t		escape;
	bool_t		in_range;
	bool_t		got_char;
	bool_t		done;
	int		numRange;
	char		*lo		= NULL;
	char		*hi		= NULL;
	bool_t		skip_ber;

	for (; p_error == no_parse_error; ) {
		if (s >= s_end)
			break;

		if (*s == PERCENT_SIGN) {
			s++;
			/*
			 * If the format is %s, it is interpreted
			 * as a string.
			 */
			if (s >= s_end) {
				p_error = parse_unsupported_format;
				break;
			}
			skip_ber = FALSE;
			switch (*s) {
				case 's':
					fmt->type = mmt_item;
					break;
				case 'n':	/* null */
				case 'x':	/* skip the next element */
					skip_ber = TRUE;
					/* FALLTHRU */
				case 'b':	/* boolean */
				case 'e':	/* enumerated */
				case 'i':	/* int */
				case 'o':	/* octet string */
				case 'B':	/* bit string */
					fmt->match.berString = s_strndup(s, 1);
					fmt->type = skip_ber ?
						mmt_berstring_null :
						mmt_berstring;
					break;
				case 'a':	/* octet string */
					if (yp2ldap) {
						fmt->match.berString =
							s_strndup(s, 1);
						fmt->type = skip_ber ?
							mmt_berstring_null :
							mmt_berstring;
						break;
					} /* else FALLTHRU */
				case '{':	/* begin sequence */
				case '[':	/* begin set */
				case '}':	/* end sequence */
				case ']':	/* end set */
				case 'l':	/* length of next item */
				case 'O':	/* octet string */
				case 't':	/* tag of next item */
				case 'T':	/* skip tag of next item */
				case 'v':	/* seq of strings */
				case 'V':	/* seq of strings + lengths */
				default:
					p_error = parse_bad_ber_format;
					break;
			}
			s++;
		} else if (*s == ASTERIX_CHAR) {
			fmt->type = mmt_any;
			s++;
			while (s < s_end && *s == ASTERIX_CHAR)
				s++;

		} else if (*s == OPEN_BRACKET) {
			escape = FALSE;
			in_range = FALSE;
			got_char = FALSE;
			numRange = 0;
			done = FALSE;
			s++;
			for (; s < s_end; s++) {
				if (escape) {
					escape = FALSE;
				} else if (*s == DASH_CHAR) {
					if (in_range || !got_char) {
						p_error = parse_unexpected_dash;
						break;
					}
					in_range = TRUE;
					got_char = FALSE;
					continue;
				} else if (*s == CLOSE_BRACKET) {
					if (in_range) {
						p_error = parse_unexpected_dash;
					}
					done = TRUE;
					break;
				} else if (*s == ESCAPE_CHAR) {
					escape = TRUE;
					continue;
				}
				if (in_range) {
					hi[numRange - 1] = *s;
					in_range = FALSE;
				} else {
					lo = s_realloc(lo, numRange + 1);
					hi = s_realloc(hi, numRange + 1);
					if (lo == NULL || hi == NULL)
						break;
					lo[numRange] = *s;
					hi[numRange] = *s;
					numRange++;
					got_char = TRUE;
				}
			}
			if (p_error != no_parse_error) {
				break;
			} else if (!done) {
				p_error = parse_mismatched_brackets;
				break;
			}
			s++;
			fmt->type = mmt_single;
			fmt->match.single.numRange = numRange;
			fmt->match.single.lo = (unsigned char *)lo;
			fmt->match.single.hi = (unsigned char *)hi;
		} else {
			/* go to next key symbol - copy escaped key symbols */
			escape = FALSE;
			done = FALSE;
			while (s < s_end) {
				if (escape)
					escape = FALSE;
				else {
				    switch (*s) {
					case OPEN_BRACKET:
					case ASTERIX_CHAR:
					case PERCENT_SIGN:
						done = TRUE;
						break;
					case ESCAPE_CHAR:
						escape = !escape;
						break;
					default:
						break;
				    }
				}
				if (done)
					break;
				s++;
			}
			if (escape) {
				p_error = parse_unmatched_escape;
				break;
			}
			fmt->type = mmt_string;
			fmt->match.string =
				s_strndup_esc(begin_fmt, s - begin_fmt);
			if (fmt->match.string == NULL)
				break;
		}

		if (p_error == no_parse_error)
			return (s);
	}
	if (lo != NULL)
		free(lo);
	if (hi != NULL)
		free(hi);
	return (NULL);
}

/*
 * FUNCTION:	get_next_print_format_item
 *
 *	Get the next format token from the string
 *
 * RETURN VALUE:	NULL if error
 *			position of beginning next token after
 *			token
 *
 * INPUT:		the format string
 */

const char *
get_next_print_format_item(
	const char		*begin_fmt,
	const char		*end_fmt,
	__nis_mapping_format_t	*fmt)
{
	const char		*s	= begin_fmt;
	const char		*s_end	= end_fmt;
	bool_t			skip_ber;

	for (; p_error == no_parse_error; ) {
		if (s >= s_end) {
			p_error = parse_internal_error;
			break;
		}

		if (*s == PERCENT_SIGN) {
			s++;
			if (s >= s_end) {
				p_error = parse_unsupported_format;
				break;
			}
			skip_ber = FALSE;
			/*
			 * If the format is %s, it is interpretted
			 * as a string.
			 */
			switch (*s) {
				case 's':
					fmt->type = mmt_item;
					break;
				case 'n':	/* null */
				case 'x':	/* skip the next element */
					skip_ber = TRUE;
					/* FALLTHRU */
				case 'b':	/* boolean */
				case 'e':	/* enumerated */
				case 'i':	/* int */
				case 'o':	/* octet string */
				case 'B':	/* bit string */
					fmt->match.berString = s_strndup(s, 1);
					fmt->type = skip_ber ?
						mmt_berstring_null :
						mmt_berstring;
					break;
				case '{':	/* begin sequence */
				case '[':	/* begin set */
				case '}':	/* end sequence */
				case ']':	/* end set */
				case 'a':	/* octet string */
				case 'l':	/* length of next item */
				case 'O':	/* octet string */
				case 't':	/* tag of next item */
				case 'T':	/* skip tag of next item */
				case 'v':	/* seq of strings */
				case 'V':	/* seq of strings + lengths */
				default:
					p_error = parse_bad_ber_format;
					break;
			}
			s++;
		} else {
			while (s < s_end) {
				if (*s == PERCENT_SIGN)
					break;
				else if (*s == ESCAPE_CHAR)
					s++;
				s++;
			}
			if (s > s_end) {
				p_error = parse_unmatched_escape;
				break;
			}
			fmt->match.string =
				s_strndup_esc(begin_fmt, s - begin_fmt);
			if (fmt->match.string == NULL)
				break;
			fmt->type = mmt_string;
		}
		if (p_error == no_parse_error)
			return (s);
	}
	return (NULL);
}

/*
 * FUNCTION:	get_ldap_filter
 *
 *	Gets an LDAP filter - see RFC 2254. Note that this does not
 *	determine if the ldap filter is valid. This only determines
 *	that the parentheses are balanced.
 *
 * RETURN VALUE:	NULL if error
 *			position of beginning next token after
 *			filter
 *
 * INPUT:		the begin and end of string
 *
 * OUTPUT:		the begin and end of LDAP filter
 *
 */

const char *
get_ldap_filter(const char **begin, const char **end)
{
	const char	*s		= *begin;
	const char	*s_begin;
	const char	*s_end		= *end;
	int		nParen;

	for (; p_error == no_parse_error; ) {
		while (s < s_end && is_whitespace(*s))
			s++;
		if (s == s_end) {
			s = NULL;
			break;
		}

		s_begin = s;
		if (*s == OPEN_PAREN_CHAR) {
			nParen = 1;
			s++;
			while (s < s_end && nParen > 0) {
				if (*s == ESCAPE_CHAR)
					s++;
				else if (*s == OPEN_PAREN_CHAR)
					nParen++;
				else if (*s == CLOSE_PAREN_CHAR)
					nParen--;
				s++;
			}
			if (nParen == 0) {
				*begin = s_begin;
				*end = s;
				while (s < s_end && is_whitespace(*s))
					s++;
			} else
				s = NULL;
		} else
			s = NULL;
		if (p_error == no_parse_error)
			break;
	}
	if (s == NULL)
		p_error = parse_invalid_ldap_search_filter;

	return (s);
}

/*
 * FUNCTION:	get_ava_list
 *
 *	Gets an attribute value assertion list
 *
 * RETURN VALUE:	NULL if error
 *			position of beginning next token after
 *			after attribute assertion
 *
 * INPUT:		the begin and end of string
 *			Indicator if ava list is part of a nisplus
 *			item
 *
 * OUTPUT:		the begin and end of LDAP filter
 *
 */

const char *
get_ava_list(const char **begin, const char **end, bool_t end_nisplus)
{
	const char	*s		= *begin;
	const char	*s_begin;
	const char	*s_end		= *end;
	bool_t		in_quote;
	bool_t		got_equal;
	bool_t		got_data;

	for (; p_error == no_parse_error; ) {
		while (s < s_end && is_whitespace(*s))
			s++;
		if (s == s_end) {
			s = NULL;
			break;
		}

		in_quote = FALSE;
		got_equal = FALSE;
		got_data = FALSE;
		s_begin = s;
		while (s < s_end) {
			if (*s == ESCAPE_CHAR) {
			    s++;
			    got_data = TRUE;
			} else if (*s == DOUBLE_QUOTE_CHAR) {
			    in_quote = !in_quote;
			    got_data = TRUE;
			} else if (in_quote)
				;
			else if (*s == EQUAL_CHAR) {
			    if (end_nisplus && got_data && got_equal)
				break;
			    if (!got_data || got_equal) {
				got_equal = FALSE;
				break;
			    }
			    got_equal = TRUE;
			    got_data = FALSE;
			} else if (*s == COMMA_CHAR) {
			    if (!got_data || !got_equal)
				break;
			    got_data = FALSE;
			    got_equal = FALSE;
			} else if (is_whitespace(*s))
				;
			else
				got_data = TRUE;
			s++;
		}
		if (!got_data || !got_equal || in_quote)
			s = NULL;
		else {
			*begin = s_begin;
			*end = s;
			while (s < s_end && is_whitespace(*s))
				s++;
		}
		if (p_error == no_parse_error)
			break;
	}
	if (s == NULL)
		p_error = parse_invalid_ldap_search_filter;

	return (s);
}

/* Utility functions */
bool_t
validate_dn(const char *s, int len)
{
	const char *end = s + len;
	bool_t	valid;

	valid = skip_get_dn(s, end) == end;

	if (!valid)
		p_error = parse_bad_dn;
	return (valid);
}

bool_t
validate_ldap_filter(const char *s, const char *end)
{
	const char	*s_begin;
	const char	*s_end;

	s_begin = s;
	s_end = end;

	if (*s == OPEN_PAREN_CHAR) {
		s = get_ldap_filter(&s_begin, &s_end);
	} else {
		/* Assume an attribute value list */
		s = get_ava_list(&s_begin, &s_end, FALSE);
	}
	if (s == NULL || s_end != end)
		p_error = parse_invalid_ldap_search_filter;

	return (p_error == no_parse_error);
}

char *
s_strndup(const char *s, int n)
{
	char *d = (char *)malloc(n + 1);

	if (d != NULL) {
		(void) memcpy(d, s, n);
		d[n] = '\0';
	} else {
		p_error = parse_no_mem_error;
	}

	return (d);
}

char *
s_strndup_esc(const char *s, int n)
{
	char	*d	= (char *)malloc(n + 1);
	int	i;
	int	j;

	if (d != NULL) {
		for (i = 0, j = 0; i < n; i++) {
			if (s[i] == ESCAPE_CHAR)
				i++;
			d[j++] = s[i];
		}
		d[j] = '\0';
	} else {
		p_error = parse_no_mem_error;
	}

	return (d);
}

void *
s_calloc(size_t n, size_t size)
{
	void *d = (char *)calloc(n, size);

	if (d == NULL) {
		p_error = parse_no_mem_error;
	}

	return (d);
}

void *
s_malloc(size_t size)
{
	void *d = malloc(size);
	if (d == NULL)
		p_error = parse_no_mem_error;
	return (d);
}

void *
s_realloc(void *s, size_t size)
{
	s = realloc(s, size);
	if (s == NULL)
		p_error = parse_no_mem_error;
	return (s);
}

char *
s_strdup(const char *s)
{
	return (s != NULL ? s_strndup(s, strlen(s)) : NULL);
}

bool_t
is_whitespace(int c)
{
	return (c == ' ' || c == '\t');
}

bool_t
is_string_ok(char *buffer, int buflen)
{
	int i;

	if (buffer == NULL)
		return (FALSE);

	for (i = 0; i < buflen; i++) {
		if (!is_whitespace(buffer[i])) {
			if (buffer[i] == POUND_SIGN)
				return (TRUE);
			else
				return (FALSE);
		}
	}
	return (TRUE);
}

/*
 * Returns true if the first string is contained at the beginning of the
 * second string. Otherwise returns false.
 */

bool_t
contains_string(const char *s1, const char *s2)
{
	return (strncasecmp(s1, s2, strlen(s1)) == 0);
}

/*
 * Returns the next character position in the second string, if the first
 * string is contained at the beginning of the second string. Otherwise
 * returns NULL.
 */

const char *
skip_string(const char *s1, const char *s2, int len)
{
	int len1 = strlen(s1);

	if (len >= len1 && strncasecmp(s1, s2, strlen(s1)) == 0)
		return (s2 + len1);
	else
		return (NULL);
}

/*
 * The second string is not necessarily null terminated.
 * same_string returns true if the second string matches the first.
 * Otherwise returns false.
 */

bool_t
same_string(const char *s1, const char *s2, int len)
{
	int len1 = strlen(s1);

	return (len1 == len && strncasecmp(s1, s2, len1) == 0);
}

void
report_error(const char	*str, const char *attr)
{
	char	fmt_buf[1024];
	int	pos		= 0;

	if (command_line_source != NULL) {
		snprintf(fmt_buf, sizeof (fmt_buf), "Error parsing %s: ",
			command_line_source);
		pos = strlen(fmt_buf);
	} else if (file_source != NULL) {
		snprintf(fmt_buf, sizeof (fmt_buf), "Error parsing file '%s': ",
			file_source);
		pos = strlen(fmt_buf);
	} else if (ldap_source != NULL) {
		snprintf(fmt_buf, sizeof (fmt_buf), "Error for LDAP dn '%s': ",
			ldap_source);
		pos = strlen(fmt_buf);
	}

	if (start_line_num != 0) {
		snprintf(fmt_buf + pos, sizeof (fmt_buf) - pos, "at line %d: ",
			start_line_num);
		pos += strlen(fmt_buf + pos);
	}

	if (attr != NULL) {
		snprintf(fmt_buf + pos, sizeof (fmt_buf) - pos,
			"for attribute %s: ", attr);
		pos += strlen(fmt_buf + pos);
	}

	if (cons != NULL) {
		snprintf(fmt_buf + pos, sizeof (fmt_buf) - pos, "%s\n",
			parse_error_msg[p_error]);
		fprintf(cons, fmt_buf, str == NULL ? "" : str);
	} else {
		snprintf(fmt_buf + pos, sizeof (fmt_buf) - pos, "%s",
			parse_error_msg[p_error]);
		syslog(LOG_ERR, fmt_buf, str == NULL ? "" : str);
	}
}

void
report_error2(
	const char	*str1,
	const char	*str2)
{
	char	fmt_buf[1024];

	if (cons != NULL) {
		snprintf(fmt_buf, sizeof (fmt_buf),
			"%s\n",  parse_error_msg[p_error]);
		fprintf(cons, fmt_buf, str1, str2);
	} else {
		syslog(LOG_ERR, parse_error_msg[p_error], str1, str2);
	}
}

void
report_conn_error(
	conn_error	e,
	const char	*str1,
	const char	*str2)
{
	char	fmt_buf[1024];

	if (cons != NULL) {
		snprintf(fmt_buf, sizeof (fmt_buf),
			"%s\n",  conn_error_msg[e]);
		fprintf(cons, fmt_buf,
			str1 == NULL ? "" : str1,
			str2 == NULL ? "" : str2);
	} else {
		syslog(LOG_ERR,
			conn_error_msg[e],
			str1 == NULL ? "" : str1,
			str2 == NULL ? "" : str2);
	}
}

void
report_info(
	const char	*str,
	const char	*arg)
{
	if (cons != NULL) {
		fputs(str, cons);
		if (arg != NULL)
			fputs(arg, cons);
		fputs("\n", cons);
	} else
		syslog(LOG_INFO, str, arg);
}